Class: TonSdkRuby::Hashmap

Inherits:
Object
  • Object
show all
Extended by:
TonSdkRuby
Includes:
TonSdkRuby
Defined in:
lib/ton-sdk-ruby/boc/hashmap.rb

Direct Known Subclasses

HashmapE

Constant Summary

Constants included from TonSdkRuby

DEPTH_BITS, FLAG_BOUNCEABLE, FLAG_NON_BOUNCEABLE, FLAG_TEST_ONLY, HASH_BITS, INT32_MAX, INT32_MIN, LEAN_BOC_MAGIC_PREFIX, LEAN_BOC_MAGIC_PREFIX_CRC, REACH_BOC_MAGIC_PREFIX, VERSION

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TonSdkRuby

augment, base64_to_bytes, bits_to_big_int, bits_to_big_uint, bits_to_bytes, bits_to_hex, bits_to_int_uint, breadth_first_sort, bytes_compare, bytes_needed_for_words_bip39, bytes_to_base64, bytes_to_bits, bytes_to_data_string, bytes_to_hex, bytes_to_string, bytes_to_uint, crc16, crc16_bytes_be, crc32c, crc32c_bytes_le, depth_first_sort, deserialize, deserialize_cell, deserialize_fift, deserialize_header, generate_words_bip39, get_mapper, hex_to_bits, hex_to_bytes, hex_to_data_string, read_json_from_link, read_post_json_from_link, require_type, rollback, serialize_cell, sha256, sha512, sign_cell, slice_into_chunks, string_to_bytes, uint_to_hex, validate_library_reference, validate_merkle_proof, validate_merkle_update, validate_ordinary, validate_pruned_branch

Constructor Details

#initialize(key_size, options = {}) ⇒ Hashmap

Returns a new instance of Hashmap.



9
10
11
12
13
14
15
16
17
18
19
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 9

def initialize(key_size, options = {})
  serializers = options.fetch(:serializers, {})
  deserializers = options.fetch(:deserializers, {})

  @hashmap = {}
  @key_size = key_size
  @serialize_key = serializers.fetch(:key, -> (key) { key })
  @serialize_value = serializers.fetch(:value, -> (value) { value })
  @deserialize_key = deserializers.fetch(:key, -> (key) { key })
  @deserialize_value = deserializers.fetch(:value, -> (value) { value })
end

Instance Attribute Details

#hashmapObject (readonly)

Returns the value of attribute hashmap.



7
8
9
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 7

def hashmap
  @hashmap
end

#key_sizeObject (readonly)

Returns the value of attribute key_size.



7
8
9
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 7

def key_size
  @key_size
end

Class Method Details

.deserialize(key_size, slice, options = {}) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 256

def self.deserialize(key_size, slice, options = {})
  if slice.bits.length < 2
    raise 'Hashmap: can\'t be empty. It must contain at least 1 key-value pair.'
  end

  hashmap = Hashmap.new(key_size, options)
  nodes = deserialize_edge(slice, key_size)

  nodes.each do |key, value|
    hashmap.set_raw(key, value)
  end

  hashmap
end

.deserialize_edge(edge, key_size, key = []) ⇒ Object



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 271

def self.deserialize_edge(edge, key_size, key = [])
  nodes = []

  key.concat(deserialize_label(edge, key_size - key.length))

  if key.length == key_size
    value = Builder.new.store_slice(edge).cell
    return nodes.concat([[key, value]])
  end
  edge.refs.each_with_index do |_r, i|
    fork_edge = edge.load_ref.parse
    fork_key = key + [i]

    nodes.concat(deserialize_edge(fork_edge, key_size, fork_key))
  end

  nodes
end

.deserialize_label(edge, m) ⇒ Object



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 290

def self.deserialize_label(edge, m)
  # m = length at most possible bits of n (key)

  # hml_short$0
  if edge.load_bit == 0
    return deserialize_label_short(edge)
  end

  # hml_long$10
  if edge.load_bit == 0
    return deserialize_label_long(edge, m)
  end

  # hml_same$11
  deserialize_label_same(edge, m)
end

