Sereal-Path

 view release on metacpan or  search on metacpan

Tie/Tie.xs  view on Meta::CPAN

        tied->count = count;
        result = sv_2mortal(newRV_noinc((SV*) newAV()));
        how = PERL_MAGIC_tied;
    } else if ((type & SRL_ITERATOR_INFO_REF_TO) == SRL_ITERATOR_INFO_REF_TO) {
        sereal_iterator_tied_scalar_t *scalar = NULL;
        Newx(scalar, 1, sereal_iterator_tied_scalar_t);
        if (!scalar) croak("Out of memory");
        scalar->store = NULL;

        tied = (sereal_iterator_tied_t*) scalar;
        tied_class_name = "Sereal::Path::Tie::Scalar";
        tied->count = count;
        result = sv_2mortal(newRV_noinc(FRESH_SV()));
        how = PERL_MAGIC_tiedscalar;
    } else {
        return srl_iterator_decode(aTHX_ iter);
    }

    {
        // copy iterator logic
        // TODO fix potential memleaks, how to free memory if srl_shallow_copy_iterator() croaks????
        // TODO get remove of unnesseccary copying
        tied->iter = NULL;
        Newx(tied->iter, 1, srl_iterator_t);
        if (tied->iter == NULL) croak("Out of memory");
        srl_shallow_copy_iterator(aTHX_ iter, tied->iter);
    }

    /* 
     * (1) creates new RV pointing to SV
     * (2) blesses SV to tied_class_name and store srl_iterator_tied_t* into SV
     * (3) mortalizes SV becase sv_magic() increas counter:
     * http://perldoc.perl.org/perlguts.html#Assigning-Magic
     * The obj argument is stored in the mg_obj field of the MAGIC structure.
     * If it is not the same as the sv argument, the reference count of the obj
     * object is incremented. If it is the same, or if the how argument is
     * PERL_MAGIC_arylen , or if it is a NULL pointer, then obj is merely
     * stored, without the reference count being incremented.
     * (4) adds magic referenced by tie to result SV
     */

    tie = newRV_noinc(FRESH_SV());                  // (1)
    tie = sv_setref_pv(tie, tied_class_name, tied); // (2)
    tie = sv_2mortal(tie);                          // (3)
    sv_magic(SvRV(result), tie, how, NULL, 0);      // (4)

    srl_iterator_step_in(aTHX_ tied->iter, 1);
    tied->depth = srl_iterator_stack_depth(aTHX_ tied->iter);
    return result;
}

MODULE = Sereal::Path::Tie   PACKAGE = Sereal::Path::Tie
PROTOTYPES: DISABLE

SV *
new(CLASS, src)
    const char *CLASS;
    SV *src
  PREINIT:
    srl_iterator_t *iter;
  PPCODE:
    if (   !sv_isobject(src)
        || !sv_isa(src, "Sereal::Path::Iterator")
        || SvTYPE(SvRV(src)) != SVt_PVMG)
    {
        warn("Sereal::Path::Iterator::new() -- src is not "
             "a blessed 'Sereal::Path::Iterator' SV reference");
        XSRETURN_UNDEF;
    }

    iter = INT2PTR(srl_iterator_t*, SvIV((SV*) SvRV(src)));
    ST(0) = srl_tie_new_tied_sv(aTHX_ iter);
    XSRETURN(1);

MODULE = Sereal::Path::Tie   PACKAGE = Sereal::Path::Tie::Scalar
PROTOTYPES: DISABLE

void
DESTROY(this)
    sereal_iterator_tied_scalar_t *this;
  CODE:
    if (this->store != NULL)
        SvREFCNT_dec(this->store);
    if (this->iter != NULL)
        srl_destroy_iterator(aTHX_ this->iter);
    Safefree(this);

void
FETCH(this)
    sereal_iterator_tied_scalar_t *this;
  PPCODE:
    if (this->store == NULL) {
        ST(0) = srl_tie_new_tied_sv(aTHX_ this->iter);
    } else {
        ST(0) = sv_2mortal(SvREFCNT_inc(this->store));
    }

    XSRETURN(1);

