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,20 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ module Float
6
+
7
+ def __erlang_type__
8
+ :new_float
9
+ end
10
+
11
+ def __erlang_evolve__
12
+ ETF::NewFloat.new(self)
13
+ end
14
+
15
+ module ClassMethods
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ #
6
+ # dictionary {bert, dict, KeysAndValues}
7
+ #
8
+ # Dictionaries (hash tables) are expressed via an array of 2-tuples representing the key/value pairs.
9
+ # The KeysAndValues array is mandatory, such that an empty dict is expressed as {bert, dict, []}.
10
+ # Keys and values may be any term.
11
+ # For example, {bert, dict, [{name, <<"Tom">>}, {age, 30}]}.
12
+ #
13
+ # See: http://bert-rpc.org/
14
+ #
15
+ module Hash
16
+
17
+ def __erlang_type__
18
+ :bert_dict
19
+ end
20
+
21
+ def __erlang_evolve__
22
+ ::Erlang::Tuple[:bert, :dict, map do |(key, value)|
23
+ ::Erlang::Tuple[key, value]
24
+ end].__erlang_evolve__
25
+ end
26
+
27
+ module ClassMethods
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ module Integer
6
+
7
+ UINT8_MIN = 0.freeze
8
+ UINT8_MAX = (+(1 << 8) - 1).freeze
9
+
10
+ INT32_MIN = (-(1 << 31) + 1).freeze
11
+ INT32_MAX = (+(1 << 31) - 1).freeze
12
+
13
+ SMALL_BIG_N_MAX = 255.freeze
14
+
15
+ def __erlang_type__
16
+ if self >= UINT8_MIN and self <= UINT8_MAX
17
+ :small_integer
18
+ elsif self >= INT32_MIN and self <= INT32_MAX
19
+ :integer
20
+ else
21
+ n = (abs.to_s(2).bytesize / 8.0).ceil
22
+ if n <= SMALL_BIG_N_MAX
23
+ :small_big
24
+ else
25
+ :large_big
26
+ end
27
+ end
28
+ end
29
+
30
+ def __erlang_evolve__
31
+ case __erlang_type__
32
+ when :small_integer
33
+ ETF::SmallInteger.new(self)
34
+ when :integer
35
+ ETF::Integer.new(self)
36
+ when :small_big
37
+ ETF::SmallBig.new(self)
38
+ when :large_big
39
+ ETF::LargeBig.new(self)
40
+ end
41
+ end
42
+
43
+ module ClassMethods
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,29 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ #
6
+ # nil {bert, nil}
7
+ #
8
+ # Erlang equates nil with the empty array [] while other languages do not.
9
+ # Even though NIL appears as a primitive in the serialization specification, BERT only uses it to represent the empty array.
10
+ # In order to be language agnostic, nil is encoded as a separate complex type to allow for disambiguation.
11
+ #
12
+ # See: http://bert-rpc.org/
13
+ #
14
+ module NilClass
15
+
16
+ def __erlang_type__
17
+ :bert_nil
18
+ end
19
+
20
+ def __erlang_evolve__
21
+ ::Erlang::Tuple[:bert, :nil].__erlang_evolve__
22
+ end
23
+
24
+ module ClassMethods
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ module Object
6
+
7
+ def __erlang_type__
8
+ raise NotImplementedError, "#__erlang_type__ undefined for #{inspect} of class #{self.class}"
9
+ end
10
+
11
+ def __erlang_evolve__
12
+ raise NotImplementedError, "#__erlang_evolve__ undefined for #{inspect} of class #{self.class}"
13
+ end
14
+
15
+ def __erlang_dump__(buffer)
16
+ __erlang_evolve__.serialize(buffer)
17
+ end
18
+
19
+ module ClassMethods
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ #
6
+ # regex {bert, regex, Source, Options}
7
+ #
8
+ # Regular expressions are expressed by their source binary and PCRE options.
9
+ # Options is a list of atoms representing the PCRE options.
10
+ # For example, {bert, regex, <<"^c(a*)t$">>, [caseless]} would represent a case insensitive regular epxression that would match "cat".
11
+ # See re:compile/2 for valid options.
12
+ #
13
+ # See: http://bert-rpc.org/
14
+ #
15
+ module Regexp
16
+
17
+ def __erlang_type__
18
+ :bert_regex
19
+ end
20
+
21
+ def __erlang_evolve__
22
+ opts = []
23
+ opts << :caseless if (options & ::Regexp::IGNORECASE) != 0
24
+ opts << :extended if (options & ::Regexp::EXTENDED) != 0
25
+ opts << :multiline if (options & ::Regexp::MULTILINE) != 0
26
+ ::Erlang::Tuple[:bert, :regex, source, opts].__erlang_evolve__
27
+ end
28
+
29
+ module ClassMethods
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ BINARY_ENCODING = Encoding.find("binary")
6
+ UTF8_ENCODING = Encoding.find("utf-8")
7
+
8
+ module String
9
+
10
+ def __erlang_type__
11
+ :binary
12
+ end
13
+
14
+ def __erlang_evolve__
15
+ ETF::Binary.new(self)
16
+ end
17
+
18
+ def to_utf8_binary
19
+ encode(UTF8_ENCODING).force_encoding(BINARY_ENCODING)
20
+ rescue EncodingError
21
+ data = dup.force_encoding(UTF8_ENCODING)
22
+ raise unless data.valid_encoding?
23
+ data.force_encoding(BINARY_ENCODING)
24
+ end
25
+
26
+ def from_utf8_binary
27
+ force_encoding(UTF8_ENCODING).encode!
28
+ end
29
+
30
+ module ClassMethods
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ module Symbol
6
+
7
+ def __erlang_type__
8
+ if to_s.bytesize < 256
9
+ if to_s.ascii_only?
10
+ :small_atom
11
+ else
12
+ :small_atom_utf8
13
+ end
14
+ else
15
+ if to_s.ascii_only?
16
+ :atom
17
+ else
18
+ :atom_utf8
19
+ end
20
+ end
21
+ end
22
+
23
+ def __erlang_evolve__
24
+ case __erlang_type__
25
+ when :atom
26
+ ETF::Atom.new(to_s)
27
+ when :atom_utf8
28
+ ETF::AtomUTF8.new(to_s)
29
+ when :small_atom
30
+ ETF::SmallAtom.new(to_s)
31
+ when :small_atom_utf8
32
+ ETF::SmallAtomUTF8.new(to_s)
33
+ end
34
+ end
35
+
36
+ def to_utf8_binary
37
+ to_s.to_utf8_binary
38
+ end
39
+
40
+ module ClassMethods
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ #
6
+ # time {bert, time, Megaseconds, Seconds, Microseconds}
7
+ #
8
+ # The given time is the number of Megaseconds + Seconds + Microseconds elapsed since 00:00 GMT, January 1, 1970 (zero hour).
9
+ # For example, 2009-10-11 at 14:12:01 and 446,228 microseconds would be expressed as {bert, time, 1255, 295581, 446228}.
10
+ # In english, this is 1255 megaseconds (millions of seconds) + 295,581 seconds + 446,228 microseconds (millionths of a second) since zero hour.
11
+ #
12
+ # See: http://bert-rpc.org/
13
+ #
14
+ module Time
15
+
16
+ def __erlang_type__
17
+ :bert_time
18
+ end
19
+
20
+ def __erlang_evolve__
21
+ ::Erlang::Tuple[:bert, :time, to_i / 1_000_000, to_i % 1_000_000, usec].__erlang_evolve__
22
+ end
23
+
24
+ module ClassMethods
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ module Erlang
2
+ module ETF
3
+ module Extensions
4
+
5
+ #
6
+ # boolean {bert, true} or {bert, false}
7
+ #
8
+ # Erlang equates the true and false atoms with booleans while other languages do not have this behavior.
9
+ # To disambiguate these cases, booleans are expressed as their own complex type.
10
+ #
11
+ # See: http://bert-rpc.org/
12
+ #
13
+ module TrueClass
14
+
15
+ def __erlang_type__
16
+ :bert_boolean
17
+ end
18
+
19
+ def __erlang_evolve__
20
+ ::Erlang::Tuple[:bert, :true].__erlang_evolve__
21
+ end
22
+
23
+ module ClassMethods
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,57 @@
1
+ require 'bigdecimal'
2
+
3
+ module Erlang
4
+ module ETF
5
+
6
+ #
7
+ # 1 | 31
8
+ # -- | ------------
9
+ # 99 | Float String
10
+ #
11
+ # A float is stored in string format. the format used in sprintf
12
+ # to format the float is "%.20e" (there are more bytes allocated
13
+ # than necessary). To unpack the float use sscanf with format
14
+ # "%lf".
15
+ #
16
+ # This term is used in minor version 0 of the external format; it
17
+ # has been superseded by NEW_FLOAT_EXT .
18
+ #
19
+ class Float
20
+ include Term
21
+
22
+ FLOAT_STRING_FORMAT = "%.20e".freeze
23
+
24
+ uint8 :tag, always: Terms::FLOAT_EXT
25
+
26
+ string :float_string
27
+
28
+ undef serialize_float_string
29
+ def serialize_float_string(buffer)
30
+ if float_string.is_a?(::BigDecimal)
31
+ buffer << (FLOAT_STRING_FORMAT % float_string).ljust(31, "\000")
32
+ else
33
+ buffer << float_string
34
+ end
35
+ end
36
+
37
+ undef deserialize_float_string
38
+ def deserialize_float_string(buffer)
39
+ self.float_string = buffer.read(31)
40
+ end
41
+
42
+ finalize
43
+
44
+ def initialize(float_string)
45
+ @float_string = float_string
46
+ end
47
+
48
+ def __ruby_evolve__
49
+ if float_string.is_a?(::BigDecimal)
50
+ float_string
51
+ else
52
+ ::BigDecimal.new(float_string.gsub("\000", ""))
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,67 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 4 | N1 | N2 | N3 | N4 | N5
6
+ # --- | ------- | --- | ------ | ----- | ---- | -------------
7
+ # 117 | NumFree | Pid | Module | Index | Uniq | Free vars ...
8
+ #
9
+ # Pid
10
+ # is a process identifier as in PID_EXT. It represents the
11
+ # process in which the fun was created.
12
+ # Module
13
+ # is an encoded as an atom, using ATOM_EXT, SMALL_ATOM_EXT or
14
+ # ATOM_CACHE_REF. This is the module that the fun is
15
+ # implemented in.
16
+ # Index
17
+ # is an integer encoded using SMALL_INTEGER_EXT or INTEGER_EXT.
18
+ # It is typically a small index into the module's fun table.
19
+ # Uniq
20
+ # is an integer encoded using SMALL_INTEGER_EXT or INTEGER_EXT.
21
+ # Uniq is the hash value of the parse for the fun.
22
+ # Free vars
23
+ # is NumFree number of terms, each one encoded according to its
24
+ # type.
25
+ #
26
+ class Fun
27
+ include Term
28
+
29
+ uint8 :tag, always: Terms::FUN_EXT
30
+
31
+ uint16be :num_free, always: -> { free_vars.size }
32
+
33
+ term :pid # pid
34
+
35
+ term :mod # atom, small_atom
36
+
37
+ term :index # small_integer, integer
38
+
39
+ term :uniq # small_integer, integer
40
+
41
+ term :free_vars, type: :array
42
+
43
+ deserialize do |buffer|
44
+ num_free, = buffer.read(BYTES_16).unpack(UINT16BE_PACK)
45
+ deserialize_pid(buffer)
46
+ deserialize_mod(buffer)
47
+ deserialize_index(buffer)
48
+ deserialize_uniq(buffer)
49
+ self.free_vars = []
50
+ num_free.times do
51
+ self.free_vars << Terms.deserialize(buffer)
52
+ end
53
+ self
54
+ end
55
+
56
+ finalize
57
+
58
+ def initialize(pid, mod, index, uniq, free_vars = [])
59
+ self.pid = pid
60
+ self.mod = mod
61
+ self.index = index
62
+ self.uniq = uniq
63
+ self.free_vars = free_vars
64
+ end
65
+ end
66
+ end
67
+ end