.deserialize_label_long(edge, m) ⇒ Object



313
314
315
316
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 313

def self.deserialize_label_long(edge, m)
  length = edge.load_uint(Math.log2(m + 1).ceil)
  edge.load_bits(length)
end

.deserialize_label_same(edge, m) ⇒ Object



318
319
320
321
322
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 318

def self.deserialize_label_same(edge, m)
  repeated = edge.load_bit
  length = edge.load_uint(Math.log2(m + 1).ceil)
  Array.new(length) { repeated }
end

.deserialize_label_short(edge) ⇒ Object



307
308
309
310
311
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 307

def self.deserialize_label_short(edge)
  length = edge.bits.index(0)
  edge.skip(length + 1)
  edge.load_bits(length)
end

.parse(key_size, slice, options = {}) ⇒ Object



328
329
330
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 328

def self.parse(key_size, slice, options = {})
  deserialize(key_size, slice, options)
end

.serialize_edge(nodes) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 124

def self.serialize_edge(nodes)
  # hme_empty$0
  if nodes.empty?
    label = serialize_label_short([])

    return Builder.new
                  .store_bits(label)
                  .cell
  end

  edge = Builder.new
  label = serialize_label(nodes)
  edge.store_bits(label)

  # hmn_leaf#_
  if nodes.length == 1
    leaf = serialize_leaf(nodes[0])
    edge.store_slice(leaf.parse)
  end

  # hmn_fork#_
  if nodes.length > 1
    # Left edge can be empty, anyway we need to create hme_empty$0 to support right one
    left_nodes, right_nodes = serialize_fork(nodes)
    left_edge = serialize_edge(left_nodes)

    edge.store_ref(left_edge)

    unless right_nodes.empty?
      right_edge = serialize_edge(right_nodes)

      edge.store_ref(right_edge)
    end
  end

  edge.cell
end

.serialize_fork(nodes) ⇒ Object



162
163
164
165
166
167
168
169
170
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 162

def self.serialize_fork(nodes)
  # Serialize nodes to edges
  nodes.reduce([[], []]) do |acc, (key, value)|
    # Sort nodes by left/right edges
    acc[key.shift].push([key, value])

    acc
  end
end

.serialize_label(nodes) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 176

def self.serialize_label(nodes)
  # Each label can always be serialized in at least two different fashions, using
  # hml_short or hml_long constructors. Usually the shortest serialization (and
  # in the case of a tieā€”the lexicographically smallest among the shortest) is
  # preferred and is generated by TVM hashmap primitives, while the other
  # variants are still considered valid.

  # Get nodes keys
  first = nodes[0][0]
  last = nodes[nodes.length - 1][0]
  # m = length at most possible bits of n (key)
  m = first.length

  same_bits_index = nil
  first.each_with_index do |el, index|
    if el != last[index]
      same_bits_index = index
      break
    end
  end
  same_bits_length = same_bits_index.nil? ? first.length : same_bits_index

  if first[0] != last[0] || m == 0
    # hml_short for zero most possible bits
    return serialize_label_short([])
  end

  label = first[0, same_bits_length]
  repeated = label.join('').match(/(^0+)|(^1+)/)[0].split('').map { |b| b.to_i }
  label_short = serialize_label_short(label)
  label_long = serialize_label_long(label, m)
  label_same = nodes.length > 1 && repeated.length > 1 ? serialize_label_same(repeated, m) : nil

  labels = [
    { bits: label.length, label: label_short },
    { bits: label.length, label: label_long },
    { bits: repeated.length, label: label_same }
  ].reject { |el| el[:label].nil? }

  # Sort labels by their length
  labels.sort_by! { |el| el[:label].length }
  # Get most compact label
  choosen = labels[0]

  # Remove label bits from nodes keys
  nodes.each { |key, _| key.shift(choosen[:bits]) }
  choosen[:label]
end

.serialize_label_long(bits, m) ⇒ Object



236
237
238
239
240
241
242
243
244
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 236

def self.serialize_label_long(bits, m)
  label = Builder.new

  label.store_bits([1, 0])
       .store_uint(bits.length, Math.log2(m + 1).ceil)
       .store_bits(bits)

  label.bits
