beambridge 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/History.txt +35 -0
  6. data/LICENSE +20 -0
  7. data/README.md +130 -0
  8. data/Rakefile +44 -0
  9. data/VERSION.yml +4 -0
  10. data/beambridge.gemspec +29 -0
  11. data/benchmarks/bench.rb +21 -0
  12. data/examples/echo/README.md +12 -0
  13. data/examples/echo/echo.erl +13 -0
  14. data/examples/echo/echo.rb +10 -0
  15. data/examples/gruff/gruff.erl +61 -0
  16. data/examples/gruff/gruff_provider.rb +30 -0
  17. data/examples/gruff/gruff_run.sh +19 -0
  18. data/examples/gruff/stat_run.sh +20 -0
  19. data/examples/gruff/stat_writer.erl +40 -0
  20. data/examples/simple/README.md +5 -0
  21. data/examples/simple/rerl.rb +110 -0
  22. data/examples/simple/rerl.sh +37 -0
  23. data/examples/tinderl/README.md +14 -0
  24. data/examples/tinderl/tinderl.erl +43 -0
  25. data/examples/tinderl/tinderl.rb +27 -0
  26. data/ext/decoder.c +398 -0
  27. data/ext/extconf.rb +11 -0
  28. data/lib/beambridge.rb +34 -0
  29. data/lib/beambridge/condition.rb +66 -0
  30. data/lib/beambridge/conditions/boolean.rb +11 -0
  31. data/lib/beambridge/conditions/hash.rb +13 -0
  32. data/lib/beambridge/conditions/static.rb +34 -0
  33. data/lib/beambridge/conditions/type.rb +17 -0
  34. data/lib/beambridge/constants.rb +36 -0
  35. data/lib/beambridge/decoder.rb +212 -0
  36. data/lib/beambridge/encoder.rb +164 -0
  37. data/lib/beambridge/errors/beambridge_error.rb +3 -0
  38. data/lib/beambridge/errors/decode_error.rb +3 -0
  39. data/lib/beambridge/errors/encode_error.rb +3 -0
  40. data/lib/beambridge/matcher.rb +21 -0
  41. data/lib/beambridge/port.rb +48 -0
  42. data/lib/beambridge/receiver.rb +69 -0
  43. data/lib/beambridge/types/function.rb +3 -0
  44. data/lib/beambridge/types/list.rb +3 -0
  45. data/lib/beambridge/types/new_function.rb +3 -0
  46. data/lib/beambridge/types/new_reference.rb +3 -0
  47. data/lib/beambridge/types/pid.rb +3 -0
  48. data/lib/beambridge/types/reference.rb +3 -0
  49. data/lib/beambridge/version.rb +3 -0
  50. data/spec/condition_spec.rb +72 -0
  51. data/spec/decode_spec.rb +143 -0
  52. data/spec/encode_spec.rb +152 -0
  53. data/spec/matcher_spec.rb +81 -0
  54. data/spec/port_spec.rb +34 -0
  55. data/spec/receiver_spec.rb +103 -0
  56. data/spec/spec_helper.rb +47 -0
  57. metadata +153 -0
