Skip to content

Commit

Permalink
Sereal::Path::Tie: return autovivified keys on keys iteration
Browse files Browse the repository at this point in the history
  • Loading branch information
Roman Studenikin committed May 27, 2016
1 parent 6ae27d8 commit ab8c188
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 20 deletions.
71 changes: 55 additions & 16 deletions Perl/Path/Tie.xs
Expand Up @@ -99,6 +99,7 @@ srl_tie_new_tied_sv(pTHX_ srl_iterator_t *iter, SV *iter_sv)

tied = (sereal_iterator_tied_t*) array;
tied_class_name = "Sereal::Path::Tie::Array";
tied->count = count;
result = sv_2mortal(newRV_noinc((SV*) newAV()));
break;
}
Expand All @@ -111,6 +112,7 @@ srl_tie_new_tied_sv(pTHX_ srl_iterator_t *iter, SV *iter_sv)
hash->store = NULL;
tied = (sereal_iterator_tied_t*) hash;
tied_class_name = "Sereal::Path::Tie::Hash";
tied->count = count * 2; // for proper iterating
result = sv_2mortal(newRV_noinc((SV*) newHV()));
break;
}
Expand All @@ -137,7 +139,6 @@ srl_tie_new_tied_sv(pTHX_ srl_iterator_t *iter, SV *iter_sv)
depth = srl_iterator_disjoin(aTHX_ tied->iter); // mark current possion as new root
srl_iterator_step_in(aTHX_ tied->iter, 1);
tied->depth = depth + 1;
tied->count = count;

assert(srl_iterator_stack_depth(aTHX_ tied->iter) == tied->depth);
return result;
Expand Down Expand Up @@ -189,6 +190,7 @@ FETCH(this, key)
XSRETURN(1);
}
}

srl_tie_goto_depth_and_maybe_copy_iterator(aTHX_ (sereal_iterator_tied_t*) this);

idx = srl_iterator_array_exists(aTHX_ this->iter, key);
Expand Down Expand Up @@ -349,40 +351,77 @@ FIRSTKEY(this)
sereal_iterator_tied_hash_t *this;
PPCODE:
srl_tie_goto_depth_and_maybe_copy_iterator(aTHX_ (sereal_iterator_tied_t*) this);
if (this->count == 0) {
ST(0) = &PL_sv_undef;
} else {
ST(0) = &PL_sv_undef;
if (this->store != NULL && HvUSEDKEYS(this->store) > 0) {
(void) hv_iterinit(this->store);
ST(0) = hv_iterkeysv(hv_iternext(this->store));
this->cur_idx = -1; //indication that we should try to fetch next key from the store
}

if (ST(0) == &PL_sv_undef) {
this->cur_idx = 0;
srl_iterator_rewind(aTHX_ this->iter, 0);
ST(0) = srl_iterator_hash_key_sv(aTHX_ this->iter);
if (this->count == 0) {
ST(0) = &PL_sv_undef;
} else {
srl_iterator_rewind(aTHX_ this->iter, 0);
ST(0) = srl_iterator_hash_key_sv(aTHX_ this->iter);
}
}
XSRETURN(1);

void
NEXTKEY(this, last)
sereal_iterator_tied_hash_t *this;
SV *last;
PREINIT:
HE *he;
PPCODE:
this->cur_idx += 2;
if (this->cur_idx >= 2*this->count) {
ST(0) = &PL_sv_undef;
} else {
srl_tie_goto_depth_and_maybe_copy_iterator(aTHX_ (sereal_iterator_tied_t*) this);
ST(0) = &PL_sv_undef;
if (this->cur_idx < 0) {
if ((he = hv_iternext(this->store)) != NULL) {
ST(0) = hv_iterkeysv(he);
}
}

if (this->cur_idx < srl_iterator_stack_index(aTHX_ this->iter)) {
while (ST(0) == &PL_sv_undef && (this->cur_idx < 0 || this->cur_idx < this->count)) {
if (this->cur_idx < 0) {
this->cur_idx = 0;
srl_iterator_rewind(aTHX_ this->iter, 0);
ST(0) = srl_iterator_hash_key_sv(aTHX_ this->iter);
} else {
this->cur_idx += 2;
if (this->cur_idx >= this->count) {
ST(0) = &PL_sv_undef;
} else {
srl_tie_goto_depth_and_maybe_copy_iterator(aTHX_ (sereal_iterator_tied_t*) this);

if (this->cur_idx < srl_iterator_stack_index(aTHX_ this->iter)) {
srl_iterator_rewind(aTHX_ this->iter, 0);
}

srl_iterator_until(aTHX_ this->iter, this->depth, this->cur_idx);
ST(0) = srl_iterator_hash_key_sv(aTHX_ this->iter);
}
}
if (ST(0) != &PL_sv_undef && this->store != NULL && hv_exists_ent(this->store, ST(0), 0)) {
ST(0) = &PL_sv_undef;
continue;
}

srl_iterator_until(aTHX_ this->iter, this->depth, this->cur_idx);
ST(0) = srl_iterator_hash_key_sv(aTHX_ this->iter);
}

XSRETURN(1);

void
SCALAR(this)
sereal_iterator_tied_hash_t *this;
PPCODE:
ST(0) = sv_2mortal(newSVuv(this->count)); // TODO hv_scalar
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
Expand Down
7 changes: 3 additions & 4 deletions Perl/Path/t/110_tie_autovivify.t
Expand Up @@ -7,6 +7,7 @@ use Sereal::Path::Tie;
use Sereal::Encoder qw/encode_sereal/;

my $data = {
a => {aa => 1},
foo => {},
bar => undef,
};
Expand All @@ -21,10 +22,8 @@ is_deeply($tie->{baz}{test2}[200], $data->{baz}{test2}[200], 'data->{baz}{tes
is_deeply($tie->{baz}{test2}[200][0], $data->{baz}{test2}[200][0], 'data->{baz}{test2}[200][0]');
is_deeply($tie->{baz}{test2}[200][300], $data->{baz}{test2}[200][300], 'data->{baz}{test2}[200][300]');

TODO: {
local $TODO = "autovivified keys are not returned at the moment with keys traversal";
is_deeply([sort keys %$tie], [sort keys %$data], "sort keys data after vivification");
}
is_deeply([sort keys %$tie], [sort keys %$data], "sort keys data after vivification");
is_deeply($tie, $data, "full structure after vivification");
#=============================================================================================#

$data = [ 4 ];
Expand Down

0 comments on commit ab8c188

Please sign in to comment.