erlang-etf 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +6 -0
- data/erlang-etf.gemspec +30 -0
- data/lib/erlang/etf.rb +40 -0
- data/lib/erlang/etf/atom.rb +46 -0
- data/lib/erlang/etf/atom_utf8.rb +44 -0
- data/lib/erlang/etf/bert.rb +74 -0
- data/lib/erlang/etf/binary.rb +44 -0
- data/lib/erlang/etf/bit_binary.rb +47 -0
- data/lib/erlang/etf/export.rb +44 -0
- data/lib/erlang/etf/extensions.rb +157 -0
- data/lib/erlang/etf/extensions/array.rb +29 -0
- data/lib/erlang/etf/extensions/big_decimal.rb +22 -0
- data/lib/erlang/etf/extensions/erlang-export.rb +26 -0
- data/lib/erlang/etf/extensions/erlang-list.rb +31 -0
- data/lib/erlang/etf/extensions/erlang-nil.rb +22 -0
- data/lib/erlang/etf/extensions/erlang-pid.rb +22 -0
- data/lib/erlang/etf/extensions/erlang-string.rb +22 -0
- data/lib/erlang/etf/extensions/erlang-tuple.rb +31 -0
- data/lib/erlang/etf/extensions/false_class.rb +28 -0
- data/lib/erlang/etf/extensions/float.rb +20 -0
- data/lib/erlang/etf/extensions/hash.rb +32 -0
- data/lib/erlang/etf/extensions/integer.rb +48 -0
- data/lib/erlang/etf/extensions/nil_class.rb +29 -0
- data/lib/erlang/etf/extensions/object.rb +24 -0
- data/lib/erlang/etf/extensions/regexp.rb +34 -0
- data/lib/erlang/etf/extensions/string.rb +35 -0
- data/lib/erlang/etf/extensions/symbol.rb +45 -0
- data/lib/erlang/etf/extensions/time.rb +29 -0
- data/lib/erlang/etf/extensions/true_class.rb +28 -0
- data/lib/erlang/etf/float.rb +57 -0
- data/lib/erlang/etf/fun.rb +67 -0
- data/lib/erlang/etf/integer.rb +29 -0
- data/lib/erlang/etf/large_big.rb +53 -0
- data/lib/erlang/etf/large_tuple.rb +55 -0
- data/lib/erlang/etf/list.rb +50 -0
- data/lib/erlang/etf/new_float.rb +33 -0
- data/lib/erlang/etf/new_fun.rb +98 -0
- data/lib/erlang/etf/new_reference.rb +59 -0
- data/lib/erlang/etf/nil.rb +23 -0
- data/lib/erlang/etf/pid.rb +45 -0
- data/lib/erlang/etf/port.rb +34 -0
- data/lib/erlang/etf/reference.rb +41 -0
- data/lib/erlang/etf/small_atom.rb +48 -0
- data/lib/erlang/etf/small_atom_utf8.rb +44 -0
- data/lib/erlang/etf/small_big.rb +59 -0
- data/lib/erlang/etf/small_integer.rb +29 -0
- data/lib/erlang/etf/small_tuple.rb +56 -0
- data/lib/erlang/etf/string.rb +46 -0
- data/lib/erlang/etf/term.rb +101 -0
- data/lib/erlang/etf/terms.rb +105 -0
- data/lib/erlang/etf/version.rb +5 -0
- data/spec/erlang/etf/atom_spec.rb +90 -0
- data/spec/erlang/etf/atom_utf8_spec.rb +90 -0
- data/spec/erlang/etf/binary_spec.rb +90 -0
- data/spec/erlang/etf/bit_binary_spec.rb +99 -0
- data/spec/erlang/etf/export_spec.rb +58 -0
- data/spec/erlang/etf/extensions/array_spec.rb +40 -0
- data/spec/erlang/etf/extensions/big_decimal_spec.rb +26 -0
- data/spec/erlang/etf/extensions/erlang-export_spec.rb +32 -0
- data/spec/erlang/etf/extensions/erlang-list_spec.rb +76 -0
- data/spec/erlang/etf/extensions/erlang-nil_spec.rb +24 -0
- data/spec/erlang/etf/extensions/erlang-pid_spec.rb +33 -0
- data/spec/erlang/etf/extensions/erlang-string_spec.rb +26 -0
- data/spec/erlang/etf/extensions/erlang-tuple_spec.rb +56 -0
- data/spec/erlang/etf/extensions/false_class_spec.rb +29 -0
- data/spec/erlang/etf/extensions/float_spec.rb +24 -0
- data/spec/erlang/etf/extensions/hash_spec.rb +90 -0
- data/spec/erlang/etf/extensions/integer_spec.rb +259 -0
- data/spec/erlang/etf/extensions/nil_class_spec.rb +29 -0
- data/spec/erlang/etf/extensions/object_spec.rb +30 -0
- data/spec/erlang/etf/extensions/regexp_spec.rb +35 -0
- data/spec/erlang/etf/extensions/string_spec.rb +43 -0
- data/spec/erlang/etf/extensions/symbol_spec.rb +64 -0
- data/spec/erlang/etf/extensions/time_spec.rb +32 -0
- data/spec/erlang/etf/extensions/true_class_spec.rb +29 -0
- data/spec/erlang/etf/float_spec.rb +92 -0
- data/spec/erlang/etf/fun_spec.rb +132 -0
- data/spec/erlang/etf/integer_spec.rb +57 -0
- data/spec/erlang/etf/large_big_spec.rb +67 -0
- data/spec/erlang/etf/large_tuple_spec.rb +119 -0
- data/spec/erlang/etf/list_spec.rb +159 -0
- data/spec/erlang/etf/new_float_spec.rb +92 -0
- data/spec/erlang/etf/new_fun_spec.rb +146 -0
- data/spec/erlang/etf/new_reference_spec.rb +60 -0
- data/spec/erlang/etf/nil_spec.rb +50 -0
- data/spec/erlang/etf/pid_spec.rb +61 -0
- data/spec/erlang/etf/port_spec.rb +58 -0
- data/spec/erlang/etf/reference_spec.rb +58 -0
- data/spec/erlang/etf/small_atom_spec.rb +90 -0
- data/spec/erlang/etf/small_atom_utf8_spec.rb +90 -0
- data/spec/erlang/etf/small_big_spec.rb +67 -0
- data/spec/erlang/etf/small_integer_spec.rb +57 -0
- data/spec/erlang/etf/small_tuple_spec.rb +112 -0
- data/spec/erlang/etf/string_spec.rb +92 -0
- data/spec/erlang/etf/term_spec.rb +27 -0
- data/spec/erlang/etf/terms_spec.rb +23 -0
- data/spec/erlang/etf_spec.rb +23 -0
- data/spec/erlang_spec.rb +77 -0
- data/spec/spec_helper.rb +7 -0
- metadata +310 -0
@@ -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
|