Ancient

 view release on metacpan or  search on metacpan

xs/file/file.c  view on Meta::CPAN

    /* not_empty / is_not_empty */
    Newxz(cb, 1, FileLineCallback);
    cb->predicate = pred_is_not_empty;
    cb->perl_callback = NULL;
    sv = newSViv(PTR2IV(cb));
    hv_store(g_file_callback_registry, "not_empty", 9, sv, 0);
    hv_store(g_file_callback_registry, "is_not_empty", 12, SvREFCNT_inc(sv), 0);

    /* comment / is_comment */
    Newxz(cb, 1, FileLineCallback);
    cb->predicate = pred_is_comment;
    cb->perl_callback = NULL;
    sv = newSViv(PTR2IV(cb));
    hv_store(g_file_callback_registry, "comment", 7, sv, 0);
    hv_store(g_file_callback_registry, "is_comment", 10, SvREFCNT_inc(sv), 0);

    /* not_comment / is_not_comment */
    Newxz(cb, 1, FileLineCallback);
    cb->predicate = pred_is_not_comment;
    cb->perl_callback = NULL;
    sv = newSViv(PTR2IV(cb));
    hv_store(g_file_callback_registry, "not_comment", 11, sv, 0);
    hv_store(g_file_callback_registry, "is_not_comment", 14, SvREFCNT_inc(sv), 0);
}

static FileLineCallback* file_get_callback(pTHX_ const char *name) {
    SV **svp;
    if (!g_file_callback_registry) return NULL;
    svp = hv_fetch(g_file_callback_registry, name, strlen(name), 0);
    if (svp && SvIOK(*svp)) {
        return INT2PTR(FileLineCallback*, SvIVX(*svp));
    }
    return NULL;
}

/* Process lines with callback - MULTICALL optimized */
static XS(xs_each_line) {
    dXSARGS;
    const char *path;
    SV *callback;
    IV idx;
    SV *line;
    CV *block_cv;

    if (items != 2) croak("Usage: file::each_line(path, callback)");

    path = SvPV_nolen(ST(0));
    callback = ST(1);

    if (!SvROK(callback) || SvTYPE(SvRV(callback)) != SVt_PVCV) {
        croak("Second argument must be a code reference");
    }

    block_cv = (CV*)SvRV(callback);
    idx = file_lines_open(aTHX_ path);
    if (idx < 0) {
        XSRETURN_EMPTY;
    }

    /* Process each line with the callback */
    /* Set both $_ and pass as argument so callbacks can use either style */
    {
        SV *old_defsv = DEFSV;
        SAVESPTR(DEFSV);  /* Automatically restore $_ on scope exit */

        while ((line = file_lines_next(aTHX_ idx)) != &PL_sv_undef) {
            dSP;
            ENTER;
            SAVETMPS;
            DEFSV_set(line);  /* Set $_ */
            PUSHMARK(SP);
            XPUSHs(line);  /* Don't mortalise - line is freed by file_lines_close or DEFSV restore */
            PUTBACK;
            call_sv(callback, G_DISCARD);
            FREETMPS;
            LEAVE;
            SvREFCNT_dec(line);  /* Release our reference after callback completes */
        }
    }

    file_lines_close(idx);
    XSRETURN_EMPTY;
}

/* Grep lines with callback or registered predicate name */
static XS(xs_grep_lines) {
    dXSARGS;
    const char *path;
    SV *predicate;
    IV idx;
    SV *line;
    AV *result;
    CV *block_cv = NULL;
    FileLineCallback *fcb = NULL;

    if (items != 2) croak("Usage: file::grep_lines(path, &predicate or $name)");

    path = SvPV_nolen(ST(0));
    predicate = ST(1);
    result = newAV();

    /* Check if predicate is a name or coderef */
    if (SvROK(predicate) && SvTYPE(SvRV(predicate)) == SVt_PVCV) {
        block_cv = (CV*)SvRV(predicate);
    } else {
        const char *name = SvPV_nolen(predicate);
        fcb = file_get_callback(aTHX_ name);
        if (!fcb) {
            croak("file::grep_lines: unknown predicate '%s'", name);
        }
    }

    idx = file_lines_open(aTHX_ path);
    if (idx < 0) {
        ST(0) = sv_2mortal(newRV_noinc((SV*)result));
        XSRETURN(1);
    }

    /* C predicate path - fastest */
    if (fcb && fcb->predicate) {
        while ((line = file_lines_next(aTHX_ idx)) != &PL_sv_undef) {

xs/file/file.c  view on Meta::CPAN

            count = call_sv(callback, G_SCALAR);
            SPAGAIN;
            if (count > 0) {
                result_sv = POPs;
                av_push(result, SvREFCNT_inc(result_sv));
            }
            PUTBACK;
        }
        DEFSV_set(old_defsv);
    }

    file_lines_close(idx);
    ST(0) = sv_2mortal(newRV_noinc((SV*)result));
    XSRETURN(1);
}

/* Register a Perl callback */
static XS(xs_register_line_callback) {
    dXSARGS;
    const char *name;
    STRLEN name_len;
    SV *coderef;
    FileLineCallback *cb;
    SV *sv;

    if (items != 2) croak("Usage: file::register_line_callback($name, \\&coderef)");

    name = SvPV(ST(0), name_len);
    coderef = ST(1);

    if (!SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) {
        croak("file::register_line_callback: second argument must be a coderef");
    }

    file_init_callback_registry(aTHX);

    /* If already registered, just update the perl_callback in place */
    {
        FileLineCallback *existing = file_get_callback(aTHX_ name);
        if (existing) {
            /* Update existing - free old perl_callback and set new one */
            if (existing->perl_callback) {
                SvREFCNT_dec(existing->perl_callback);
            }
            existing->perl_callback = newSVsv(coderef);
            existing->predicate = NULL;  /* Clear any C predicate */
            XSRETURN_YES;
        }
    }

    Newxz(cb, 1, FileLineCallback);
    cb->predicate = NULL;  /* No C function */
    cb->perl_callback = newSVsv(coderef);

    sv = newSViv(PTR2IV(cb));
    hv_store(g_file_callback_registry, name, name_len, sv, 0);

    XSRETURN_YES;
}

/* List registered callbacks */
static XS(xs_list_line_callbacks) {
    dXSARGS;
    AV *result;
    HE *entry;

    PERL_UNUSED_VAR(items);

    result = newAV();
    if (g_file_callback_registry) {
        hv_iterinit(g_file_callback_registry);
        while ((entry = hv_iternext(g_file_callback_registry))) {
            av_push(result, newSVsv(hv_iterkeysv(entry)));
        }
    }

    ST(0) = sv_2mortal(newRV_noinc((SV*)result));
    XSRETURN(1);
}

/* ============================================
   Hook registration XS functions
   ============================================ */

/* Register a Perl read hook */
static XS(xs_register_read_hook) {
    dXSARGS;
    SV *coderef;
    FileHookEntry *entry;

    if (items != 1) croak("Usage: file::register_read_hook(\\&coderef)");

    coderef = ST(0);
    if (!SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) {
        croak("file::register_read_hook: argument must be a coderef");
    }

    /* Use the hook list for Perl callbacks */
    Newxz(entry, 1, FileHookEntry);
    entry->name = "perl_read_hook";
    entry->c_func = NULL;
    entry->perl_callback = newSVsv(coderef);
    entry->priority = FILE_HOOK_PRIORITY_NORMAL;
    entry->user_data = NULL;
    entry->next = g_file_hooks[FILE_HOOK_PHASE_READ];
    g_file_hooks[FILE_HOOK_PHASE_READ] = entry;

    XSRETURN_YES;
}

/* Register a Perl write hook */
static XS(xs_register_write_hook) {
    dXSARGS;
    SV *coderef;
    FileHookEntry *entry;

    if (items != 1) croak("Usage: file::register_write_hook(\\&coderef)");

    coderef = ST(0);
    if (!SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) {
        croak("file::register_write_hook: argument must be a coderef");
    }

    /* Use the hook list for Perl callbacks */
    Newxz(entry, 1, FileHookEntry);
    entry->name = "perl_write_hook";
    entry->c_func = NULL;
    entry->perl_callback = newSVsv(coderef);
    entry->priority = FILE_HOOK_PRIORITY_NORMAL;
    entry->user_data = NULL;
    entry->next = g_file_hooks[FILE_HOOK_PHASE_WRITE];
    g_file_hooks[FILE_HOOK_PHASE_WRITE] = entry;

    XSRETURN_YES;
}

/* Clear all hooks for a phase */
static XS(xs_clear_hooks) {
    dXSARGS;
    const char *phase_name;
    FileHookPhase phase;
    FileHookEntry *entry, *next;

    if (items != 1) croak("Usage: file::clear_hooks($phase)");

    phase_name = SvPV_nolen(ST(0));

    if (strcmp(phase_name, "read") == 0) {
        phase = FILE_HOOK_PHASE_READ;
        g_file_read_hook = NULL;
        g_file_read_hook_data = NULL;
    } else if (strcmp(phase_name, "write") == 0) {
        phase = FILE_HOOK_PHASE_WRITE;
        g_file_write_hook = NULL;
        g_file_write_hook_data = NULL;
    } else if (strcmp(phase_name, "open") == 0) {
        phase = FILE_HOOK_PHASE_OPEN;
    } else if (strcmp(phase_name, "close") == 0) {
        phase = FILE_HOOK_PHASE_CLOSE;
    } else {
        croak("file::clear_hooks: unknown phase '%s' (use read, write, open, close)", phase_name);
    }

    /* Free hook list */
    entry = g_file_hooks[phase];
    while (entry) {
        next = entry->next;
        if (entry->perl_callback) {
            SvREFCNT_dec(entry->perl_callback);
        }
        Safefree(entry);
        entry = next;
    }
    g_file_hooks[phase] = NULL;

    XSRETURN_YES;
}