end

.serialize_label_same(bits, m) ⇒ Object



246
247
248
249
250
251
252
253
254
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 246

def self.serialize_label_same(bits, m)
  label = Builder.new

  label.store_bits([1, 1])
       .store_bit(bits[0])
       .store_uint(bits.length, Math.log2(m + 1).ceil)

  label.bits
end

.serialize_label_short(bits) ⇒ Object



225
226
227
228
229
230
231
232
233
234
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 225

def self.serialize_label_short(bits)
  label = Builder.new

  label.store_bit(0)
       .store_bits(bits.map { 1 })
       .store_bit(0)
       .store_bits(bits)

  label.bits
end

.serialize_leaf(node) ⇒ Object



172
173
174
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 172

def self.serialize_leaf(node)
  node[1]
end

Instance Method Details

#add(key, value) ⇒ Object



51
52
53
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 51

def add(key, value)
  has(key) ? self : set(key, value)
end

#cellObject



324
325
326
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 324

def cell
  serialize
end

#delete(key) ⇒ Object



77
78
79
80
81
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 77

def delete(key)
  k = serialize_key(key).join('')
  hashmap.delete(k)
  self
end

#eachObject



21
22
23
24
25
26
27
28
29
30
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 21

def each
  result = []
  @hashmap.each do |k, v|
    key = deserialize_key(k.chars.map(&:to_i))
    value = deserialize_value(v)

    result << (yield [key, value])
  end
  result
end

#for_each(&callback) ⇒ Object



87
88
89
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 87

def for_each(&callback)
  self.each { |key, value| callback.call(key, value) }
end

#get(key) ⇒ Object



32
33
34
35
36
37
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 32

def get(key)
  k = serialize_key(key).join('')
  v = @hashmap[k]

  v.nil? ? nil : deserialize_value(v)
end

#get_add(key, value) ⇒ Object



65
66
67
68
69
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 65

def get_add(key, value)
  prev = get(key)
  add(key, value)
  prev
end

#get_raw(key) ⇒ Object



91
92
93
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 91

def get_raw(key)
  hashmap[key.join('')]
end

#get_replace(key, value) ⇒ Object



71
72
73
74
75
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 71

def get_replace(key, value)
  prev = get(key)
  replace(key, value)
  prev
end

#get_set(key, value) ⇒ Object



59
60
61
62
63
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 59

def get_set(key, value)
  prev = get(key)
  set(key, value)
  prev
end

#has(key) ⇒ Object



39
40
41
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 39

def has(key)
  !get(key).nil?
end

#is_empty?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 83

def is_empty?
  hashmap.size == 0
end

#replace(key, value) ⇒ Object



55
56
57
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 55

def replace(key, value)
  has(key) ? set(key, value) : self
end

#serializeObject



114
115
116
117
118
119
120
121
122
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 114

def serialize
  nodes = sort_hashmap

  if nodes.empty?
    raise 'Hashmap: can\'t be empty. It must contain at least 1 key-value pair.'
  end

  Hashmap.serialize_edge(nodes)
end

#set(key, value) ⇒ Object



43
44
45
46
47
48
49
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 43

def set(key, value)
  k = serialize_key(key).join('')
  v = serialize_value(value)
  @hashmap[k] = v

  self
end

#set_raw(key, value) ⇒ Object



95
96
97
98
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 95

def set_raw(key, value)
  hashmap[key.join('')] = value
  self
end

#sort_hashmapObject



100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ton-sdk-ruby/boc/hashmap.rb', line 100

def sort_hashmap
  sorted = hashmap.reduce([]) do |acc, (bitstring, value)|
    key = bitstring.chars.map(&:to_i)
    # Sort keys by DESC to serialize labels correctly later
    order = bitstring.to_i(2)
    lt = acc.find_index { |el| order > el[:order] }
    index = lt ? lt : acc.length

    acc.insert(index, { order: order, key: key, value: value })
  end

  sorted.map { |el| [el[:key], el[:value]] }
end