hashtable 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +39 -126
- data/lib/hashtable/hash_table.rb +130 -76
- data/lib/hashtable/sparse_bit_array.rb +46 -26
- data/lib/hashtable/traits.rb +37 -56
- data/lib/hashtable/version.rb +1 -1
- data/lib/hashtable.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a2d080d4eda5fb41e97c4661131fe7b3124530d4cb3604e907ca1294b38fe20
|
4
|
+
data.tar.gz: 456c01089c6b9764016386137ef4be6ac247fbef83921861d803659c798d9dec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89feb259b63fc1795ff796a0cc434dfe78108aad338a756be4e331034103c2ca3096f617130e4d1d85a24ec7921a7f8d118a08e512b3eae9cd7b4cef8728e690
|
7
|
+
data.tar.gz: a442d8711dcfa1db95600bd48166e35b12f5612f3b94fbe80d08b10a3ea00aaff49002e0f238eaa1273b456a170f9ee8722b9eb79b03934d686271c71ff32274
|
data/README.md
CHANGED
@@ -52,116 +52,57 @@ end
|
|
52
52
|
Store numbers:
|
53
53
|
|
54
54
|
```ruby
|
55
|
-
class Traits
|
56
|
-
def hash_lookup_key(key)
|
57
|
-
key
|
58
|
-
end
|
59
|
-
|
60
|
-
def lookup_key_to_storage_key(key)
|
61
|
-
key
|
62
|
-
end
|
63
|
-
|
64
|
-
def storage_key_to_lookup_key(key)
|
65
|
-
key
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
55
|
table = HashTable::HashTable.new(2)
|
70
|
-
|
71
|
-
table.set(
|
72
|
-
table.set(
|
73
|
-
table.set(
|
74
|
-
table.set(
|
75
|
-
table.set(
|
76
|
-
table.
|
56
|
+
table.set(3, 7, HashTable::IdentityHashTraits.new)
|
57
|
+
table.set(4, 5, HashTable::IdentityHashTraits.new)
|
58
|
+
table.set(5, 6, HashTable::IdentityHashTraits.new)
|
59
|
+
table.set(6, 7, HashTable::IdentityHashTraits.new)
|
60
|
+
table.set(8, 9, HashTable::IdentityHashTraits.new)
|
61
|
+
table.set(9, 19, HashTable::IdentityHashTraits.new)
|
62
|
+
expect(table.size).to eq(6)
|
63
|
+
expect(table.get(3, HashTable::IdentityHashTraits.new)).to eq(7)
|
64
|
+
expect(table.get(4, HashTable::IdentityHashTraits.new)).to eq(5)
|
65
|
+
expect(table.get(5, HashTable::IdentityHashTraits.new)).to eq(6)
|
66
|
+
expect(table.get(6, HashTable::IdentityHashTraits.new)).to eq(7)
|
67
|
+
expect(table.get(8, HashTable::IdentityHashTraits.new)).to eq(9)
|
68
|
+
expect(table.get(9, HashTable::IdentityHashTraits.new)).to eq(19)
|
69
|
+
expect(table.capacity).to eq(16)
|
77
70
|
```
|
78
71
|
|
79
72
|
Store strings:
|
80
73
|
|
81
74
|
```ruby
|
82
|
-
class StringTraits
|
83
|
-
attr_reader :string_table, :string_index
|
84
|
-
|
85
|
-
def initialize
|
86
|
-
@string_table = "\0"
|
87
|
-
@string_index = 1
|
88
|
-
end
|
89
|
-
|
90
|
-
def hash_lookup_key(key)
|
91
|
-
result = 0
|
92
|
-
key.each_byte { |byte| result += byte * 13 }
|
93
|
-
result
|
94
|
-
end
|
95
|
-
|
96
|
-
def lookup_key_to_storage_key(key)
|
97
|
-
@string_table += "#{key}\0"
|
98
|
-
old_si = @string_index
|
99
|
-
@string_index += key.length + 1
|
100
|
-
old_si
|
101
|
-
end
|
102
|
-
|
103
|
-
def storage_key_to_lookup_key(offset)
|
104
|
-
@string_table[offset..-1][/[^\0]+/]
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
75
|
table = HashTable::HashTable.new(2)
|
110
|
-
traits =
|
76
|
+
traits = HashTable::StringIdentityHashTraits.new
|
111
77
|
table.set('ViewController64.h', 'ViewController64.h', traits)
|
112
78
|
table.set('ViewController65.h', 'ViewController65.h', traits)
|
113
79
|
table.set('ViewController66.h', 'ViewController66.h', traits)
|
114
80
|
table.set('ViewController67.h', 'ViewController67.h', traits)
|
115
81
|
table.set('ViewController68.h', 'ViewController68.h', traits)
|
116
82
|
table.set('ViewController69.h', 'ViewController69.h', traits)
|
117
|
-
|
118
|
-
table.get('ViewController64.h', traits)
|
119
|
-
|
120
|
-
|
83
|
+
expect(table.size).to eq(6)
|
84
|
+
expect(table.get('ViewController64.h', traits)).to eq('ViewController64.h')
|
85
|
+
expect(table.get('ViewController65.h', traits)).to eq('ViewController65.h')
|
86
|
+
expect(table.get('ViewController66.h', traits)).to eq('ViewController66.h')
|
87
|
+
expect(table.get('ViewController67.h', traits)).to eq('ViewController67.h')
|
88
|
+
expect(table.get('ViewController68.h', traits)).to eq('ViewController68.h')
|
89
|
+
expect(table.get('ViewController69.h', traits)).to eq('ViewController69.h')
|
90
|
+
expect(table.capacity).to eq(16)
|
91
|
+
expect(traits.string_table).to eq("\u0000ViewController64.h\u0000ViewController65.h\u0000ViewController66.h\u0000ViewController67.h\u0000ViewController68.h\u0000ViewController69.h\u0000")
|
121
92
|
```
|
122
93
|
|
123
94
|
Store just key string:
|
124
95
|
|
125
96
|
```ruby
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
def initialize
|
130
|
-
@string_table = "\0"
|
131
|
-
@string_index = 1
|
132
|
-
end
|
133
|
-
|
134
|
-
def hash_lookup_key(key)
|
135
|
-
result = 0
|
136
|
-
key.each_byte { |byte| result += byte * 13 }
|
137
|
-
result
|
138
|
-
end
|
139
|
-
|
140
|
-
def lookup_key_to_storage_key(key)
|
141
|
-
@string_table += "#{key}\0"
|
142
|
-
old_si = @string_index
|
143
|
-
@string_index += key.length + 1
|
144
|
-
old_si
|
145
|
-
end
|
146
|
-
|
147
|
-
def storage_key_to_lookup_key(offset)
|
148
|
-
@string_table[offset..-1][/[^\0]+/]
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
|
153
|
-
table = HashTable::HashTable.new(2)
|
154
|
-
traits = StringTraits.new
|
155
|
-
|
97
|
+
table = HashTable::HashTable.new
|
98
|
+
traits = HashTable::StringIdentityHashTraits.new
|
156
99
|
(0..31).each do |i|
|
157
|
-
|
100
|
+
table.add("ViewController#{i}.h", traits)
|
158
101
|
end
|
159
|
-
#
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
# 32
|
164
|
-
p table.num_entries
|
102
|
+
p "#{table.size}---#{table.capacity}----#{table.num_entries}"
|
103
|
+
expect(table.size).to eq(32)
|
104
|
+
expect(table.capacity).to eq(64)
|
105
|
+
expect(table.num_entries).to eq(32)
|
165
106
|
```
|
166
107
|
|
167
108
|
Store strings and expand capacity:
|
@@ -171,44 +112,16 @@ Store strings and expand capacity:
|
|
171
112
|
- capacity is power_of_two
|
172
113
|
|
173
114
|
```ruby
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
def initialize
|
178
|
-
@string_table = "\0"
|
179
|
-
@string_index = 1
|
180
|
-
end
|
181
|
-
|
182
|
-
def hash_lookup_key(key)
|
183
|
-
result = 0
|
184
|
-
key.each_byte { |byte| result += byte * 13 }
|
185
|
-
result
|
186
|
-
end
|
187
|
-
|
188
|
-
def lookup_key_to_storage_key(key)
|
189
|
-
@string_table += "#{key}\0"
|
190
|
-
old_si = @string_index
|
191
|
-
@string_index += key.length + 1
|
192
|
-
old_si
|
193
|
-
end
|
194
|
-
|
195
|
-
def storage_key_to_lookup_key(offset)
|
196
|
-
@string_table[offset..-1][/[^\0]+/]
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
table = HashTable::HashTable.new(8192, expand: true)
|
201
|
-
traits = StringTraits.new
|
115
|
+
table = HashTable::HashTable.new(1364, expand: true)
|
116
|
+
traits = HashTable::StringHashTraits.new
|
202
117
|
buckets = (0..1363).map do |i|
|
203
|
-
|
204
|
-
|
118
|
+
table.set("ViewController#{i}.h",
|
119
|
+
["/Users/ws/Desktop/llvm/TestAndTestApp/TestAndTestApp/Group/h2/#{i}", "ViewController#{i}.h"], traits)
|
205
120
|
end
|
206
|
-
#
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
# 5461
|
211
|
-
p table.num_entries
|
121
|
+
p "#{table.size}---#{table.capacity}----#{table.num_entries}"
|
122
|
+
expect(table.size).to eq(1364)
|
123
|
+
expect(table.capacity).to eq(8192)
|
124
|
+
expect(table.num_entries).to eq(4097)
|
212
125
|
```
|
213
126
|
|
214
127
|
## Contributing
|
data/lib/hashtable/hash_table.rb
CHANGED
@@ -1,24 +1,91 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'benchmark'
|
4
|
+
|
3
5
|
# module HashTable
|
4
6
|
module HashTable
|
7
|
+
|
8
|
+
EXPAND_KEY_COUNT_CONST = {
|
9
|
+
8 => [0, 0],
|
10
|
+
16 => [6, 9],
|
11
|
+
32 => [8, 17],
|
12
|
+
64 => [13, 33],
|
13
|
+
128 => [23, 65],
|
14
|
+
256 => [44, 129],
|
15
|
+
512 => [86, 257],
|
16
|
+
1024 => [171, 513],
|
17
|
+
2048 => [341, 1025],
|
18
|
+
4096 => [682, 2049],
|
19
|
+
8192 => [1364, 4097],
|
20
|
+
16_384 => [2729, 8193],
|
21
|
+
32_768 => [5459, 16_385],
|
22
|
+
65_536 => [10_920, 32_769],
|
23
|
+
131_072 => [21_842, 65_537],
|
24
|
+
262_144 => [43_687, 131_073],
|
25
|
+
524_288 => [87_377, 262_145],
|
26
|
+
1_048_576 => [174_758, 524_289],
|
27
|
+
2_097_152 => [349_520, 1_048_577],
|
28
|
+
4_194_304 => [699_045, 2_097_153],
|
29
|
+
8_388_608 => [1_398_095, 4_194_305],
|
30
|
+
16_777_216 => [2_796_196, 8_388_609],
|
31
|
+
33_554_432 => [5_592_398, 16_777_217],
|
32
|
+
67_108_864 => [11_184_803, 33_554_433],
|
33
|
+
134_217_728 => [22_369_613, 67_108_865],
|
34
|
+
268_435_456 => [44_739_234, 134_217_729],
|
35
|
+
536_870_912 => [89_478_476, 268_435_457],
|
36
|
+
1_073_741_824 => [178_956_961, 536_870_913],
|
37
|
+
2_147_483_648 => [357_913_931, 1_073_741_825],
|
38
|
+
4_294_967_296 => [715_827_872, 2_147_483_649],
|
39
|
+
8_589_934_592 => [1_431_655_754, 4_294_967_297],
|
40
|
+
17_179_869_184 => [2_863_311_519, 8_589_934_593],
|
41
|
+
34_359_738_368 => [5_726_623_049, 17_179_869_185],
|
42
|
+
68_719_476_736 => [11_453_246_110, 34_359_738_369],
|
43
|
+
137_438_953_472 => [22_906_492_232, 68_719_476_737],
|
44
|
+
274_877_906_944 => [45_812_984_477, 137_438_953_473],
|
45
|
+
549_755_813_888 => [91_625_968_967, 274_877_906_945],
|
46
|
+
1_099_511_627_776 => [183_251_937_948, 549_755_813_889]
|
47
|
+
}.freeze
|
5
48
|
# HashTable
|
6
49
|
# @abstract
|
7
50
|
class HashTable
|
8
|
-
|
9
|
-
|
51
|
+
attr_reader :size
|
52
|
+
|
53
|
+
# @return [HashTable]
|
54
|
+
# @param [object] p_value placeholder value with @buckets_v
|
55
|
+
# @param [Bool] Does it need to be expanded
|
56
|
+
def self.new_from_vlaue_placeholder(count = 0, p_value = nil, expand: false)
|
57
|
+
new(count, p_value: p_value, expand: expand)
|
58
|
+
end
|
10
59
|
|
11
|
-
|
12
|
-
|
13
|
-
|
60
|
+
# @return [HashTable]
|
61
|
+
# @param [Integer] count of entries
|
62
|
+
# @param [object] p_key placeholder key with @buckets_k
|
63
|
+
# @param [object] p_value placeholder value with @buckets_v
|
64
|
+
# @param [Bool] Does it need to be expanded
|
65
|
+
def initialize(count = 0, p_key: nil, p_value: nil, expand: false)
|
66
|
+
capacity = calculate_count(count, expand: expand)
|
67
|
+
@buckets_k = Array.new(capacity, p_key)
|
68
|
+
@buckets_v = Array.new(capacity, p_value)
|
14
69
|
@present = SparseBitArray.new
|
15
70
|
@deleted = SparseBitArray.new
|
16
71
|
@expand = expand
|
17
|
-
@
|
72
|
+
@size = 0
|
18
73
|
end
|
19
74
|
|
20
|
-
def
|
21
|
-
|
75
|
+
def num_entries
|
76
|
+
raise 'count must less than 2_863_311_519' if size > 2_863_311_519
|
77
|
+
return size unless @expand
|
78
|
+
|
79
|
+
es = EXPAND_KEY_COUNT_CONST[capacity]
|
80
|
+
size - es[0] + es[1]
|
81
|
+
end
|
82
|
+
|
83
|
+
def keys
|
84
|
+
@buckets_k
|
85
|
+
end
|
86
|
+
|
87
|
+
def values
|
88
|
+
@buckets_v
|
22
89
|
end
|
23
90
|
|
24
91
|
def empty?
|
@@ -26,36 +93,27 @@ module HashTable
|
|
26
93
|
end
|
27
94
|
|
28
95
|
def capacity
|
29
|
-
@
|
96
|
+
@buckets_k.length
|
30
97
|
end
|
31
98
|
|
32
99
|
# @return [Integer] Find the entry whose key has the specified hash value,
|
33
100
|
# using the specified traits defining hash function and equality.
|
34
101
|
# @param [object] key
|
35
102
|
# @param [object] traits
|
36
|
-
def find(key, traits)
|
37
|
-
|
38
|
-
|
39
|
-
unless traits.respond_to?(:storage_key_to_lookup_key)
|
40
|
-
raise ArgumentError,
|
41
|
-
'traits must respond to storage_key_to_lookup_key method'
|
42
|
-
end
|
43
|
-
h = traits.hash_lookup_key(key) % capacity
|
103
|
+
def find(key, traits = IdentityHashTraits.new)
|
104
|
+
nums = capacity - 1
|
105
|
+
h = traits.hash_lookup_key(key) & nums
|
44
106
|
i = h
|
45
|
-
fisrt_unsed = nil
|
46
107
|
loop do
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
i = (i + 1) % capacity
|
108
|
+
bucket = @buckets_k[i]
|
109
|
+
return i if !bucket.nil? && present?(i) && traits.storage_key_to_lookup_key(bucket) == key
|
110
|
+
|
111
|
+
break unless deleted?(i)
|
112
|
+
|
113
|
+
i = (i + 1) & nums
|
54
114
|
break if i == h
|
55
115
|
end
|
56
|
-
|
57
|
-
|
58
|
-
fisrt_unsed
|
116
|
+
i
|
59
117
|
end
|
60
118
|
|
61
119
|
# @return [Integer] internal key
|
@@ -63,23 +121,20 @@ module HashTable
|
|
63
121
|
# @param [object] key
|
64
122
|
# @param [object] value
|
65
123
|
# @param [object] traits
|
66
|
-
def set(key, value, traits)
|
124
|
+
def set(key, value, traits = IdentityHashTraits.new)
|
67
125
|
set_as_interal(key, traits, value)
|
68
126
|
end
|
69
127
|
|
70
|
-
def get(key, traits)
|
128
|
+
def get(key, traits = IdentityHashTraits.new)
|
71
129
|
i = find(key, traits)
|
72
|
-
|
73
|
-
raise ArgumentError if bucket.nil? || bucket.empty?
|
74
|
-
|
75
|
-
bucket.last
|
130
|
+
@buckets_v[i]
|
76
131
|
end
|
77
132
|
|
78
133
|
# @return [Integer] internal key
|
79
134
|
# Set the entry using a key type that the specified Traits can convert from a real key to an internal key.
|
80
135
|
# @param [object] key
|
81
136
|
# @param [object] traits
|
82
|
-
def add(key, traits)
|
137
|
+
def add(key, traits = IdentityHashTraits.new)
|
83
138
|
set_as_interal(key, traits)
|
84
139
|
end
|
85
140
|
|
@@ -87,13 +142,13 @@ module HashTable
|
|
87
142
|
# Set the entry using a key type that the specified Traits can convert from a real key to an internal key.
|
88
143
|
# @param [object] key
|
89
144
|
# @param [object] traits
|
90
|
-
def adds(keys, traits)
|
145
|
+
def adds(keys, traits = IdentityHashTraits.new)
|
91
146
|
keys.map { |key| set_as_interal(key, traits) }
|
92
147
|
end
|
93
148
|
|
94
149
|
protected
|
95
150
|
|
96
|
-
attr_reader :present, :deleted, :
|
151
|
+
attr_reader :present, :deleted, :buckets_k, :buckets_v
|
97
152
|
|
98
153
|
def present?(key)
|
99
154
|
@present.test?(key)
|
@@ -109,62 +164,61 @@ module HashTable
|
|
109
164
|
# @param [object] traits
|
110
165
|
# @param [object] value
|
111
166
|
# @param [object] internal_key
|
112
|
-
def set_as_interal(key, traits, value = nil, internal_key = nil)
|
113
|
-
unless traits.respond_to?(:lookup_key_to_storage_key)
|
114
|
-
raise ArgumentError,
|
115
|
-
'traits must respond to lookup_key_to_storage_key method'
|
116
|
-
end
|
117
|
-
unless traits.respond_to?(:storage_key_to_lookup_key)
|
118
|
-
raise ArgumentError,
|
119
|
-
'traits must respond to storage_key_to_lookup_key method'
|
120
|
-
end
|
121
|
-
|
167
|
+
def set_as_interal(key, traits = IdentityHashTraits.new, value = nil, internal_key = nil)
|
122
168
|
index = find(key, traits)
|
123
|
-
|
124
|
-
if
|
169
|
+
bucket_k = @buckets_k[index]
|
170
|
+
if bucket_k.nil?
|
125
171
|
raise ArgumentError if present?(index)
|
126
172
|
|
127
|
-
|
128
|
-
|
173
|
+
@buckets_k[index] = bucket_k = internal_key.nil? ? traits.lookup_key_to_storage_key(key) : internal_key
|
174
|
+
@buckets_v[index] = traits.lookup_key_to_storage_value(bucket_k, value)
|
129
175
|
@present.set(index)
|
130
176
|
@deleted.reset(index)
|
177
|
+
@size += 1
|
131
178
|
grow(traits)
|
132
179
|
else
|
133
|
-
raise ArgumentError unless present?(index)
|
134
|
-
raise ArgumentError unless traits.storage_key_to_lookup_key(@buckets[index].first) == key
|
180
|
+
raise ArgumentError unless present?(index) || traits.storage_key_to_lookup_key(bucket_k) == key
|
135
181
|
|
136
|
-
|
182
|
+
@buckets_v[index] = traits.lookup_key_to_storage_value(bucket_k, value)
|
137
183
|
end
|
138
|
-
|
139
|
-
|
140
|
-
bucket[0]
|
184
|
+
bucket_k
|
141
185
|
end
|
142
186
|
|
143
187
|
private
|
144
188
|
|
145
|
-
def
|
146
|
-
|
147
|
-
|
148
|
-
|
189
|
+
def need_grow(size, count, expand: false)
|
190
|
+
raise 'count must less than 2_863_311_519' if count > 2_863_311_519
|
191
|
+
|
192
|
+
count *= 2
|
193
|
+
return size >= count / 3 + 1 unless expand
|
194
|
+
|
195
|
+
size >= EXPAND_KEY_COUNT_CONST[count][0]
|
196
|
+
end
|
197
|
+
|
198
|
+
def calculate_count(size, expand: false)
|
199
|
+
return 8 if size < 6
|
200
|
+
raise 'count must less than 2_863_311_519' if size > 2_863_311_519
|
201
|
+
|
202
|
+
if expand
|
203
|
+
EXPAND_KEY_COUNT_CONST.each_pair { |key, e| return key / 2 if size < e[0] }
|
204
|
+
else
|
205
|
+
2**(size * 3 / 2 + 1 - 1).bit_length
|
149
206
|
end
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
@num_entries = m + 1 if @expand
|
158
|
-
new_capacity = m * 2
|
159
|
-
return if new_capacity <= capacity
|
160
|
-
|
161
|
-
new_map = HashTable.new(new_capacity, expand: @expand)
|
207
|
+
end
|
208
|
+
|
209
|
+
def grow(traits = IdentityHashTraits.new)
|
210
|
+
return unless need_grow(size, capacity, expand: @expand)
|
211
|
+
|
212
|
+
new_map = HashTable.new(size, expand: @expand)
|
162
213
|
@present.each do |i|
|
163
|
-
|
214
|
+
key = @buckets_k[i]
|
215
|
+
lookup_key = traits.storage_key_to_lookup_key(key)
|
164
216
|
# Private methods cannot be called with an explicit receiver and protected ones can.
|
165
|
-
new_map.set_as_interal(lookup_key, traits, @
|
217
|
+
new_map.set_as_interal(lookup_key, traits, @buckets_v[i], key)
|
166
218
|
end
|
167
|
-
|
219
|
+
|
220
|
+
@buckets_k = new_map.buckets_k
|
221
|
+
@buckets_v = new_map.buckets_v
|
168
222
|
@present = new_map.present
|
169
223
|
@deleted = new_map.deleted
|
170
224
|
end
|
@@ -30,11 +30,18 @@ module HashTable
|
|
30
30
|
@bits[index]
|
31
31
|
end
|
32
32
|
|
33
|
+
def ==(other)
|
34
|
+
return false unless index == other.index
|
35
|
+
|
36
|
+
(0...BITWORDS_PER_ELEMENT).each do |i|
|
37
|
+
return false unless @bits[i] == other.bits[i]
|
38
|
+
end || true
|
39
|
+
end
|
40
|
+
|
33
41
|
def empty?
|
34
42
|
(0...BITWORDS_PER_ELEMENT).each do |i|
|
35
43
|
return false unless @bits[i].zero?
|
36
|
-
end
|
37
|
-
true
|
44
|
+
end || true
|
38
45
|
end
|
39
46
|
|
40
47
|
def set(index)
|
@@ -48,9 +55,8 @@ module HashTable
|
|
48
55
|
def test_and_set?(index)
|
49
56
|
unless test(index)
|
50
57
|
set(Idx)
|
51
|
-
|
52
|
-
end
|
53
|
-
false
|
58
|
+
true
|
59
|
+
end || false
|
54
60
|
end
|
55
61
|
|
56
62
|
def reset(index)
|
@@ -64,8 +70,13 @@ module HashTable
|
|
64
70
|
# v = (v * 0x0101010101010101) & UINT_64_MAX
|
65
71
|
# v >>= 56
|
66
72
|
def count
|
67
|
-
|
68
|
-
v =
|
73
|
+
@bits.inject(0) do |nums, b|
|
74
|
+
v = b
|
75
|
+
v -= ((v >> 1) & 0x5555555555555555)
|
76
|
+
v = (v & 0x3333333333333333) + ((v >> 2) & 0x3333333333333333)
|
77
|
+
v = (v + (v >> 4) & 0x0F0F0F0F0F0F0F0F)
|
78
|
+
v = (v * 0x0101010101010101) & UINT_64_MAX
|
79
|
+
v >>= 56
|
69
80
|
nums + v
|
70
81
|
end
|
71
82
|
end
|
@@ -74,11 +85,12 @@ module HashTable
|
|
74
85
|
def first
|
75
86
|
(0...BITWORDS_PER_ELEMENT).each do |i|
|
76
87
|
v = @bits[i]
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
88
|
+
unless v.zero?
|
89
|
+
count = v.digits(2).index(1)
|
90
|
+
return i * BITWORD_SIZE + count
|
91
|
+
end
|
81
92
|
end
|
93
|
+
raise ArgumentError, 'Illegal empty element'
|
82
94
|
end
|
83
95
|
|
84
96
|
# @return [Integer] the index of the last set bit
|
@@ -86,11 +98,12 @@ module HashTable
|
|
86
98
|
(0...BITWORDS_PER_ELEMENT).each do |i|
|
87
99
|
index = BITWORDS_PER_ELEMENT - i - 1
|
88
100
|
v = @bits[index]
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
101
|
+
unless v.zero?
|
102
|
+
count = BIT_WORD - v.bit_length
|
103
|
+
return index * BITWORD_SIZE + BITWORD_SIZE - count - 1
|
104
|
+
end
|
93
105
|
end
|
106
|
+
raise ArgumentError, 'Illegal empty element'
|
94
107
|
end
|
95
108
|
|
96
109
|
# @return [Integer] the index of the next set bit starting from the "index" bit.
|
@@ -153,6 +166,10 @@ module HashTable
|
|
153
166
|
bits >>= 1
|
154
167
|
end
|
155
168
|
end
|
169
|
+
|
170
|
+
protected
|
171
|
+
|
172
|
+
attr_reader :bits
|
156
173
|
end
|
157
174
|
|
158
175
|
# SparseBitArray is an implementation of a bitmap that is sparse by only storing the elements that have non-zero bits set.
|
@@ -163,6 +180,7 @@ module HashTable
|
|
163
180
|
# @return [Integer] Pointer to our current Element.
|
164
181
|
# This has no visible effect on the external state of a SparseBitArray
|
165
182
|
# It's just used to improve performance in the common case of testing/modifying bits with similar indices.
|
183
|
+
# attr_reader :current_index
|
166
184
|
attr_reader :current_index
|
167
185
|
|
168
186
|
def initialize
|
@@ -177,8 +195,7 @@ module HashTable
|
|
177
195
|
|
178
196
|
e_index = index / ELEMENT_SIZE
|
179
197
|
element_i = lower_bound(e_index)
|
180
|
-
|
181
|
-
return false if element_i == last || elements[element_i].index != e_index
|
198
|
+
return false if element_i == elements.length || elements[element_i].index != e_index
|
182
199
|
|
183
200
|
elements[element_i].test?(index % ELEMENT_SIZE)
|
184
201
|
end
|
@@ -205,13 +222,14 @@ module HashTable
|
|
205
222
|
e_index = index / ELEMENT_SIZE
|
206
223
|
element_i = lower_bound(e_index)
|
207
224
|
new_e = elements[element_i]
|
208
|
-
unless new_e.nil?
|
209
|
-
|
210
|
-
|
225
|
+
eql = new_e.index <=> e_index unless new_e.nil?
|
226
|
+
if eql.nil? || eql != 0
|
227
|
+
element_i += 1 if eql == -1
|
228
|
+
new_e = SparseBitArrayElement.new(e_index)
|
229
|
+
@elements.insert(element_i, new_e)
|
211
230
|
end
|
212
231
|
@current_index = element_i
|
213
|
-
|
214
|
-
@elements[@current_index].set(index % ELEMENT_SIZE)
|
232
|
+
new_e.set(index % ELEMENT_SIZE)
|
215
233
|
end
|
216
234
|
|
217
235
|
# @return [Void] Test, Set a bit in the bitmap
|
@@ -269,12 +287,14 @@ module HashTable
|
|
269
287
|
@current_index -= 1 if @current_index == elements.length
|
270
288
|
element_i = @current_index
|
271
289
|
element = @elements[element_i]
|
272
|
-
return element_i if element.index == index
|
273
290
|
|
274
|
-
@current_index =
|
291
|
+
@current_index = case element.index <=> index
|
292
|
+
when 0
|
293
|
+
element_i
|
294
|
+
when 1
|
275
295
|
@elements[0..element_i].rindex { |e| e.index <= index } || 0
|
276
|
-
|
277
|
-
@elements[element_i..].
|
296
|
+
when -1
|
297
|
+
@elements[element_i..].count { |e| e.index < index } + element_i
|
278
298
|
end
|
279
299
|
end
|
280
300
|
end
|
data/lib/hashtable/traits.rb
CHANGED
@@ -1,84 +1,65 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: false
|
2
2
|
|
3
3
|
# module HashTable
|
4
4
|
module HashTable
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
key
|
12
|
-
end
|
13
|
-
|
14
|
-
def storage_key_to_lookup_key(key)
|
15
|
-
key
|
16
|
-
end
|
5
|
+
# IdentityHashTraits
|
6
|
+
class IdentityHashTraits
|
7
|
+
define_method(:hash_lookup_key) { |key| key }
|
8
|
+
define_method(:lookup_key_to_storage_key) { |key| key }
|
9
|
+
define_method(:storage_key_to_lookup_key) { |key| key }
|
10
|
+
define_method(:lookup_key_to_storage_value) { |_key, value| value }
|
17
11
|
end
|
18
12
|
|
19
|
-
|
20
|
-
|
13
|
+
# StringIdentityHashTraits
|
14
|
+
class StringIdentityHashTraits < IdentityHashTraits
|
15
|
+
attr_reader :string_table, :buckets
|
21
16
|
|
22
|
-
def initialize
|
17
|
+
def initialize(&block)
|
18
|
+
super
|
23
19
|
@string_table = "\0"
|
24
|
-
@
|
20
|
+
@buckets = {}
|
21
|
+
@indexs = {}
|
25
22
|
end
|
26
23
|
|
27
24
|
def hash_lookup_key(key)
|
28
|
-
|
29
|
-
key.each_byte { |byte| result += byte * 13 }
|
30
|
-
result
|
25
|
+
key.downcase.bytes.inject(:+) * 13
|
31
26
|
end
|
32
27
|
|
33
28
|
def lookup_key_to_storage_key(key)
|
34
|
-
@
|
35
|
-
|
36
|
-
|
29
|
+
return @buckets[key] unless @buckets[key].nil?
|
30
|
+
|
31
|
+
old_si = @string_table.length
|
32
|
+
@buckets[key] = old_si
|
33
|
+
@string_table << "#{key}\0".b
|
34
|
+
@indexs[old_si] = key
|
37
35
|
old_si
|
38
36
|
end
|
39
37
|
|
40
38
|
def storage_key_to_lookup_key(offset)
|
41
|
-
@
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class StringTraits2
|
46
|
-
attr_reader :string_table, :buckets, :hash_h
|
39
|
+
return @indexs[offset] unless @indexs[offset].nil?
|
47
40
|
|
48
|
-
|
49
|
-
@
|
50
|
-
|
51
|
-
@buckets = {}
|
52
|
-
@hash_h = {}
|
41
|
+
key = @string_table[offset..][/[^\0]+/]
|
42
|
+
@indexs[offset] = key
|
43
|
+
key
|
53
44
|
end
|
45
|
+
end
|
54
46
|
|
55
|
-
|
56
|
-
|
47
|
+
# StringHashTraits
|
48
|
+
class StringHashTraits < StringIdentityHashTraits
|
49
|
+
attr_reader :string_table, :buckets
|
57
50
|
|
58
|
-
|
59
|
-
|
60
|
-
@
|
61
|
-
result
|
51
|
+
def initialize(&block)
|
52
|
+
super
|
53
|
+
@block = block
|
62
54
|
end
|
63
55
|
|
64
|
-
def
|
65
|
-
return
|
56
|
+
def lookup_key_to_storage_value(key, values)
|
57
|
+
return if values.nil? || values.empty?
|
66
58
|
|
67
|
-
|
68
|
-
|
69
|
-
@string_index += key.length + 1
|
70
|
-
@buckets[key] = old_si
|
71
|
-
old_si
|
72
|
-
end
|
59
|
+
bs = values.map { |v| lookup_key_to_storage_key(v) }.unshift(key)
|
60
|
+
return bs if @block.nil?
|
73
61
|
|
74
|
-
|
75
|
-
value.inject([@buckets[key]]) do |sum, v|
|
76
|
-
sum << lookup_key_to_storage_key(v)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def storage_key_to_lookup_key(offset)
|
81
|
-
@string_table[offset..][/[^\0]+/]
|
62
|
+
@block.call(bs)
|
82
63
|
end
|
83
64
|
end
|
84
65
|
end
|
data/lib/hashtable/version.rb
CHANGED
data/lib/hashtable.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hashtable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cat1237
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-05-
|
11
|
+
date: 2022-05-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|