Scalar-List-Utils

 view release on metacpan or  search on metacpan

ListUtil.xs  view on Meta::CPAN

    SAVESPTR(GvSV(PL_defgv));
#ifdef dMULTICALL
    assert(cv);
    if(!CvISXSUB(cv)) {
        dMULTICALL;
        I32 gimme = G_SCALAR;

        UNUSED_VAR_newsp;
        PUSH_MULTICALL(cv);

        for(index = 1 ; index < items ; index++) {
            SV *def_sv = GvSV(PL_defgv) = args[index];
#  ifdef SvTEMP_off
            SvTEMP_off(def_sv);
#  endif
            MULTICALL;
            if(SvTRUEx(*PL_stack_sp)) {
#  ifdef PERL_HAS_BAD_MULTICALL_REFCOUNT
                if(CvDEPTH(multicall_cv) > 1)
                    SvREFCNT_inc_simple_void_NN(multicall_cv);
#  endif
                POP_MULTICALL;
                ST(0) = ST(index);
                XSRETURN(1);
            }
        }
#  ifdef PERL_HAS_BAD_MULTICALL_REFCOUNT
        if(CvDEPTH(multicall_cv) > 1)
            SvREFCNT_inc_simple_void_NN(multicall_cv);
#  endif
        POP_MULTICALL;
    }
    else
#endif
    {
        for(index = 1 ; index < items ; index++) {
            dSP;
            GvSV(PL_defgv) = args[index];

            PUSHMARK(SP);
            call_sv((SV*)cv, G_SCALAR);
            if(SvTRUEx(*PL_stack_sp)) {
                ST(0) = ST(index);
                XSRETURN(1);
            }
        }
    }
    XSRETURN_UNDEF;
}


void
any(block,...)
    SV *block
ALIAS:
    none   = 0
    all    = 1
    any    = 2
    notall = 3
PROTOTYPE: &@
PPCODE:
{
    int ret_true = !(ix & 2); /* return true at end of loop for none/all; false for any/notall */
    int invert   =  (ix & 1); /* invert block test for all/notall */
    SV **args = &PL_stack_base[ax];
    CV *cv    = sv_to_cv(block,
                         ix == 0 ? "none" :
                         ix == 1 ? "all" :
                         ix == 2 ? "any" :
                         ix == 3 ? "notall" :
                         "unknown 'any' alias");

    SAVESPTR(GvSV(PL_defgv));
#ifdef dMULTICALL
    assert(cv);
    if(!CvISXSUB(cv)) {
        dMULTICALL;
        I32 gimme = G_SCALAR;
        int index;

        UNUSED_VAR_newsp;
        PUSH_MULTICALL(cv);
        for(index = 1; index < items; index++) {
            SV *def_sv = GvSV(PL_defgv) = args[index];
#  ifdef SvTEMP_off
            SvTEMP_off(def_sv);
#  endif

            MULTICALL;
            if(SvTRUEx(*PL_stack_sp) ^ invert) {
                POP_MULTICALL;
                ST(0) = ret_true ? &PL_sv_no : &PL_sv_yes;
                XSRETURN(1);
            }
        }
        POP_MULTICALL;
    }
    else
#endif
    {
        int index;
        for(index = 1; index < items; index++) {
            dSP;
            GvSV(PL_defgv) = args[index];

            PUSHMARK(SP);
            call_sv((SV*)cv, G_SCALAR);
            if(SvTRUEx(*PL_stack_sp) ^ invert) {
                ST(0) = ret_true ? &PL_sv_no : &PL_sv_yes;
                XSRETURN(1);
            }
        }
    }

    ST(0) = ret_true ? &PL_sv_yes : &PL_sv_no;
    XSRETURN(1);
}

void
head(size,...)
PROTOTYPE: $@
ALIAS:
    head = 0
    tail = 1
