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,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