List-Flatten-XS

 view release on metacpan or  search on metacpan

lib/List/Flatten/XS.xs  view on Meta::CPAN


    return sv_2mortal(newRV_inc((SV *)result));
}

static SV *
_flatten_per_level(pTHX_ SV *ref, IV level)
{
    AV *stack = (AV *)sv_2mortal((SV *)newAV());
    AV *result = (AV *)sv_2mortal((SV *)newAV());

    // This is to detect circular reference
    HV *memo = (HV *)sv_2mortal((SV *)newHV());

    IV i = 0;
    SV *tmp;
    AV *ary = (AV *)SvRV(ref);
    while (1) {
        while (i < av_len(ary) + 1) {
            tmp = AV_FETCH_MUST(ary, i++);
            if ((av_len(stack) + 1) / 2 >= level) {
                AV_PUSH_INC(result, tmp);
                continue;
            }

            if (IS_ARRAYREF(tmp)) {
                if (hv_exists_ent(memo, tmp, 0)) {
                    SvREFCNT_inc(stack);
                    Perl_croak(aTHX_ "tried to flatten recursive list(circular references)");
                }
                // store the pointer of array reference
                hv_store_ent(memo, tmp, &PL_sv_undef, 0);

                // push value to the stack
                av_push(stack, (SV *)ary);
                av_push(stack, sv_2mortal(newSViv(i)));
                ary = (AV *)SvRV(tmp);
                i = 0;
            } else {
                AV_PUSH_INC(result, tmp);
            }
        }

        if (av_len(stack) + 1 == 0) break;
        
        SV *idx = av_pop(stack);
        i = SvIV(idx);
        SV *poped = av_pop(stack);
        ary = (AV *)poped; // Already done SvRV(SV *)
    }

    return sv_2mortal(newRV_inc((SV *)result));
}

MODULE = List::Flatten::XS    PACKAGE = List::Flatten::XS
PROTOTYPES: DISABLE

void *
flatten(ref, svlevel = sv_2mortal(newSViv(-1)))
    SV *ref;
    SV *svlevel;
PPCODE:
{
    if (!SvROK(ref) || SvTYPE(SvRV(ref)) != SVt_PVAV)
        Perl_croak(aTHX_ "Please pass an array reference to the first argument");
    
    IV level = SvIV(svlevel);
    SV *result = (level < 0) ? _fast_flatten(aTHX_ ref)
                    : _flatten_per_level(aTHX_ ref, level);

    if (GIMME_V == G_ARRAY) {
        AV *av_result = (AV *)SvRV(result);
        IV len = av_len(av_result) + 1;
        for (IV i = 0; i < len; i++)
            ST(i) = AV_FETCH_MUST(av_result, i);
        XSRETURN(len);
    }

    ST(0) = result;
    XSRETURN(1);
}



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