rlp-lite 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b94a62e8d77c1f5752f02e1088db2394fb281800a444fc744bcb17e2c8bb1d3f
4
- data.tar.gz: 4708183e54acac23ef32ebd0f74c2b78cdced4778752dbe6b2d51260363dd6b8
3
+ metadata.gz: 9ee2acc28b11b60da5de5a0609e09ab49f4b256b8cd18a78b56cc5fbf3608620
4
+ data.tar.gz: 8cc0a1436872cf91951b860e6302b5ef71369cb7e429037c1ae81f5697eb7acd
5
5
  SHA512:
6
- metadata.gz: 5fadf8e0fe94f689ba0ee198bd7e2d94c5bd6d606261d26c2308a2a676fe9ddb9e5489ec00ff31b2e592987727982b02bdeb48dea3ee105c8ab8ca1fa8b9988a
7
- data.tar.gz: 390a76f7d97500ee445e653a98eb250ff254f0936167e6900444f5e3540439453023f463c7272ed5d453416d2f2d2a46cd884736dc9399f26b6b1eb95c1c1f0a
6
+ metadata.gz: d652299872dfa39154cf5799123763acd24d4292e41519409db8d9e77f72d7e4f5ff1b38e1c090138fd2f046048899fda8bb8987f8cb64e1bdf1408af749e882
7
+ data.tar.gz: 620a9e400cbf5c915ddbc81ae0c8e11e0d6991c4feade7cd5c2d2f1801aa75745bd4ec1490e14ec27c86d058f2d19642dc421de0d6ea52396b5cc519e531a47c
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
- # Recursive Length Prefix (RLP) Lite
1
+ # Recursive-Length Prefix (RLP) Serialization Lite
2
2
 
3
3
 
4
- rlp-lite - light-weight machinery to serialize / deserialze via rlp
4
+ rlp-lite - light-weight ("lite") machinery to serialize / deserialze using the recursive-length prefix (rlp) scheme
5
5
 
6
6
 
7
7
 
@@ -15,8 +15,235 @@ rlp-lite - light-weight machinery to serialize / deserialze via rlp
15
15
 
16
16
  ## Usage
17
17
 
18
- to be done
19
18
 