/* Check if hooks are registered for a phase */
static XS(xs_has_hooks) {
    dXSARGS;
    const char *phase_name;
    FileHookPhase phase;
    int has;

xs/file/file.c  view on Meta::CPAN

        cv_set_call_checker(cv, file_call_checker_1arg, ckobj);

        cv = newXS("file::dirname", xs_dirname, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_dirname));
        cv_set_call_checker(cv, file_call_checker_1arg, ckobj);

        cv = newXS("file::extname", xs_extname, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_extname));
        cv_set_call_checker(cv, file_call_checker_1arg, ckobj);

        cv = newXS("file::slurp", xs_slurp, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_slurp));
        cv_set_call_checker(cv, file_call_checker_1arg, ckobj);

        cv = newXS("file::slurp_raw", xs_slurp_raw, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_slurp_raw));
        cv_set_call_checker(cv, file_call_checker_1arg, ckobj);

        cv = newXS("file::lines", xs_lines, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_lines));
        cv_set_call_checker(cv, file_call_checker_1arg, ckobj);

        cv = newXS("file::readdir", xs_readdir, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_readdir));
        cv_set_call_checker(cv, file_call_checker_1arg, ckobj);

        /* 2-arg functions with call checker */
        cv = newXS("file::spew", xs_spew, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_spew));
        cv_set_call_checker(cv, file_call_checker_2arg, ckobj);

        cv = newXS("file::append", xs_append, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_append));
        cv_set_call_checker(cv, file_call_checker_2arg, ckobj);

        cv = newXS("file::copy", xs_copy, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_copy));
        cv_set_call_checker(cv, file_call_checker_2arg, ckobj);

        cv = newXS("file::move", xs_move, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_move));
        cv_set_call_checker(cv, file_call_checker_2arg, ckobj);

        cv = newXS("file::chmod", xs_chmod, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_chmod));
        cv_set_call_checker(cv, file_call_checker_2arg, ckobj);

        cv = newXS("file::atomic_spew", xs_atomic_spew, __FILE__);
        ckobj = newSViv(PTR2IV(pp_file_atomic_spew));
        cv_set_call_checker(cv, file_call_checker_2arg, ckobj);
    }

    /* Functions without custom op optimization */
    newXS("file::join", xs_join, __FILE__);
    newXS("file::each_line", xs_each_line, __FILE__);
    newXS("file::grep_lines", xs_grep_lines, __FILE__);
    newXS("file::count_lines", xs_count_lines, __FILE__);
    newXS("file::find_line", xs_find_line, __FILE__);
    newXS("file::map_lines", xs_map_lines, __FILE__);
    newXS("file::register_line_callback", xs_register_line_callback, __FILE__);
    newXS("file::list_line_callbacks", xs_list_line_callbacks, __FILE__);

    /* File hooks */
    newXS("file::register_read_hook", xs_register_read_hook, __FILE__);
    newXS("file::register_write_hook", xs_register_write_hook, __FILE__);
    newXS("file::clear_hooks", xs_clear_hooks, __FILE__);
    newXS("file::has_hooks", xs_has_hooks, __FILE__);

    /* Head and tail */
    newXS("file::head", xs_head, __FILE__);
    newXS("file::tail", xs_tail, __FILE__);

    /* Import function */
    newXS("file::import", XS_file_import, __FILE__);

    /* Memory-mapped files */
    newXS("file::mmap_open", xs_mmap_open, __FILE__);
    newXS("file::mmap::data", xs_mmap_data, __FILE__);
    newXS("file::mmap::sync", xs_mmap_sync, __FILE__);
    newXS("file::mmap::close", xs_mmap_close, __FILE__);
    newXS("file::mmap::DESTROY", xs_mmap_DESTROY, __FILE__);

    /* Line iterators */
    newXS("file::lines_iter", xs_lines_iter, __FILE__);
    newXS("file::lines::next", xs_lines_iter_next, __FILE__);
    newXS("file::lines::eof", xs_lines_iter_eof, __FILE__);
    newXS("file::lines::close", xs_lines_iter_close, __FILE__);
    newXS("file::lines::DESTROY", xs_lines_iter_DESTROY, __FILE__);

    /* Register cleanup for global destruction */
    Perl_call_atexit(aTHX_ file_cleanup_callback_registry, NULL);

    Perl_xs_boot_epilog(aTHX_ ax);
}



( run in 0.943 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )