schmurfy-bert 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
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