bert 1.0.0 → 1.1.0
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/.gitignore +3 -0
- data/History.txt +9 -0
- data/README.md +8 -5
- data/Rakefile +34 -4
- data/VERSION +1 -1
- data/bench/bench.rb +36 -0
- data/bench/decode_bench.rb +87 -0
- data/bench/encode_bench.rb +36 -0
- data/bench/results.txt +55 -0
- data/bert.gemspec +14 -6
- data/ext/bert/c/decode.c +438 -0
- data/ext/bert/c/extconf.rb +11 -0
- data/lib/bert.rb +14 -23
- data/lib/bert/bert.rb +21 -0
- data/lib/bert/decode.rb +248 -0
- data/lib/bert/decoder.rb +1 -53
- data/lib/bert/encode.rb +142 -0
- data/lib/bert/encoder.rb +13 -14
- data/lib/bert/types.rb +21 -0
- data/test/decoder_test.rb +51 -24
- data/test/encoder_test.rb +10 -5
- data/test/test_helper.rb +1 -12
- metadata +15 -14
data/.gitignore
CHANGED
data/History.txt
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
= 1.1.0 / 2009-10-27
|
2
|
+
* Major Changes
|
3
|
+
* Remove reliance on Erlectricity.
|
4
|
+
* Bug fixes
|
5
|
+
* Fix unsigned int problem in C decoder
|
6
|
+
* Fix stack overflow segfault in C binary decoder for > 8MB binaries
|
7
|
+
* Optimize C bytelist decoder
|
8
|
+
* Fix bignum encoding
|
9
|
+
|
1
10
|
= 1.0.0 / 2009-10-19
|
2
11
|
* No changes. Production ready!
|
3
12
|
|
data/README.md
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
BERT
|
2
2
|
====
|
3
3
|
|
4
|
-
|
4
|
+
A BERT (Binary ERlang Term) serialization library for Ruby. It can
|
5
5
|
encode Ruby objects into BERT format and decode BERT binaries into Ruby
|
6
6
|
objects.
|
7
7
|
|
8
8
|
See the BERT specification at [bert-rpc.org](http://bert-rpc.org).
|
9
9
|
|
10
|
-
Instances of the following Ruby classes will be automatically converted to the
|
10
|
+
Instances of the following Ruby classes will be automatically converted to the
|
11
|
+
proper simple BERT type:
|
11
12
|
|
12
13
|
* Fixnum
|
13
14
|
* Float
|
@@ -15,7 +16,8 @@ Instances of the following Ruby classes will be automatically converted to the p
|
|
15
16
|
* Array
|
16
17
|
* String
|
17
18
|
|
18
|
-
Instances of the following Ruby classes will be automatically converted to the
|
19
|
+
Instances of the following Ruby classes will be automatically converted to the
|
20
|
+
proper complex BERT type:
|
19
21
|
|
20
22
|
* NilClass
|
21
23
|
* TrueClass
|
@@ -24,10 +26,11 @@ Instances of the following Ruby classes will be automatically converted to the p
|
|
24
26
|
* Time
|
25
27
|
* Regexp
|
26
28
|
|
27
|
-
To designate tuples, simply prefix an Array literal with a `t` or use the
|
29
|
+
To designate tuples, simply prefix an Array literal with a `t` or use the
|
30
|
+
BERT::Tuple class:
|
28
31
|
|
29
32
|
t[:foo, [1, 2, 3]]
|
30
|
-
BERT::Tuple
|
33
|
+
BERT::Tuple[:foo, [1, 2, 3]]
|
31
34
|
|
32
35
|
Both of these will be converted to (in Erlang syntax):
|
33
36
|
|
data/Rakefile
CHANGED
@@ -10,8 +10,10 @@ begin
|
|
10
10
|
gem.email = "tom@mojombo.com"
|
11
11
|
gem.homepage = "http://github.com/mojombo/bert"
|
12
12
|
gem.authors = ["Tom Preston-Werner"]
|
13
|
-
gem.add_dependency('erlectricity', '>= 1.1.0')
|
14
13
|
gem.add_development_dependency("thoughtbot-shoulda")
|
14
|
+
gem.require_paths = ["lib", "ext"]
|
15
|
+
gem.files.include("ext")
|
16
|
+
gem.extensions << 'ext/bert/c/extconf.rb'
|
15
17
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
18
|
end
|
17
19
|
rescue LoadError
|
@@ -19,12 +21,42 @@ rescue LoadError
|
|
19
21
|
end
|
20
22
|
|
21
23
|
require 'rake/testtask'
|
22
|
-
Rake::TestTask.new(:
|
24
|
+
Rake::TestTask.new(:runtests) do |test|
|
23
25
|
test.libs << 'lib' << 'test'
|
24
26
|
test.pattern = 'test/**/*_test.rb'
|
25
27
|
test.verbose = true
|
26
28
|
end
|
27
29
|
|
30
|
+
task :make do
|
31
|
+
Dir.chdir('ext/bert/c') { `make` }
|
32
|
+
end
|
33
|
+
|
34
|
+
task :clean do
|
35
|
+
['rm -f ext/bert/c/*.bundle', 'rm -f ext/bert/c/*.o'].each do |cmd|
|
36
|
+
`#{cmd}` && puts(cmd)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
task :test => :check_dependencies do
|
41
|
+
require 'fileutils'
|
42
|
+
|
43
|
+
puts "\nCleaning extension build files and running all specs in native ruby mode..."
|
44
|
+
['rm -f ext/bert/c/*.bundle', 'rm -f ext/bert/c/*.o'].each do |cmd|
|
45
|
+
`#{cmd}` && puts(cmd)
|
46
|
+
end
|
47
|
+
pid = fork do
|
48
|
+
exec 'rake runtests'
|
49
|
+
end
|
50
|
+
Process.waitpid(pid)
|
51
|
+
|
52
|
+
puts "\nRunning `make` to build extensions and rerunning decoder specs..."
|
53
|
+
Dir.chdir('ext/bert/c') { `make` }
|
54
|
+
pid = fork do
|
55
|
+
exec 'rake runtests'
|
56
|
+
end
|
57
|
+
Process.waitpid(pid)
|
58
|
+
end
|
59
|
+
|
28
60
|
begin
|
29
61
|
require 'rcov/rcovtask'
|
30
62
|
Rcov::RcovTask.new do |test|
|
@@ -38,8 +70,6 @@ rescue LoadError
|
|
38
70
|
end
|
39
71
|
end
|
40
72
|
|
41
|
-
task :test => :check_dependencies
|
42
|
-
|
43
73
|
task :default => :test
|
44
74
|
|
45
75
|
require 'rake/rdoctask'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.1.0
|
data/bench/bench.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bert'
|
5
|
+
require 'json'
|
6
|
+
require 'yajl'
|
7
|
+
require 'benchmark'
|
8
|
+
|
9
|
+
ITER = 1_000
|
10
|
+
|
11
|
+
tiny = t[:ok, :awesome]
|
12
|
+
small = t[:ok, :answers, [42] * 42]
|
13
|
+
large = ["abc" * 1000] * 100
|
14
|
+
complex = [42, {:foo => 'bac' * 100}, t[(1..100).to_a]] * 10
|
15
|
+
|
16
|
+
Benchmark.bm do |bench|
|
17
|
+
bench.report("BERT tiny") {ITER.times {BERT.decode(BERT.encode(tiny))}}
|
18
|
+
bench.report("BERT small") {ITER.times {BERT.decode(BERT.encode(small))}}
|
19
|
+
bench.report("BERT large") {ITER.times {BERT.decode(BERT.encode(large))}}
|
20
|
+
bench.report("BERT complex") {ITER.times {BERT.decode(BERT.encode(complex))}}
|
21
|
+
|
22
|
+
bench.report("JSON tiny") {ITER.times {JSON.load(JSON.dump(tiny))}}
|
23
|
+
bench.report("JSON small") {ITER.times {JSON.load(JSON.dump(small))}}
|
24
|
+
bench.report("JSON large") {ITER.times {JSON.load(JSON.dump(large))}}
|
25
|
+
bench.report("JSON complex") {ITER.times {JSON.load(JSON.dump(complex))}}
|
26
|
+
|
27
|
+
bench.report("YAJL tiny") {ITER.times {Yajl::Parser.parse(Yajl::Encoder.encode(tiny))}}
|
28
|
+
bench.report("YAJL small") {ITER.times {Yajl::Parser.parse(Yajl::Encoder.encode(small))}}
|
29
|
+
bench.report("YAJL large") {ITER.times {Yajl::Parser.parse(Yajl::Encoder.encode(large))}}
|
30
|
+
bench.report("YAJL complex") {ITER.times {Yajl::Parser.parse(Yajl::Encoder.encode(complex))}}
|
31
|
+
|
32
|
+
bench.report("Ruby tiny") {ITER.times {Marshal.load(Marshal.dump(tiny))}}
|
33
|
+
bench.report("Ruby small") {ITER.times {Marshal.load(Marshal.dump(small))}}
|
34
|
+
bench.report("Ruby large") {ITER.times {Marshal.load(Marshal.dump(large))}}
|
35
|
+
bench.report("Ruby complex") {ITER.times {Marshal.load(Marshal.dump(complex))}}
|
36
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'json'
|
5
|
+
require 'yajl'
|
6
|
+
require 'benchmark'
|
7
|
+
|
8
|
+
ITER = 1_000
|
9
|
+
|
10
|
+
def setup
|
11
|
+
tiny = t[:ok, :awesome]
|
12
|
+
small = t[:ok, :answers, [42] * 42]
|
13
|
+
large = ["abc" * 1000] * 100
|
14
|
+
complex = [42, {:foo => 'bac' * 100}, t[(1..100).to_a]] * 10
|
15
|
+
|
16
|
+
$tiny_encoded_bert = BERT.encode(tiny)
|
17
|
+
$small_encoded_bert = BERT.encode(small)
|
18
|
+
$large_encoded_bert = BERT.encode(large)
|
19
|
+
$complex_encoded_bert = BERT.encode(complex)
|
20
|
+
|
21
|
+
$tiny_encoded_json = JSON.dump(tiny)
|
22
|
+
$small_encoded_json = JSON.dump(small)
|
23
|
+
$large_encoded_json = JSON.dump(large)
|
24
|
+
$complex_encoded_json = JSON.dump(complex)
|
25
|
+
|
26
|
+
$tiny_encoded_yajl = Yajl::Encoder.encode(tiny)
|
27
|
+
$small_encoded_yajl = Yajl::Encoder.encode(small)
|
28
|
+
$large_encoded_yajl = Yajl::Encoder.encode(large)
|
29
|
+
$complex_encoded_yajl = Yajl::Encoder.encode(complex)
|
30
|
+
|
31
|
+
$tiny_encoded_ruby = Marshal.dump(tiny)
|
32
|
+
$small_encoded_ruby = Marshal.dump(small)
|
33
|
+
$large_encoded_ruby = Marshal.dump(large)
|
34
|
+
$complex_encoded_ruby = Marshal.dump(complex)
|
35
|
+
end
|
36
|
+
|
37
|
+
Benchmark.bm(13) do |bench|
|
38
|
+
pid = fork do
|
39
|
+
Dir.chdir(File.join(File.dirname(__FILE__), *%w[.. ext bert c])) { `make` }
|
40
|
+
require 'bert'
|
41
|
+
raise "Could not load C extension" unless BERT::Decode.impl == 'C'
|
42
|
+
setup
|
43
|
+
puts "BERT C Extension Decoder"
|
44
|
+
bench.report("BERT tiny") {ITER.times {BERT.decode($tiny_encoded_bert)}}
|
45
|
+
bench.report("BERT small") {ITER.times {BERT.decode($small_encoded_bert)}}
|
46
|
+
bench.report("BERT large") {ITER.times {BERT.decode($large_encoded_bert)}}
|
47
|
+
bench.report("BERT complex") {ITER.times {BERT.decode($complex_encoded_bert)}}
|
48
|
+
puts
|
49
|
+
end
|
50
|
+
Process.waitpid(pid)
|
51
|
+
|
52
|
+
pid = fork do
|
53
|
+
Dir.chdir(File.join(File.dirname(__FILE__), *%w[.. ext bert c])) do
|
54
|
+
['*.bundle', '*.o'].each { |pat| `rm -f #{pat}` }
|
55
|
+
end
|
56
|
+
require 'bert'
|
57
|
+
raise "Not using Ruby decoder" unless BERT::Decode.impl == 'Ruby'
|
58
|
+
setup
|
59
|
+
puts "BERT Pure Ruby Decoder"
|
60
|
+
bench.report("BERT tiny") {ITER.times {BERT.decode($tiny_encoded_bert)}}
|
61
|
+
bench.report("BERT small") {ITER.times {BERT.decode($small_encoded_bert)}}
|
62
|
+
bench.report("BERT large") {ITER.times {BERT.decode($large_encoded_bert)}}
|
63
|
+
bench.report("BERT complex") {ITER.times {BERT.decode($complex_encoded_bert)}}
|
64
|
+
puts
|
65
|
+
end
|
66
|
+
Process.waitpid(pid)
|
67
|
+
|
68
|
+
require 'bert'
|
69
|
+
setup
|
70
|
+
|
71
|
+
bench.report("JSON tiny") {ITER.times {JSON.load($tiny_encoded_json)}}
|
72
|
+
bench.report("JSON small") {ITER.times {JSON.load($small_encoded_json)}}
|
73
|
+
bench.report("JSON large") {ITER.times {JSON.load($large_encoded_json)}}
|
74
|
+
bench.report("JSON complex") {ITER.times {JSON.load($complex_encoded_json)}}
|
75
|
+
puts
|
76
|
+
|
77
|
+
bench.report("YAJL tiny") {ITER.times {Yajl::Parser.parse($tiny_encoded_yajl)}}
|
78
|
+
bench.report("YAJL small") {ITER.times {Yajl::Parser.parse($small_encoded_yajl)}}
|
79
|
+
bench.report("YAJL large") {ITER.times {Yajl::Parser.parse($large_encoded_yajl)}}
|
80
|
+
bench.report("YAJL complex") {ITER.times {Yajl::Parser.parse($complex_encoded_yajl)}}
|
81
|
+
puts
|
82
|
+
|
83
|
+
bench.report("Ruby tiny") {ITER.times {Marshal.load($tiny_encoded_ruby)}}
|
84
|
+
bench.report("Ruby small") {ITER.times {Marshal.load($small_encoded_ruby)}}
|
85
|
+
bench.report("Ruby large") {ITER.times {Marshal.load($large_encoded_ruby)}}
|
86
|
+
bench.report("Ruby complex") {ITER.times {Marshal.load($complex_encoded_ruby)}}
|
87
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bert'
|
5
|
+
require 'json'
|
6
|
+
require 'yajl'
|
7
|
+
require 'benchmark'
|
8
|
+
|
9
|
+
ITER = 1_000
|
10
|
+
|
11
|
+
tiny = t[:ok, :awesome]
|
12
|
+
small = t[:ok, :answers, [42] * 42]
|
13
|
+
large = ["abc" * 1000] * 100
|
14
|
+
complex = [42, {:foo => 'bac' * 100}, t[(1..100).to_a]] * 10
|
15
|
+
|
16
|
+
Benchmark.bm(13) do |bench|
|
17
|
+
bench.report("BERT tiny") {ITER.times {BERT.encode(tiny)}}
|
18
|
+
bench.report("BERT small") {ITER.times {BERT.encode(small)}}
|
19
|
+
bench.report("BERT large") {ITER.times {BERT.encode(large)}}
|
20
|
+
bench.report("BERT complex") {ITER.times {BERT.encode(complex)}}
|
21
|
+
puts
|
22
|
+
bench.report("JSON tiny") {ITER.times {JSON.dump(tiny)}}
|
23
|
+
bench.report("JSON small") {ITER.times {JSON.dump(small)}}
|
24
|
+
bench.report("JSON large") {ITER.times {JSON.dump(large)}}
|
25
|
+
bench.report("JSON complex") {ITER.times {JSON.dump(complex)}}
|
26
|
+
puts
|
27
|
+
bench.report("JSON tiny") {ITER.times {Yajl::Encoder.encode(tiny)}}
|
28
|
+
bench.report("JSON small") {ITER.times {Yajl::Encoder.encode(small)}}
|
29
|
+
bench.report("JSON large") {ITER.times {Yajl::Encoder.encode(large)}}
|
30
|
+
bench.report("JSON complex") {ITER.times {Yajl::Encoder.encode(complex)}}
|
31
|
+
puts
|
32
|
+
bench.report("Ruby tiny") {ITER.times {Marshal.dump(tiny)}}
|
33
|
+
bench.report("Ruby small") {ITER.times {Marshal.dump(small)}}
|
34
|
+
bench.report("Ruby large") {ITER.times {Marshal.dump(large)}}
|
35
|
+
bench.report("Ruby complex") {ITER.times {Marshal.dump(complex)}}
|
36
|
+
end
|
data/bench/results.txt
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
user system total real
|
2
|
+
|
3
|
+
Comparisons
|
4
|
+
|
5
|
+
JSON tiny 0.020000 0.000000 0.020000 ( 0.017486)
|
6
|
+
JSON small 0.070000 0.000000 0.070000 ( 0.080681)
|
7
|
+
JSON large 15.260000 0.600000 15.860000 ( 16.427857)
|
8
|
+
JSON complex 1.470000 0.010000 1.480000 ( 1.558230)
|
9
|
+
|
10
|
+
YAJL tiny 0.010000 0.000000 0.010000 ( 0.015537)
|
11
|
+
YAJL small 0.050000 0.000000 0.050000 ( 0.061879)
|
12
|
+
YAJL large 3.610000 0.800000 4.410000 ( 4.675255)
|
13
|
+
YAJL complex 1.030000 0.000000 1.030000 ( 1.066871)
|
14
|
+
|
15
|
+
Ruby tiny 0.010000 0.000000 0.010000 ( 0.007117)
|
16
|
+
Ruby small 0.020000 0.000000 0.020000 ( 0.015964)
|
17
|
+
Ruby large 0.040000 0.000000 0.040000 ( 0.042695)
|
18
|
+
Ruby complex 0.040000 0.000000 0.040000 ( 0.048395)
|
19
|
+
|
20
|
+
Ruby encoder / Ruby decoder
|
21
|
+
41503465479e8762916d6997d91639f0d7308a13
|
22
|
+
|
23
|
+
BERT tiny 0.090000 0.000000 0.090000 ( 0.092357)
|
24
|
+
BERT small 0.830000 0.000000 0.830000 ( 0.853270)
|
25
|
+
BERT large 4.190000 0.620000 4.810000 ( 4.959149)
|
26
|
+
BERT complex 19.380000 0.080000 19.460000 ( 20.402862)
|
27
|
+
|
28
|
+
Simple C decoder / Ruby encoder
|
29
|
+
41503465479e8762916d6997d91639f0d7308a13
|
30
|
+
|
31
|
+
BERT tiny 0.030000 0.000000 0.030000 ( 0.033826)
|
32
|
+
BERT small 0.390000 0.010000 0.400000 ( 0.413229)
|
33
|
+
BERT large 2.270000 0.550000 2.820000 ( 3.029141)
|
34
|
+
BERT complex 8.680000 0.040000 8.720000 ( 9.097990)
|
35
|
+
|
36
|
+
Smarter Ruby decoder
|
37
|
+
|
38
|
+
BERT tiny 0.070000 0.000000 0.070000 ( 0.075155)
|
39
|
+
BERT small 0.810000 0.010000 0.820000 ( 0.831905)
|
40
|
+
BERT large 4.340000 0.600000 4.940000 ( 5.064875)
|
41
|
+
BERT complex 18.460000 0.070000 18.530000 ( 19.096184)
|
42
|
+
|
43
|
+
Smarter C decoder
|
44
|
+
|
45
|
+
BERT tiny 0.030000 0.000000 0.030000 ( 0.035685)
|
46
|
+
BERT small 0.350000 0.010000 0.360000 ( 0.358929)
|
47
|
+
BERT large 2.410000 0.560000 2.970000 ( 3.056593)
|
48
|
+
BERT complex 7.910000 0.040000 7.950000 ( 8.236641)
|
49
|
+
|
50
|
+
Smart C Decoder only
|
51
|
+
|
52
|
+
BERT tiny 0.000000 0.000000 0.000000 ( 0.001820)
|
53
|
+
BERT small 0.000000 0.000000 0.000000 ( 0.003859)
|
54
|
+
BERT large 0.430000 0.010000 0.440000 ( 0.499631)
|
55
|
+
BERT complex 0.080000 0.010000 0.090000 ( 0.086992)
|
data/bert.gemspec
CHANGED
@@ -5,13 +5,14 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bert}
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "1.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Tom Preston-Werner"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-28}
|
13
13
|
s.description = %q{BERT Serializiation for Ruby}
|
14
14
|
s.email = %q{tom@mojombo.com}
|
15
|
+
s.extensions = ["ext/bert/c/extconf.rb"]
|
15
16
|
s.extra_rdoc_files = [
|
16
17
|
"LICENSE",
|
17
18
|
"README.md"
|
@@ -24,10 +25,20 @@ Gem::Specification.new do |s|
|
|
24
25
|
"README.md",
|
25
26
|
"Rakefile",
|
26
27
|
"VERSION",
|
28
|
+
"bench/bench.rb",
|
29
|
+
"bench/decode_bench.rb",
|
30
|
+
"bench/encode_bench.rb",
|
31
|
+
"bench/results.txt",
|
27
32
|
"bert.gemspec",
|
33
|
+
"ext/bert/c/decode.c",
|
34
|
+
"ext/bert/c/extconf.rb",
|
28
35
|
"lib/bert.rb",
|
36
|
+
"lib/bert/bert.rb",
|
37
|
+
"lib/bert/decode.rb",
|
29
38
|
"lib/bert/decoder.rb",
|
39
|
+
"lib/bert/encode.rb",
|
30
40
|
"lib/bert/encoder.rb",
|
41
|
+
"lib/bert/types.rb",
|
31
42
|
"test/bert_test.rb",
|
32
43
|
"test/decoder_test.rb",
|
33
44
|
"test/encoder_test.rb",
|
@@ -35,7 +46,7 @@ Gem::Specification.new do |s|
|
|
35
46
|
]
|
36
47
|
s.homepage = %q{http://github.com/mojombo/bert}
|
37
48
|
s.rdoc_options = ["--charset=UTF-8"]
|
38
|
-
s.require_paths = ["lib"]
|
49
|
+
s.require_paths = ["lib", "ext"]
|
39
50
|
s.rubygems_version = %q{1.3.5}
|
40
51
|
s.summary = %q{BERT Serializiation for Ruby}
|
41
52
|
s.test_files = [
|
@@ -50,14 +61,11 @@ Gem::Specification.new do |s|
|
|
50
61
|
s.specification_version = 3
|
51
62
|
|
52
63
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
-
s.add_runtime_dependency(%q<erlectricity>, [">= 1.1.0"])
|
54
64
|
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
55
65
|
else
|
56
|
-
s.add_dependency(%q<erlectricity>, [">= 1.1.0"])
|
57
66
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
58
67
|
end
|
59
68
|
else
|
60
|
-
s.add_dependency(%q<erlectricity>, [">= 1.1.0"])
|
61
69
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
62
70
|
end
|
63
71
|
end
|
data/ext/bert/c/decode.c
ADDED
@@ -0,0 +1,438 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include <string.h>
|
3
|
+
|
4
|
+
#define ERL_VERSION 131
|
5
|
+
#define ERL_SMALL_INT 97
|
6
|
+
#define ERL_INT 98
|
7
|
+
#define ERL_SMALL_BIGNUM 110
|
8
|
+
#define ERL_LARGE_BIGNUM 111
|
9
|
+
#define ERL_FLOAT 99
|
10
|
+
#define ERL_ATOM 100
|
11
|
+
#define ERL_SMALL_TUPLE 104
|
12
|
+
#define ERL_LARGE_TUPLE 105
|
13
|
+
#define ERL_NIL 106
|
14
|
+
#define ERL_STRING 107
|
15
|
+
#define ERL_LIST 108
|
16
|
+
#define ERL_BIN 109
|
17
|
+
|
18
|
+
static VALUE mBERT;
|
19
|
+
static VALUE cDecode;
|
20
|
+
static VALUE cTuple;
|
21
|
+
void Init_decode();
|
22
|
+
|
23
|
+
VALUE method_decode(VALUE klass, VALUE rString);
|
24
|
+
|
25
|
+
VALUE read_any_raw(unsigned char **pData);
|
26
|
+
|
27
|
+
// printers
|
28
|
+
|
29
|
+
void p(VALUE val) {
|
30
|
+
rb_funcall(rb_mKernel, rb_intern("p"), 1, val);
|
31
|
+
}
|
32
|
+
|
33
|
+
// checkers
|
34
|
+
|
35
|
+
void check_int(int num) {
|
36
|
+
char buf[17];
|
37
|
+
sprintf(buf, "%u", num);
|
38
|
+
rb_raise(rb_eStandardError, buf);
|
39
|
+
}
|
40
|
+
|
41
|
+
void check_str(char *str) {
|
42
|
+
rb_raise(rb_eStandardError, str);
|
43
|
+
}
|
44
|
+
|
45
|
+
// string peekers/readers
|
46
|
+
|
47
|
+
unsigned int peek_1(unsigned char **pData) {
|
48
|
+
return (unsigned int) **pData;
|
49
|
+
}
|
50
|
+
|
51
|
+
unsigned int peek_2(unsigned char **pData) {
|
52
|
+
return (unsigned int) ((**pData << 8) + *(*pData + 1));
|
53
|
+
}
|
54
|
+
|
55
|
+
unsigned int peek_4(unsigned char **pData) {
|
56
|
+
return (unsigned int) ((**pData << 24) + (*(*pData + 1) << 16) + (*(*pData + 2) << 8) + *(*pData + 3));
|
57
|
+
}
|
58
|
+
|
59
|
+
unsigned int read_1(unsigned char **pData) {
|
60
|
+
unsigned int val = peek_1(pData);
|
61
|
+
*pData += 1;
|
62
|
+
return val;
|
63
|
+
}
|
64
|
+
|
65
|
+
unsigned int read_2(unsigned char **pData) {
|
66
|
+
unsigned int val = peek_2(pData);
|
67
|
+
*pData += 2;
|
68
|
+
return val;
|
69
|
+
}
|
70
|
+
|
71
|
+
unsigned int read_4(unsigned char **pData) {
|
72
|
+
unsigned int val = peek_4(pData);
|
73
|
+
*pData += 4;
|
74
|
+
return val;
|
75
|
+
}
|
76
|
+
|
77
|
+
// tuples
|
78
|
+
|
79
|
+
VALUE read_tuple(unsigned char **pData, unsigned int arity);
|
80
|
+
|
81
|
+
VALUE read_dict_pair(unsigned char **pData) {
|
82
|
+
if(read_1(pData) != ERL_SMALL_TUPLE) {
|
83
|
+
rb_raise(rb_eStandardError, "Invalid dict pair, not a small tuple");
|
84
|
+
}
|
85
|
+
|
86
|
+
int arity = read_1(pData);
|
87
|
+
|
88
|
+
if(arity != 2) {
|
89
|
+
rb_raise(rb_eStandardError, "Invalid dict pair, not a 2-tuple");
|
90
|
+
}
|
91
|
+
|
92
|
+
return read_tuple(pData, arity);
|
93
|
+
}
|
94
|
+
|
95
|
+
VALUE read_dict(unsigned char **pData) {
|
96
|
+
int type = read_1(pData);
|
97
|
+
if(!(type == ERL_LIST || type == ERL_NIL)) {
|
98
|
+
rb_raise(rb_eStandardError, "Invalid dict spec, not an erlang list");
|
99
|
+
}
|
100
|
+
|
101
|
+
unsigned int length = 0;
|
102
|
+
if(type == ERL_LIST) {
|
103
|
+
length = read_4(pData);
|
104
|
+
}
|
105
|
+
|
106
|
+
VALUE cHash = rb_const_get(rb_cObject, rb_intern("Hash"));
|
107
|
+
VALUE hash = rb_funcall(cHash, rb_intern("new"), 0);
|
108
|
+
|
109
|
+
int i;
|
110
|
+
for(i = 0; i < length; ++i) {
|
111
|
+
VALUE pair = read_dict_pair(pData);
|
112
|
+
VALUE first = rb_ary_entry(pair, 0);
|
113
|
+
VALUE last = rb_ary_entry(pair, 1);
|
114
|
+
rb_funcall(hash, rb_intern("store"), 2, first, last);
|
115
|
+
}
|
116
|
+
|
117
|
+
if(type == ERL_LIST) {
|
118
|
+
read_1(pData);
|
119
|
+
}
|
120
|
+
|
121
|
+
return hash;
|
122
|
+
}
|
123
|
+
|
124
|
+
VALUE read_complex_type(unsigned char **pData, int arity) {
|
125
|
+
VALUE type = read_any_raw(pData);
|
126
|
+
ID id = SYM2ID(type);
|
127
|
+
if(id == rb_intern("nil")) {
|
128
|
+
return Qnil;
|
129
|
+
} else if(id == rb_intern("true")) {
|
130
|
+
return Qtrue;
|
131
|
+
} else if(id == rb_intern("false")) {
|
132
|
+
return Qfalse;
|
133
|
+
} else if(id == rb_intern("time")) {
|
134
|
+
VALUE megasecs = read_any_raw(pData);
|
135
|
+
VALUE msecs = rb_funcall(megasecs, rb_intern("*"), 1, INT2NUM(1000000));
|
136
|
+
VALUE secs = read_any_raw(pData);
|
137
|
+
VALUE microsecs = read_any_raw(pData);
|
138
|
+
VALUE stamp = rb_funcall(msecs, rb_intern("+"), 1, secs);
|
139
|
+
return rb_funcall(rb_cTime, rb_intern("at"), 2, stamp, microsecs);
|
140
|
+
} else if(id == rb_intern("regex")) {
|
141
|
+
VALUE source = read_any_raw(pData);
|
142
|
+
VALUE opts = read_any_raw(pData);
|
143
|
+
int flags = 0;
|
144
|
+
if(rb_ary_includes(opts, ID2SYM(rb_intern("caseless"))))
|
145
|
+
flags = flags | 1;
|
146
|
+
if(rb_ary_includes(opts, ID2SYM(rb_intern("extended"))))
|
147
|
+
flags = flags | 2;
|
148
|
+
if(rb_ary_includes(opts, ID2SYM(rb_intern("multiline"))))
|
149
|
+
flags = flags | 4;
|
150
|
+
return rb_funcall(rb_cRegexp, rb_intern("new"), 2, source, INT2NUM(flags));
|
151
|
+
} else if(id == rb_intern("dict")) {
|
152
|
+
return read_dict(pData);
|
153
|
+
} else {
|
154
|
+
return Qnil;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
VALUE read_tuple(unsigned char **pData, unsigned int arity) {
|
159
|
+
if(arity > 0) {
|
160
|
+
VALUE tag = read_any_raw(pData);
|
161
|
+
if(SYM2ID(tag) == rb_intern("bert")) {
|
162
|
+
return read_complex_type(pData, arity);
|
163
|
+
} else {
|
164
|
+
VALUE tuple = rb_funcall(cTuple, rb_intern("new"), 1, INT2NUM(arity));
|
165
|
+
rb_ary_store(tuple, 0, tag);
|
166
|
+
int i;
|
167
|
+
for(i = 1; i < arity; ++i) {
|
168
|
+
rb_ary_store(tuple, i, read_any_raw(pData));
|
169
|
+
}
|
170
|
+
return tuple;
|
171
|
+
}
|
172
|
+
} else {
|
173
|
+
return rb_funcall(cTuple, rb_intern("new"), 0);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
VALUE read_small_tuple(unsigned char **pData) {
|
178
|
+
if(read_1(pData) != ERL_SMALL_TUPLE) {
|
179
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small tuple");
|
180
|
+
}
|
181
|
+
|
182
|
+
int arity = read_1(pData);
|
183
|
+
return read_tuple(pData, arity);
|
184
|
+
}
|
185
|
+
|
186
|
+
VALUE read_large_tuple(unsigned char **pData) {
|
187
|
+
if(read_1(pData) != ERL_LARGE_TUPLE) {
|
188
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a large tuple");
|
189
|
+
}
|
190
|
+
|
191
|
+
unsigned int arity = read_4(pData);
|
192
|
+
return read_tuple(pData, arity);
|
193
|
+
}
|
194
|
+
|
195
|
+
// lists
|
196
|
+
|
197
|
+
VALUE read_list(unsigned char **pData) {
|
198
|
+
if(read_1(pData) != ERL_LIST) {
|
199
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an erlang list");
|
200
|
+
}
|
201
|
+
|
202
|
+
unsigned int size = read_4(pData);
|
203
|
+
|
204
|
+
VALUE array = rb_ary_new2(size);
|
205
|
+
|
206
|
+
int i;
|
207
|
+
for(i = 0; i < size; ++i) {
|
208
|
+
rb_ary_store(array, i, read_any_raw(pData));
|
209
|
+
}
|
210
|
+
|
211
|
+
read_1(pData);
|
212
|
+
|
213
|
+
return array;
|
214
|
+
}
|
215
|
+
|
216
|
+
// primitives
|
217
|
+
|
218
|
+
void read_string_raw(unsigned char *dest, unsigned char **pData, unsigned int length) {
|
219
|
+
memcpy((char *) dest, (char *) *pData, length);
|
220
|
+
*(dest + length) = (unsigned char) 0;
|
221
|
+
*pData += length;
|
222
|
+
}
|
223
|
+
|
224
|
+
VALUE read_bin(unsigned char **pData) {
|
225
|
+
if(read_1(pData) != ERL_BIN) {
|
226
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an erlang binary");
|
227
|
+
}
|
228
|
+
|
229
|
+
unsigned int length = read_4(pData);
|
230
|
+
|
231
|
+
VALUE rStr = rb_str_new((char *) *pData, length);
|
232
|
+
*pData += length;
|
233
|
+
|
234
|
+
return rStr;
|
235
|
+
}
|
236
|
+
|
237
|
+
VALUE read_string(unsigned char **pData) {
|
238
|
+
if(read_1(pData) != ERL_STRING) {
|
239
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an erlang string");
|
240
|
+
}
|
241
|
+
|
242
|
+
int length = read_2(pData);
|
243
|
+
VALUE array = rb_ary_new2(length);
|
244
|
+
|
245
|
+
int i = 0;
|
246
|
+
for(i; i < length; ++i) {
|
247
|
+
rb_ary_store(array, i, INT2NUM(**pData));
|
248
|
+
*pData += 1;
|
249
|
+
}
|
250
|
+
|
251
|
+
return array;
|
252
|
+
}
|
253
|
+
|
254
|
+
VALUE read_atom(unsigned char **pData) {
|
255
|
+
if(read_1(pData) != ERL_ATOM) {
|
256
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an atom");
|
257
|
+
}
|
258
|
+
|
259
|
+
int length = read_2(pData);
|
260
|
+
|
261
|
+
unsigned char buf[length + 1];
|
262
|
+
read_string_raw(buf, pData, length);
|
263
|
+
|
264
|
+
return ID2SYM(rb_intern((char *) buf));
|
265
|
+
}
|
266
|
+
|
267
|
+
VALUE read_small_int(unsigned char **pData) {
|
268
|
+
if(read_1(pData) != ERL_SMALL_INT) {
|
269
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small int");
|
270
|
+
}
|
271
|
+
|
272
|
+
int value = read_1(pData);
|
273
|
+
|
274
|
+
return INT2FIX(value);
|
275
|
+
}
|
276
|
+
|
277
|
+
VALUE read_int(unsigned char **pData) {
|
278
|
+
if(read_1(pData) != ERL_INT) {
|
279
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an int");
|
280
|
+
}
|
281
|
+
|
282
|
+
long long value = read_4(pData);
|
283
|
+
|
284
|
+
long long negative = ((value >> 31) & 0x1 == 1);
|
285
|
+
|
286
|
+
if(negative) {
|
287
|
+
value = (value - ((long long) 1 << 32));
|
288
|
+
}
|
289
|
+
|
290
|
+
return INT2FIX(value);
|
291
|
+
}
|
292
|
+
|
293
|
+
VALUE read_small_bignum(unsigned char **pData) {
|
294
|
+
if(read_1(pData) != ERL_SMALL_BIGNUM) {
|
295
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small bignum");
|
296
|
+
}
|
297
|
+
|
298
|
+
unsigned int size = read_1(pData);
|
299
|
+
unsigned int sign = read_1(pData);
|
300
|
+
|
301
|
+
VALUE num = INT2NUM(0);
|
302
|
+
VALUE tmp;
|
303
|
+
|
304
|
+
unsigned char buf[size + 1];
|
305
|
+
read_string_raw(buf, pData, size);
|
306
|
+
|
307
|
+
int i;
|
308
|
+
for(i = 0; i < size; ++i) {
|
309
|
+
tmp = INT2FIX(*(buf + i));
|
310
|
+
tmp = rb_funcall(tmp, rb_intern("<<"), 1, INT2NUM(i * 8));
|
311
|
+
num = rb_funcall(num, rb_intern("+"), 1, tmp);
|
312
|
+
}
|
313
|
+
|
314
|
+
if(sign) {
|
315
|
+
num = rb_funcall(num, rb_intern("*"), 1, INT2NUM(-1));
|
316
|
+
}
|
317
|
+
|
318
|
+
return num;
|
319
|
+
}
|
320
|
+
|
321
|
+
VALUE read_large_bignum(unsigned char **pData) {
|
322
|
+
if(read_1(pData) != ERL_LARGE_BIGNUM) {
|
323
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small bignum");
|
324
|
+
}
|
325
|
+
|
326
|
+
unsigned int size = read_4(pData);
|
327
|
+
unsigned int sign = read_1(pData);
|
328
|
+
|
329
|
+
VALUE num = INT2NUM(0);
|
330
|
+
VALUE tmp;
|
331
|
+
|
332
|
+
unsigned char buf[size + 1];
|
333
|
+
read_string_raw(buf, pData, size);
|
334
|
+
|
335
|
+
int i;
|
336
|
+
for(i = 0; i < size; ++i) {
|
337
|
+
tmp = INT2FIX(*(buf + i));
|
338
|
+
tmp = rb_funcall(tmp, rb_intern("<<"), 1, INT2NUM(i * 8));
|
339
|
+
|
340
|
+
num = rb_funcall(num, rb_intern("+"), 1, tmp);
|
341
|
+
}
|
342
|
+
|
343
|
+
if(sign) {
|
344
|
+
num = rb_funcall(num, rb_intern("*"), 1, INT2NUM(-1));
|
345
|
+
}
|
346
|
+
|
347
|
+
return num;
|
348
|
+
}
|
349
|
+
|
350
|
+
VALUE read_float(unsigned char **pData) {
|
351
|
+
if(read_1(pData) != ERL_FLOAT) {
|
352
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a float");
|
353
|
+
}
|
354
|
+
|
355
|
+
unsigned char buf[32];
|
356
|
+
read_string_raw(buf, pData, 31);
|
357
|
+
|
358
|
+
VALUE rString = rb_str_new2((char *) buf);
|
359
|
+
|
360
|
+
return rb_funcall(rString, rb_intern("to_f"), 0);
|
361
|
+
}
|
362
|
+
|
363
|
+
VALUE read_nil(unsigned char **pData) {
|
364
|
+
if(read_1(pData) != ERL_NIL) {
|
365
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a nil list");
|
366
|
+
}
|
367
|
+
|
368
|
+
return rb_ary_new2(0);
|
369
|
+
}
|
370
|
+
|
371
|
+
// read_any_raw
|
372
|
+
|
373
|
+
VALUE read_any_raw(unsigned char **pData) {
|
374
|
+
switch(peek_1(pData)) {
|
375
|
+
case ERL_SMALL_INT:
|
376
|
+
return read_small_int(pData);
|
377
|
+
break;
|
378
|
+
case ERL_INT:
|
379
|
+
return read_int(pData);
|
380
|
+
break;
|
381
|
+
case ERL_FLOAT:
|
382
|
+
return read_float(pData);
|
383
|
+
break;
|
384
|
+
case ERL_ATOM:
|
385
|
+
return read_atom(pData);
|
386
|
+
break;
|
387
|
+
case ERL_SMALL_TUPLE:
|
388
|
+
return read_small_tuple(pData);
|
389
|
+
break;
|
390
|
+
case ERL_LARGE_TUPLE:
|
391
|
+
return read_large_tuple(pData);
|
392
|
+
break;
|
393
|
+
case ERL_NIL:
|
394
|
+
return read_nil(pData);
|
395
|
+
break;
|
396
|
+
case ERL_STRING:
|
397
|
+
return read_string(pData);
|
398
|
+
break;
|
399
|
+
case ERL_LIST:
|
400
|
+
return read_list(pData);
|
401
|
+
break;
|
402
|
+
case ERL_BIN:
|
403
|
+
return read_bin(pData);
|
404
|
+
break;
|
405
|
+
case ERL_SMALL_BIGNUM:
|
406
|
+
return read_small_bignum(pData);
|
407
|
+
break;
|
408
|
+
case ERL_LARGE_BIGNUM:
|
409
|
+
return read_large_bignum(pData);
|
410
|
+
break;
|
411
|
+
}
|
412
|
+
return Qnil;
|
413
|
+
}
|
414
|
+
|
415
|
+
VALUE method_decode(VALUE klass, VALUE rString) {
|
416
|
+
unsigned char *data = (unsigned char *) StringValuePtr(rString);
|
417
|
+
|
418
|
+
unsigned char **pData = &data;
|
419
|
+
|
420
|
+
// check protocol version
|
421
|
+
if(read_1(pData) != ERL_VERSION) {
|
422
|
+
rb_raise(rb_eStandardError, "Bad Magic");
|
423
|
+
}
|
424
|
+
|
425
|
+
return read_any_raw(pData);
|
426
|
+
}
|
427
|
+
|
428
|
+
VALUE method_impl(VALUE klass) {
|
429
|
+
return rb_str_new("C", 1);
|
430
|
+
}
|
431
|
+
|
432
|
+
void Init_decode() {
|
433
|
+
mBERT = rb_const_get(rb_cObject, rb_intern("BERT"));
|
434
|
+
cDecode = rb_define_class_under(mBERT, "Decode", rb_cObject);
|
435
|
+
cTuple = rb_const_get(mBERT, rb_intern("Tuple"));
|
436
|
+
rb_define_singleton_method(cDecode, "decode", method_decode, 1);
|
437
|
+
rb_define_singleton_method(cDecode, "impl", method_impl, 0);
|
438
|
+
}
|