rlp-lite 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b94a62e8d77c1f5752f02e1088db2394fb281800a444fc744bcb17e2c8bb1d3f
4
+ data.tar.gz: 4708183e54acac23ef32ebd0f74c2b78cdced4778752dbe6b2d51260363dd6b8
5
+ SHA512:
6
+ metadata.gz: 5fadf8e0fe94f689ba0ee198bd7e2d94c5bd6d606261d26c2308a2a676fe9ddb9e5489ec00ff31b2e592987727982b02bdeb48dea3ee105c8ab8ca1fa8b9988a
7
+ data.tar.gz: 390a76f7d97500ee445e653a98eb250ff254f0936167e6900444f5e3540439453023f463c7272ed5d453416d2f2d2a46cd884736dc9399f26b6b1eb95c1c1f0a
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.0.1 / 2022-11-16
2
+
3
+ * Everything is new. First release
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/rlp-lite.rb
6
+ lib/rlp-lite/decoder.rb
7
+ lib/rlp-lite/encoder.rb
8
+ lib/rlp-lite/sedes.rb
9
+ lib/rlp-lite/sedes/big_endian_int.rb
10
+ lib/rlp-lite/sedes/binary.rb
11
+ lib/rlp-lite/sedes/list.rb
12
+ lib/rlp-lite/util.rb
13
+ lib/rlp-lite/version.rb
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Recursive Length Prefix (RLP) Lite
2
+
3
+
4
+ rlp-lite - light-weight machinery to serialize / deserialze via rlp
5
+
6
+
7
+
8
+ * home :: [github.com/pixelartexchange/artbase](https://github.com/pixelartexchange/artbase)
9
+ * bugs :: [github.com/pixelartexchange/artbase/issues](https://github.com/pixelartexchange/artbase/issues)
10
+ * gem :: [rubygems.org/gems/rlp-lite](https://rubygems.org/gems/rlp-lite)
11
+ * rdoc :: [rubydoc.info/gems/rlp-lite](http://rubydoc.info/gems/rlp-lite)
12
+
13
+
14
+
15
+
16
+ ## Usage
17
+
18
+ to be done
19
+
20
+
21
+
22
+
23
+ ## License
24
+
25
+ The scripts are dedicated to the public domain.
26
+ Use it as you please with no restrictions whatsoever.
27
+
28
+
29
+ ## Questions? Comments?
30
+
31
+
32
+ Post them on the [D.I.Y. Punk (Pixel) Art reddit](https://old.reddit.com/r/DIYPunkArt). Thanks.
33
+
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'hoe'
2
+ require './lib/rlp-lite/version.rb'
3
+
4
+
5
+ Hoe.spec 'rlp-lite' do
6
+
7
+ self.version = Rlp::VERSION
8
+
9
+ self.summary = "rlp-lite - light-weight machinery to serialize / deserialze via rlp (recursive length prefix)"
10
+ self.description = summary
11
+
12
+ self.urls = { home: 'https://github.com/pixelartexchange/artbase' }
13
+
14
+ self.author = 'Gerald Bauer'
15
+ self.email = 'wwwmake@googlegroups.com'
16
+
17
+ # switch extension to .markdown for gihub formatting
18
+ self.readme_file = 'README.md'
19
+ self.history_file = 'CHANGELOG.md'
20
+
21
+ self.extra_deps = [
22
+ ]
23
+
24
+ self.licenses = ['Public Domain']
25
+
26
+ self.spec_extras = {
27
+ required_ruby_version: '>= 2.3'
28
+ }
29
+
30
+ end
31
+
@@ -0,0 +1,97 @@
1
+ module Rlp
2
+
3
+ # Provides an RLP-decoder.
4
+ module Decoder
5
+ extend self
6
+
7
+ # Decodes an RLP-encoded object.
8
+ #
9
+ # @param rlp [String] an RLP-encoded object.
10
+ # @return [Object] the decoded and maybe deserialized object.
11
+ # @raise [Eth::Rlp::DecodingError] if the input string does not end after
12
+ # the root item.
13
+ def perform(rlp)
14
+ rlp = Util.hex_to_bin( rlp ) if Util.is_hex?( rlp )
15
+ rlp = Util.str_to_bytes( rlp )
16
+ begin
17
+ item, next_start = consume_item( rlp, 0 )
18
+ rescue Exception => e
19
+ raise DecodingError, "Cannot decode rlp string: #{e}"
20
+ end
21
+ raise DecodingError, "RLP string ends with #{rlp.size - next_start} superfluous bytes" if next_start != rlp.size
22
+ return item
23
+ end
24
+
25
+
26
+ private
27
+
28
+ # Consume an RLP-encoded item from the given start.
29
+ def consume_item(rlp, start)
30
+ t, l, s = consume_length_prefix rlp, start
31
+ consume_payload rlp, s, t, l
32
+ end
33
+
34
+ # Consume an RLP length prefix at the given position.
35
+ def consume_length_prefix(rlp, start)
36
+ b0 = rlp[start].ord
37
+ if b0 < PRIMITIVE_PREFIX_OFFSET
38
+
39
+ # single byte
40
+ [:str, 1, start]
41
+ elsif b0 < PRIMITIVE_PREFIX_OFFSET + SHORT_LENGTH_LIMIT
42
+ raise DecodingError, "Encoded as short string although single byte was possible" if (b0 - PRIMITIVE_PREFIX_OFFSET == 1) && rlp[start + 1].ord < PRIMITIVE_PREFIX_OFFSET
43
+
44
+ # short string
45
+ [:str, b0 - PRIMITIVE_PREFIX_OFFSET, start + 1]
46
+ elsif b0 < LIST_PREFIX_OFFSET
47
+ enforce_no_zero_bytes rlp, start
48
+
49
+ # long string
50
+ ll = b0 - PRIMITIVE_PREFIX_OFFSET - SHORT_LENGTH_LIMIT + 1
51
+ l = Util.big_endian_to_int rlp[(start + 1)...(start + 1 + ll)]
52
+ raise DecodingError, "Long string prefix used for short string" if l < SHORT_LENGTH_LIMIT
53
+ [:str, l, start + 1 + ll]
54
+ elsif b0 < LIST_PREFIX_OFFSET + SHORT_LENGTH_LIMIT
55
+
56
+ # short list
57
+ [:list, b0 - LIST_PREFIX_OFFSET, start + 1]
58
+ else
59
+ enforce_no_zero_bytes rlp, start
60
+
61
+ # long list
62
+ ll = b0 - LIST_PREFIX_OFFSET - SHORT_LENGTH_LIMIT + 1
63
+ l = Util.big_endian_to_int rlp[(start + 1)...(start + 1 + ll)]
64
+ raise DecodingError, "Long list prefix used for short list" if l < SHORT_LENGTH_LIMIT
65
+ [:list, l, start + 1 + ll]
66
+ end
67
+ end
68
+
69
+ # Enforce RLP slices to not start with empty bytes.
70
+ def enforce_no_zero_bytes(rlp, start)
71
+ raise DecodingError, "Length starts with zero bytes" if rlp.slice(start + 1) == BYTE_ZERO
72
+ end
73
+
74
+ # Consume an RLP payload at the given position of given type and size.
75
+ def consume_payload(rlp, start, type, length)
76
+ case type
77
+ when :str
78
+ [rlp[start...(start + length)], start + length]
79
+ when :list
80
+ items = []
81
+ next_item_start = start
82
+ payload_end = next_item_start + length
83
+ while next_item_start < payload_end
84
+ item, next_item_start = consume_item rlp, next_item_start
85
+ items.push item
86
+ end
87
+ raise DecodingError, "List length prefix announced a too small length" if next_item_start > payload_end
88
+ [items, next_item_start]
89
+ else
90
+ raise TypeError, "Type must be either :str or :list"
91
+ end
92
+ end
93
+ end
94
+
95
+
96
+ end # module Rlp
97
+
@@ -0,0 +1,59 @@
1
+
2
+ # Provides an recursive-length prefix (RLP) encoder and decoder.
3
+ module Rlp
4
+
5
+ # Provides an RLP-encoder.
6
+ module Encoder
7
+ extend self
8
+
9
+ # Encodes a Ruby object in RLP format.
10
+ #
11
+ # @param obj [Object] a Ruby object.
12
+ # @return [String] the RLP encoded item.
13
+ # @raise [Eth::Rlp::EncodingError] in the rather unlikely case that the item
14
+ # is too big to encode (will not happen).
15
+ # @raise [Eth::Rlp::SerializationError] if the serialization fails.
16
+ def perform(obj)
17
+ item = Sedes.infer(obj).serialize(obj)
18
+ result = encode_raw( item )
19
+ end
20
+
21
+ private
22
+
23
+ # 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}"
29
+ end
30
+
31
+ # Encodes a single primitive.
32
+ def encode_primitive(item)
33
+ return Util.str_to_bytes item if item.size == 1 && item.ord < PRIMITIVE_PREFIX_OFFSET
34
+ payload = Util.str_to_bytes item
35
+ prefix = length_prefix payload.size, PRIMITIVE_PREFIX_OFFSET
36
+ "#{prefix}#{payload}"
37
+ end
38
+
39
+ # Encodes a single list.
40
+ def encode_list(list)
41
+ payload = list.map { |item| encode_raw item }.join
42
+ prefix = length_prefix payload.size, LIST_PREFIX_OFFSET
43
+ "#{prefix}#{payload}"
44
+ end
45
+
46
+ # Determines a length prefix.
47
+ def length_prefix(length, offset)
48
+ if length < SHORT_LENGTH_LIMIT
49
+ (offset + length).chr
50
+ elsif length < LONG_LENGTH_LIMIT
51
+ length_string = Util.int_to_big_endian( length )
52
+ length_len = (offset + SHORT_LENGTH_LIMIT - 1 + length_string.size).chr
53
+ "#{length_len}#{length_string}"
54
+ else
55
+ raise EncodingError, "Length greater than 256**8: #{length}"
56
+ end
57
+ end
58
+ end
59
+ end # module Rlp
@@ -0,0 +1,73 @@
1
+
2
+
3
+ module Rlp
4
+ module Sedes
5
+
6
+ # A serializable, big-endian, unsigned integer type.
7
+ class BigEndianInt
8
+
9
+ # Create a serializable, big-endian, unsigned integer.
10
+ #
11
+ # @param size [Integer] the size of the big endian.
12
+ def initialize(size = nil)
13
+ @size = size
14
+ end
15
+
16
+ # Serialize a big-endian integer.
17
+ #
18
+ # @param obj [Integer] the integer to be serialized.
19
+ # @return [String] a serialized big-endian integer.
20
+ # @raise [SerializationError] if provided object is not an integer.
21
+ # @raise [SerializationError] if provided integer is negative.
22
+ # @raise [SerializationError] if provided integer is too big for @size.
23
+ def serialize(obj)
24
+ raise SerializationError, "Can only serialize integers" unless obj.is_a?(Integer)
25
+ raise SerializationError, "Cannot serialize negative integers" if obj < 0
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)
28
+ @size ? "#{BYTE_ZERO * [0, @size - s.size].max}#{s}" : s
29
+ end
30
+
31
+ # Deserializes an unsigned integer.
32
+ #
33
+ # @param serial [String] the serialized integer.
34
+ # @return [Integer] a number.
35
+ # @raise [DeserializationError] if provided serial is of wrong size.
36
+ # @raise [DeserializationError] if provided serial is not of minimal length.
37
+ def deserialize(serial)
38
+ raise DeserializationError, "Invalid serialization (wrong size)" if @size && serial.size != @size
39
+ raise DeserializationError, "Invalid serialization (not minimal length)" if !@size && serial.size > 0 && serial[0] == BYTE_ZERO
40
+ serial = serial || BYTE_ZERO
41
+ _big_endian_to_int(serial)
42
+ end
43
+
44
+
45
+ ###
46
+ # private helpers
47
+
48
+
49
+ # Converts an integer to big endian.
50
+ #
51
+ # @param num [Integer] integer to be converted.
52
+ # @return [String] packed, big-endian integer string.
53
+ def _int_to_big_endian( num )
54
+ hex = num.to_s(16)
55
+ hex = "0#{hex}" if hex.size.odd?
56
+
57
+ [hex].pack("H*") ## note Util.hex_to_bin() "inline" shortcut
58
+ end
59
+
60
+ # Converts a big endian to an interger.
61
+ #
62
+ # @param str [String] big endian to be converted.
63
+ # @return [Integer] an unpacked integer number.
64
+ def _big_endian_to_int(str)
65
+ str.unpack("H*").first.to_i(16)
66
+ end
67
+
68
+
69
+
70
+ end # class BigEndianInt
71
+
72
+ end # module Sedes
73
+ end # module Rlp
@@ -0,0 +1,103 @@
1
+ module Rlp
2
+ module Sedes
3
+
4
+ # A sedes type for binary values.
5
+ class Binary
6
+
7
+ # Create a serializable binary of fixed size.
8
+ #
9
+ # @param l [Integer] the fixed size of the binary.
10
+ # @param allow_empty [Boolean] indicator wether empty binaries should be allowed.
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)
14
+ end
15
+
16
+ # Checks wether the given object is of a valid binary type.
17
+ #
18
+ # @param obj [Object] the supposed binary item to check.
19
+ # @return [Boolean] true if valid.
20
+ def self.valid_type?(obj)
21
+ obj.instance_of? String
22
+ end
23
+
24
+
25
+ # Create a serializable binary of variable size.
26
+ #
27
+ # @param min_length [Integer] the minimum size of the binary.
28
+ # @param max_length [Integer] the maximum size of the binary.
29
+ # @param allow_empty [Boolean] indicator wether empty binaries should be allowed.
30
+ def initialize(min_length: 0, max_length: INFINITY, allow_empty: false)
31
+ @min_length = min_length
32
+ @max_length = max_length
33
+ @allow_empty = allow_empty
34
+ end
35
+
36
+ # Serializes a binary.
37
+ #
38
+ # @param obj [String] the binary to serialize.
39
+ # @return [Object] a serialized binary.
40
+ # @raise [SerializationError] if provided object is of invalid type.
41
+ # @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
46
+ serial
47
+ end
48
+
49
+ # Deserializes a binary.
50
+ #
51
+ # @param serial [Object] the serialized binary.
52
+ # @return [String] a deserialized binary.
53
+ # @raise [DeserializationError] if provided serial is of wrong type.
54
+ # @raise [DeserializationError] if provided serial is of wrong length.
55
+ 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
58
+ serial
59
+ end
60
+
61
+ # Checks wether the given length fits the defined size boundaries of the
62
+ # binary type.
63
+ #
64
+ # @param length [Integer] the supposed length of the binary item.
65
+ # @return [Boolean] true if valid.
66
+ def valid_length?(length)
67
+ (@min_length <= length && length <= @max_length) ||
68
+ (@allow_empty && length == 0)
69
+ end
70
+
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
+ end # class Binary
100
+
101
+
102
+ end # module Sedes
103
+ end # module Rlp
@@ -0,0 +1,68 @@
1
+
2
+ module Rlp
3
+ module Sedes
4
+
5
+ # A sedes type for lists of fixed length.
6
+ class List < Array
7
+
8
+ # Create a serializable list of fixed size.
9
+ #
10
+ # @param elements [Array] an array indicating the structure of the list.
11
+ # @param strict [Boolean] an option to enforce the given structure.
12
+ def initialize(elements: [], strict: true)
13
+ super()
14
+
15
+ @strict = strict
16
+ elements.each do |e|
17
+ if Sedes.is_sedes?(e)
18
+ push e
19
+ elsif Sedes.is_list?(e)
20
+ push List.new(elements: e)
21
+ else
22
+ raise TypeError, "Instances of List must only contain sedes objects or nested sequences thereof."
23
+ end
24
+ end
25
+ end
26
+
27
+ # Serialize an array.
28
+ #
29
+ # @param obj [Array] the array to be serialized.
30
+ # @return [Array] a serialized list.
31
+ # @raise [SerializationError] if provided array is not a sequence.
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)
35
+ raise SerializationError, "List has wrong length" if (@strict && self.size != obj.size) || self.size < obj.size
36
+ result = []
37
+ obj.zip(self).each_with_index do |(element, sedes), i|
38
+ result.push sedes.serialize(element)
39
+ end
40
+ result
41
+ end
42
+
43
+ # Deserializes a list.
44
+ #
45
+ # @param serial [Array] the serialized list.
46
+ # @return [Array] a deserialized list.
47
+ # @raise [DeserializationError] if provided serial is not a sequence.
48
+ # @raise [DeserializationError] if provided serial is of wrong length.
49
+ def deserialize(serial)
50
+ raise DeserializationError, "Can only deserialize sequences" unless Sedes.is_list?(serial)
51
+ raise DeserializationError, "List has wrong length" if @strict && serial.size != self.size
52
+ result = []
53
+ len = [serial.size, self.size].min
54
+ len.times do |i|
55
+ sedes = self[i]
56
+ element = serial[i]
57
+ result.push sedes.deserialize(element)
58
+ end
59
+ result.freeze
60
+ end
61
+
62
+
63
+
64
+ end # class List
65
+
66
+
67
+ end # module Sedes
68
+ end # module Rlp
@@ -0,0 +1,73 @@
1
+
2
+
3
+ module Rlp
4
+ module Sedes
5
+
6
+ # Provides a singleton {Rlp::Sedes} class to infer objects and types.
7
+ class << self
8
+
9
+ # Tries to find a sedes objects suitable for a given Ruby object.
10
+ #
11
+ # The sedes objects considered are `obj`'s class, {big_endian_int} and
12
+ # {binary}. If `obj` is a list, an {Rlp::Sedes::List} will be
13
+ # constructed recursively.
14
+ #
15
+ # @param obj [Object] the Ruby object for which to find a sedes object.
16
+ # @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}"
23
+ end
24
+
25
+
26
+ # Determines if an object is a sedes object.
27
+ #
28
+ # @param obj [Object] the object to check.
29
+ # @return [Boolean] true if it's serializable and deserializable.
30
+ def is_sedes?(obj)
31
+ obj.respond_to?(:serialize) && obj.respond_to?(:deserialize)
32
+ end
33
+
34
+ # A utility to use a big-endian, unsigned integer sedes type with
35
+ # unspecified length.
36
+ #
37
+ # @return [Rlp::Sedes::BigEndianInt] a big-endian, unsigned integer sedes.
38
+ def big_endian_int
39
+ @big_endian_int ||= BigEndianInt.new
40
+ end
41
+
42
+ # A utility to use a binary sedes type.
43
+ #
44
+ # @return [Rlp::Sedes::Binary] a binary sedes.
45
+ def binary
46
+ @binary ||= Binary.new
47
+ end
48
+
49
+ ##############################
50
+ ### more helpers
51
+
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
+
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
+
68
+
69
+
70
+
71
+ end
72
+ end
73
+ end # module Rlp
@@ -0,0 +1,108 @@
1
+
2
+ module Rlp
3
+ module Util
4
+ extend self
5
+
6
+
7
+
8
+
9
+ # Checks if a string is hex-adecimal.
10
+ #
11
+ # @param str [String] a string to be checked.
12
+ # @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
16
+ str.match /\A[0-9a-fA-F]*\z/
17
+ end
18
+
19
+ # Removes the `0x` prefix of a hexa-decimal string.
20
+ #
21
+ # @param hex [String] a prefixed hex-string.
22
+ # @return [String] an unprefixed hex-string.
23
+ def remove_hex_prefix(hex)
24
+ return hex[2..-1] if is_prefixed? hex
25
+ return hex
26
+ end
27
+
28
+ # Checks if a string is prefixed with `0x`.
29
+ #
30
+ # @param hex [String] a string to be checked.
31
+ # @return [String] a match if true; `nil` if not.
32
+ def is_prefixed?(hex)
33
+ hex.match /\A0x/
34
+ end
35
+
36
+ # Packs a hexa-decimal string into a binary string. Also works with
37
+ # `0x`-prefixed strings.
38
+ #
39
+ # @param hex [String] a hexa-decimal string to be packed.
40
+ # @return [String] a packed binary string.
41
+ # @raise [TypeError] if value is not a string or string is not hex.
42
+ def hex_to_bin(hex)
43
+ raise TypeError, "Value must be an instance of String" unless hex.instance_of? String
44
+ hex = remove_hex_prefix hex
45
+ raise TypeError, "Non-hexadecimal digit found" unless is_hex? hex
46
+ [hex].pack("H*")
47
+ end
48
+
49
+ # Unpacks a binary string to a hexa-decimal string.
50
+ #
51
+ # @param bin [String] a binary string to be unpacked.
52
+ # @return [String] a hexa-decimal string.
53
+ # @raise [TypeError] if value is not a string.
54
+ def bin_to_hex(bin)
55
+ raise TypeError, "Value must be an instance of String" unless bin.instance_of? String
56
+ bin.unpack("H*").first
57
+ end
58
+
59
+
60
+ # Converts a binary string to bytes.
61
+ #
62
+ # @param str [String] binary string to be converted.
63
+ # @return [Object] the string bytes.
64
+ def str_to_bytes(str)
65
+ is_bytes?(str) ? str : str.b
66
+ end
67
+
68
+ # Checks if a string is a byte-string.
69
+ #
70
+ # @param str [String] a string to check.
71
+ # @return [Boolean] true if it's an ASCII-8bit encoded byte-string.
72
+ def is_bytes?(str)
73
+ str && str.instance_of?(String) && str.encoding.name == 'ASCII-8BIT'
74
+ end
75
+
76
+
77
+
78
+ # Converts an integer to big endian.
79
+ #
80
+ # @param num [Integer] integer to be converted.
81
+ # @return [String] packed, big-endian integer string.
82
+ def int_to_big_endian(num)
83
+ hex = num.to_s(16) unless is_hex? num
84
+ hex = "0#{hex}" if hex.size.odd?
85
+ hex_to_bin hex
86
+ end
87
+
88
+
89
+
90
+ # Converts a big endian to an interger.
91
+ #
92
+ # @param str [String] big endian to be converted.
93
+ # @return [Integer] an unpacked integer number.
94
+ def big_endian_to_int(str)
95
+ str.unpack("H*").first.to_i(16)
96
+ end
97
+
98
+ # Deserializes big endian data string to integer.
99
+ #
100
+ # @param str [String] serialized big endian integer string.
101
+ # @return [Integer] an deserialized unsigned integer.
102
+ def deserialize_big_endian_to_int(str)
103
+ Sedes.big_endian_int.deserialize str.sub(/\A(\x00)+/, "")
104
+ end
105
+
106
+
107
+ end # module Util
108
+ end # module Rlp
@@ -0,0 +1,5 @@
1
+
2
+ module Rlp
3
+ VERSION='0.0.1'
4
+ end
5
+
data/lib/rlp-lite.rb ADDED
@@ -0,0 +1,82 @@
1
+
2
+ require_relative "rlp-lite/version"
3
+ require_relative "rlp-lite/util"
4
+
5
+ require_relative "rlp-lite/decoder"
6
+ require_relative "rlp-lite/encoder"
7
+
8
+ require_relative "rlp-lite/sedes/big_endian_int"
9
+ require_relative "rlp-lite/sedes/binary"
10
+ require_relative "rlp-lite/sedes/list"
11
+ require_relative "rlp-lite/sedes"
12
+
13
+
14
+
15
+ # Provides an recursive-length prefix (RLP) encoder and decoder.
16
+
17
+ module Rlp
18
+ ## add constants "inline" here
19
+ ## (no need to use a Constant namespace - why? why not?)
20
+
21
+
22
+ ## todo/check - use encoding -ascii-8bit for source file or ? - why? why not?
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
+
28
+
29
+
30
+ # The RLP short length limit.
31
+ SHORT_LENGTH_LIMIT = 56
32
+
33
+ # The RLP long length limit.
34
+ LONG_LENGTH_LIMIT = (256 ** 8)
35
+
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)
45
+
46
+
47
+ # The Rlp module exposes a variety of exceptions grouped as {RlpException}.
48
+ class RlpException < StandardError; end
49
+
50
+ # An error-type to point out RLP-encoding errors.
51
+ class EncodingError < RlpException; end
52
+
53
+ # An error-type to point out RLP-decoding errors.
54
+ class DecodingError < RlpException; end
55
+
56
+ # An error-type to point out RLP-type serialization errors.
57
+ class SerializationError < RlpException; end
58
+
59
+ # An error-type to point out RLP-type serialization errors.
60
+ class DeserializationError < RlpException; end
61
+
62
+ # A wrapper to represent already RLP-encoded data.
63
+ class Data < String; end
64
+
65
+
66
+ # Performes an {Rlp::Encoder} on any ruby object.
67
+ #
68
+ # @param obj [Object] any ruby object.
69
+ # @return [String] a packed, RLP-encoded item.
70
+ def self.encode(obj) Encoder.perform( obj ); end
71
+
72
+
73
+ # Performes an {Rlp::Decoder} on any RLP-encoded item.
74
+ #
75
+ # @param rlp [String] a packed, RLP-encoded item.
76
+ # @return [Object] a decoded ruby object.
77
+ def self.decode(rlp) Decoder.perform( rlp ); end
78
+ end # module Rlp
79
+
80
+
81
+
82
+
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rlp-lite
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-11-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7'
33
+ - !ruby/object:Gem::Dependency
34
+ name: hoe
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.23'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.23'
47
+ description: rlp-lite - light-weight machinery to serialize / deserialze via rlp (recursive
48
+ length prefix)
49
+ email: wwwmake@googlegroups.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files:
53
+ - CHANGELOG.md
54
+ - Manifest.txt
55
+ - README.md
56
+ files:
57
+ - CHANGELOG.md
58
+ - Manifest.txt
59
+ - README.md
60
+ - Rakefile
61
+ - lib/rlp-lite.rb
62
+ - lib/rlp-lite/decoder.rb
63
+ - lib/rlp-lite/encoder.rb
64
+ - lib/rlp-lite/sedes.rb
65
+ - lib/rlp-lite/sedes/big_endian_int.rb
66
+ - lib/rlp-lite/sedes/binary.rb
67
+ - lib/rlp-lite/sedes/list.rb
68
+ - lib/rlp-lite/util.rb
69
+ - lib/rlp-lite/version.rb
70
+ homepage: https://github.com/pixelartexchange/artbase
71
+ licenses:
72
+ - Public Domain
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options:
76
+ - "--main"
77
+ - README.md
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '2.3'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.3.7
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: rlp-lite - light-weight machinery to serialize / deserialze via rlp (recursive
95
+ length prefix)
96
+ test_files: []