erlang-etf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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