void
STORE(this, value)
    sereal_iterator_tied_scalar_t *this;
    SV *value;
  CODE:
    if (this->store != NULL)
        SvREFCNT_dec(this->store);

    this->store = value;
    SvREFCNT_inc(value);

void
UNTIE(this)
  CODE:
    croak("UNTIE is not supported");

MODULE = Sereal::Path::Tie   PACKAGE = Sereal::Path::Tie::Array
PROTOTYPES: DISABLE

void
DESTROY(this)
    sereal_iterator_tied_array_t *this;
  CODE:
    if (this->store != NULL)
        SvREFCNT_dec((SV*) this->store);
    if (this->iter != NULL)
        srl_destroy_iterator(aTHX_ this->iter);
    Safefree(this);

void
FETCH(this, key)
    sereal_iterator_tied_array_t *this;
    I32 key;
  PREINIT:
    IV idx;
    SV **svptr;
  PPCODE:
    if (this->store != NULL && (svptr = av_fetch(this->store, key, 0)) != NULL) {
        ST(0) = sv_2mortal(SvREFCNT_inc(*svptr));
        XSRETURN(1);
    }

    idx = srl_iterator_array_exists(aTHX_ this->iter, key);
    if (idx == SRL_ITER_NOT_FOUND) {
        ST(0) = &PL_sv_undef;
    } else {
        srl_iterator_array_goto(aTHX_ this->iter, key);
        ST(0) = srl_tie_new_tied_sv(aTHX_ this->iter);
    }

    XSRETURN(1);

void
FETCHSIZE(this)
    sereal_iterator_tied_array_t *this;
  PREINIT:
    U32 len;
    U32 avlen;
  PPCODE:
    avlen = (U32) (this->store != NULL ? av_len(this->store) + 1 : 0);
    len = (avlen > this->count ? avlen : this->count);
    ST(0) = sv_2mortal(newSVuv(len));
    XSRETURN(1);

void
EXISTS(this, key)
    sereal_iterator_tied_array_t *this;
    I32 key;
  PREINIT:
    IV result;
  PPCODE:
    if (this->store != NULL && av_exists(this->store, (SSize_t) key) != 0) {
        ST(0) = &PL_sv_yes;
        XSRETURN(1);
    }

    result = srl_iterator_array_exists(aTHX_ this->iter, key);
    ST(0) = (result == SRL_ITER_NOT_FOUND ? &PL_sv_undef : &PL_sv_yes);
    XSRETURN(1);

void
STORE(this, key, value)
    sereal_iterator_tied_array_t *this;
    I32 key;
    SV *value;
  CODE:
    if (this->store == NULL)
        this->store = newAV();

    av_store(this->store, key, value);
    SvREFCNT_inc(value);

void
STORESIZE(this, count)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
EXTEND(this, count)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
DELETE(this, key)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
CLEAR(this)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
PUSH(this, ...)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
POP(this)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
SHIFT(this)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
UNSHIFT(this, ...)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
SPLICE(this, offset, length, LIST)
  CODE:
    croak("Tied to Sereal::Path::Tie::Array array is read-only");

void
UNTIE(this)
  CODE:
    croak("UNTIE is not supported");

MODULE = Sereal::Path::Tie   PACKAGE = Sereal::Path::Tie::Hash
PROTOTYPES: DISABLE

void
DESTROY(this)
    sereal_iterator_tied_hash_t *this;
  CODE:
    if (this->store != NULL)
        SvREFCNT_dec((SV*) this->store);
    if (this->iter != NULL)
        srl_destroy_iterator(aTHX_ this->iter);
    Safefree(this);

void
FETCH(this, key)
    sereal_iterator_tied_hash_t *this;
    SV *key;
  PREINIT:
    HE *he;
    const char *keyname;
    STRLEN keyname_length;
  PPCODE:
    if (this->store != NULL) {
        if ((he = hv_fetch_ent(this->store, key, 0, 0)) != NULL) {
            ST(0) = sv_2mortal(SvREFCNT_inc(HeVAL(he)));
            XSRETURN(1);
        }
    }

    keyname = SvPV(key, keyname_length);
    if (srl_iterator_hash_exists(aTHX_ this->iter, keyname, keyname_length) == SRL_ITER_NOT_FOUND) {
        ST(0) = &PL_sv_undef;
    } else {
        ST(0) = srl_tie_new_tied_sv(aTHX_ this->iter);
    }

    XSRETURN(1);

void
EXISTS(this, key)
    sereal_iterator_tied_hash_t *this;
    SV *key;
  PREINIT:
    const char *keyname;
    STRLEN keyname_length;
  PPCODE:
    if (this->store != NULL && hv_exists_ent(this->store, key, 0)) {
        ST(0) = &PL_sv_yes;
        XSRETURN(1);
    }

    keyname = SvPV(key, keyname_length);
    ST(0) = srl_iterator_hash_exists(aTHX_ this->iter, keyname, keyname_length) == SRL_ITER_NOT_FOUND
          ? &PL_sv_undef
          : &PL_sv_yes;

    XSRETURN(1);

void
FIRSTKEY(this)
    sereal_iterator_tied_hash_t *this;
  PREINIT:
    const char *keyname;
    STRLEN keyname_length;
  PPCODE:
    if (this->store != NULL && HvUSEDKEYS(this->store) > 0) {
        this->cur_idx = -1; //indication that we should try to fetch next key from the store

        (void) hv_iterinit(this->store);
        ST(0) = hv_iterkeysv(hv_iternext(this->store));
        XSRETURN(1);
    }

    if (this->count > 0) {
        srl_iterator_rewind(aTHX_ this->iter, 0);
        srl_iterator_hash_key(aTHX_ this->iter, &keyname, &keyname_length);
        this->cur_idx = 1; // following call of NEXTKEY will set it to 2
        ST(0) = sv_2mortal(newSVpvn(keyname, keyname_length));
        XSRETURN(1);
    }

    ST(0) = &PL_sv_undef;
    XSRETURN(1);

void
NEXTKEY(this, last)
    sereal_iterator_tied_hash_t *this;
    SV *last;
  PREINIT:
    HE *he;
    U32 stack_idx;
    const char *keyname;
    STRLEN keyname_length;
  PPCODE:
    if (this->cur_idx < 0 && (he = hv_iternext(this->store)) != NULL) {
        ST(0) = hv_iterkeysv(he);
        XSRETURN(1);
    }

    assert(this->depth == srl_iterator_stack_depth(aTHX_ this->iter));
    for (this->cur_idx += 1; this->cur_idx < (I32) this->count; this->cur_idx += 1) {
        stack_idx = srl_iterator_stack_index(aTHX_ this->iter);
        if (this->cur_idx < (I32) stack_idx) {
            srl_iterator_rewind(aTHX_ this->iter, 0);
            stack_idx = 0;
        }

        srl_iterator_next(aTHX_ this->iter, this->cur_idx - stack_idx);
        srl_iterator_hash_key(aTHX_ this->iter, &keyname, &keyname_length);
        ST(0) = sv_2mortal(newSVpvn(keyname, keyname_length));
        this->cur_idx += 1;

        if (this->store == NULL || hv_exists_ent(this->store, ST(0), 0) == 0) {
            XSRETURN(1);
        }
    }

    ST(0) = &PL_sv_undef;
    XSRETURN(1);

void
SCALAR(this)
    sereal_iterator_tied_hash_t *this;
  PPCODE:
    if (this->count > 0) {
        ST(0) = sv_2mortal(newSVuv(1));
    } else if (this->store == NULL) {
        ST(0) = sv_2mortal(newSVuv(0));
    } else {
        ST(0) = hv_scalar(this->store);
    }
    XSRETURN(1);

void
STORE(this, key, value)
    sereal_iterator_tied_hash_t *this;
    SV *key;
    SV *value;
  CODE:
    if (this->store == NULL)
        this->store = newHV();

    hv_store_ent(this->store, key, value, 0);
    SvREFCNT_inc(value);

void
DELETE(this, key)
  CODE:
    croak("Tied to Sereal::Path::Tie::Hash hash is read-only");

void
CLEAR(this)
  CODE:
    croak("Tied to Sereal::Path::Tie::Hash hash is read-only");

void
UNTIE(this)
  CODE:
    croak("UNTIE is not supported");



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