Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Re-work hash iteration to use a HashIter.
This replaces the existing gather/take approach with maps over them,
which for .kv/.keys/.pairs is much more efficient as we never create a
load of Pair objects only to throw them away. Seems not to regress any
spectests on JVM or Parrot, and makes some TODO tests pass, most likely
because 'for %h.vk -> $k, $v is rw { ... }' is probably fixed.
  • Loading branch information
jnthn committed Jul 25, 2013
1 parent fe35215 commit 787295d
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 45 deletions.
199 changes: 167 additions & 32 deletions src/core/EnumMap.pm
@@ -1,3 +1,156 @@
my class HashIter is Iterator {
has $!reified; # Parcel we return after reifying
has Mu $!hashiter; # the VM level hash iterator
has Mu $!keystore; # key store, if it's a typed hash
has int $!mode; # pair = 0, kv = 1, k = 2, v = 3, invert = 4

method new($hash, :$keystore, :$pairs, :$kv, :$k, :$v, :$invert) {
my $new := nqp::create(self);
$new.BUILD($hash, $keystore,
$pairs ?? 0 !!
$kv ?? 1 !!
$k ?? 2 !!
$v ?? 3 !!
$invert ?? 4 !!
0);
$new;
}

method BUILD($hash, $keystore, Int $mode) {
$!hashiter := nqp::iterator(nqp::getattr(nqp::decont($hash), EnumMap, '$!storage'));
$!mode = $mode;
$!keystore := nqp::getattr(nqp::decont($keystore), EnumMap, '$!storage')
if $keystore.DEFINITE;
self
}

method reify($n = 1000, :$sink) {
unless nqp::isconcrete($!reified) {
my int $count = nqp::istype($n, Whatever) ?? 1000 !! $n.Int;
my int $i = 0;
my int $mode = $!mode;
my Mu $rpa := nqp::list();
my $it := $!hashiter;

my Mu $pairish;
if $mode == 0 {
if nqp::defined($!keystore) {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, Pair.new(
:key(nqp::atkey($!keystore, nqp::iterkey_s($pairish))),
:value(nqp::hllize(nqp::iterval($pairish)))));
$i = $i + 1;
}
}
else {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, Pair.new(
:key(nqp::p6box_s(nqp::iterkey_s($pairish))),
:value(nqp::hllize(nqp::iterval($pairish)))));
$i = $i + 1;
}
}
}
elsif $mode == 1 {
if nqp::defined($!keystore) {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, nqp::atkey($!keystore, nqp::iterkey_s($pairish)).item);
nqp::push($rpa, nqp::hllize(nqp::iterval($pairish)).item);
$i = $i + 1;
}
}
else {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, nqp::p6box_s(nqp::iterkey_s($pairish)));
nqp::push($rpa, nqp::hllize(nqp::iterval($pairish)).item);
$i = $i + 1;
}
}
}
elsif $mode == 2 {
if nqp::defined($!keystore) {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, nqp::atkey($!keystore, nqp::iterkey_s($pairish)).item);
$i = $i + 1;
}
}
else {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, nqp::p6box_s(nqp::iterkey_s($pairish)));
$i = $i + 1;
}
}
}
elsif $mode == 3 {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, nqp::hllize(nqp::iterval($pairish)).item);
$i = $i + 1;
}
}
elsif $mode == 4 {
if nqp::defined($!keystore) {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, Pair.new(
:value(nqp::atkey($!keystore, nqp::iterkey_s($pairish))),
:key(nqp::hllize(nqp::iterval($pairish)))));
$i = $i + 1;
}
}
else {
while $it && $i < $count {
$pairish := nqp::shift($it);
nqp::push($rpa, Pair.new(
:value(nqp::p6box_s(nqp::iterkey_s($pairish))),
:key(nqp::hllize(nqp::iterval($pairish)))));
$i = $i + 1;
}
}
}
else {
die "Unknown hash iteration mode";
}

if $it {
my $nextiter := nqp::create(self);
nqp::bindattr($nextiter, HashIter, '$!hashiter', $it);
nqp::bindattr($nextiter, HashIter, '$!keystore', $!keystore);
nqp::bindattr_i($nextiter, HashIter, '$!mode', $mode);
nqp::push($rpa, $nextiter);
}

$!reified := nqp::p6parcel($rpa, nqp::null());
# release references to objects we no longer need/own
$!hashiter := Any;
}
$!reified;
}

method infinite() { False }

multi method DUMP(HashIter:D: :$indent-step = 4, :%ctx?) {
return DUMP(self, :$indent-step) unless %ctx;

my Mu $attrs := nqp::list();
nqp::push($attrs, '$!reified' );
nqp::push($attrs, $!reified );
nqp::push($attrs, '$!hashiter' );
nqp::push($attrs, $!hashiter );
nqp::push($attrs, '$!keystore' );
nqp::push($attrs, $!keystore );
nqp::push($attrs, '$!mode' );
nqp::push($attrs, $!mode );
self.DUMP-OBJECT-ATTRS($attrs, :$indent-step, :%ctx);
}
}

my class EnumMap does Associative {
# declared in BOOTSTRAP.pm:
# has $!storage; # Parrot Hash PMC of key->value mappings
Expand Down Expand Up @@ -51,43 +204,25 @@ my class EnumMap does Associative {
method iterator(EnumMap:) { self.pairs.iterator }
method list(EnumMap:) { self.pairs }

method keys(EnumMap:) { self.pairs.map( { $_.key } ) }
method kv(EnumMap:) { self.pairs.map( { $_.kv } ) }
method values(EnumMap:) { self.pairs.map( { $_.value } ) }

method keys(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!storage);
HashIter.new(self, :k).list
}
method kv(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!storage);
HashIter.new(self, :kv).list
}
method values(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!storage);
HashIter.new(self, :v).list
}
method pairs(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!storage);
gather {
my Mu $iter := nqp::iterator($!storage);
my Mu $pair;
while $iter {
$pair := nqp::shift($iter);
#?if jvm
take Pair.new(:key(nqp::p6box_s(nqp::iterkey_s($pair))), :value(nqp::iterval($pair)));
#?endif
#?if parrot
take Pair.new(:key($pair.key), :value($pair.value));
#?endif
}
Nil
}
HashIter.new(self, :pairs).list
}
method invert(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!storage);
gather {
my Mu $iter := nqp::iterator($!storage);
my Mu $pair;
while $iter {
$pair := nqp::shift($iter);
#?if jvm
take Pair.new(:key(nqp::iterval($pair)), :value(nqp::p6box_s(nqp::iterkey_s($pair))));
#?endif
#?if parrot
take Pair.new(:key($pair.value), :value($pair.key));
#?endif
}
Nil
}
HashIter.new(self, :invert).list
}

method at_key($key) is rw {
Expand Down
32 changes: 19 additions & 13 deletions src/core/Hash.pm
Expand Up @@ -256,19 +256,25 @@ my class Hash {
?? nqp::p6bool(nqp::existskey($!keys, nqp::unbox_s(key.WHICH)))
!! False
}
method pairs() {
return unless nqp::defined(nqp::getattr(self, EnumMap, '$!storage'));
gather {
my Mu $iter := nqp::iterator(nqp::getattr(self, EnumMap, '$!storage'));
my Mu $pair;
my Mu $key;
while $iter {
$pair := nqp::shift($iter);
$key := nqp::atkey(nqp::getattr(self, $?CLASS, '$!keys'), nqp::iterkey_s($pair));
take Pair.new(:key($key), :value(nqp::iterval($pair)));
}
Nil
}
method keys(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!keys);
HashIter.new(self, :keystore($!keys), :k).list
}
method kv(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!keys);
HashIter.new(self, :keystore($!keys), :kv).list
}
method values(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!keys);
HashIter.new(self, :keystore($!keys), :v).list
}
method pairs(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!keys);
HashIter.new(self, :keystore($!keys), :pairs).list
}
method invert(EnumMap:) {
return unless self.DEFINITE && nqp::defined($!keys);
HashIter.new(self, :keystore($!keys), :invert).list
}
multi method perl(::?CLASS:D \SELF:) {
'Hash['
Expand Down

0 comments on commit 787295d

Please sign in to comment.