schmurfy-bert 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bert.gemspec ADDED
@@ -0,0 +1,72 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{schmurfy-bert}
8
+ s.version = "1.2.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tom Preston-Werner"]
12
+ s.date = %q{2010-12-07}
13
+ s.description = %q{BERT Serializiation for Ruby}
14
+ s.email = %q{tom@mojombo.com}
15
+ s.extensions = ["ext/bert/c/extconf.rb", "ext/bert/c/extconf.rb"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".gitignore",
23
+ "History.txt",
24
+ "LICENSE",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bench/bench.rb",
29
+ "bench/decode_bench.rb",
30
+ "bench/encode_bench.rb",
31
+ "bench/results.txt",
32
+ "bert.gemspec",
33
+ "ext/bert/c/extconf.rb",
34
+ "lib/bert.rb",
35
+ "lib/bert/bert.rb",
36
+ "lib/bert/decode.rb",
37
+ "lib/bert/decoder.rb",
38
+ "lib/bert/encode.rb",
39
+ "lib/bert/encoder.rb",
40
+ "lib/bert/terms.rb",
41
+ "lib/bert/types.rb",
42
+ "test/bert_test.rb",
43
+ "test/decoder_test.rb",
44
+ "test/encoder_test.rb",
45
+ "test/test_helper.rb"
46
+ ]
47
+ s.homepage = %q{http://github.com/mojombo/bert}
48
+ s.rdoc_options = ["--charset=UTF-8"]
49
+ s.require_paths = ["lib", "ext"]
50
+ s.rubygems_version = %q{1.3.7}
51
+ s.summary = %q{BERT Serializiation for Ruby}
52
+ s.test_files = [
53
+ "test/bert_test.rb",
54
+ "test/decoder_test.rb",
55
+ "test/encoder_test.rb",
56
+ "test/test_helper.rb"
57
+ ]
58
+
59
+ if s.respond_to? :specification_version then
60
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
61
+ s.specification_version = 3
62
+
63
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
65
+ else
66
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
67
+ end
68
+ else
69
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
70
+ end
71
+ end
72
+
@@ -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)
data/lib/bert.rb ADDED
@@ -0,0 +1,20 @@
1
+
2
+ require 'stringio'
3
+
4
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext])
5
+
6
+ require 'bert/bert'
7
+ require 'bert/types'
8
+ require 'bert/terms'
9
+
10
+ require 'bert/decode'
11
+
12
+ require 'bert/encode'
13
+
14
+ require 'bert/encoder'
15
+ require 'bert/decoder'
16
+
17
+ # Global method for specifying that an array should be encoded as a tuple.
18
+ def t
19
+ BERT::Tuple
20
+ end
data/lib/bert/bert.rb ADDED
@@ -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,269 @@
1
+ module BERT
2
+ class Decode
3
+ attr_accessor :in
4
+ include Types
5
+ include Terms
6
+
7
+ def self.impl
8
+ 'Ruby'
9
+ end
10
+
11
+ def self.decode(string)
12
+ new(StringIO.new(string)).read_any
13
+ end
14
+
15
+ def initialize(ins)
16
+ @in = ins
17
+ @peeked = ""
18
+ end
19
+
20
+ def read_any
21
+ fail("Bad Magic") unless read_1 == MAGIC
22
+ read_any_raw
23
+ end
24
+
25
+ def read_any_raw
26
+ case peek_1
27
+ when ATOM then read_atom
28
+ when SMALL_INT then read_small_int
29
+ when INT then read_int
30
+ when SMALL_BIGNUM then read_small_bignum
31
+ when LARGE_BIGNUM then read_large_bignum
32
+ when FLOAT then read_float
33
+ when SMALL_TUPLE then read_small_tuple
34
+ when LARGE_TUPLE then read_large_tuple
35
+ when NIL then read_nil
36
+ when STRING then read_erl_string
37
+ when LIST then read_list
38
+ when BIN then read_bin
39
+ when PID then read_pid
40
+ else
41
+ fail("Unknown term tag: #{peek_1}")
42
+ end
43
+ end
44
+
45
+ def read(length)
46
+ if length < @peeked.length
47
+ result = @peeked[0...length]
48
+ @peeked = @peeked[length..-1]
49
+ length = 0
50
+ else
51
+ result = @peeked
52
+ @peeked = ''
53
+ length -= result.length
54
+ end
55
+
56
+ if length > 0
57
+ result << @in.read(length)
58
+ end
59
+ result.force_encoding('UTF-8')
60
+ end
61
+
62
+ def peek(length)
63
+ if length <= @peeked.length
64
+ @peeked[0...length]
65
+ else
66
+ read_bytes = @in.read(length - @peeked.length)
67
+ @peeked << read_bytes if read_bytes
68
+ @peeked
69
+ end
70
+ end
71
+
72
+ def peek_1
73
+ peek(1).unpack("C").first
74
+ end
75
+
76
+ def peek_2
77
+ peek(2).unpack("n").first
78
+ end
79
+
80
+ def read_1
81
+ read(1).unpack("C").first
82
+ end
83
+
84
+ def read_2
85
+ read(2).unpack("n").first
86
+ end
87
+
88
+ def read_4
89
+ read(4).unpack("N").first
90
+ end
91
+
92
+ def read_string(length)
93
+ read(length)
94
+ end
95
+
96
+ def read_atom
97
+ fail("Invalid Type, not an atom") unless read_1 == ATOM
98
+ length = read_2
99
+ a = read_string(length)
100
+ case a
101
+ when ""
102
+ Marshal.load("\004\b:\005") # Workaround for inability to do ''.to_sym
103
+ else
104
+ a.to_sym
105
+ end
106
+ end
107
+
108
+ def read_small_int
109
+ fail("Invalid Type, not a small int") unless read_1 == SMALL_INT
110
+ read_1
111
+ end
112
+
113
+ def read_int
114
+ fail("Invalid Type, not an int") unless read_1 == INT
115
+ value = read_4
116
+ negative = (value >> 31)[0] == 1
117
+ value = (value - (1 << 32)) if negative
118
+ attempt_cast(Fixnum, value)
119
+ end
120
+
121
+ def read_small_bignum
122
+ fail("Invalid Type, not a small bignum") unless read_1 == SMALL_BIGNUM
123
+ size = read_1
124
+ sign = read_1
125
+ bytes = read_string(size).unpack("C" * size)
126
+ added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
127
+ byte, index = *byte_index
128
+ value = (byte * (256 ** index))
129
+ sign != 0 ? (result - value) : (result + value)
130
+ end
131
+ attempt_cast(Bignum, added)
132
+ end
133
+
134
+ def read_large_bignum
135
+ fail("Invalid Type, not a large bignum") unless read_1 == LARGE_BIGNUM
136
+ size = read_4
137
+ sign = read_1
138
+ bytes = read_string(size).unpack("C" * size)
139
+ added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
140
+ byte, index = *byte_index
141
+ value = (byte * (256 ** index))
142
+ sign != 0 ? (result - value) : (result + value)
143
+ end
144
+ attempt_cast(Bignum, added)
145
+ end
146
+
147
+ def read_float
148
+ fail("Invalid Type, not a float") unless read_1 == FLOAT
149
+ string_value = read_string(31)
150
+ result = string_value.to_f
151
+ end
152
+
153
+ def read_small_tuple
154
+ fail("Invalid Type, not a small tuple") unless read_1 == SMALL_TUPLE
155
+ read_tuple(read_1)
156
+ end
157
+
158
+ def read_large_tuple
159
+ fail("Invalid Type, not a small tuple") unless read_1 == LARGE_TUPLE
160
+ read_tuple(read_4)
161
+ end
162
+
163
+ def read_tuple(arity)
164
+ if arity > 0
165
+ tag = read_any_raw
166
+ if tag == :bert
167
+ read_complex_type(arity)
168
+ else
169
+ tuple = Tuple.new(arity)
170
+ tuple[0] = tag
171
+ (arity - 1).times { |i| tuple[i + 1] = read_any_raw }
172
+ tuple
173
+ end
174
+ else
175
+ Tuple.new
176
+ end
177
+ end
178
+
179
+ def read_complex_type(arity)
180
+ case read_any_raw
181
+ when :nil
182
+ nil
183
+ when :true
184
+ true
185
+ when :false
186
+ false
187
+ when :time
188
+ Time.at(read_any_raw * 1_000_000 + read_any_raw, read_any_raw)
189
+ when :regex
190
+ source = read_any_raw
191
+ opts = read_any_raw
192
+ options = 0
193
+ options |= Regexp::EXTENDED if opts.include?(:extended)
194
+ options |= Regexp::IGNORECASE if opts.include?(:caseless)
195
+ options |= Regexp::MULTILINE if opts.include?(:multiline)
196
+ Regexp.new(source, options)
197
+ when :dict
198
+ read_dict
199
+ else
200
+ nil
201
+ end
202
+ end
203
+
204
+ def read_dict
205
+ type = read_1
206
+ fail("Invalid dict spec, not an erlang list") unless [LIST, NIL].include?(type)
207
+ if type == LIST
208
+ length = read_4
209
+ else
210
+ length = 0
211
+ end
212
+ hash = {}
213
+ length.times do |i|
214
+ pair = read_any_raw
215
+ hash[pair[0]] = pair[1]
216
+ end
217
+ read_1 if type == LIST
218
+ hash
219
+ end
220
+
221
+ def read_nil
222
+ fail("Invalid Type, not a nil list") unless read_1 == NIL
223
+ []
224
+ end
225
+
226
+ def read_erl_string
227
+ fail("Invalid Type, not an erlang string") unless read_1 == STRING
228
+ length = read_2
229
+ read_string(length).unpack('C' * length)
230
+ end
231
+
232
+ def read_list
233
+ fail("Invalid Type, not an erlang list") unless read_1 == LIST
234
+ length = read_4
235
+ list = (0...length).map { |i| read_any_raw }
236
+ read_1
237
+ list
238
+ end
239
+
240
+ def read_bin
241
+ fail("Invalid Type, not an erlang binary") unless read_1 == BIN
242
+ length = read_4
243
+ read_string(length)
244
+ end
245
+
246
+ def read_pid
247
+ fail("Invalid Type, not a pid") unless read_1 == PID
248
+ node = read_atom
249
+ id = read_4
250
+ serial = read_4
251
+ creation = read_1
252
+ Pid.new(node, id, serial, creation)
253
+ end
254
+
255
+ def fail(str)
256
+ raise str
257
+ end
258
+
259
+ private
260
+
261
+ def attempt_cast(klass, value)
262
+ if klass.respond_to?(:induced_from)
263
+ klass.induced_from(value)
264
+ else
265
+ value
266
+ end
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,11 @@
1
+ module BERT
2
+ class Decoder
3
+ # Decode a BERT into a Ruby object.
4
+ # +bert+ is the BERT String
5
+ #
6
+ # Returns a Ruby object
7
+ def self.decode(bert)
8
+ Decode.decode(bert)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,178 @@
1
+ module BERT
2
+ class Encode
3
+ include Types
4
+ include Terms
5
+
6
+ attr_accessor :out
7
+
8
+ def initialize(out)
9
+ self.out = out
10
+ end
11
+
12
+ def self.encode(data)
13
+ io = StringIO.new
14
+ self.new(io).write_any(data)
15
+
16
+ # set correct encoding type
17
+ if io.string.respond_to?(:force_encoding)
18
+ io.string.force_encoding("ASCII-8BIT")
19
+ else
20
+ io.string
21
+ end
22
+ end
23
+
24
+ def write_any obj
25
+ write_1 MAGIC
26
+ write_any_raw obj
27
+ end
28
+
29
+ def write_any_raw obj
30
+ case obj
31
+ when Symbol then write_symbol(obj)
32
+ when Fixnum, Bignum then write_fixnum(obj)
33
+ when Float then write_float(obj)
34
+ when Tuple then write_tuple(obj)
35
+ when Array then write_list(obj)
36
+ when String then write_binary(obj)
37
+ when Pid then write_pid(obj)
38
+ else
39
+ fail(obj)
40
+ end
41
+ end
42
+
43
+ def self.write_byte(byte)
44
+ [byte].pack("C")
45
+ end
46
+
47
+ def write_1(byte)
48
+ out.write(
49
+ self.class.write_byte(byte)
50
+ )
51
+ end
52
+
53
+ def self.write_short(short)
54
+ [short].pack("n")
55
+ end
56
+
57
+ def write_2(short)
58
+ out.write(
59
+ self.class.write_short(short)
60
+ )
61
+ end
62
+
63
+ def self.write_long(long)
64
+ [long].pack("N")
65
+ end
66
+
67
+ def write_4(long)
68
+ out.write(
69
+ self.class.write_long(long)
70
+ )
71
+ end
72
+
73
+ def self.write_string(string)
74
+ string
75
+ end
76
+
77
+ def write_string(string)
78
+ out.write(
79
+ self.class.write_string(string)
80
+ )
81
+ end
82
+
83
+ def write_boolean(bool)
84
+ write_symbol(bool.to_s.to_sym)
85
+ end
86
+
87
+ def write_pid(pid)
88
+ write_1(PID)
89
+ write_symbol(pid.node)
90
+ write_4((pid.node_id & 0x7fff))
91
+ write_4((pid.serial & 0x1fff))
92
+ write_1((pid.creation & 0x3))
93
+ end
94
+
95
+ def write_symbol(sym)
96
+ fail(sym) unless sym.is_a?(Symbol)
97
+ data = sym.to_s
98
+ write_1 ATOM
99
+ write_2 data.length
100
+ write_string data
101
+ end
102
+
103
+ def write_fixnum(num)
104
+ if num >= 0 && num < 256
105
+ write_1 SMALL_INT
106
+ write_1 num
107
+ elsif num <= MAX_INT && num >= MIN_INT
108
+ write_1 INT
109
+ write_4 num
110
+ else
111
+ write_bignum num
112
+ end
113
+ end
114
+
115
+ def write_float(float)
116
+ write_1 FLOAT
117
+ write_string format("%15.15e", float).ljust(31, "\000")
118
+ end
119
+
120
+ def write_bignum(num)
121
+ n = (num.to_s(2).size / 8.0).ceil
122
+ if n < 256
123
+ write_1 SMALL_BIGNUM
124
+ write_1 n
125
+ write_bignum_guts(num)
126
+ else
127
+ write_1 LARGE_BIGNUM
128
+ write_4 n
129
+ write_bignum_guts(num)
130
+ end
131
+ end
132
+
133
+ def write_bignum_guts(num)
134
+ write_1 (num >= 0 ? 0 : 1)
135
+ num = num.abs
136
+ while num != 0
137
+ rem = num % 256
138
+ write_1 rem
139
+ num = num >> 8
140
+ end
141
+ end
142
+
143
+ def write_tuple(data)
144
+ fail(data) unless data.is_a? Array
145
+
146
+ if data.length < 256
147
+ write_1 SMALL_TUPLE
148
+ write_1 data.length
149
+ else
150
+ write_1 LARGE_TUPLE
151
+ write_4 data.length
152
+ end
153
+
154
+ data.each { |e| write_any_raw e }
155
+ end
156
+
157
+ def write_list(data)
158
+ fail(data) unless data.is_a? Array
159
+ write_1 NIL and return if data.empty?
160
+ write_1 LIST
161
+ write_4 data.length
162
+ data.each{|e| write_any_raw e }
163
+ write_1 NIL
164
+ end
165
+
166
+ def write_binary(data)
167
+ write_1 BIN
168
+ write_4 data.bytes.count
169
+ write_string data
170
+ end
171
+
172
+ private
173
+
174
+ def fail(obj)
175
+ raise "Cannot encode to erlang external format: #{obj.inspect}"
176
+ end
177
+ end
178
+ end