bert 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ # Loads mkmf which is used to make makefiles for Ruby extensions
2
+ require 'mkmf'
3
+
4
+ # Give it a name
5
+ extension_name = 'decode'
6
+
7
+ # The destination
8
+ dir_config(extension_name)
9
+
10
+ # Do the work
11
+ create_makefile(extension_name)
@@ -1,33 +1,24 @@
1
1
  require 'rubygems'
2
- require 'erlectricity'
3
2
 
4
- require 'bert/encoder'
5
- require 'bert/decoder'
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext])
6
4
 
7
- module BERT
8
- def self.encode(ruby)
9
- Encoder.encode(ruby)
10
- end
5
+ require 'bert/bert'
6
+ require 'bert/types'
11
7
 
12
- def self.decode(bert)
13
- Decoder.decode(bert)
14
- end
15
-
16
- def self.ebin(str)
17
- bytes = []
18
- str.each_byte { |b| bytes << b.to_s }
19
- "<<" + bytes.join(',') + ">>"
20
- end
8
+ begin
9
+ # try to load the C extension
10
+ require 'bert/c/decode'
11
+ rescue LoadError
12
+ # fall back on the pure ruby version
13
+ require 'bert/decode'
21
14
  end
22
15
 
23
- module BERT
24
- class Tuple < Array
25
- def inspect
26
- "t#{super}"
27
- end
28
- end
29
- end
16
+ require 'bert/encode'
17
+
18
+ require 'bert/encoder'
19
+ require 'bert/decoder'
30
20
 
21
+ # Global method for specifying that an array should be encoded as a tuple.
31
22
  def t
32
23
  BERT::Tuple
33
24
  end
@@ -0,0 +1,21 @@
1
+ module BERT
2
+ def self.encode(ruby)
3
+ Encoder.encode(ruby)
4
+ end
5
+
6
+ def self.decode(bert)
7
+ Decoder.decode(bert)
8
+ end
9
+
10
+ def self.ebin(str)
11
+ bytes = []
12
+ str.each_byte { |b| bytes << b.to_s }
13
+ "<<" + bytes.join(',') + ">>"
14
+ end
15
+
16
+ class Tuple < Array
17
+ def inspect
18
+ "t#{super}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,248 @@
1
+ module BERT
2
+ class Decode
3
+ attr_accessor :in
4
+ include Types
5
+
6
+ def self.impl
7
+ 'Ruby'
8
+ end
9
+
10
+ def self.decode(string)
11
+ new(StringIO.new(string)).read_any
12
+ end
13
+
14
+ def initialize(ins)
15
+ @in = ins
16
+ @peeked = ""
17
+ end
18
+
19
+ def read_any
20
+ fail("Bad Magic") unless read_1 == MAGIC
21
+ read_any_raw
22
+ end
23
+
24
+ def read_any_raw
25
+ case peek_1
26
+ when ATOM then read_atom
27
+ when SMALL_INT then read_small_int
28
+ when INT then read_int
29
+ when SMALL_BIGNUM then read_small_bignum
30
+ when LARGE_BIGNUM then read_large_bignum
31
+ when FLOAT then read_float
32
+ when SMALL_TUPLE then read_small_tuple
33
+ when LARGE_TUPLE then read_large_tuple
34
+ when NIL then read_nil
35
+ when STRING then read_erl_string
36
+ when LIST then read_list
37
+ when BIN then read_bin
38
+ else
39
+ fail("Unknown term tag: #{peek_1}")
40
+ end
41
+ end
42
+
43
+ def read(length)
44
+ if length < @peeked.length
45
+ result = @peeked[0...length]
46
+ @peeked = @peeked[length..-1]
47
+ length = 0
48
+ else
49
+ result = @peeked
50
+ @peeked = ''
51
+ length -= result.length
52
+ end
53
+
54
+ if length > 0
55
+ result << @in.read(length)
56
+ end
57
+ result
58
+ end
59
+
60
+ def peek(length)
61
+ if length <= @peeked.length
62
+ @peeked[0...length]
63
+ else
64
+ read_bytes = @in.read(length - @peeked.length)
65
+ @peeked << read_bytes if read_bytes
66
+ @peeked
67
+ end
68
+ end
69
+
70
+ def peek_1
71
+ peek(1).unpack("C").first
72
+ end
73
+
74
+ def peek_2
75
+ peek(2).unpack("n").first
76
+ end
77
+
78
+ def read_1
79
+ read(1).unpack("C").first
80
+ end
81
+
82
+ def read_2
83
+ read(2).unpack("n").first
84
+ end
85
+
86
+ def read_4
87
+ read(4).unpack("N").first
88
+ end
89
+
90
+ def read_string(length)
91
+ read(length)
92
+ end
93
+
94
+ def read_atom
95
+ fail("Invalid Type, not an atom") unless read_1 == ATOM
96
+ length = read_2
97
+ a = read_string(length)
98
+ case a
99
+ when ""
100
+ Marshal.load("\004\b:\005") # Workaround for inability to do ''.to_sym
101
+ else
102
+ a.to_sym
103
+ end
104
+ end
105
+
106
+ def read_small_int
107
+ fail("Invalid Type, not a small int") unless read_1 == SMALL_INT
108
+ read_1
109
+ end
110
+
111
+ def read_int
112
+ fail("Invalid Type, not an int") unless read_1 == INT
113
+ value = read_4
114
+ negative = (value >> 31)[0] == 1
115
+ value = (value - (1 << 32)) if negative
116
+ value = Fixnum.induced_from(value)
117
+ end
118
+
119
+ def read_small_bignum
120
+ fail("Invalid Type, not a small bignum") unless read_1 == SMALL_BIGNUM
121
+ size = read_1
122
+ sign = read_1
123
+ bytes = read_string(size).unpack("C" * size)
124
+ added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
125
+ byte, index = *byte_index
126
+ value = (byte * (256 ** index))
127
+ sign != 0 ? (result - value) : (result + value)
128
+ end
129
+ Bignum.induced_from(added)
130
+ end
131
+
132
+ def read_large_bignum
133
+ fail("Invalid Type, not a large bignum") unless read_1 == LARGE_BIGNUM
134
+ size = read_4
135
+ sign = read_1
136
+ bytes = read_string(size).unpack("C" * size)
137
+ added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
138
+ byte, index = *byte_index
139
+ value = (byte * (256 ** index))
140
+ sign != 0 ? (result - value) : (result + value)
141
+ end
142
+ Bignum.induced_from(added)
143
+ end
144
+
145
+ def read_float
146
+ fail("Invalid Type, not a float") unless read_1 == FLOAT
147
+ string_value = read_string(31)
148
+ result = string_value.to_f
149
+ end
150
+
151
+ def read_small_tuple
152
+ fail("Invalid Type, not a small tuple") unless read_1 == SMALL_TUPLE
153
+ read_tuple(read_1)
154
+ end
155
+
156
+ def read_large_tuple
157
+ fail("Invalid Type, not a small tuple") unless read_1 == LARGE_TUPLE
158
+ read_tuple(read_4)
159
+ end
160
+
161
+ def read_tuple(arity)
162
+ if arity > 0
163
+ tag = read_any_raw
164
+ if tag == :bert
165
+ read_complex_type(arity)
166
+ else
167
+ tuple = Tuple.new(arity)
168
+ tuple[0] = tag
169
+ (arity - 1).times { |i| tuple[i + 1] = read_any_raw }
170
+ tuple
171
+ end
172
+ else
173
+ Tuple.new
174
+ end
175
+ end
176
+
177
+ def read_complex_type(arity)
178
+ case read_any_raw
179
+ when :nil
180
+ nil
181
+ when :true
182
+ true
183
+ when :false
184
+ false
185
+ when :time
186
+ Time.at(read_any_raw * 1_000_000 + read_any_raw, read_any_raw)
187
+ when :regex
188
+ source = read_any_raw
189
+ opts = read_any_raw
190
+ options = 0
191
+ options |= Regexp::EXTENDED if opts.include?(:extended)
192
+ options |= Regexp::IGNORECASE if opts.include?(:caseless)
193
+ options |= Regexp::MULTILINE if opts.include?(:multiline)
194
+ Regexp.new(source, options)
195
+ when :dict
196
+ read_dict
197
+ else
198
+ nil
199
+ end
200
+ end
201
+
202
+ def read_dict
203
+ type = read_1
204
+ fail("Invalid dict spec, not an erlang list") unless [LIST, NIL].include?(type)
205
+ if type == LIST
206
+ length = read_4
207
+ else
208
+ length = 0
209
+ end
210
+ hash = {}
211
+ length.times do |i|
212
+ pair = read_any_raw
213
+ hash[pair[0]] = pair[1]
214
+ end
215
+ read_1 if type == LIST
216
+ hash
217
+ end
218
+
219
+ def read_nil
220
+ fail("Invalid Type, not a nil list") unless read_1 == NIL
221
+ []
222
+ end
223
+
224
+ def read_erl_string
225
+ fail("Invalid Type, not an erlang string") unless read_1 == STRING
226
+ length = read_2
227
+ read_string(length).unpack('C' * length)
228
+ end
229
+
230
+ def read_list
231
+ fail("Invalid Type, not an erlang list") unless read_1 == LIST
232
+ length = read_4
233
+ list = (0...length).map { |i| read_any_raw }
234
+ read_1
235
+ list
236
+ end
237
+
238
+ def read_bin
239
+ fail("Invalid Type, not an erlang binary") unless read_1 == BIN
240
+ length = read_4
241
+ read_string(length)
242
+ end
243
+
244
+ def fail(str)
245
+ raise str
246
+ end
247
+ end
248
+ end
@@ -5,59 +5,7 @@ module BERT
5
5
  #
6
6
  # Returns a Ruby object
7
7
  def self.decode(bert)
8
- simple_ruby = Erlectricity::Decoder.decode(bert)
9
- convert(simple_ruby)
10
- end
11
-
12
- # Convert Erlectricity representation of BERT complex types into
13
- # corresponding Ruby types.
14
- # +item+ is the Ruby object to convert
15
- #
16
- # Returns the converted Ruby object
17
- def self.convert(item)
18
- case item
19
- when TrueClass, FalseClass
20
- item.to_s.to_sym
21
- when Erl::List
22
- item.map { |x| convert(x) }
23
- when Array
24
- if item[0] == :bert
25
- convert_bert(item)
26
- else
27
- Tuple.new(item.map { |x| convert(x) })
28
- end
29
- else
30
- item
31
- end
32
- end
33
-
34
- # Convert complex types.
35
- # +item+ is the complex type array
36
- #
37
- # Returns the converted Ruby object
38
- def self.convert_bert(item)
39
- case item[1]
40
- when :nil
41
- nil
42
- when :dict
43
- item[2].inject({}) do |acc, x|
44
- acc[convert(x[0])] = convert(x[1]); acc
45
- end
46
- when TrueClass
47
- true
48
- when FalseClass
49
- false
50
- when :time
51
- Time.at(item[2] * 1_000_000 + item[3], item[4])
52
- when :regex
53
- options = 0
54
- options |= Regexp::EXTENDED if item[3].include?(:extended)
55
- options |= Regexp::IGNORECASE if item[3].include?(:caseless)
56
- options |= Regexp::MULTILINE if item[3].include?(:multiline)
57
- Regexp.new(item[2], options)
58
- else
59
- nil
60
- end
8
+ Decode.decode(bert)
61
9
  end
62
10
  end
63
11
  end
@@ -0,0 +1,142 @@
1
+ module BERT
2
+ class Encode
3
+ include Types
4
+
5
+ attr_accessor :out
6
+
7
+ def initialize(out)
8
+ self.out = out
9
+ end
10
+
11
+ def self.encode(data)
12
+ io = StringIO.new
13
+ self.new(io).write_any(data)
14
+ io.string
15
+ end
16
+
17
+ def write_any obj
18
+ write_1 MAGIC
19
+ write_any_raw obj
20
+ end
21
+
22
+ def write_any_raw obj
23
+ case obj
24
+ when Symbol then write_symbol(obj)
25
+ when Fixnum, Bignum then write_fixnum(obj)
26
+ when Float then write_float(obj)
27
+ when Tuple then write_tuple(obj)
28
+ when Array then write_list(obj)
29
+ when String then write_binary(obj)
30
+ else
31
+ fail(obj)
32
+ end
33
+ end
34
+
35
+ def write_1(byte)
36
+ out.write([byte].pack("C"))
37
+ end
38
+
39
+ def write_2(short)
40
+ out.write([short].pack("n"))
41
+ end
42
+
43
+ def write_4(long)
44
+ out.write([long].pack("N"))
45
+ end
46
+
47
+ def write_string(string)
48
+ out.write(string)
49
+ end
50
+
51
+ def write_boolean(bool)
52
+ write_symbol(bool.to_s.to_sym)
53
+ end
54
+
55
+ def write_symbol(sym)
56
+ fail(sym) unless sym.is_a?(Symbol)
57
+ data = sym.to_s
58
+ write_1 ATOM
59
+ write_2 data.length
60
+ write_string data
61
+ end
62
+
63
+ def write_fixnum(num)
64
+ if num >= 0 && num < 256
65
+ write_1 SMALL_INT
66
+ write_1 num
67
+ elsif num <= MAX_INT && num >= MIN_INT
68
+ write_1 INT
69
+ write_4 num
70
+ else
71
+ write_bignum num
72
+ end
73
+ end
74
+
75
+ def write_float(float)
76
+ write_1 FLOAT
77
+ write_string format("%15.15e", float).ljust(31, "\000")
78
+ end
79
+
80
+ def write_bignum(num)
81
+ if num.is_a?(Bignum)
82
+ n = num.size
83
+ else
84
+ n = (num.to_s(2).size / 8.0).ceil
85
+ end
86
+ if n <= 256
87
+ write_1 SMALL_BIGNUM
88
+ write_1 n
89
+ write_bignum_guts(num)
90
+ else
91
+ write_1 LARGE_BIGNUM
92
+ write_4 n
93
+ write_bignum_guts(num)
94
+ end
95
+ end
96
+
97
+ def write_bignum_guts(num)
98
+ write_1 (num >= 0 ? 0 : 1)
99
+ num = num.abs
100
+ while num != 0
101
+ rem = num % 256
102
+ write_1 rem
103
+ num = num >> 8
104
+ end
105
+ end
106
+
107
+ def write_tuple(data)
108
+ fail(data) unless data.is_a? Array
109
+
110
+ if data.length < 256
111
+ write_1 SMALL_TUPLE
112
+ write_1 data.length
113
+ else
114
+ write_1 LARGE_TUPLE
115
+ write_4 data.length
116
+ end
117
+
118
+ data.each { |e| write_any_raw e }
119
+ end
120
+
121
+ def write_list(data)
122
+ fail(data) unless data.is_a? Array
123
+ write_1 NIL and return if data.empty?
124
+ write_1 LIST
125
+ write_4 data.length
126
+ data.each{|e| write_any_raw e }
127
+ write_1 NIL
128
+ end
129
+
130
+ def write_binary(data)
131
+ write_1 BIN
132
+ write_4 data.length
133
+ write_string data
134
+ end
135
+
136
+ private
137
+
138
+ def fail(obj)
139
+ raise "Cannot encode to erlang external format: #{obj.inspect}"
140
+ end
141
+ end
142
+ end