@@ -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 = 'decoder'
6
+
7
+ # The destination
8
+ dir_config(extension_name)
9
+
10
+ # Do the work
11
+ create_makefile(extension_name)
@@ -0,0 +1,34 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext])
2
+
3
+ require 'stringio'
4
+
5
+ require 'beambridge/version'
6
+ require 'beambridge/constants'
7
+ require 'beambridge/types/new_reference'
8
+ require 'beambridge/types/pid'
9
+ require 'beambridge/types/function'
10
+ require 'beambridge/types/list'
11
+
12
+ begin
13
+ # try to load the decoder C extension
14
+ require 'decoder'
15
+ rescue LoadError
16
+ # fall back on the pure ruby version
17
+ require 'beambridge/decoder'
18
+ end
19
+
20
+ require 'beambridge/encoder'
21
+
22
+ require 'beambridge/port'
23
+ require 'beambridge/matcher'
24
+ require 'beambridge/condition'
25
+ require 'beambridge/conditions/boolean'
26
+ require 'beambridge/conditions/hash'
27
+ require 'beambridge/conditions/static'
28
+ require 'beambridge/conditions/type'
29
+ require 'beambridge/receiver'
30
+ require 'beambridge/errors/beambridge_error'
31
+ require 'beambridge/errors/decode_error'
32
+ require 'beambridge/errors/encode_error'
33
+
34
+ Erl = Beambridge
@@ -0,0 +1,66 @@
1
+ module Beambridge
2
+ class Condition
3
+ def self.for(a)
4
+ case a
5
+ when Condition then a
6
+ when Class then TypeCondition.new(a)
7
+ else StaticCondition.new(a)
8
+ end
9
+ end
10
+
11
+ def initialize
12
+ end
13
+
14
+ def binding_for(arg)
15
+ nil
16
+ end
17
+
18
+ def satisfies?(arg)
19
+ false
20
+ end
21
+
22
+ alias === satisfies?
23
+ end
24
+
25
+ module Conditions
26
+ def atom
27
+ TypeCondition.new(Symbol)
28
+ end
29
+
30
+ def any
31
+ TypeCondition.new(Object)
32
+ end
33
+
34
+ def number
35
+ TypeCondition.new(Fixnum)
36
+ end
37
+
38
+ def pid
39
+ TypeCondition.new(Beambridge::Pid)
40
+ end
41
+
42
+ def ref
43
+ TypeCondition.new(Beambridge::NewReference)
44
+ end
45
+
46
+ def string
47
+ TypeCondition.new(String)
48
+ end
49
+
50
+ def list
51
+ TypeCondition.new(Array)
52
+ end
53
+
54
+ def hash
55
+ HashCondition.new()
56
+ end
57
+
58
+ def boolean
59
+ BooleanCondition.new()
60
+ end
61
+ end
62
+
63
+ extend Conditions
64
+ end
65
+
66
+ Any = Object
@@ -0,0 +1,11 @@
1
+ module Beambridge
2
+ class BooleanCondition < Condition
3
+ def satisfies?(arg)
4
+ [TrueClass, FalseClass].include?(arg.class)
5
+ end
6
+
7
+ def binding_for(arg)
8
+ arg
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Beambridge
2
+ class HashCondition < Condition
3
+ def satisfies?(arg)
4
+ return false unless arg.class == Array
5
+ arg.all? { |x| x.class == Array && x.length == 2 }
6
+ end
7
+
8
+ def binding_for(arg)
9
+ flattened = arg.inject([]) { |memo, kv| memo + kv }
10
+ Hash[*flattened]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ module Beambridge
2
+ class StaticCondition < Condition
3
+ attr_accessor :value
4
+ def initialize(value)
5
+ if value.is_a?(Array)
6
+ self.value = value.map do |v|
7
+ Condition.for(v)
8
+ end
9
+ else
10
+ self.value = value
11
+ end
12
+ end
13
+
14
+ def satisfies?(arg)
15
+ if value.is_a?(Array)
16
+ return false unless arg.is_a?(Array)
17
+ return false if value.length != arg.length
18
+ value.zip(arg).all? do |l, r|
19
+ l.respond_to?(:satisfies?) ? l.satisfies?(r) : l.eql?(r)
20
+ end
21
+ else
22
+ arg.eql?(value)
23
+ end
24
+ end
25
+
26
+ def binding_for(arg)
27
+ if value.is_a?(Array)
28
+ value.zip(arg).map { |l, r| l.binding_for(r) }.compact
29
+ else
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ module Beambridge
2
+ class TypeCondition < Condition
3
+ attr_accessor :type
4
+
5
+ def initialize(type)
6
+ self.type = type
7
+ end
8
+
9
+ def satisfies?(arg)
10
+ arg.is_a?(self.type)
11
+ end
12
+
13
+ def binding_for(arg)
14
+ arg
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ module Beambridge
2
+ module External
3
+ module Types
4
+ SMALL_INT = 97
5
+ INT = 98
6
+
7
+ SMALL_BIGNUM = 110
8
+ LARGE_BIGNUM = 111
9
+
10
+ FLOAT = 99
11
+
12
+ ATOM = 100
13
+ REF = 101 #old style reference
14
+ NEW_REF = 114
15
+ PORT = 102 #not supported accross node boundaries
16
+ PID = 103
17
+
18
+ SMALL_TUPLE = 104
19
+ LARGE_TUPLE = 105
20
+
21
+ NIL = 106
22
+ STRING = 107
23
+ LIST = 108
24
+ BIN = 109
25
+
26
+ FUN = 117
27
+ NEW_FUN = 112
28
+ end
29
+
30
+ VERSION = 131
31
+
32
+ MAX_INT = (1 << 27) -1
33
+ MIN_INT = -(1 << 27)
34
+ MAX_ATOM = 255
35
+ end
36
+ end
@@ -0,0 +1,212 @@
1
+ module Beambridge
2
+ class Decoder
3
+ attr_accessor :in
4
+ include Beambridge::External::Types
5
+
6
+ def self.decode(string)
7
+ new(StringIO.new(string)).read_any
8
+ end
9
+
10
+ def initialize(ins)
11
+ @in = ins
12
+ @peeked = ""
13
+ end
14
+
15
+ def read_any
16
+ fail("Bad Magic") unless read_1 == Beambridge::External::VERSION
17
+ read_any_raw
18
+ end
19
+
20
+ def read_any_raw
21
+ case peek_1
22
+ when ATOM then read_atom
23
+ when SMALL_INT then read_small_int
24
+ when INT then read_int
25
+ when SMALL_BIGNUM then read_small_bignum
26
+ when LARGE_BIGNUM then read_large_bignum
27
+ when FLOAT then read_float
28
+ when NEW_REF then read_new_reference
29
+ when PID then read_pid
30
+ when SMALL_TUPLE then read_small_tuple
31
+ when LARGE_TUPLE then read_large_tuple
32
+ when NIL then read_nil
33
+ when STRING then read_erl_string
34
+ when LIST then read_list
35
+ when BIN then read_bin
36
+ else
37
+ fail("Unknown term tag: #{peek_1}")
38
+ end
39
+ end
40
+
41
+ def read(length)
42
+ if length < @peeked.length
43
+ result = @peeked[0...length]
44
+ @peeked = @peeked[length..-1]
45
+ length = 0
46
+ else
47
+ result = @peeked
48
+ @peeked = ''
49
+ length -= result.length
50
+ end
51
+
52
+ if length > 0
53
+ result << @in.read(length)
54
+ end
55
+ result
56
+ end
57
+
58
+ def peek(length)
59
+ if length <= @peeked.length
60
+ @peeked[0...length]
61
+ else
62
+ read_bytes = @in.read(length - @peeked.length)
63
+ @peeked << read_bytes if read_bytes
64
+ @peeked
65
+ end
66
+ end
67
+
68
+ def peek_1
69
+ peek(1).unpack("C").first
70
+ end
71
+
72
+ def peek_2
73
+ peek(2).unpack("n").first
74
+ end
75
+
76
+ def read_1
77
+ read(1).unpack("C").first
78
+ end
79
+
80
+ def read_2
81
+ read(2).unpack("n").first
82
+ end
83
+
84
+ def read_4
85
+ read(4).unpack("N").first
86
+ end
87
+
88
+ def read_string(length)
89
+ read(length)
90
+ end
91
+
92
+ def read_atom
93
+ fail("Invalid Type, not an atom") unless read_1 == ATOM
94
+ length = read_2
95
+ a = read_string(length)
96
+ case a
97
+ when "true"
98
+ true
99
+ when "false"
100
+ false
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
+ value = Fixnum.induced_from(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
+ Bignum.induced_from(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
+ Bignum.induced_from(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_new_reference
154
+ fail("Invalid Type, not a new-style reference") unless read_1 == NEW_REF
155
+ size = read_2
156
+ node = read_atom
157
+ creation = read_1
158
+ id = (0...size).map { |i| read_4 }
159
+ NewReference.new(node, creation, id)
160
+ end
161
+
162
+ def read_pid
163
+ fail("Invalid Type, not a pid") unless read_1 == PID
164
+ node = read_atom
165
+ id = read_4
166
+ serial = read_4
167
+ creation = read_1
168
+ Pid.new(node, id, serial, creation)
169
+ end
170
+
171
+ def read_small_tuple
172
+ fail("Invalid Type, not a small tuple") unless read_1 == SMALL_TUPLE
173
+ arity = read_1
174
+ (0...arity).map { |i| read_any_raw }
175
+ end
176
+
177
+ def read_large_tuple
178
+ fail("Invalid Type, not a small tuple") unless read_1 == LARGE_TUPLE
179
+ arity = read_4
180
+ (0...arity).map { |i| read_any_raw }
181
+ end
182
+
183
+ def read_nil
184
+ fail("Invalid Type, not a nil list") unless read_1 == NIL
185
+ Beambridge::List.new([])
186
+ end
187
+
188
+ def read_erl_string
189
+ fail("Invalid Type, not an erlang string") unless read_1 == STRING
190
+ length = read_2
191
+ Beambridge::List.new(read_string(length).unpack('C' * length))
192
+ end
193
+
194
+ def read_list
195
+ fail("Invalid Type, not an erlang list") unless read_1 == LIST
196
+ length = read_4
197
+ list = (0...length).map { |i| read_any_raw }
198
+ read_1
199
+ Beambridge::List.new(list)
200
+ end
201
+
202
+ def read_bin
203
+ fail("Invalid Type, not an erlang binary") unless read_1 == BIN
204
+ length = read_4
205
+ read_string(length)
206
+ end
207
+
208
+ def fail(str)
209
+ raise DecodeError, str
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,164 @@
1
+ module Beambridge
2
+ class Encoder
3
+ include Beambridge::External::Types
4
+
5
+ attr_accessor :out
6
+
7
+ def initialize(out)
8
+ self.out = out
9
+ end
10
+
11
+ def self.encode(data)
12
+ io = StringIO.new
13
+ self.new(io).write_any(data)
14
+ io.string
15
+ end
16
+
17
+ def write_any obj
18
+ write_1 Beambridge::External::VERSION
19
+ write_any_raw obj
20
+ end
21
+
22
+ def write_any_raw obj
23
+ case obj
24
+ when Symbol then write_symbol(obj)
25
+ when Fixnum, Bignum then write_fixnum(obj)
26
+ when Float then write_float(obj)
27
+ when Beambridge::NewReference then write_new_reference(obj)
28
+ when Beambridge::Pid then write_pid(obj)
29
+ when Beambridge::List then write_list(obj)
30
+ when Array then write_tuple(obj)
31
+ when String then write_binary(obj)
32
+ when Time then write_any_raw(obj.to_i.divmod(1000000) + [obj.usec])
33
+ when TrueClass, FalseClass then write_boolean(obj)
34
+ else
35
+ fail(obj)
36
+ end
37
+ end
38
+
39
+ def write_1(byte)
40
+ out.write([byte].pack("C"))
41
+ end
42
+
43
+ def write_2(short)
44
+ out.write([short].pack("n"))
45
+ end
46
+
47
+ def write_4(long)
48
+ out.write([long].pack("N"))
49
+ end
50
+
51
+ def write_string(string)
52
+ out.write(string)
53
+ end
54
+
55
+ def write_boolean(bool)
56
+ write_symbol(bool.to_s.to_sym)
57
+ end
58
+
59
+ def write_symbol(sym)
60
+ fail(sym) unless sym.is_a?(Symbol)
61
+ data = sym.to_s
62
+ write_1 ATOM
63
+ write_2 data.length
64
+ write_string data
65
+ end
66
+
67
+ def write_fixnum(num)
68
+ if num >= 0 && num < 256
69
+ write_1 SMALL_INT
70
+ write_1 num
71
+ elsif num <= Beambridge::External::MAX_INT && num >= Beambridge::External::MIN_INT
72
+ write_1 INT
73
+ write_4 num
74
+ else
75
+ write_bignum num
76
+ end
77
+ end
78
+
79
+ def write_float(float)
80
+ write_1 FLOAT
81
+ write_string format("%15.15e", float).ljust(31, "\000")
82
+ end
83
+
84
+ def write_bignum(num)
85
+ if num.is_a?(Bignum)
86
+ n = num.size
87
+ else
88
+ n = (num.to_s(2).size / 8.0).ceil
89
+ end
90
+ if n <= 256
91
+ write_1 SMALL_BIGNUM
92
+ write_1 n
93
+ write_bignum_guts(num)
94
+ else
95
+ write_1 LARGE_BIGNUM
96
+ write_4 n
97
+ write_bignum_guts(num)
98
+ end
99
+ end
100
+
101
+ def write_bignum_guts(num)
102
+ write_1 (num >= 0 ? 0 : 1)
103
+ num = num.abs
104
+ while num != 0
105
+ rem = num % 256
106
+ write_1 rem
107
+ num = num >> 8
108
+ end
109
+ end
110
+
111
+ def write_new_reference(ref)
112
+ fail(ref) unless ref.is_a?(Beambridge::NewReference)
113
+ write_1 NEW_REF
114
+ write_2 ref.id.length
115
+ write_symbol(ref.node)
116
+ write_1 ref.creation
117
+ write_string ref.id.pack('N' * ref.id.length)
118
+ end
119
+
120
+ def write_pid(pid)
121
+ fail(pid) unless pid.is_a? Beambridge::Pid
122
+ write_1 PID
123
+ write_symbol(pid.node)
124
+ write_4 pid.id
125
+ write_4 pid.serial
126
+ write_1 pid.creation
127
+ end
128
+
129
+ def write_tuple(data)
130
+ fail(data) unless data.is_a? Array
131
+
132
+ if data.length < 256
133
+ write_1 SMALL_TUPLE
134
+ write_1 data.length
135
+ else
136
+ write_1 LARGE_TUPLE
137
+ write_4 data.length
138
+ end
139
+
140
+ data.each { |e| write_any_raw e }
141
+ end
142
+
143
+ def write_list(data)
144
+ fail(data) unless data.is_a? Array
145
+ write_1 NIL and return if data.empty?
146
+ write_1 LIST
147
+ write_4 data.length
148
+ data.each{|e| write_any_raw e }
149
+ write_1 NIL
150
+ end
151
+
152
+ def write_binary(data)
153
+ write_1 BIN
154
+ write_4 data.bytesize
155
+ write_string data
156
+ end
157
+
158
+ private
159
+
160
+ def fail(obj)
161
+ raise EncodeError, "Cannot encode to erlang external format: #{obj.inspect}"
162
+ end
163
+ end
164
+ end