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.
16
17
18
19
|
# File 'lib/bert/decode.rb', line 16
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
13
14
|
# File 'lib/bert/decode.rb', line 10
def self.decode(string)
io = StringIO.new(string)
io.set_encoding('binary') if io.respond_to?(:set_encoding)
new(io).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
246
247
248
|
# File 'lib/bert/decode.rb', line 246
def fail(str)
raise str
end
|
#peek(length) ⇒ Object
62
63
64
65
66
67
68
69
70
|
# File 'lib/bert/decode.rb', line 62
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
72
73
74
|
# File 'lib/bert/decode.rb', line 72
def peek_1
peek(1).unpack("C").first
end
|
#peek_2 ⇒ Object
76
77
78
|
# File 'lib/bert/decode.rb', line 76
def peek_2
peek(2).unpack("n").first
end
|
#read(length) ⇒ Object
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
# File 'lib/bert/decode.rb', line 45
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
80
81
82
|
# File 'lib/bert/decode.rb', line 80
def read_1
read(1).unpack("C").first
end
|
#read_2 ⇒ Object
84
85
86
|
# File 'lib/bert/decode.rb', line 84
def read_2
read(2).unpack("n").first
end
|
#read_4 ⇒ Object
88
89
90
|
# File 'lib/bert/decode.rb', line 88
def read_4
read(4).unpack("N").first
end
|
#read_any ⇒ Object
21
22
23
24
|
# File 'lib/bert/decode.rb', line 21
def read_any
fail("Bad Magic") unless read_1 == MAGIC
read_any_raw
end
|
#read_any_raw ⇒ Object
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
# File 'lib/bert/decode.rb', line 26
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
96
97
98
99
100
101
102
103
104
105
106
|
# File 'lib/bert/decode.rb', line 96
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
240
241
242
243
244
|
# File 'lib/bert/decode.rb', line 240
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
# File 'lib/bert/decode.rb', line 179
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
# File 'lib/bert/decode.rb', line 204
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
226
227
228
229
230
|
# File 'lib/bert/decode.rb', line 226
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
147
148
149
150
151
|
# File 'lib/bert/decode.rb', line 147
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
113
114
115
116
117
118
119
|
# File 'lib/bert/decode.rb', line 113
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
end
|
#read_large_bignum ⇒ Object
134
135
136
137
138
139
140
141
142
143
144
145
|
# File 'lib/bert/decode.rb', line 134
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
added
end
|
#read_large_tuple ⇒ Object
158
159
160
161
|
# File 'lib/bert/decode.rb', line 158
def read_large_tuple
fail("Invalid Type, not a small tuple") unless read_1 == LARGE_TUPLE
read_tuple(read_4)
end
|
#read_list ⇒ Object
232
233
234
235
236
237
238
|
# File 'lib/bert/decode.rb', line 232
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
221
222
223
224
|
# File 'lib/bert/decode.rb', line 221
def read_nil
fail("Invalid Type, not a nil list") unless read_1 == NIL
[]
end
|
#read_small_bignum ⇒ Object
121
122
123
124
125
126
127
128
129
130
131
132
|
# File 'lib/bert/decode.rb', line 121
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
added
end
|
#read_small_int ⇒ Object
108
109
110
111
|
# File 'lib/bert/decode.rb', line 108
def read_small_int
fail("Invalid Type, not a small int") unless read_1 == SMALL_INT
read_1
end
|
#read_small_tuple ⇒ Object
153
154
155
156
|
# File 'lib/bert/decode.rb', line 153
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
92
93
94
|
# File 'lib/bert/decode.rb', line 92
def read_string(length)
read(length)
end
|
#read_tuple(arity) ⇒ Object
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
# File 'lib/bert/decode.rb', line 163
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
|