scale.rb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/scale/base.rb ADDED
@@ -0,0 +1,322 @@
1
+ module Scale
2
+ module Types
3
+
4
+ module SingleValue
5
+ attr_reader :value
6
+
7
+ def initialize(value)
8
+ @value = value
9
+ end
10
+
11
+ def ==(another)
12
+ self.value == another.value
13
+ end
14
+ end
15
+
16
+ # value: one of nil, false, true, scale object
17
+ module Option
18
+ include SingleValue
19
+
20
+ module ClassMethods
21
+ def decode(scale_bytes)
22
+ byte = scale_bytes.get_next_bytes(1)
23
+ if byte == [0]
24
+ return self.new(nil)
25
+ elsif byte == [1]
26
+ if self::INNER_TYPE_STR == 'boolean'
27
+ return self.new(false)
28
+ else
29
+ # big process
30
+ value = type_of(self::INNER_TYPE_STR).decode(scale_bytes)
31
+ return self.new(value)
32
+ end
33
+ elsif byte == [2]
34
+ if self::INNER_TYPE_STR == 'boolean'
35
+ return self.new(true)
36
+ else
37
+ raise "bad data"
38
+ end
39
+ else
40
+ raise "bad data"
41
+ end
42
+ end
43
+
44
+ def inner_type(type_str)
45
+ self.const_set(:INNER_TYPE_STR, type_str)
46
+ end
47
+ end
48
+
49
+ def self.included(base)
50
+ base.extend(ClassMethods)
51
+ end
52
+
53
+ def encode
54
+ # TODO: add Null type
55
+ if self.value.nil?
56
+ "00"
57
+ else
58
+ return "02" if self.value.class == TrueClass && self.value === true
59
+ return "01" if self.value.class == FalseClass && self.value === false
60
+ "01" + self.value.encode
61
+ end
62
+ end
63
+ end
64
+
65
+ module FixedWidthUInt
66
+ include SingleValue
67
+
68
+ module ClassMethods
69
+ def decode(scale_bytes)
70
+ class_name = self.to_s
71
+ bytes = scale_bytes.get_next_bytes self::BYTES_LENGTH
72
+ bytes_reversed = bytes.reverse
73
+ hex = bytes_reversed.reduce('0x') { |hex, byte| hex + byte.to_s(16).rjust(2, '0') }
74
+ self.new(hex.to_i(16))
75
+ end
76
+ end
77
+
78
+ def self.included(base)
79
+ base.extend(ClassMethods)
80
+ end
81
+
82
+ def encode
83
+ bytes = self.value.to_s(16).rjust(self.class::BYTES_LENGTH*2, '0').scan(/.{2}/).reverse.map {|hex| hex.to_i(16) }
84
+ bytes.bytes_to_hex[2..]
85
+ end
86
+ end
87
+
88
+
89
+ module Struct
90
+ include SingleValue
91
+ # new(1.to_u32, U32(69))
92
+ module ClassMethods
93
+ def decode(scale_bytes)
94
+ item_values = self::ITEM_TYPE_STRS.map do |item_type_str|
95
+ type_of(item_type_str).decode(scale_bytes)
96
+ end
97
+
98
+ value = {}
99
+ self::ITEM_NAMES.zip(item_values) do |attr, val|
100
+ value[attr] = val
101
+ end
102
+
103
+ result = self.new(value)
104
+ value.each_pair do |attr, val|
105
+ result.send "#{attr}=", val
106
+ end
107
+ return result
108
+ end
109
+
110
+ def items(**items)
111
+ attr_names = []
112
+ attr_type_strs = []
113
+
114
+ items.each_pair do |attr_name, attr_type_str|
115
+ attr_names << attr_name
116
+ attr_type_strs << attr_type_str
117
+ end
118
+
119
+ self.const_set(:ITEM_NAMES, attr_names)
120
+ self.const_set(:ITEM_TYPE_STRS, attr_type_strs)
121
+ self.attr_accessor *attr_names
122
+ end
123
+ end
124
+
125
+ def self.included(base)
126
+ base.extend ClassMethods
127
+ end
128
+
129
+ def encode
130
+ [].tap do |result|
131
+ self.value.each_pair do |attr_name, attr_value|
132
+ result << attr_value.encode
133
+ end
134
+ end.join
135
+ end
136
+ end
137
+
138
+ module Tuple
139
+ include SingleValue
140
+
141
+ module ClassMethods
142
+ def decode(scale_bytes)
143
+ values = self::TYPE_STRS.map do |type_str|
144
+ type_of(type_str).decode(scale_bytes)
145
+ end
146
+ return self.new(values)
147
+ end
148
+
149
+ def inner_types(*type_strs)
150
+ self.const_set(:TYPE_STRS, type_strs)
151
+ end
152
+ end
153
+
154
+ def self.included(base)
155
+ base.extend(ClassMethods)
156
+ end
157
+
158
+ def encode
159
+ self.value.map(&:encode).join
160
+ end
161
+ end
162
+
163
+ module Enum
164
+ include SingleValue
165
+
166
+ module ClassMethods
167
+ def decode(scale_bytes)
168
+ index = scale_bytes.get_next_bytes(1)[0]
169
+ if self.const_defined? "ITEM_NAMES"
170
+ item_type_str = self::ITEM_TYPE_STRS[index]
171
+ raise "There is no such member with index #{index} for enum #{self}" if item_type_str.nil?
172
+ value = type_of(item_type_str).decode(scale_bytes)
173
+ return self.new(value)
174
+ else
175
+ value = self::VALUES[index]
176
+ return self.new(value)
177
+ end
178
+ end
179
+
180
+ def items(**items)
181
+ attr_names = []
182
+ attr_type_strs = []
183
+
184
+ items.each_pair do |attr_name, attr_type_str|
185
+ attr_names << attr_name
186
+ attr_type_strs << attr_type_str
187
+ end
188
+
189
+ self.const_set(:ITEM_NAMES, attr_names)
190
+ self.const_set(:ITEM_TYPE_STRS, attr_type_strs)
191
+ end
192
+
193
+ def values(*values)
194
+ self.const_set(:VALUES, values)
195
+ end
196
+ end
197
+
198
+ def self.included(base)
199
+ base.extend(ClassMethods)
200
+ end
201
+
202
+ def encode
203
+ if self.class.const_defined? "ITEM_NAMES"
204
+ value_type_str = self.value.class.to_s.split("::").last.to_s
205
+ index = self::class::ITEM_TYPE_STRS.index(value_type_str).to_s(16).rjust(2, '0')
206
+ index + self.value.encode
207
+ else
208
+ self::class::VALUES.index(self.value).to_s(16).rjust(2, '0')
209
+ end
210
+ end
211
+ end
212
+
213
+ module Vec
214
+ include SingleValue # value is an array
215
+
216
+ module ClassMethods
217
+ def decode(scale_bytes, raw=false)
218
+ number = Scale::Types::Compact.decode(scale_bytes).value
219
+ items = []
220
+ number.times do
221
+ item = type_of(self::INNER_TYPE_STR).decode(scale_bytes)
222
+ items << item
223
+ end
224
+ raw ? items : self.new(items)
225
+ end
226
+
227
+ def inner_type(type_str)
228
+ self.const_set(:INNER_TYPE_STR, type_str)
229
+ end
230
+ end
231
+
232
+ def self.included(base)
233
+ base.extend ClassMethods
234
+ end
235
+
236
+ def encode
237
+ number = Scale::Types::Compact.new(self.value.length).encode
238
+ [number].tap do |result|
239
+ self.value.each do |element|
240
+ result << element.encode
241
+ end
242
+ end.join
243
+ end
244
+ end
245
+
246
+ module Set
247
+ include SingleValue
248
+
249
+ module ClassMethods
250
+ def decode(scale_bytes)
251
+ value = "Scale::Types::U#{self::BYTES_LENGTH*8}".constantize.decode(scale_bytes).value
252
+ return self.new [] if not value || value <= 0
253
+
254
+ result = self::VALUES.select{ |_, mask| value & mask > 0 }.keys
255
+ return self.new result
256
+ end
257
+
258
+ # values is a hash:
259
+ # {
260
+ # "TransactionPayment" => 0b00000001,
261
+ # "Transfer" => 0b00000010,
262
+ # "Reserve" => 0b00000100,
263
+ # ...
264
+ # }
265
+ def values(values, bytes_length=1)
266
+ raise "byte length is wrong: #{bytes_length}" if not [1, 2, 4, 8, 16].include?(bytes_length)
267
+ self.const_set(:VALUES, values)
268
+ self.const_set(:BYTES_LENGTH, bytes_length)
269
+ end
270
+ end
271
+
272
+ def self.included(base)
273
+ base.extend ClassMethods
274
+ end
275
+
276
+ def encode
277
+ value = self.class::VALUES.select{ |str, _| self.value.include?(str) }.values.sum
278
+ "Scale::Types::U#{self.class::BYTES_LENGTH*8}".constantize.new(value).encode
279
+ end
280
+ end
281
+
282
+ module VecU8FixedLength
283
+ include SingleValue
284
+
285
+ module ClassMethods
286
+ def decode(scale_bytes)
287
+ class_name = self.to_s
288
+ length = class_name[class_name.length-1]
289
+ raise "length is wrong: #{length}" if not ["2", "3", "4", "8", "16", "20", "32", "64"].include?(length)
290
+ length = length.to_i
291
+
292
+ bytes = scale_bytes.get_next_bytes(length)
293
+ str = bytes.pack("C*").force_encoding("utf-8")
294
+ if str.valid_encoding?
295
+ self.new str
296
+ else
297
+ self.new bytes.bytes_to_hex
298
+ end
299
+ end
300
+ end
301
+
302
+ def self.included(base)
303
+ base.extend ClassMethods
304
+ end
305
+
306
+ def encode
307
+ class_name = self.class.to_s
308
+ length = class_name[class_name.length-1]
309
+ raise "length is wrong: #{length}" if not ["2", "3", "4", "8", "16", "20", "32", "64"].include?(length)
310
+ length = length.to_i
311
+
312
+ if self.value.start_with?("0x") && self.value.length == (length*2+2)
313
+ self.value[2..]
314
+ else
315
+ bytes = self.value.unpack("C*")
316
+ bytes.bytes_to_hex[2..]
317
+ end
318
+ end
319
+ end
320
+
321
+ end
322
+ end