erlang-etf 1.0.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.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +49 -0
  8. data/Rakefile +6 -0
  9. data/erlang-etf.gemspec +30 -0
  10. data/lib/erlang/etf.rb +40 -0
  11. data/lib/erlang/etf/atom.rb +46 -0
  12. data/lib/erlang/etf/atom_utf8.rb +44 -0
  13. data/lib/erlang/etf/bert.rb +74 -0
  14. data/lib/erlang/etf/binary.rb +44 -0
  15. data/lib/erlang/etf/bit_binary.rb +47 -0
  16. data/lib/erlang/etf/export.rb +44 -0
  17. data/lib/erlang/etf/extensions.rb +157 -0
  18. data/lib/erlang/etf/extensions/array.rb +29 -0
  19. data/lib/erlang/etf/extensions/big_decimal.rb +22 -0
  20. data/lib/erlang/etf/extensions/erlang-export.rb +26 -0
  21. data/lib/erlang/etf/extensions/erlang-list.rb +31 -0
  22. data/lib/erlang/etf/extensions/erlang-nil.rb +22 -0
  23. data/lib/erlang/etf/extensions/erlang-pid.rb +22 -0
  24. data/lib/erlang/etf/extensions/erlang-string.rb +22 -0
  25. data/lib/erlang/etf/extensions/erlang-tuple.rb +31 -0
  26. data/lib/erlang/etf/extensions/false_class.rb +28 -0
  27. data/lib/erlang/etf/extensions/float.rb +20 -0
  28. data/lib/erlang/etf/extensions/hash.rb +32 -0
  29. data/lib/erlang/etf/extensions/integer.rb +48 -0
  30. data/lib/erlang/etf/extensions/nil_class.rb +29 -0
  31. data/lib/erlang/etf/extensions/object.rb +24 -0
  32. data/lib/erlang/etf/extensions/regexp.rb +34 -0
  33. data/lib/erlang/etf/extensions/string.rb +35 -0
  34. data/lib/erlang/etf/extensions/symbol.rb +45 -0
  35. data/lib/erlang/etf/extensions/time.rb +29 -0
  36. data/lib/erlang/etf/extensions/true_class.rb +28 -0
  37. data/lib/erlang/etf/float.rb +57 -0
  38. data/lib/erlang/etf/fun.rb +67 -0
  39. data/lib/erlang/etf/integer.rb +29 -0
  40. data/lib/erlang/etf/large_big.rb +53 -0
  41. data/lib/erlang/etf/large_tuple.rb +55 -0
  42. data/lib/erlang/etf/list.rb +50 -0
  43. data/lib/erlang/etf/new_float.rb +33 -0
  44. data/lib/erlang/etf/new_fun.rb +98 -0
  45. data/lib/erlang/etf/new_reference.rb +59 -0
  46. data/lib/erlang/etf/nil.rb +23 -0
  47. data/lib/erlang/etf/pid.rb +45 -0
  48. data/lib/erlang/etf/port.rb +34 -0
  49. data/lib/erlang/etf/reference.rb +41 -0
  50. data/lib/erlang/etf/small_atom.rb +48 -0
  51. data/lib/erlang/etf/small_atom_utf8.rb +44 -0
  52. data/lib/erlang/etf/small_big.rb +59 -0
  53. data/lib/erlang/etf/small_integer.rb +29 -0
  54. data/lib/erlang/etf/small_tuple.rb +56 -0
  55. data/lib/erlang/etf/string.rb +46 -0
  56. data/lib/erlang/etf/term.rb +101 -0
  57. data/lib/erlang/etf/terms.rb +105 -0
  58. data/lib/erlang/etf/version.rb +5 -0
  59. data/spec/erlang/etf/atom_spec.rb +90 -0
  60. data/spec/erlang/etf/atom_utf8_spec.rb +90 -0
  61. data/spec/erlang/etf/binary_spec.rb +90 -0
  62. data/spec/erlang/etf/bit_binary_spec.rb +99 -0
  63. data/spec/erlang/etf/export_spec.rb +58 -0
  64. data/spec/erlang/etf/extensions/array_spec.rb +40 -0
  65. data/spec/erlang/etf/extensions/big_decimal_spec.rb +26 -0
  66. data/spec/erlang/etf/extensions/erlang-export_spec.rb +32 -0
  67. data/spec/erlang/etf/extensions/erlang-list_spec.rb +76 -0
  68. data/spec/erlang/etf/extensions/erlang-nil_spec.rb +24 -0
  69. data/spec/erlang/etf/extensions/erlang-pid_spec.rb +33 -0
  70. data/spec/erlang/etf/extensions/erlang-string_spec.rb +26 -0
  71. data/spec/erlang/etf/extensions/erlang-tuple_spec.rb +56 -0
  72. data/spec/erlang/etf/extensions/false_class_spec.rb +29 -0
  73. data/spec/erlang/etf/extensions/float_spec.rb +24 -0
  74. data/spec/erlang/etf/extensions/hash_spec.rb +90 -0
  75. data/spec/erlang/etf/extensions/integer_spec.rb +259 -0
  76. data/spec/erlang/etf/extensions/nil_class_spec.rb +29 -0
  77. data/spec/erlang/etf/extensions/object_spec.rb +30 -0
  78. data/spec/erlang/etf/extensions/regexp_spec.rb +35 -0
  79. data/spec/erlang/etf/extensions/string_spec.rb +43 -0
  80. data/spec/erlang/etf/extensions/symbol_spec.rb +64 -0
  81. data/spec/erlang/etf/extensions/time_spec.rb +32 -0
  82. data/spec/erlang/etf/extensions/true_class_spec.rb +29 -0
  83. data/spec/erlang/etf/float_spec.rb +92 -0
  84. data/spec/erlang/etf/fun_spec.rb +132 -0
  85. data/spec/erlang/etf/integer_spec.rb +57 -0
  86. data/spec/erlang/etf/large_big_spec.rb +67 -0
  87. data/spec/erlang/etf/large_tuple_spec.rb +119 -0
  88. data/spec/erlang/etf/list_spec.rb +159 -0
  89. data/spec/erlang/etf/new_float_spec.rb +92 -0
  90. data/spec/erlang/etf/new_fun_spec.rb +146 -0
  91. data/spec/erlang/etf/new_reference_spec.rb +60 -0
  92. data/spec/erlang/etf/nil_spec.rb +50 -0
  93. data/spec/erlang/etf/pid_spec.rb +61 -0
  94. data/spec/erlang/etf/port_spec.rb +58 -0
  95. data/spec/erlang/etf/reference_spec.rb +58 -0
  96. data/spec/erlang/etf/small_atom_spec.rb +90 -0
  97. data/spec/erlang/etf/small_atom_utf8_spec.rb +90 -0
  98. data/spec/erlang/etf/small_big_spec.rb +67 -0
  99. data/spec/erlang/etf/small_integer_spec.rb +57 -0
  100. data/spec/erlang/etf/small_tuple_spec.rb +112 -0
  101. data/spec/erlang/etf/string_spec.rb +92 -0
  102. data/spec/erlang/etf/term_spec.rb +27 -0
  103. data/spec/erlang/etf/terms_spec.rb +23 -0
  104. data/spec/erlang/etf_spec.rb +23 -0
  105. data/spec/erlang_spec.rb +77 -0
  106. data/spec/spec_helper.rb +7 -0
  107. metadata +310 -0
@@ -0,0 +1,34 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | N | 4 | 1
6
+ # --- | ---- | -- | --------
7
+ # 102 | Node | ID | Creation
8
+ #
9
+ # Encode a port object (obtained form open_port/2). The ID is a
10
+ # node specific identifier for a local port. Port operations are
11
+ # not allowed across node boundaries. The Creation works just like
12
+ # in REFERENCE_EXT.
13
+ #
14
+ class Port
15
+ include Term
16
+
17
+ uint8 :tag, always: Terms::PORT_EXT
18
+
19
+ term :node
20
+
21
+ uint32be :id, maximum: (1 << 28) - 1
22
+
23
+ int8 :creation, maximum: (1 << 2) - 1
24
+
25
+ finalize
26
+
27
+ def initialize(node, id, creation)
28
+ @node = node
29
+ @id = id
30
+ @creation = creation
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | N | 4 | 1
6
+ # --- | ---- | -- | --------
7
+ # 101 | Node | ID | Creation
8
+ #
9
+ # Encode a reference object (an object generated with make_ref/0).
10
+ # The Node term is an encoded atom, i.e. ATOM_EXT, SMALL_ATOM_EXT
11
+ # or ATOM_CACHE_REF. The ID field contains a big-endian unsigned
12
+ # integer, but should be regarded as uninterpreted data since this
13
+ # field is node specific. Creation is a byte containing a node
14
+ # serial number that makes it possible to separate old (crashed)
15
+ # nodes from a new one.
16
+ #
17
+ # In ID, only 18 bits are significant; the rest should be 0.
18
+ # In Creation, only 2 bits are significant; the rest should be 0.
19
+ # See NEW_REFERENCE_EXT.
20
+ #
21
+ class Reference
22
+ include Term
23
+
24
+ uint8 :tag, always: Terms::REFERENCE_EXT
25
+
26
+ term :node
27
+
28
+ uint32be :id, maximum: (1 << 18) - 1
29
+
30
+ int8 :creation, maximum: (1 << 2) - 1
31
+
32
+ finalize
33
+
34
+ def initialize(node, id, creation)
35
+ @node = node
36
+ @id = id
37
+ @creation = creation
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 1 | Len
6
+ # --- | --- | --------
7
+ # 115 | Len | AtomName
8
+ #
9
+ # An atom is stored with a 1 byte unsigned length, followed by Len
10
+ # numbers of 8 bit Latin1 characters that forms the AtomName.
11
+ # Longer atoms can be represented by ATOM_EXT.
12
+ #
13
+ # Note the SMALL_ATOM_EXT was introduced in erts version 5.7.2 and
14
+ # require an exchange of the DFLAG_SMALL_ATOM_TAGS distribution
15
+ # flag in the distribution handshake.
16
+ #
17
+ class SmallAtom
18
+ include Term
19
+
20
+ uint8 :tag, always: Terms::SMALL_ATOM_EXT
21
+
22
+ uint8 :len, default: 0 do
23
+ string :atom_name
24
+ end
25
+
26
+ undef deserialize_atom_name
27
+ def deserialize_atom_name(buffer)
28
+ self.atom_name = buffer.read(len).from_utf8_binary
29
+ end
30
+
31
+ undef serialize_atom_name
32
+ def serialize_atom_name(buffer)
33
+ buffer << atom_name.to_utf8_binary
34
+ end
35
+
36
+ finalize
37
+
38
+ def initialize(atom_name)
39
+ @atom_name = atom_name
40
+ @len = atom_name.to_s.bytesize
41
+ end
42
+
43
+ def __ruby_evolve__
44
+ atom_name.intern
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 1 | Len
6
+ # --- | --- | --------
7
+ # 119 | Len | AtomName
8
+ #
9
+ # An atom is stored with a 1 byte unsigned length, followed by Len
10
+ # bytes containing the AtomName encoded in UTF-8. Longer atoms
11
+ # encoded in UTF-8 can be represented using ATOM_UTF8_EXT.
12
+ #
13
+ class SmallAtomUTF8
14
+ include Term
15
+
16
+ uint8 :tag, always: Terms::SMALL_ATOM_UTF8_EXT
17
+
18
+ uint8 :len, default: 0 do
19
+ string :atom_name
20
+ end
21
+
22
+ undef deserialize_atom_name
23
+ def deserialize_atom_name(buffer)
24
+ self.atom_name = buffer.read(len).from_utf8_binary
25
+ end
26
+
27
+ undef serialize_atom_name
28
+ def serialize_atom_name(buffer)
29
+ buffer << atom_name.to_utf8_binary
30
+ end
31
+
32
+ finalize
33
+
34
+ def initialize(atom_name)
35
+ @atom_name = atom_name
36
+ @len = atom_name.to_s.bytesize
37
+ end
38
+
39
+ def __ruby_evolve__
40
+ atom_name.intern
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 1 | 1 | n
6
+ # --- | - | ---- | ---------------
7
+ # 110 | n | Sign | d(0) ... d(n-1)
8
+ #
9
+ # Bignums are stored in unary form with a Sign byte that is 0 if
10
+ # the binum is positive and 1 if is negative. The digits are
11
+ # stored with the LSB byte stored first. To calculate the integer
12
+ # the following formula can be used:
13
+ # B = 256
14
+ # (d0*B^0 + d1*B^1 + d2*B^2 + ... d(N-1)*B^(n-1))
15
+ #
16
+ class SmallBig
17
+ include Term
18
+
19
+ uint8 :tag, always: Terms::SMALL_BIG_EXT
20
+
21
+ uint8 :n, default: 0 do
22
+ uint8 :sign, always: -> { (integer >= 0) ? 0 : 1 }
23
+ string :integer
24
+ end
25
+
26
+ undef serialize_integer
27
+
28
+ def serialize_integer(buffer)
29
+ start = buffer.bytesize
30
+ buffer << [integer.abs.to_s(2).reverse!].pack(BIN_LSB_PACK)
31
+ self.n = buffer.bytesize - start
32
+ buffer
33
+ end
34
+
35
+ undef after_serialize_n
36
+
37
+ def after_serialize_n(buffer)
38
+ buffer[@n_start, BYTES_8] = serialize_n ""
39
+ end
40
+
41
+ deserialize do |buffer|
42
+ deserialize_n(buffer)
43
+ sign, = buffer.read(BYTES_8).unpack(UINT8_PACK)
44
+ self.integer = buffer.read(n).unpack(BIN_LSB_PACK).at(0).reverse!.to_i(2) * ((sign == 0) ? 1 : -1)
45
+ self
46
+ end
47
+
48
+ finalize
49
+
50
+ def initialize(integer)
51
+ @integer = integer
52
+ end
53
+
54
+ def __ruby_evolve__
55
+ integer
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 1
6
+ # -- | ---
7
+ # 97 | Int
8
+ #
9
+ # Unsigned 8 bit integer.
10
+ #
11
+ class SmallInteger
12
+ include Term
13
+
14
+ uint8 :tag, always: Terms::SMALL_INTEGER_EXT
15
+
16
+ uint8 :int
17
+
18
+ finalize
19
+
20
+ def initialize(int)
21
+ @int = int
22
+ end
23
+
24
+ def __ruby_evolve__
25
+ int
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,56 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 1 | N
6
+ # --- | ----- | --------
7
+ # 104 | Arity | Elements
8
+ #
9
+ # SMALL_TUPLE_EXT encodes a tuple. The Arity field is an unsigned
10
+ # byte that determines how many element that follows in the
11
+ # Elements section.
12
+ #
13
+ class SmallTuple
14
+ include Term
15
+
16
+ uint8 :tag, always: Terms::SMALL_TUPLE_EXT
17
+
18
+ uint8 :arity, always: -> { elements.size }
19
+
20
+ term :elements, type: :array
21
+
22
+ deserialize do |buffer|
23
+ arity, = buffer.read(BYTES_8).unpack(UINT8_PACK)
24
+ self.elements = []
25
+ arity.times do
26
+ self.elements << Terms.deserialize(buffer)
27
+ end
28
+ self
29
+ end
30
+
31
+ finalize
32
+
33
+ def initialize(elements)
34
+ @elements = elements
35
+ end
36
+
37
+ def serialize_header(buffer)
38
+ serialize_tag(buffer)
39
+ serialize_arity(buffer)
40
+ end
41
+
42
+ def bert?
43
+ elements[0].respond_to?(:atom_name) &&
44
+ elements[0].atom_name == BERT_PREFIX
45
+ end
46
+
47
+ def __ruby_evolve__
48
+ if bert?
49
+ ::Erlang::ETF::BERT.evolve(self)
50
+ else
51
+ ::Erlang::Tuple[*elements.map(&:__ruby_evolve__)]
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,46 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 2 | Len
6
+ # --- | --- | ----------
7
+ # 107 | Len | Characters
8
+ #
9
+ # String does NOT have a corresponding Erlang representation, but
10
+ # is an optimization for sending lists of bytes (integer in the
11
+ # range 0-255) more efficiently over the distribution. Since the
12
+ # Length field is an unsigned 2 byte integer (big endian),
13
+ # implementations must make sure that lists longer than 65535
14
+ # elements are encoded as LIST_EXT.
15
+ #
16
+ class String
17
+ include Term
18
+
19
+ uint8 :tag, always: Terms::STRING_EXT
20
+
21
+ uint16be :len, default: 0 do
22
+ string :characters
23
+ end
24
+
25
+ undef deserialize_characters
26
+ def deserialize_characters(buffer)
27
+ self.characters = buffer.read(len).from_utf8_binary
28
+ end
29
+
30
+ undef serialize_characters
31
+ def serialize_characters(buffer)
32
+ buffer << characters.to_utf8_binary
33
+ end
34
+
35
+ finalize
36
+
37
+ def initialize(characters)
38
+ @characters = characters
39
+ end
40
+
41
+ def __ruby_evolve__
42
+ ::Erlang::String.new(characters)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,101 @@
1
+ module Erlang
2
+ module ETF
3
+ module Term
4
+
5
+ class << self
6
+
7
+ # Extends the including class with +ClassMethods+.
8
+ #
9
+ # @param [Class] subclass the inheriting class
10
+ def included(base)
11
+ super
12
+
13
+ base.send(:include, ::Binary::Protocol)
14
+ base.extend ClassMethods
15
+ end
16
+
17
+ private :included
18
+ end
19
+
20
+ BERT_PREFIX = "bert".freeze
21
+
22
+ BIN_LSB_PACK = 'b*'.freeze
23
+ BIN_MSB_PACK = 'B*'.freeze
24
+
25
+ module ClassMethods
26
+
27
+ def term(name, options = {})
28
+ if options.key?(:always)
29
+ __define_always__(name, options[:always])
30
+ else
31
+ if options.key?(:default)
32
+ __define_default__(name, options[:default])
33
+ else
34
+ attr_accessor name
35
+ end
36
+
37
+ if options[:type] == :array
38
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
39
+ def deserialize_#{name}(buffer)
40
+ raise NotImplementedError
41
+ end
42
+ RUBY
43
+ else
44
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
45
+ def deserialize_#{name}(buffer)
46
+ self.#{name} = Terms.deserialize(buffer)
47
+ end
48
+ RUBY
49
+ end
50
+ end
51
+
52
+ if options[:type] == :array
53
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
54
+ def serialize_#{name}(buffer)
55
+ #{name}.each do |term|
56
+ term.serialize(buffer)
57
+ end
58
+ end
59
+ RUBY
60
+ else
61
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
62
+ def serialize_#{name}(buffer)
63
+ #{name}.serialize(buffer)
64
+ end
65
+ RUBY
66
+ end
67
+
68
+ serialization << :"serialize_#{name}"
69
+ fields << name
70
+ end
71
+
72
+ end
73
+
74
+ def ==(other)
75
+ self.class === other &&
76
+ self.class.fields.all? do |field|
77
+ __send__(field) == other.__send__(field)
78
+ end
79
+ end
80
+
81
+ def __erlang_type__
82
+ word = self.class.name.split('::').last.dup
83
+ word.gsub!('::', '/')
84
+ word.gsub!(/(?:([A-Za-z\d])|^)(UTF8)(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
85
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
86
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
87
+ word.tr!("-", "_")
88
+ word.downcase!
89
+ word.intern
90
+ end
91
+
92
+ def __erlang_evolve__
93
+ self
94
+ end
95
+
96
+ def __ruby_evolve__
97
+ self
98
+ end
99
+ end
100
+ end
101
+ end