19
+ ``` ruby
20
+ require 'rlp-lite'
21
+
22
+ ######
23
+ ## encode
24
+
25
+ list = ['ruby', 'rlp', 255]
26
+
27
+ encoded = Rlp.encode( list )
28
+ #=> "\xCB\x84ruby\x83rlp\x81\xFF".b
29
+
30
+ #######
31
+ ## decode
32
+
33
+ decoded = Rlp.decode( "\xCB\x84ruby\x83rlp\x81\xFF".b )
34
+ #=> ["ruby", "rlp", "\xFF".b]
35
+ ```
36
+
37
+
38
+
39
+ Note: All integers get returned (decoded) as big-endian integers in binary buffers (that is, string with binary "ASCII-8BIT" encoding)
40
+ e.g. `"\xFF".b` and not `255`.
41
+
42
+
43
+
44
+ More examples from the official ethereum rlp tests,
45
+ see [test/data/rlptest.json](test/data/rlptest.json).
46
+
47
+
48
+ ``` ruby
49
+ ######
50
+ # lists of lists
51
+ obj = [ [ [], [] ], [] ]
52
+ encoded = Rlp.encode( obj )
53
+ #=> "\xC4\xC2\xC0\xC0\xC0".b
54
+
55
+ decoded = Rlp.decode( "\xC4\xC2\xC0\xC0\xC0".b )
56
+ #=> [[[], []], []]
57
+
58
+ # or using a hex string (not a binary buffer)
59
+ decoded = Rlp.decode( "0xc4c2c0c0c0" )
60
+ #=> [[[], []], []]
61
+
62
+
63
+ #####
64
+ # dict(onary)
65
+ obj = [["key1", "val1"],
66
+ ["key2", "val2"],
67
+ ["key3", "val3"],
68
+ ["key4", "val4"]]
69
+ encoded = Rlp.encode( obj )
70
+ #=> "\xEC\xCA\x84key1\x84val1\xCA\x84key2\x84val2\xCA\x84key3\x84val3\xCA\x84key4\x84val4".b
71
+
72
+ decoded = Rlp.decode( "\xEC\xCA\x84key1\x84val1\xCA\x84key2\x84val2\xCA\x84key3\x84val3\xCA\x84key4\x84val4".b )
73
+ #=> [["key1", "val1"], ["key2", "val2"], ["key3", "val3"], ["key4", "val4"]]
74
+
75
+ # or using a hex string (not a binary buffer)
76
+ decoded = Rlp.decode( "0xecca846b6579318476616c31ca846b6579328476616c32ca846b6579338476616c33ca846b6579348476616c34" )
77
+ #=> [["key1", "val1"], ["key2", "val2"], ["key3", "val3"], ["key4", "val4"]]
78
+ ```
79
+
80
+
81
+
82
+ ## More About the Recursive-Length Prefix (RLP) Serialization
83
+
84
+ via [Recursive-Length Prefix (RLP) Serialization](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/)
85
+
86
+
87
+ The RLP encoding function takes in an item. An item is defined as follows:
88
+
89
+ - a string (i.e. byte array) is an item
90
+ - a list of items is an item
91
+
92
+ For example, all of the following are items:
93
+
94
+ - an empty string;
95
+ - the string containing the word "cat";
96
+ - a list containing any number of strings;
97
+ - and a more complex data structures like `["cat", ["puppy", "cow"], "horse", [[]], "pig", [""], "sheep"]`.
98
+
99
+ Note that in the context of the rest of this page, 'string' means "a certain number of bytes of binary data"; no special encodings are used, and no knowledge about the content of the strings is implied.
100
+
101
+ RLP encoding is defined as follows:
102
+
103
+ - For a single byte whose value is in the `[0x00, 0x7f]` (decimal `[0, 127]`) range, that byte is its own RLP encoding.
104
+ - Otherwise, if a string is 0-55 bytes long, the RLP encoding consists of a single byte with value **0x80** (dec. 128) plus the length of the string followed by the string. The range of the first byte is thus `[0x80, 0xb7]` (dec. `[128, 183]`).
105
+ - If a string is more than 55 bytes long, the RLP encoding consists of a single byte with value **0xb7** (dec. 183) plus the length in bytes of the length of the string in binary form, followed by the length of the string, followed by the string. For example, a 1024 byte long string would be encoded as `\xb9\x04\x00` (dec. `185, 4, 0`) followed by the string. Here, `0xb9` (183 + 2 = 185) as the first byte, followed by the 2 bytes `0x0400` (dec. 1024) that denote the length of the actual string. The range of the first byte is thus `[0xb8, 0xbf]` (dec. `[184, 191]`).
106
+ - If the total payload of a list (i.e. the combined length of all its items being RLP encoded) is 0-55 bytes long, the RLP encoding consists of a single byte with value **0xc0** plus the length of the list followed by the concatenation of the RLP encodings of the items. The range of the first byte is thus `[0xc0, 0xf7]` (dec. `[192, 247]`).
107
+ - If the total payload of a list is more than 55 bytes long, the RLP encoding consists of a single byte with value **0xf7** plus the length in bytes of the length of the payload in binary form, followed by the length of the payload, followed by the concatenation of the RLP encodings of the items. The range of the first byte is thus `[0xf8, 0xff]` (dec. `[248, 255]`).
108
+
109
+ In code, this is:
110
+
111
+ ```ruby
112
+ PRIMITIVE_PREFIX_OFFSET = 0x80 # The RLP primitive type offset (dec. 128).
113
+ LIST_PREFIX_OFFSET = 0xc0 # The RLP array type offset (dec. 192).
114
+
115
+ def rlp_encode( input )
116
+ if input.instance_of?( String )
117
+ if input.length == 1 && input.ord < PRIMITIVE_PREFIX_OFFSET
118
+ input
119
+ else
120
+ encode_length( input.length, PRIMITIVE_PREFIX_OFFSET ) + input
121
+ end
122
+ elsif input.instance_of?( Array )
123
+ output = ''
124
+ input.each do |item|
125
+ output += rlp_encode( item )
126
+ end
127
+ encode_length( output.length, LIST_PREFIX_OFFSET ) + output
128
+ else
129
+ raise ArgumentError, "type error"
130
+ end
131
+ end
132
+
133
+ def encode_length( l, offset )
134
+ if l < 56
135
+ (l + offset).chr
136
+ elsif l < 256**8 ## 256**8 = 18446744073709551616
137
+ bl = to_binary( l )
138
+ (bl.length + offset + 55).chr + bl
139
+ else
140
+ raise ArgumentError, "input too long"
141
+ end
142
+ end
143
+
144
+ def to_binary(x)
145
+ x == 0 ? '' : to_binary( x / 256 ) + (x % 256).chr
146
+ end
147
+ ```
148
+
149
+
150
+ **Examples**
151
+
152
+ - the string "dog" = [ 0x83, 'd', 'o', 'g' ]
153
+ - the list [ "cat", "dog" ] = `[ 0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g' ]`
154
+ - the empty string ('null') = `[ 0x80 ]`
155
+ - the empty list = `[ 0xc0 ]`
156
+ - the integer 0 = `[ 0x80 ]`
157
+ - the encoded integer 0 ('\\x00') = `[ 0x00 ]`
158
+ - the encoded integer 15 ('\\x0f') = `[ 0x0f ]`
159
+ - the encoded integer 1024 ('\\x04\\x00') = `[ 0x82, 0x04, 0x00 ]`
160
+ - the [set theoretical representation](http://en.wikipedia.org/wiki/Set-theoretic_definition_of_natural_numbers) of three, `[ [], [[]], [ [], [[]] ] ] = [ 0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0 ]`
161
+ - the string "Lorem ipsum dolor sit amet, consectetur adipisicing elit" = `[ 0xb8, 0x38, 'L', 'o', 'r', 'e', 'm', ' ', ... , 'e', 'l', 'i', 't' ]`
162
+
163
+
164
+ **RLP decoding**
165
+
166
+ According to the rules and process of RLP encoding, the input of RLP decode is regarded as an array of binary data. The RLP decoding process is as follows:
167
+
168
+ 1. according to the first byte (i.e. prefix) of input data and decoding the data type, the length of the actual data and offset;
169
+
170
+ 2. according to the type and offset of data, decode the data correspondingly;
171
+
172
+ 3. continue to decode the rest of the input;
173
+
174
+ Among them, the rules of decoding data types and offset is as follows:
175
+
176
+ 1. the data is a string if the range of the first byte (i.e. prefix) is [0x00, 0x7f], and the string is the first byte itself exactly;
177
+
178
+ 2. the data is a string if the range of the first byte is [0x80, 0xb7], and the string whose length is equal to the first byte minus 0x80 follows the first byte;
179
+
180
+ 3. the data is a string if the range of the first byte is [0xb8, 0xbf], and the length of the string whose length in bytes is equal to the first byte minus 0xb7 follows the first byte, and the string follows the length of the string;
181
+
182
+ 4. the data is a list if the range of the first byte is [0xc0, 0xf7], and the concatenation of the RLP encodings of all items of the list which the total payload is equal to the first byte minus 0xc0 follows the first byte;
183
+
184
+ 5. the data is a list if the range of the first byte is [0xf8, 0xff], and the total payload of the list whose length is equal to the first byte minus 0xf7 follows the first byte, and the concatenation of the RLP encodings of all items of the list follows the total payload of the list;
185
+
186
+ In code, this is:
187
+
188
+ ```ruby
189
+ def rlp_decode( input, output=[] )
190
+ return output[0] if input.length == 0
191
+
192
+ offset, dataLen, type = decode_length( input )
193
+
194
+ if type == String
195
+ output << input[ offset, dataLen ]
196
+ elsif type == Array
197
+ list = []
198
+ rlp_decode( input[ offset, dataLen], list )
199
+ output << list
200
+ else
201
+ raise ArgumentError, "type error"
202
+ end
203
+
204
+ rlp_decode( input[ (offset + dataLen)..-1], output )
205
+ end
206
+
207
+
208
+ def decode_length( input )
209
+ length = input.length
210
+
211
+ raise ArgumentError, "input is null" if length == 0
212
+
213
+ prefix = input[0].ord
214
+ if prefix <= 0x7f
215
+ [0, 1, String]
216
+ elsif prefix <= 0xb7 && length > prefix - 0x80
217
+ strLen = prefix - 0x80
218
+ [1, strLen, String]
219
+ elsif prefix <= 0xbf && length > prefix - 0xb7 && length > prefix - 0xb7 + to_integer( input[1, prefix - 0xb7] )
220
+ lenOfStrLen = prefix - 0xb7
221
+ strLen = to_integer( input[1, lenOfStrLen] )
222
+ [1 + lenOfStrLen, strLen, String]
223
+ elsif prefix <= 0xf7 && length > prefix - 0xc0
224
+ listLen = prefix - 0xc0
225
+ [1, listLen, Array]
226
+ elsif prefix <= 0xff && length > prefix - 0xf7 && length > prefix - 0xf7 + to_integer( input[1, prefix - 0xf7])
227
+ lenOfListLen = prefix - 0xf7
228
+ listLen = to_integer( input[1, lenOfListLen] )
229
+ [1 + lenOfListLen, listLen, Array]
230
+ else
231
+ raise ArgumentError, "input don't conform RLP encoding form"
232
+ end
233
+ end
234
+
235
+
236
+ def to_integer( b )
237
+ length = b.length
238
+ if length == 0
239
+ raise ArgumentError, "input is null"
240
+ elsif length == 1
241
+ b[0].ord
242
+ else
243
+ b[-1].ord + to_integer( b[0, length-1] ) * 256
244
+ end
245
+ end
246
+ ```
20
247
 
21
248
 
22
249
 
data/Rakefile CHANGED
@@ -2,6 +2,14 @@ require 'hoe'
2
2
  require './lib/rlp-lite/version.rb'
3
3
 
4
4
 
5
+ ###
6
+ # hack/ quick fix for broken intuit_values - overwrite with dummy
7
+ class Hoe
8
+ def intuit_values( input ); end
9
+ end
10
+
11
+
12
+
5
13
  Hoe.spec 'rlp-lite' do
6
14
 
7
15
  self.version = Rlp::VERSION
@@ -1,17 +1,17 @@
1
1
  module Rlp
2
2
 
3
+
3
4
  # Provides an RLP-decoder.
4
- module Decoder
5
- extend self
5
+ class Decoder
6
6
 
7
7
  # Decodes an RLP-encoded object.
8
8
  #
9
9
  # @param rlp [String] an RLP-encoded object.
10
10
  # @return [Object] the decoded and maybe deserialized object.
11
- # @raise [Eth::Rlp::DecodingError] if the input string does not end after
11
+ # @raise [Rlp::DecodingError] if the input string does not end after
12
12
  # the root item.
13
- def perform(rlp)
14
- rlp = Util.hex_to_bin( rlp ) if Util.is_hex?( rlp )
13
+ def perform( rlp )
14
+ rlp = Util.hex_to_bin( rlp ) if Util.is_hex?( rlp )
15
15
  rlp = Util.str_to_bytes( rlp )
16
16
  begin
17
17
  item, next_start = consume_item( rlp, 0 )
@@ -23,6 +23,7 @@
23
23
  end
24
24
 
25
25
 
26
+
26
27
  private
27
28
 
28
29
  # Consume an RLP-encoded item from the given start.
@@ -1,33 +1,40 @@
1
1
 
2
- # Provides an recursive-length prefix (RLP) encoder and decoder.
3
2
  module Rlp
4
3
 
4
+
5
5
  # Provides an RLP-encoder.
6
- module Encoder
7
- extend self
6
+ class Encoder
7
+
8
8
 
9
9
  # Encodes a Ruby object in RLP format.
10
10
  #
11
11
  # @param obj [Object] a Ruby object.
12
12
  # @return [String] the RLP encoded item.
13
- # @raise [Eth::Rlp::EncodingError] in the rather unlikely case that the item
13
+ # @raise [Rlp::EncodingError] in the rather unlikely case that the item
14
14
  # is too big to encode (will not happen).
15
- # @raise [Eth::Rlp::SerializationError] if the serialization fails.
16
- def perform(obj)
15
+ # @raise [Rlp::SerializationError] if the serialization fails.
16
+ def perform( obj )
17
17
  item = Sedes.infer(obj).serialize(obj)
18
18
  result = encode_raw( item )
19
19
  end
20
20
 
21
- private
21
+
22
+ private
22
23
 
23
24
  # Encodes the raw item.
24
- def encode_raw(item)
25
- return item if item.instance_of? Rlp::Data
26
- return encode_primitive item if Sedes.is_primitive? item
27
- return encode_list item if Sedes.is_list? item
28
- raise EncodingError "Cannot encode object of type #{item.class.name}"
25
+ def encode_raw( item )
26
+ if item.instance_of?( Rlp::Data )
27
+ item
28
+ elsif Util.is_primitive?( item )
29
+ encode_primitive item
30
+ elsif Util.is_list?( item )
31
+ encode_list item
32
+ else
33
+ raise EncodingError "Cannot encode object of type #{item.class.name}"
34
+ end
29
35
  end
30
36
 
37
+
31
38
  # Encodes a single primitive.
32
39
  def encode_primitive(item)
33
40
  return Util.str_to_bytes item if item.size == 1 && item.ord < PRIMITIVE_PREFIX_OFFSET
@@ -56,4 +63,4 @@
56
63
  end
57
64
  end
58
65
  end
59
- end # module Rlp
66
+ end # module Rlp
@@ -24,7 +24,7 @@
24
24
  raise SerializationError, "Can only serialize integers" unless obj.is_a?(Integer)
25
25
  raise SerializationError, "Cannot serialize negative integers" if obj < 0
26
26
  raise SerializationError, "Integer too large (does not fit in #{@size} bytes)" if @size && obj >= 256 ** @size
27
- s = obj == 0 ? BYTE_EMPTY : _int_to_big_endian(obj)
27
+ s = obj == 0 ? BYTE_EMPTY : _int_to_big_endian( obj )
28
28
  @size ? "#{BYTE_ZERO * [0, @size - s.size].max}#{s}" : s
29
29
  end
30
30
 
@@ -38,7 +38,7 @@
38
38
  raise DeserializationError, "Invalid serialization (wrong size)" if @size && serial.size != @size
39
39
  raise DeserializationError, "Invalid serialization (not minimal length)" if !@size && serial.size > 0 && serial[0] == BYTE_ZERO
40
40
  serial = serial || BYTE_ZERO
41
- _big_endian_to_int(serial)
41
+ _big_endian_to_int( serial )
42
42
  end
43
43
 
44
44
 
@@ -9,16 +9,18 @@
9
9
  # @param l [Integer] the fixed size of the binary.
10
10
  # @param allow_empty [Boolean] indicator wether empty binaries should be allowed.
11
11
  # @return [Eth::Rlp::Sedes::Binary] a serializable binary of fixed size.
12
- def self.fixed_length(l, allow_empty: false)
13
- new(min_length: l, max_length: l, allow_empty: allow_empty)
12
+ def self.fixed_length( l, allow_empty: false )
13
+ new( min_length: l,
14
+ max_length: l,
15
+ allow_empty: allow_empty )
14
16
  end
15
17
 
16
18
  # Checks wether the given object is of a valid binary type.
17
19
  #
18
20
  # @param obj [Object] the supposed binary item to check.
19
21
  # @return [Boolean] true if valid.
20
- def self.valid_type?(obj)
21
- obj.instance_of? String
22
+ def self.valid_type?( obj )
23
+ obj.instance_of?( String )
22
24
  end
23
25
 
24
26
 
@@ -27,7 +29,7 @@
27
29
  # @param min_length [Integer] the minimum size of the binary.
28
30
  # @param max_length [Integer] the maximum size of the binary.
29
31
  # @param allow_empty [Boolean] indicator wether empty binaries should be allowed.
30
- def initialize(min_length: 0, max_length: INFINITY, allow_empty: false)
32
+ def initialize( min_length: 0, max_length: INFINITY, allow_empty: false )
31
33
  @min_length = min_length
32
34
  @max_length = max_length
33
35
  @allow_empty = allow_empty
@@ -39,13 +41,17 @@
39
41
  # @return [Object] a serialized binary.
40
42
  # @raise [SerializationError] if provided object is of invalid type.
41
43
  # @raise [SerializationError] if provided binary is of invalid length.
42
- def serialize(obj)
43
- raise SerializationError, "Object is not a serializable (#{obj.class})" unless self.class.valid_type? obj
44
- serial = _str_to_bytes( obj )
45
- raise SerializationError, "Object has invalid length" unless valid_length? serial.size
44
+ def serialize( obj )
45
+ raise SerializationError, "Object is not a serializable (#{obj.class})" unless self.class.valid_type?( obj )
46
+
47
+ ## make sure string is with binary encoding (ASCII-8BIT)
48
+ ## note: was Util.str_to_bytes( obj )
49
+ serial = obj.encoding.name == 'ASCII-8BIT' ? obj : obj.b
50
+ raise SerializationError, "Object has invalid length" unless valid_length?( serial.size )
46
51
  serial
47
52
  end
48
53
 
54
+
49
55
  # Deserializes a binary.
50
56
  #
51
57
  # @param serial [Object] the serialized binary.
@@ -53,8 +59,8 @@
53
59
  # @raise [DeserializationError] if provided serial is of wrong type.
54
60
  # @raise [DeserializationError] if provided serial is of wrong length.
55
61
  def deserialize(serial)
56
- raise DeserializationError, "Objects of type #{serial.class} cannot be deserialized" unless _is_primitive? serial
57
- raise DeserializationError, "#{serial.class} has invalid length" unless valid_length? serial.size
62
+ raise DeserializationError, "Objects of type #{serial.class} cannot be deserialized" unless serial.instance_of?(String)
63
+ raise DeserializationError, "#{serial.class} has invalid length" unless valid_length?( serial.size )
58
64
  serial
59
65
  end
60
66
 
@@ -63,39 +69,11 @@
63
69
  #
64
70
  # @param length [Integer] the supposed length of the binary item.
65
71
  # @return [Boolean] true if valid.
66
- def valid_length?(length)
72
+ def valid_length?( length )
67
73
  (@min_length <= length && length <= @max_length) ||
68
74
  (@allow_empty && length == 0)
69
75
  end
70
76
 
71
- #######
72
- # private helpers
73
-
74
- # Converts a binary string to bytes.
75
- #
76
- # @param str [String] binary string to be converted.
77
- # @return [Object] the string bytes.
78
- def _str_to_bytes(str)
79
- _is_bytes?(str) ? str : str.b
80
- end
81
-
82
- # Checks if a string is a byte-string.
83
- #
84
- # @param str [String] a string to check.
85
- # @return [Boolean] true if it's an ASCII-8bit encoded byte-string.
86
- def _is_bytes?(str)
87
- str && str.instance_of?(String) && str.encoding.name == 'ASCII-8BIT'
88
- end
89
-
90
- # Checks if the given item is a string primitive.
91
- #
92
- # @param item [Object] the item to check.
93
- # @return [Boolean] true if it's a string primitive.
94
- def _is_primitive?(item)
95
- item.instance_of?(String)
96
- end
97
-
98
-
99
77
  end # class Binary
100
78
 
101
79
 
@@ -9,14 +9,14 @@
9
9
  #
10
10
  # @param elements [Array] an array indicating the structure of the list.
11
11
  # @param strict [Boolean] an option to enforce the given structure.
12
- def initialize(elements: [], strict: true)
12
+ def initialize( elements: [], strict: true )
13
13
  super()
14
14
 
15
15
  @strict = strict
16
16
  elements.each do |e|
17
17
  if Sedes.is_sedes?(e)
18
18
  push e
19
- elsif Sedes.is_list?(e)
19
+ elsif Util.is_list?(e)
20
20
  push List.new(elements: e)
21
21
  else
22
22
  raise TypeError, "Instances of List must only contain sedes objects or nested sequences thereof."
@@ -30,8 +30,8 @@
30
30
  # @return [Array] a serialized list.
31
31
  # @raise [SerializationError] if provided array is not a sequence.
32
32
  # @raise [SerializationError] if provided array is of wrong length.
33
- def serialize(obj)
34
- raise SerializationError, "Can only serialize sequences" unless Sedes.is_list?(obj)
33
+ def serialize( obj )
34
+ raise SerializationError, "Can only serialize sequences" unless Util.is_list?(obj)
35
35
  raise SerializationError, "List has wrong length" if (@strict && self.size != obj.size) || self.size < obj.size
36
36
  result = []
37
37
  obj.zip(self).each_with_index do |(element, sedes), i|
@@ -47,7 +47,7 @@
47
47
  # @raise [DeserializationError] if provided serial is not a sequence.
48
48
  # @raise [DeserializationError] if provided serial is of wrong length.
49
49
  def deserialize(serial)
50
- raise DeserializationError, "Can only deserialize sequences" unless Sedes.is_list?(serial)
50
+ raise DeserializationError, "Can only deserialize sequences" unless Util.is_list?(serial)
51
51
  raise DeserializationError, "List has wrong length" if @strict && serial.size != self.size
52
52
  result = []
53
53
  len = [serial.size, self.size].min
@@ -3,8 +3,7 @@
3
3
  module Rlp
4
4
  module Sedes
5
5
 
6
- # Provides a singleton {Rlp::Sedes} class to infer objects and types.
7
- class << self
6
+ # Provides a {Rlp::Sedes} module to infer objects and types.
8
7
 
9
8
  # Tries to find a sedes objects suitable for a given Ruby object.
10
9
  #
@@ -14,12 +13,18 @@
14
13
  #
15
14
  # @param obj [Object] the Ruby object for which to find a sedes object.
16
15
  # @raise [TypeError] if no appropriate sedes could be found.
17
- def infer(obj)
18
- return obj.class if is_sedes?( obj.class )
19
- return big_endian_int if obj.is_a?(Integer) && obj >= 0
20
- return binary if Binary.valid_type? obj
21
- return List.new(elements: obj.map { |item| infer( item ) }) if is_list?( obj )
22
- raise TypeError, "Did not find sedes handling type #{obj.class.name}"
16
+ def self.infer( obj )
17
+ if is_sedes?( obj.class )
18
+ obj.class
19
+ elsif obj.is_a?(Integer) && obj >= 0
20
+ big_endian_int
21
+ elsif Binary.valid_type?( obj ) ## note: same as obj.is_a?( String )
22
+ binary
23
+ elsif Util.is_list?( obj )
24
+ List.new( elements: obj.map { |item| infer( item ) } )
25
+ else
26
+ raise TypeError, "Did not find sedes handling type #{obj.class.name}"
27
+ end
23
28
  end
24
29
 
25
30
 
@@ -27,7 +32,7 @@
27
32
  #
28
33
  # @param obj [Object] the object to check.
29
34
  # @return [Boolean] true if it's serializable and deserializable.
30
- def is_sedes?(obj)
35
+ def self.is_sedes?(obj)
31
36
  obj.respond_to?(:serialize) && obj.respond_to?(:deserialize)
32
37
  end
33
38
 
@@ -35,39 +40,19 @@
35
40
  # unspecified length.
36
41
  #
37
42
  # @return [Rlp::Sedes::BigEndianInt] a big-endian, unsigned integer sedes.
38
- def big_endian_int
43
+ def self.big_endian_int
39
44
  @big_endian_int ||= BigEndianInt.new
40
45
  end
41
46
 
42
47
  # A utility to use a binary sedes type.
43
48
  #
44
49
  # @return [Rlp::Sedes::Binary] a binary sedes.
45
- def binary
50
+ def self.binary
46
51
  @binary ||= Binary.new
47
52
  end
48
53
 
49
- ##############################
50
- ### more helpers
51
54
 
52
- # Checks if the given item is a string primitive.
53
- #
54
- # @param item [Object] the item to check.
55
- # @return [Boolean] true if it's a string primitive.
56
- def is_primitive?(item)
57
- item.instance_of?(String)
58
- end
59
55
 
60
- # Checks if the given item is a list.
61
- #
62
- # @param item [Object] the item to check.
63
- # @return [Boolean] true if it's a list.
64
- def is_list?(item)
65
- !is_primitive?(item) && item.respond_to?(:each)
66
- end
67
56
 
68
-
69
-
70
-
71
- end
72
- end
73
- end # module Rlp
57
+ end # module Sedes
58
+ end # module Rlp
data/lib/rlp-lite/util.rb CHANGED
@@ -4,15 +4,13 @@ module Util
4
4
  extend self
5
5
 
6
6
 
7
-
8
-
9
- # Checks if a string is hex-adecimal.
7
+ # Checks if a string is hex-adecimal (string).
10
8
  #
11
9
  # @param str [String] a string to be checked.
12
10
  # @return [String] a match if true; `nil` if not.
13
- def is_hex?(str)
14
- return false unless str.is_a? String
15
- str = remove_hex_prefix str
11
+ def is_hex?( str )
12
+ return false unless str.is_a?( String )
13
+ str = strip_hex_prefix( str )
16
14
  str.match /\A[0-9a-fA-F]*\z/
17
15
  end
18
16
 
@@ -20,18 +18,25 @@ module Util
20
18
  #
21
19
  # @param hex [String] a prefixed hex-string.
22
20
  # @return [String] an unprefixed hex-string.
23
- def remove_hex_prefix(hex)
24
- return hex[2..-1] if is_prefixed? hex
25
- return hex
21
+ def strip_hex_prefix(hex)
22
+ is_prefixed?( hex ) ? hex[2..-1] : hex
26
23
  end
24
+ alias_method :remove_hex_prefix, :strip_hex_prefix
25
+ alias_method :strip_0x, :strip_hex_prefix ## add more alias - why? why not?
27
26
 
28
27
  # Checks if a string is prefixed with `0x`.
29
28
  #
30
29
  # @param hex [String] a string to be checked.
31
30
  # @return [String] a match if true; `nil` if not.
32
31
  def is_prefixed?(hex)
33
- hex.match /\A0x/
32
+ ## was: hex.match /\A0x/
33
+ ## tood/check: add support for (upcase) 0X too - why? why not?
34
+ hex.start_with?( '0x' ) ||
35
+ hex.start_with?( '0X' )
34
36
  end
37
+ alias_method :is_hex_prefixed?, :is_prefixed?
38
+ alias_method :start_with_0x?, :is_prefixed?
39
+
35
40
 
36
41
  # Packs a hexa-decimal string into a binary string. Also works with
37
42
  # `0x`-prefixed strings.
@@ -39,9 +44,9 @@ module Util
39
44
  # @param hex [String] a hexa-decimal string to be packed.
40
45
  # @return [String] a packed binary string.
41
46
  # @raise [TypeError] if value is not a string or string is not hex.
42
- def hex_to_bin(hex)
47
+ def hex_to_bin( hex )
43
48
  raise TypeError, "Value must be an instance of String" unless hex.instance_of? String
44
- hex = remove_hex_prefix hex
49
+ hex = remove_hex_prefix( hex )
45
50
  raise TypeError, "Non-hexadecimal digit found" unless is_hex? hex
46
51
  [hex].pack("H*")
47
52
  end
@@ -64,6 +69,7 @@ module Util
64
69
  def str_to_bytes(str)
65
70
  is_bytes?(str) ? str : str.b
66
71
  end
72
+ ## todo/check - rename to str_to_binary - why? why not?
67
73
 
68
74
  # Checks if a string is a byte-string.
69
75
  #
@@ -72,6 +78,7 @@ module Util
72
78
  def is_bytes?(str)
73
79
  str && str.instance_of?(String) && str.encoding.name == 'ASCII-8BIT'
74
80
  end
81
+ ## todo/check - rename to is binary? is_binary?
75
82
 
76
83
 
77
84
 
@@ -79,10 +86,10 @@ module Util
79
86
  #
80
87
  # @param num [Integer] integer to be converted.
81
88
  # @return [String] packed, big-endian integer string.
82
- def int_to_big_endian(num)
83
- hex = num.to_s(16) unless is_hex? num
89
+ def int_to_big_endian( num )
90
+ hex = num.to_s(16)
84
91
  hex = "0#{hex}" if hex.size.odd?
85
- hex_to_bin hex
92
+ hex_to_bin( hex )
86
93
  end
87
94
 
88
95
 
@@ -91,18 +98,39 @@ module Util
91
98
  #
92
99
  # @param str [String] big endian to be converted.
93
100
  # @return [Integer] an unpacked integer number.
94
- def big_endian_to_int(str)
95
- str.unpack("H*").first.to_i(16)
101
+ def big_endian_to_int( str )
102
+ str.unpack("H*")[0].to_i(16)
96
103
  end
97
104
 
105
+
106
+
98
107
  # Deserializes big endian data string to integer.
99
108
  #
100
109
  # @param str [String] serialized big endian integer string.
101
110
  # @return [Integer] an deserialized unsigned integer.
102
111
  def deserialize_big_endian_to_int(str)
103
- Sedes.big_endian_int.deserialize str.sub(/\A(\x00)+/, "")
112
+ Sedes.big_endian_int.deserialize str.sub( /\A(\x00)+/, '' )
113
+ end
114
+
115
+
116
+ # Checks if the given item is a string primitive.
117
+ #
118
+ # @param item [Object] the item to check.
119
+ # @return [Boolean] true if it's a string primitive.
120
+ def is_primitive?( item )
121
+ item.instance_of?(String)
104
122
  end
105
123
 
124
+ # Checks if the given item is a list.
125
+ #
126
+ # @param item [Object] the item to check.
127
+ # @return [Boolean] true if it's a list.
128
+ def is_list?( item )
129
+ !is_primitive?(item) && item.respond_to?(:each)
130
+ end
131
+
132
+
133
+
106
134
 
107
135
  end # module Util
108
136
  end # module Rlp
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Rlp
3
- VERSION='0.0.1'
3
+ VERSION='0.1.1'
4
4
  end
5
5
 
data/lib/rlp-lite.rb CHANGED
@@ -21,27 +21,20 @@ module Rlp
21
21
 
22
22
  ## todo/check - use encoding -ascii-8bit for source file or ? - why? why not?
23
23
  ## use #b/.b to ensure binary encoding? - why? why not?
24
- BYTE_EMPTY = "".freeze # The empty byte is defined as "".
25
- BYTE_ZERO = "\x00".freeze # The zero byte is 0x00.
26
- BYTE_ONE = "\x01".freeze # The byte one is 0x01.
27
24
 
25
+ ## todo/check: use auto-freeze string literals magic comment - why? why not?
26
+ BYTE_EMPTY = "".b.freeze # The empty byte is defined as "".
27
+ BYTE_ZERO = "\x00".b.freeze # The zero byte is 0x00.
28
+ BYTE_ONE = "\x01".b.freeze # The byte one is 0x01.
28
29
 
29
30
 
30
- # The RLP short length limit.
31
- SHORT_LENGTH_LIMIT = 56
31
+ SHORT_LENGTH_LIMIT = 56 # The RLP short length limit.
32
+ LONG_LENGTH_LIMIT = (256 ** 8) # The RLP long length limit.
33
+ PRIMITIVE_PREFIX_OFFSET = 0x80 # The RLP primitive type offset.
34
+ LIST_PREFIX_OFFSET = 0xc0 # The RLP array type offset.
32
35
 
33
- # The RLP long length limit.
34
- LONG_LENGTH_LIMIT = (256 ** 8)
35
36
 
36
- # The RLP primitive type offset.
37
- PRIMITIVE_PREFIX_OFFSET = 0x80
38
-
39
- # The RLP array type offset.
40
- LIST_PREFIX_OFFSET = 0xc0
41
-
42
-
43
- # Infinity as constant for convenience.
44
- INFINITY = (1.0 / 0.0)
37
+ INFINITY = (1.0 / 0.0) # Infinity as constant for convenience.
45
38
 
46
39
 
47
40
  # The Rlp module exposes a variety of exceptions grouped as {RlpException}.
@@ -63,18 +56,24 @@ module Rlp
63
56
  class Data < String; end
64
57
 
65
58
 
59
+ def self.encoder() @encoder ||= Encoder.new; end
60
+ def self.decoder() @decoder ||= Decoder.new; end
61
+
62
+
66
63
  # Performes an {Rlp::Encoder} on any ruby object.
67
64
  #
68
65
  # @param obj [Object] any ruby object.
69
66
  # @return [String] a packed, RLP-encoded item.
70
- def self.encode(obj) Encoder.perform( obj ); end
67
+ def self.encode(obj) encoder.perform( obj ); end
71
68
 
72
69
 
73
70
  # Performes an {Rlp::Decoder} on any RLP-encoded item.
74
71
  #
75
72
  # @param rlp [String] a packed, RLP-encoded item.
76
73
  # @return [Object] a decoded ruby object.
77
- def self.decode(rlp) Decoder.perform( rlp ); end
74
+ def self.decode(rlp) decoder.perform( rlp ); end
75
+
76
+
78
77
  end # module Rlp
79
78
 
80
79
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rlp-lite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-16 00:00:00.000000000 Z
11
+ date: 2022-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc