Skip to content

Commit

Permalink
Added back Hash::Iterator because it's used in C-API.
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Ford committed Aug 1, 2011
1 parent a4f6089 commit 59c5b96
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 0 deletions.
34 changes: 34 additions & 0 deletions kernel/common/hash.rb
Expand Up @@ -29,6 +29,35 @@ def match?(key, key_hash)
end
end

# An external iterator that returns only entry chains from the
# Hash storage, never nil bins. While somewhat following the API
# of Enumerator, it is named Iterator because it does not provide
# <code>#each</code> and should not conflict with +Enumerator+ in
# MRI 1.8.7+. Returned by <code>Hash#to_iter</code>.

class Iterator
attr_reader :index

def initialize(entries, capacity)
@entries = entries
@capacity = capacity
@index = -1
end

# Returns the next object or +nil+.
def next(entry)
if entry and entry = entry.link
return entry
end

while (@index += 1) < @capacity
if entry = @entries[@index]
return entry
end
end
end
end

# Hash methods

attr_reader :size
Expand Down Expand Up @@ -646,6 +675,11 @@ def to_a
ary
end

# Returns an external iterator for the bins. See +Iterator+
def to_iter
Iterator.new @entries, @capacity
end

def to_hash
self
end
Expand Down
25 changes: 25 additions & 0 deletions kernel/common/hash19.rb
Expand Up @@ -84,6 +84,26 @@ def remove
end
end

# An external iterator that returns entries in insertion order. While
# somewhat following the API of Enumerator, it is named Iterator because it
# does not provide <code>#each</code> and should not conflict with
# +Enumerator+ in MRI 1.8.7+. Returned by <code>Hash#to_iter</code>.

class Iterator
def initialize(state)
@state = state
end

# Returns the next object or +nil+.
def next(entry)
if entry
return entry if entry = entry.next
else
return @state.head
end
end
end

# Hash methods

attr_reader :size
Expand Down Expand Up @@ -732,6 +752,11 @@ def to_a
ary
end

# Returns an external iterator for the bins. See +Iterator+
def to_iter
Iterator.new @state
end

def to_hash
self
end
Expand Down
24 changes: 24 additions & 0 deletions kernel/common/hash_hamt.rb
Expand Up @@ -290,6 +290,25 @@ def delete(key, key_hash)
end
end

# An external iterator that returns entries in insertion order. While
# somewhat following the API of Enumerator, it is named Iterator because it
# does not provide <code>#each</code> and should not conflict with
# +Enumerator+ in MRI 1.8.7+. Returned by <code>Hash#to_iter</code>.

class Iterator
def initialize(state)
@state = state
end

# Returns the next object or +nil+.
def next(entry)
if entry
return entry if entry = entry.next
else
return @state.head
end
end
end

# Hash methods

Expand Down Expand Up @@ -778,6 +797,11 @@ def to_a
ary
end

# Returns an external iterator for the bins. See +Iterator+
def to_iter
Iterator.new @state
end

def to_hash
self
end
Expand Down
23 changes: 23 additions & 0 deletions spec/core/hash/iterator/index_spec.rb
@@ -0,0 +1,23 @@
require File.expand_path('../../../../spec_helper', __FILE__)

with_feature :hash_bucket do
describe "Hash::Iterator#index" do
ruby_version_is ""..."1.9" do
it "returns the index of the last object returned by #next" do
h = Hash.new
a = Hash::Bucket.new 1, 2, 3
b = Hash::Bucket.new 4, 5, 6
e = h.instance_variable_get :@entries
e[2] = a
e[4] = b

iter = h.to_iter

(entry = iter.next(entry)).should == a
(entry = iter.next(entry)).should == b

iter.index.should == 4
end
end
end
end
16 changes: 16 additions & 0 deletions spec/core/hash/iterator/new_spec.rb
@@ -0,0 +1,16 @@
require File.expand_path('../../../../spec_helper', __FILE__)

with_feature :hash_bucket do
describe "Hash::Iterator.new" do
ruby_version_is ""..."1.9" do
it "initializes the iterator" do
h = Hash[:a => 1, :b => 2]
entries = h.instance_variable_get :@entries
iter = h.to_iter
iter.instance_variable_get(:@entries).should == entries
iter.instance_variable_get(:@capacity).should == entries.size
iter.instance_variable_get(:@index).should == -1
end
end
end
end
59 changes: 59 additions & 0 deletions spec/core/hash/iterator/next_spec.rb
@@ -0,0 +1,59 @@
require File.expand_path('../../../../spec_helper', __FILE__)

with_feature :hash_bucket do
describe "Hash::Iterator#next" do
before :each do
ScratchPad.record []
end

ruby_version_is ""..."1.9" do
it "returns each non-nil entry from the storage vector" do
h = Hash.new
a = Hash::Bucket.new 1, 2, 3
b = Hash::Bucket.new 4, 5, 6
c = Hash::Bucket.new 7, 8, 9
e = h.instance_variable_get :@entries
e[2] = a
e[4] = b
e[7] = c

iter = h.to_iter

while entry = iter.next(entry)
ScratchPad << entry
end
ScratchPad.recorded.should == [a, b, c]
end
end

ruby_version_is "1.9" do
it "returns each entry in the Hash" do
h = Hash[:a, 1, :b, 2, :c, 3]
iter = h.to_iter

while entry = iter.next(entry)
ScratchPad << entry.key
end
ScratchPad.recorded.should == [:a, :b, :c]
end
end
end
end

with_feature :hash_hamt do
describe "Hash::Iterator#next" do
before :each do
ScratchPad.record []
end

it "returns each entry in the Hash" do
h = Hash[:a, 1, :b, 2, :c, 3]
iter = h.to_iter

while entry = iter.next(entry)
ScratchPad << entry.key
end
ScratchPad.recorded.should == [:a, :b, :c]
end
end
end

0 comments on commit 59c5b96

Please sign in to comment.