Class: BERT::Decode
- Inherits:
-
Object
show all
- Includes:
- Types
- Defined in:
- lib/bert/decode.rb
Constant Summary
Constants included
from Types
Types::ATOM, Types::BIN, Types::FLOAT, Types::FUN, Types::INT, Types::LARGE_BIGNUM, Types::LARGE_TUPLE, Types::LIST, Types::MAGIC, Types::MAX_INT, Types::MIN_INT, Types::NEW_FUN, Types::NIL, Types::SMALL_BIGNUM, Types::SMALL_INT, Types::SMALL_TUPLE, Types::STRING
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Constructor Details
#initialize(ins) ⇒ Decode
Returns a new instance of Decode.
14
15
16
17
|
# File 'lib/bert/decode.rb', line 14
def initialize(ins)
@in = ins
@peeked = ""
end
|
Instance Attribute Details
#in ⇒ Object
Returns the value of attribute in.
3
4
5
|
# File 'lib/bert/decode.rb', line 3
def in
@in
end
|
Class Method Details
.decode(string) ⇒ Object
10
11
12
|
# File 'lib/bert/decode.rb', line 10
def self.decode(string)
new(StringIO.new(string)).read_any
end
|
.impl ⇒ Object
6
7
8
|
# File 'lib/bert/decode.rb', line 6
def self.impl
'Ruby'
end
|
Instance Method Details
#fail(str) ⇒ Object
244
245
246
|
# File 'lib/bert/decode.rb', line 244
def fail(str)
raise str
end
|
#peek(length) ⇒ Object
60
61
62
63
64
65
66
67
68
|
# File 'lib/bert/decode.rb', line 60
def peek(length)
if length <= @peeked.length
@peeked[0...length]
else
read_bytes = @in.read(length - @peeked.length)
@peeked << read_bytes if read_bytes
@peeked
end
end
|
#peek_1 ⇒ Object
70
71
72
|
# File 'lib/bert/decode.rb', line 70
def peek_1
peek(1).unpack("C").first
end
|
#peek_2 ⇒ Object
74
75
76
|
# File 'lib/bert/decode.rb', line 74
def peek_2
peek(2).unpack("n").first
end
|
#read(length) ⇒ Object
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
# File 'lib/bert/decode.rb', line 43
def read(length)
if length < @peeked.length
result = @peeked[0...length]
@peeked = @peeked[length..-1]
length = 0
else
result = @peeked
@peeked = ''
length -= result.length
end
if length > 0
result << @in.read(length)
end
result
end
|
#read_1 ⇒ Object
78
79
80
|
# File 'lib/bert/decode.rb', line 78
def read_1
read(1).unpack("C").first
end
|
#read_2 ⇒ Object
82
83
84
|
# File 'lib/bert/decode.rb', line 82
def read_2
read(2).unpack("n").first
end
|
#read_4 ⇒ Object
86
87
88
|
# File 'lib/bert/decode.rb', line 86
def read_4
read(4).unpack("N").first
end
|
#read_any ⇒ Object
19
20
21
22
|
# File 'lib/bert/decode.rb', line 19
def read_any
fail("Bad Magic") unless read_1 == MAGIC
read_any_raw
end
|
#read_any_raw ⇒ Object
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# File 'lib/bert/decode.rb', line 24
def read_any_raw
case peek_1
when ATOM then read_atom
when SMALL_INT then read_small_int
when INT then read_int
when SMALL_BIGNUM then read_small_bignum
when LARGE_BIGNUM then read_large_bignum
when FLOAT then read_float
when SMALL_TUPLE then read_small_tuple
when LARGE_TUPLE then read_large_tuple
when NIL then read_nil
when STRING then read_erl_string
when LIST then read_list
when BIN then read_bin
else
fail("Unknown term tag: #{peek_1}")
end
end
|
#read_atom ⇒ Object
94
95
96
97
98
99
100
101
102
103
104
|
# File 'lib/bert/decode.rb', line 94
def read_atom
fail("Invalid Type, not an atom") unless read_1 == ATOM
length = read_2
a = read_string(length)
case a
when ""
Marshal.load("\004\b:\005") else
a.to_sym
end
end
|
#read_bin ⇒ Object
238
239
240
241
242
|
# File 'lib/bert/decode.rb', line 238
def read_bin
fail("Invalid Type, not an erlang binary") unless read_1 == BIN
length = read_4
read_string(length)
end
|
#read_complex_type(arity) ⇒ Object
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
# File 'lib/bert/decode.rb', line 177
def read_complex_type(arity)
case read_any_raw
when :nil
nil
when :true
true
when :false
false
when :time
Time.at(read_any_raw * 1_000_000 + read_any_raw, read_any_raw)
when :regex
source = read_any_raw
opts = read_any_raw
options = 0
options |= Regexp::EXTENDED if opts.include?(:extended)
options |= Regexp::IGNORECASE if opts.include?(:caseless)
options |= Regexp::MULTILINE if opts.include?(:multiline)
Regexp.new(source, options)
when :dict
read_dict
else
nil
end
end
|
#read_dict ⇒ Object
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
# File 'lib/bert/decode.rb', line 202
def read_dict
type = read_1
fail("Invalid dict spec, not an erlang list") unless [LIST, NIL].include?(type)
if type == LIST
length = read_4
else
length = 0
end
hash = {}
length.times do |i|
pair = read_any_raw
hash[pair[0]] = pair[1]
end
read_1 if type == LIST
hash
end
|
#read_erl_string ⇒ Object
224
225
226
227
228
|
# File 'lib/bert/decode.rb', line 224
def read_erl_string
fail("Invalid Type, not an erlang string") unless read_1 == STRING
length = read_2
read_string(length).unpack('C' * length)
end
|
#read_float ⇒ Object
145
146
147
148
149
|
# File 'lib/bert/decode.rb', line 145
def read_float
fail("Invalid Type, not a float") unless read_1 == FLOAT
string_value = read_string(31)
result = string_value.to_f
end
|
#read_int ⇒ Object
111
112
113
114
115
116
117
|
# File 'lib/bert/decode.rb', line 111
def read_int
fail("Invalid Type, not an int") unless read_1 == INT
value = read_4
negative = (value >> 31)[0] == 1
value = (value - (1 << 32)) if negative
value = Fixnum.induced_from(value)
end
|
#read_large_bignum ⇒ Object
132
133
134
135
136
137
138
139
140
141
142
143
|
# File 'lib/bert/decode.rb', line 132
def read_large_bignum
fail("Invalid Type, not a large bignum") unless read_1 == LARGE_BIGNUM
size = read_4
sign = read_1
bytes = read_string(size).unpack("C" * size)
added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
byte, index = *byte_index
value = (byte * (256 ** index))
sign != 0 ? (result - value) : (result + value)
end
Bignum.induced_from(added)
end
|
#read_large_tuple ⇒ Object
156
157
158
159
|
# File 'lib/bert/decode.rb', line 156
def read_large_tuple
fail("Invalid Type, not a small tuple") unless read_1 == LARGE_TUPLE
read_tuple(read_4)
end
|
#read_list ⇒ Object
230
231
232
233
234
235
236
|
# File 'lib/bert/decode.rb', line 230
def read_list
fail("Invalid Type, not an erlang list") unless read_1 == LIST
length = read_4
list = (0...length).map { |i| read_any_raw }
read_1
list
end
|
#read_nil ⇒ Object
219
220
221
222
|
# File 'lib/bert/decode.rb', line 219
def read_nil
fail("Invalid Type, not a nil list") unless read_1 == NIL
[]
end
|
#read_small_bignum ⇒ Object
119
120
121
122
123
124
125
126
127
128
129
130
|
# File 'lib/bert/decode.rb', line 119
def read_small_bignum
fail("Invalid Type, not a small bignum") unless read_1 == SMALL_BIGNUM
size = read_1
sign = read_1
bytes = read_string(size).unpack("C" * size)
added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
byte, index = *byte_index
value = (byte * (256 ** index))
sign != 0 ? (result - value) : (result + value)
end
Bignum.induced_from(added)
end
|
#read_small_int ⇒ Object
106
107
108
109
|
# File 'lib/bert/decode.rb', line 106
def read_small_int
fail("Invalid Type, not a small int") unless read_1 == SMALL_INT
read_1
end
|
#read_small_tuple ⇒ Object
151
152
153
154
|
# File 'lib/bert/decode.rb', line 151
def read_small_tuple
fail("Invalid Type, not a small tuple") unless read_1 == SMALL_TUPLE
read_tuple(read_1)
end
|
#read_string(length) ⇒ Object
90
91
92
|
# File 'lib/bert/decode.rb', line 90
def read_string(length)
read(length)
end
|
#read_tuple(arity) ⇒ Object
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
# File 'lib/bert/decode.rb', line 161
def read_tuple(arity)
if arity > 0
tag = read_any_raw
if tag == :bert
read_complex_type(arity)
else
tuple = Tuple.new(arity)
tuple[0] = tag
(arity - 1).times { |i| tuple[i + 1] = read_any_raw }
tuple
end
else
Tuple.new
end
end
|