PPCODE:
{
    int size = 0;
    int start = 0;
    int end = 0;
    int i = 0;

    size = SvIV( ST(0) );

    if ( ix == 0 ) {
        start = 1;
        end = start + size;
        if ( size < 0 ) {
            end += items - 1;
        }
        if ( end > items ) {
            end = items;
        }
    }
    else {
        end = items;
        if ( size < 0 ) {
            start = -size + 1;
        }
        else {
            start = end - size;
        }
        if ( start < 1 ) {
            start = 1;
        }
    }

    if ( end <= start ) {
        XSRETURN(0);
    }
    else {
        EXTEND( SP, end - start );
        for ( i = start; i < end; i++ ) {
            PUSHs( sv_2mortal( newSVsv( ST(i) ) ) );
        }
        XSRETURN( end - start );
    }
}

void
pairs(...)
PROTOTYPE: @
PPCODE:
{
    int argi = 0;
    int reti = 0;
    HV *pairstash = get_hv("List::Util::_Pair::", GV_ADD);

    if(items % 2 && ckWARN(WARN_MISC))
        warn("Odd number of elements in pairs");

    {
        for(; argi < items; argi += 2) {
            SV *a = ST(argi);
            SV *b = argi < items-1 ? ST(argi+1) : &PL_sv_undef;

            AV *av = newAV();
            av_push(av, newSVsv(a));
            av_push(av, newSVsv(b));

            ST(reti) = sv_2mortal(newRV_noinc((SV *)av));
            sv_bless(ST(reti), pairstash);
            reti++;
        }
    }

    XSRETURN(reti);
}

void
unpairs(...)
PROTOTYPE: @
PPCODE:
{
    /* Unlike pairs(), we're going to trash the input values on the stack
     * almost as soon as we start generating output. So clone them first
     */
    int i;
    SV **args_copy;
    Newx(args_copy, items, SV *);
    SAVEFREEPV(args_copy);

    Copy(&ST(0), args_copy, items, SV *);

    for(i = 0; i < items; i++) {
        SV *pair = args_copy[i];
        AV *pairav;

        SvGETMAGIC(pair);

        if(SvTYPE(pair) != SVt_RV)
            croak("Not a reference at List::Util::unpairs() argument %d", i);
        if(SvTYPE(SvRV(pair)) != SVt_PVAV)
            croak("Not an ARRAY reference at List::Util::unpairs() argument %d", i);

        /* TODO: assert pair is an ARRAY ref */
        pairav = (AV *)SvRV(pair);

        EXTEND(SP, 2);

        if(AvFILL(pairav) >= 0)
            mPUSHs(newSVsv(AvARRAY(pairav)[0]));
        else
            PUSHs(&PL_sv_undef);

        if(AvFILL(pairav) >= 1)
            mPUSHs(newSVsv(AvARRAY(pairav)[1]));
        else
            PUSHs(&PL_sv_undef);
    }

    XSRETURN(items * 2);
}

void
pairkeys(...)
PROTOTYPE: @
PPCODE:
{
    int argi = 0;
    int reti = 0;

    if(items % 2 && ckWARN(WARN_MISC))
        warn("Odd number of elements in pairkeys");

    {
        for(; argi < items; argi += 2) {
            SV *a = ST(argi);

            ST(reti++) = sv_2mortal(newSVsv(a));
        }
    }

    XSRETURN(reti);
}

void
pairvalues(...)
PROTOTYPE: @
PPCODE:
{
    int argi = 0;
    int reti = 0;

    if(items % 2 && ckWARN(WARN_MISC))
        warn("Odd number of elements in pairvalues");

    {
        for(; argi < items; argi += 2) {
            SV *b = argi < items-1 ? ST(argi+1) : &PL_sv_undef;

            ST(reti++) = sv_2mortal(newSVsv(b));
        }
    }

    XSRETURN(reti);
}

void
pairfirst(block,...)
    SV *block
PROTOTYPE: &@
PPCODE:
{
    GV *agv,*bgv;
    CV *cv = sv_to_cv(block, "pairfirst");
    I32 ret_gimme = GIMME_V;
    int argi = 1; /* "shift" the block */

    if(!(items % 2) && ckWARN(WARN_MISC))
        warn("Odd number of elements in pairfirst");

    agv = gv_fetchpv("a", GV_ADD, SVt_PV);
    bgv = gv_fetchpv("b", GV_ADD, SVt_PV);
    SAVESPTR(GvSV(agv));
    SAVESPTR(GvSV(bgv));
#ifdef dMULTICALL
    assert(cv);
    if(!CvISXSUB(cv)) {
        /* Since MULTICALL is about to move it */
        SV **stack = PL_stack_base + ax;

        dMULTICALL;
        I32 gimme = G_SCALAR;

        UNUSED_VAR_newsp;
        PUSH_MULTICALL(cv);
        for(; argi < items; argi += 2) {
            SV *a = GvSV(agv) = stack[argi];
            SV *b = GvSV(bgv) = argi < items-1 ? stack[argi+1] : &PL_sv_undef;

            MULTICALL;

            if(!SvTRUEx(*PL_stack_sp))
                continue;

            POP_MULTICALL;
            if(ret_gimme == G_LIST) {
                ST(0) = sv_mortalcopy(a);
                ST(1) = sv_mortalcopy(b);
                XSRETURN(2);
            }
            else
                XSRETURN_YES;
        }
        POP_MULTICALL;
        XSRETURN(0);
    }
    else
#endif
    {
        for(; argi < items; argi += 2) {
            dSP;
            SV *a = GvSV(agv) = ST(argi);
            SV *b = GvSV(bgv) = argi < items-1 ? ST(argi+1) : &PL_sv_undef;

            PUSHMARK(SP);
            call_sv((SV*)cv, G_SCALAR);

            SPAGAIN;

            if(!SvTRUEx(*PL_stack_sp))
                continue;

            if(ret_gimme == G_LIST) {
                ST(0) = sv_mortalcopy(a);
                ST(1) = sv_mortalcopy(b);
                XSRETURN(2);
            }
            else
                XSRETURN_YES;
        }
    }

    XSRETURN(0);
}

void
pairgrep(block,...)
    SV *block
PROTOTYPE: &@
PPCODE:
{
    GV *agv,*bgv;
    CV *cv = sv_to_cv(block, "pairgrep");
    I32 ret_gimme = GIMME_V;

    /* This function never returns more than it consumed in arguments. So we
     * can build the results "live", behind the arguments
     */
    int argi = 1; /* "shift" the block */
    int reti = 0;

    if(!(items % 2) && ckWARN(WARN_MISC))
        warn("Odd number of elements in pairgrep");

    agv = gv_fetchpv("a", GV_ADD, SVt_PV);
    bgv = gv_fetchpv("b", GV_ADD, SVt_PV);
    SAVESPTR(GvSV(agv));
    SAVESPTR(GvSV(bgv));
#ifdef dMULTICALL
    assert(cv);
    if(!CvISXSUB(cv)) {
        /* Since MULTICALL is about to move it */
        SV **stack = PL_stack_base + ax;
        int i;

        dMULTICALL;
        I32 gimme = G_SCALAR;

        UNUSED_VAR_newsp;
        PUSH_MULTICALL(cv);
        for(; argi < items; argi += 2) {
            SV *a = GvSV(agv) = stack[argi];
            SV *b = GvSV(bgv) = argi < items-1 ? stack[argi+1] : &PL_sv_undef;

            MULTICALL;

            if(SvTRUEx(*PL_stack_sp)) {
                if(ret_gimme == G_LIST) {
                    /* We can't mortalise yet or they'd be mortal too early */
                    stack[reti++] = newSVsv(a);
                    stack[reti++] = newSVsv(b);
                }
                else if(ret_gimme == G_SCALAR)
                    reti++;
            }
        }
        POP_MULTICALL;

        if(ret_gimme == G_LIST)
            for(i = 0; i < reti; i++)
                sv_2mortal(stack[i]);
    }
    else
#endif
    {
        for(; argi < items; argi += 2) {
            dSP;
            SV *a = GvSV(agv) = ST(argi);
            SV *b = GvSV(bgv) = argi < items-1 ? ST(argi+1) : &PL_sv_undef;

            PUSHMARK(SP);
            call_sv((SV*)cv, G_SCALAR);

            SPAGAIN;

            if(SvTRUEx(*PL_stack_sp)) {
                if(ret_gimme == G_LIST) {
                    ST(reti++) = sv_mortalcopy(a);
                    ST(reti++) = sv_mortalcopy(b);
                }
                else if(ret_gimme == G_SCALAR)
                    reti++;
            }
        }
    }

    if(ret_gimme == G_LIST)
        XSRETURN(reti);
    else if(ret_gimme == G_SCALAR) {
        ST(0) = newSViv(reti);
        XSRETURN(1);
    }
}

void
pairmap(block,...)
    SV *block
PROTOTYPE: &@
PPCODE:
{
    GV *agv,*bgv;
    CV *cv = sv_to_cv(block, "pairmap");
    SV **args_copy = NULL;
    I32 ret_gimme = GIMME_V;

    int argi = 1; /* "shift" the block */
    int reti = 0;

    if(!(items % 2) && ckWARN(WARN_MISC))
        warn("Odd number of elements in pairmap");

    agv = gv_fetchpv("a", GV_ADD, SVt_PV);
    bgv = gv_fetchpv("b", GV_ADD, SVt_PV);
    SAVESPTR(GvSV(agv));
    SAVESPTR(GvSV(bgv));
/* This MULTICALL-based code appears to fail on perl 5.10.0 and 5.8.9
 * Skip it on those versions (RT#87857)
 */
#if defined(dMULTICALL) && (PERL_VERSION_GE(5,10,1) || PERL_VERSION_LE(5,8,8))
    assert(cv);
    if(!CvISXSUB(cv)) {
        /* Since MULTICALL is about to move it */
        SV **stack = PL_stack_base + ax;
        I32 ret_gimme = GIMME_V;
        int i;
        AV *spill = NULL; /* accumulates results if too big for stack */

        dMULTICALL;
        I32 gimme = G_LIST;

        UNUSED_VAR_newsp;
        PUSH_MULTICALL(cv);
        for(; argi < items; argi += 2) {
            int count;

            GvSV(agv) = stack[argi];
            GvSV(bgv) = argi < items-1 ? stack[argi+1]: &PL_sv_undef;

            MULTICALL;
            count = PL_stack_sp - PL_stack_base;

            if (count > 2 || spill) {
                /* We can't return more than 2 results for a given input pair
                 * without trashing the remaining arguments on the stack still
                 * to be processed, or possibly overrunning the stack end.
                 * So, we'll accumulate the results in a temporary buffer
                 * instead.
                 * We didn't do this initially because in the common case, most
                 * code blocks will return only 1 or 2 items so it won't be
                 * necessary
                 */
                int fill;

                if (!spill) {
                    spill = newAV();
                    AvREAL_off(spill); /* don't ref count its contents */
                    /* can't mortalize here as every nextstate in the code
                     * block frees temps */
                    SAVEFREESV(spill);

ListUtil.xs  view on Meta::CPAN

                if( !((iv * sign) & (~valid_bits)) ) {
                    /* Avoid altering arg's flags */
                    nv_arg = uok ? (NV)SvUV(arg) : (NV)SvIV(arg);
                    sv_setpvn(keysv, (char *) &nv_arg, 8);
                }
                else {
                    /* Read in the bytes, rather than the numeric value of the IV/UV as  *
                     * this is more efficient, despite having to sv_catpvn an extra byte.*/
                    sv_setpvn(keysv, (char *) &iv, 8);
                    /* We add an extra byte to distinguish between an IV/UV and an NV.   *
                     * We also use that byte to distinguish between a -ve IV and a UV.   */
                    if(uok) sv_catpvn(keysv, "U", 1);
                    else    sv_catpvn(keysv, "I", 1);
                }
            }
        }
        else {
            nv_arg = SvNV(arg);

            /* for NaN, use the platform's normal stringification */
            if (nv_arg != nv_arg) sv_setpvf(keysv, "%" NVgf, nv_arg);

            /* use "0" for all zeros */
            else if(nv_arg == 0) sv_setpvs(keysv, "0");
            else sv_setpvn(keysv, (char *) &nv_arg, 8);
        }
#endif
#ifdef HV_FETCH_EMPTY_HE
        he = (HE*) hv_common(seen, NULL, SvPVX(keysv), SvCUR(keysv), 0, HV_FETCH_LVALUE | HV_FETCH_EMPTY_HE, NULL, 0);
        if (HeVAL(he))
            continue;

        HeVAL(he) = &PL_sv_undef;
#else
        if(hv_exists(seen, SvPVX(keysv), SvCUR(keysv)))
            continue;

        hv_store(seen, SvPVX(keysv), SvCUR(keysv), &PL_sv_yes, 0);
#endif

        if(GIMME_V == G_LIST)
            ST(retcount) = SvOK(arg) ? arg : sv_2mortal(newSViv(0));
        retcount++;
    }

  finish:
    if(GIMME_V == G_LIST)
        XSRETURN(retcount);
    else
        ST(0) = sv_2mortal(newSViv(retcount));
}

void
zip(...)
ALIAS:
    zip_longest   = ZIP_LONGEST
    zip_shortest  = ZIP_SHORTEST
    mesh          = ZIP_MESH
    mesh_longest  = ZIP_MESH_LONGEST
    mesh_shortest = ZIP_MESH_SHORTEST
PPCODE:
    Size_t nlists = items; /* number of lists */
    AV **lists;         /* inbound lists */
    Size_t len = 0;        /* length of longest inbound list = length of result */
    Size_t i;
    bool is_mesh = (ix & ZIP_MESH);
    ix &= ~ZIP_MESH;

    if(!nlists)
        XSRETURN(0);

    Newx(lists, nlists, AV *);
    SAVEFREEPV(lists);

    /* TODO: This may or maynot work on objects with arrayification overload */
    /* Remember to unit test it */

    for(i = 0; i < nlists; i++) {
        SV *arg = ST(i);
        AV *av;

        if(!SvROK(arg) || SvTYPE(SvRV(arg)) != SVt_PVAV)
            croak("Expected an ARRAY reference to %s",
                is_mesh ? "mesh" : "zip");
        av = lists[i] = (AV *)SvRV(arg);

        if(!i) {
            len = av_count(av);
            continue;
        }

        switch(ix) {
            case 0: /* zip is alias to zip_longest */
            case ZIP_LONGEST:
                if(av_count(av) > len)
                    len = av_count(av);
                break;

            case ZIP_SHORTEST:
                if(av_count(av) < len)
                    len = av_count(av);
                break;
        }
    }

    if(is_mesh) {
        SSize_t retcount = (SSize_t)(len * nlists);

        EXTEND(SP, retcount);

        for(i = 0; i < len; i++) {
            Size_t listi;

            for(listi = 0; listi < nlists; listi++) {
                SV *item = (i < av_count(lists[listi])) ?
                    AvARRAY(lists[listi])[i] :
                    &PL_sv_undef;

                mPUSHs(newSVsv(item));
            }
        }

ListUtil.xs  view on Meta::CPAN

SV *
looks_like_number(sv)
    SV *sv
PROTOTYPE: $
CODE:
    SV *tempsv;
    SvGETMAGIC(sv);
    if(SvAMAGIC(sv) && (tempsv = AMG_CALLun(sv, numer))) {
        sv = tempsv;
    }
#if !PERL_VERSION_GE(5,8,5)
    if(SvPOK(sv) || SvPOKp(sv)) {
        RETVAL = looks_like_number(sv) ? &PL_sv_yes : &PL_sv_no;
    }
    else {
        RETVAL = (SvFLAGS(sv) & (SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK)) ? &PL_sv_yes : &PL_sv_no;
    }
#else
    RETVAL = looks_like_number(sv) ? &PL_sv_yes : &PL_sv_no;
#endif
OUTPUT:
    RETVAL

void
openhandle(SV *sv)
PROTOTYPE: $
CODE:
{
    IO *io = NULL;
    SvGETMAGIC(sv);
    if(SvROK(sv)){
        /* deref first */
        sv = SvRV(sv);
    }

    /* must be GLOB or IO */
    if(isGV(sv)){
        io = GvIO((GV*)sv);
    }
    else if(SvTYPE(sv) == SVt_PVIO){
        io = (IO*)sv;
    }

    if(io){
        /* real or tied filehandle? */
        if(IoIFP(io) || SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar)){
            XSRETURN(1);
        }
    }
    XSRETURN_UNDEF;
}

MODULE=List::Util       PACKAGE=Sub::Util

void
set_prototype(proto, code)
    SV *proto
    SV *code
PREINIT:
    SV *cv; /* not CV * */
PPCODE:
    SvGETMAGIC(code);
    if(!SvROK(code))
        croak("set_prototype: not a reference");

    cv = SvRV(code);
    if(SvTYPE(cv) != SVt_PVCV)
        croak("set_prototype: not a subroutine reference");

    if(SvPOK(proto)) {
        /* set the prototype */
        sv_copypv(cv, proto);
    }
    else {
        /* delete the prototype */
        SvPOK_off(cv);
    }

    PUSHs(code);
    XSRETURN(1);

void
set_subname(name, sub)
    SV *name
    SV *sub
PREINIT:
    CV *cv = NULL;
    GV *gv;
    HV *stash = CopSTASH(PL_curcop);
    const char *s, *end = NULL, *begin = NULL;
    MAGIC *mg;
    STRLEN namelen;
    const char* nameptr = SvPV(name, namelen);
    int utf8flag = SvUTF8(name);
#if PERL_VERSION_LT(5, 41, 3) || PERL_VERSION_GT(5, 41, 5)
    int quotes_seen = 0;
    bool need_subst = FALSE;
#endif
PPCODE:
    if (!SvROK(sub) && SvGMAGICAL(sub))
        mg_get(sub);
    if (SvROK(sub))
        cv = (CV *) SvRV(sub);
    else if (SvTYPE(sub) == SVt_PVGV)
        cv = GvCVu(sub);
    else if (!SvOK(sub))
        croak(PL_no_usym, "a subroutine");
    else if (PL_op->op_private & HINT_STRICT_REFS)
        croak("Can't use string (\"%.32s\") as %s ref while \"strict refs\" in use",
              SvPV_nolen(sub), "a subroutine");
    else if ((gv = gv_fetchsv(sub, FALSE, SVt_PVCV)))
        cv = GvCVu(gv);
    if (!cv)
        croak("Undefined subroutine %s", SvPV_nolen(sub));
    if (SvTYPE(cv) != SVt_PVCV && SvTYPE(cv) != SVt_PVFM)
        croak("Not a subroutine reference");
    for (s = nameptr; s <= nameptr + namelen; s++) {
        if (s > nameptr && *s == ':' && s[-1] == ':') {
            end = s - 1;
            begin = ++s;
#if PERL_VERSION_LT(5, 41, 3) || PERL_VERSION_GT(5, 41, 5)
            if (quotes_seen)
                need_subst = TRUE;
#endif
        }
#if PERL_VERSION_LT(5, 41, 3) || PERL_VERSION_GT(5, 41, 5)
        else if (s > nameptr && *s != '\0' && s[-1] == '\'') {
            end = s - 1;
            begin = s;
            if (quotes_seen++)
                need_subst = TRUE;
        }
#endif
    }
    s--;
    if (end) {
#if PERL_VERSION_LT(5, 41, 3) || PERL_VERSION_GT(5, 41, 5)
        SV* tmp;
        if (need_subst) {
            STRLEN length = end - nameptr + quotes_seen - (*end == '\'' ? 1 : 0);
            char* left;
            int i, j;
            tmp = sv_2mortal(newSV(length));
            left = SvPVX(tmp);
            for (i = 0, j = 0; j < end - nameptr; ++i, ++j) {
                if (nameptr[j] == '\'') {
                    left[i] = ':';
                    left[++i] = ':';
                }
                else {
                    left[i] = nameptr[j];
                }
            }
            stash = gv_stashpvn(left, length, GV_ADD | utf8flag);
        }
        else
#endif
            stash = gv_stashpvn(nameptr, end - nameptr, GV_ADD | utf8flag);
        nameptr = begin;

ListUtil.xs  view on Meta::CPAN


        if (oldhv) {
            SV* old_full_name = sv_2mortal(newSVpvn_flags(HvNAME(oldhv), HvNAMELEN_get(oldhv), HvNAMEUTF8(oldhv) ? SVf_UTF8 : 0));
            sv_catpvn(old_full_name, "::", 2);
            sv_catpvn_flags(old_full_name, GvNAME(oldgv), GvNAMELEN(oldgv), GvNAMEUTF8(oldgv) ? SV_CATUTF8 : SV_CATBYTES);

            old_data = hv_fetch_ent(DBsub, old_full_name, 0, 0);
        }

        if (old_data && HeVAL(old_data)) {
            SV* old_val = HeVAL(old_data);
            SV* new_full_name = sv_2mortal(newSVpvn_flags(HvNAME(stash), HvNAMELEN_get(stash), HvNAMEUTF8(stash) ? SVf_UTF8 : 0));
            sv_catpvn(new_full_name, "::", 2);
            sv_catpvn_flags(new_full_name, nameptr, s - nameptr, utf8flag ? SV_CATUTF8 : SV_CATBYTES);
            SvREFCNT_inc(old_val);
            if (!hv_store_ent(DBsub, new_full_name, old_val, 0))
                SvREFCNT_dec(old_val);
        }
    }

    gv = (GV *) newSV(0);
    gv_init_pvn(gv, stash, nameptr, s - nameptr, GV_ADDMULTI | utf8flag);

    /*
     * set_subname needs to create a GV to store the name. The CvGV field of a
     * CV is not refcounted, so perl wouldn't know to SvREFCNT_dec() this GV if
     * it destroys the containing CV. We use a MAGIC with an empty vtable
     * simply for the side-effect of using MGf_REFCOUNTED to store the
     * actually-counted reference to the GV.
     */
    mg = SvMAGIC(cv);
    while (mg && mg->mg_virtual != &subname_vtbl)
        mg = mg->mg_moremagic;
    if (!mg) {
        Newxz(mg, 1, MAGIC);
        mg->mg_moremagic = SvMAGIC(cv);
        mg->mg_type = PERL_MAGIC_ext;
        mg->mg_virtual = &subname_vtbl;
        SvMAGIC_set(cv, mg);
    }
    if (mg->mg_flags & MGf_REFCOUNTED)
        SvREFCNT_dec(mg->mg_obj);
    mg->mg_flags |= MGf_REFCOUNTED;
    mg->mg_obj = (SV *) gv;
    SvRMAGICAL_on(cv);
    CvANON_off(cv);
#ifndef CvGV_set
    CvGV(cv) = gv;
#else
    CvGV_set(cv, gv);
#endif
    PUSHs(sub);

void
subname(code)
    SV *code
PREINIT:
    CV *cv;
    GV *gv;
    const char *stashname;
PPCODE:
    if (!SvROK(code) && SvGMAGICAL(code))
        mg_get(code);

    if(!SvROK(code) || SvTYPE(cv = (CV *)SvRV(code)) != SVt_PVCV)
        croak("Not a subroutine reference");

    if(!(gv = CvGV(cv)))
        XSRETURN(0);

    if(GvSTASH(gv))
        stashname = HvNAME(GvSTASH(gv));
    else
        stashname = "__ANON__";

    mPUSHs(newSVpvf("%s::%s", stashname, GvNAME(gv)));
    XSRETURN(1);

BOOT:
{
    HV *lu_stash = gv_stashpvn("List::Util", 10, TRUE);
    GV *rmcgv = *(GV**)hv_fetch(lu_stash, "REAL_MULTICALL", 14, TRUE);
    SV *rmcsv;
    if(SvTYPE(rmcgv) != SVt_PVGV)
        gv_init(rmcgv, lu_stash, "List::Util", 10, TRUE);
    rmcsv = GvSVn(rmcgv);
#ifdef REAL_MULTICALL
    sv_setsv(rmcsv, &PL_sv_yes);
#else
    sv_setsv(rmcsv, &PL_sv_no);
#endif
}



( run in 0.922 second using v1.01-cache-2.11-cpan-5511b514fd6 )