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,19 @@
1
+ #!/usr/bin/env escript
2
+
3
+ -export([main/1]).
4
+
5
+ main(_Any) ->
6
+ Data = [
7
+ {apples, [10, 2, 3, 4, 4, 3]},
8
+ {oranges, [4, 8, 7, 9, 8, 9]},
9
+ {watermelons, [2, 3, 1, 5, 6, 8]},
10
+ {peaches, [9, 9, 10, 8, 7, 9]}
11
+ ],
12
+ gruff:start(),
13
+ Result = gruff:plot(
14
+ <<"My Charts">>,
15
+ <<"/Users/scott/Library/Fonts/Arial">>,
16
+ Data,
17
+ [{0, <<"2003">>}, {2, <<"2004">>}, {4, <<"2005">>}]
18
+ ),
19
+ file:write_file("out.png", Result).
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env escript
2
+
3
+ -export([main/1]).
4
+
5
+ main(_Any) ->
6
+ gruff:start(),
7
+ MemoryWriter = stat_writer:start(<<"Memory Info">>, fun() -> erlang:memory() end),
8
+ ProcessWriter = stat_writer:start(<<"Process Info">>,
9
+ fun() ->
10
+ {_, QueueLength} = erlang:process_info(erlang:whereis(gruff), message_queue_len),
11
+ [{processes, erlang:system_info(process_count)},
12
+ {gruff_queue_length, QueueLength}]
13
+ end
14
+ ),
15
+ receive
16
+ after 20000 ->
17
+ MemoryWriter ! stop,
18
+ ProcessWriter ! stop,
19
+ elang:halt()
20
+ end.
@@ -0,0 +1,40 @@
1
+ -module(stat_writer).
2
+ -export([start/2, loop/3]).
3
+
4
+ start(Title, Fun) ->
5
+ spawn(?MODULE, loop, [Title, Fun, []]).
6
+
7
+ loop(Title, Fun, []) ->
8
+ Data = accumulate([], Fun()),
9
+ loop(Title, Fun, Data, 0).
10
+
11
+ loop(Title, Fun, Data, Generation) ->
12
+ receive
13
+ {stop} -> ok
14
+ after 3000 ->
15
+ NewGeneration = Generation + 1,
16
+ NewData = accumulate(Data, Fun()),
17
+ NewChart = gruff:plot(
18
+ list_to_binary([Title, << "- Generation" >>, integer_to_list(NewGeneration)]),
19
+ <<"/Users/scott/Library/Fonts/Arial">>,
20
+ NewData,
21
+ []
22
+ ),
23
+ file:write_file(io_lib:format("~s - ~s.png", [Title, integer_to_list(NewGeneration)]),NewChart),
24
+ loop(Title, Fun, NewData, NewGeneration)
25
+ end.
26
+
27
+ process_axis({Name, PreviousReadings}, {Name, Reading}) ->
28
+ {Name, [Reading|PreviousReadings]}.
29
+
30
+ accumulate(Data, []) -> Data;
31
+ accumulate([], [{Name, Reading}|Rest]) ->
32
+ Data = [{Name, [Reading]}],
33
+ accumulate(Data, Rest);
34
+ accumulate(Data, [{Name, Reading}|Rest]) ->
35
+ MergedData = case lists:keysearch(Name, 1, Data) of
36
+ {value, Axis} -> lists:keyreplace(Name, 1, Data, process_axis(Axis, {Name, Reading}));
37
+ false ->
38
+ [{Name, [Reading]}|Data]
39
+ end,
40
+ accumulate(MergedData, Rest).
@@ -0,0 +1,5 @@
1
+ This example demonstrates how Erlang and Ruby data types get converted when
2
+ crossing from one to the other.
3
+
4
+ $ cd examples/simple
5
+ $ ./rerl.sh
@@ -0,0 +1,110 @@
1
+ #
2
+ # rerl.rb, for use with erlang's open_port
3
+ # spawn using rerl.sh escript:
4
+ #
5
+ # $ ./rerl.sh
6
+ # ./rerl.sh:35: Warning: variable 'Port' is unused
7
+ # [erlang] ruby is alive
8
+ # [erlang] sending: test
9
+ # [erlang] sending: {atom,symbol}
10
+ # [erlang] sending: {number,1}
11
+ # [erlang] sending: {string,<<"reverse">>}
12
+ # [erlang] sending: {array,[1,2,3]}
13
+ # [erlang] sending: {array,[<<"abc">>,<<"cde">>]}
14
+ # [erlang] sending: {hash,[{key,val}]}
15
+ # [erlang] sending: {object,{1,{2},3,<<"four">>}}
16
+ # [ ruby ] received: test, nil
17
+ # [ ruby ] sending: test, nil
18
+ # [erlang] received: test
19
+ # [ ruby ] received: atom, :symbol
20
+ # [ ruby ] sending: atom, :lobmys
21
+ # [erlang] received: {atom,lobmys}
22
+ # [ ruby ] received: number, 1
23
+ # [ ruby ] sending: number, 2
24
+ # [ ruby ] received: string, "reverse"
25
+ # [erlang] received: {number,2}
26
+ # [ ruby ] sending: string, "esrever"
27
+ # [ ruby ] received: array, [1, 2, 3]
28
+ # [erlang] received: {string,<<"esrever">>}
29
+ # [ ruby ] sending: array, [3, 2, 1]
30
+ # [ ruby ] received: array, ["abc", "cde"]
31
+ # [erlang] received: {array,{3,2,1}}
32
+ # [ ruby ] sending: array, ["cde", "abc"]
33
+ # [ ruby ] received: hash, {:key=>:val}
34
+ # [erlang] received: {array,{<<"cde">>,<<"abc">>}}
35
+ # [ ruby ] sending: hash, {:key=>:val, :ruby=>:true}
36
+ # [ ruby ] received: object, [1, [2], 3, "four"]
37
+ # [erlang] received: {hash,{{key,val},{ruby,true}}}
38
+ # [ ruby ] sending: object, [1, [2], 3, "four"]
39
+ # [erlang] received: {object,{1,{2},3,<<"four">>}}
40
+ #
41
+
42
+ $:.unshift File.join(File.dirname(__FILE__), *%w[../../lib])
43
+
44
+ require 'beambridge'
45
+
46
+ def log arg
47
+ puts arg
48
+ # @f ||= File.open('/tmp/rerl.log', 'w')
49
+ # @f.puts arg
50
+ # @f.flush
51
+ end
52
+
53
+ def debug meth, got = nil, send = nil
54
+ log "[ ruby ] received: #{meth}, #{got.inspect}"
55
+ log "[ ruby ] sending: #{meth}, #{send.inspect}"
56
+ end
57
+
58
+ receive do |f|
59
+ f.when(:test) do
60
+ debug(:test)
61
+ f.send!(:test)
62
+ f.receive_loop
63
+ end
64
+
65
+ f.when([:atom, Symbol]) do |sym|
66
+ debug(:atom, sym, sym.to_s.reverse.to_sym)
67
+ f.send!([:atom, sym.to_s.reverse.to_sym])
68
+ f.receive_loop
69
+ end
70
+
71
+ f.when([:bool, Erl.boolean]) do |bool|
72
+ debug(:bool, bool, !bool)
73
+ f.send!([:bool, !bool])
74
+ f.receive_loop
75
+ end
76
+
77
+ f.when([:number, Fixnum]) do |num|
78
+ debug(:number, num, num*2)
79
+ f.send!([:number, num*2])
80
+ f.receive_loop
81
+ end
82
+
83
+ f.when([:string, String]) do |str|
84
+ debug(:string, str, str.reverse)
85
+ f.send!([:string, str.reverse])
86
+ f.receive_loop
87
+ end
88
+
89
+ f.when([:array, Array]) do |arr|
90
+ debug(:array, arr, arr.reverse)
91
+ f.send!([:array, arr.reverse])
92
+ f.receive_loop
93
+ end
94
+
95
+ f.when([:hash, Erl.hash]) do |hash|
96
+ newhash = hash.dup
97
+ newhash[:ruby] = :true
98
+ debug(:hash, hash, newhash)
99
+ f.send!([:hash, newhash.to_a])
100
+ f.receive_loop
101
+ end
102
+
103
+ f.when([:object, Any]) do |obj|
104
+ debug(:object, obj, obj)
105
+ f.send!([:object, obj])
106
+ f.receive_loop
107
+ end
108
+
109
+ f.send!(:i_am_alive)
110
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env escript
2
+ main(_) ->
3
+ Cmd = "ruby rerl.rb",
4
+ Port = open_port({spawn, Cmd}, [{packet, 4}, nouse_stdio, exit_status, binary]),
5
+ loop(Port).
6
+
7
+ send(Port, Message) ->
8
+ io:format("[erlang] sending: ~p~n", [Message]),
9
+ % can also use ! instead of port_command
10
+ % Port ! { self(), { command, term_to_binary(Message) } }.
11
+ port_command(Port, term_to_binary(Message)).
12
+
13
+ loop(Port) ->
14
+ receive
15
+ {Port, {data, In}} ->
16
+ Data = binary_to_term(In),
17
+ process(Port, Data);
18
+ Any ->
19
+ io:format("[erlang] other: ~p~n", [Any])
20
+ end,
21
+ loop(Port).
22
+
23
+ process(Port, i_am_alive) ->
24
+ io:format("[erlang] ruby is alive~n"),
25
+
26
+ send(Port, test),
27
+ send(Port, {atom, symbol}),
28
+ send(Port, {bool, true}),
29
+ send(Port, {number, 1}),
30
+ send(Port, {string, <<"reverse">>}),
31
+ send(Port, {array, [1,2,3]}),
32
+ send(Port, {array, [<<"abc">>, <<"cde">>]}),
33
+ send(Port, {hash, [{key,val}]}),
34
+ send(Port, {object, {1,{2},3,<<"four">>}});
35
+
36
+ process(Port, Data) ->
37
+ io:format("[erlang] received: ~p~n", [Data]).
@@ -0,0 +1,14 @@
1
+ This is a more advanced example of Erlectricity that shows how you can
2
+ integrate with Campfire via the Ruby "tinder" gem.
3
+
4
+ $ cd examples/tinderl
5
+ $ erl
6
+ Erlang (BEAM) emulator version 5.6.4 [source] [smp:2] [async-threads:0] [kernel-poll:false]
7
+
8
+ Eshell V5.6.4 (abort with ^G)
9
+ 1> c(tinderl).
10
+ {ok,tinderl}
11
+ 2> tinderl:start("example.campfireapp.com", "tom@example.com", "secret", "My Room").
12
+ <0.38.0>
13
+ 5> tinderl:speak("Hello, World!").
14
+ {speak,<0.31.0>,<<"Hello, World!">>}
@@ -0,0 +1,43 @@
1
+ -module(tinderl).
2
+ -export([start/4, stop/0, speak/1, paste/1]).
3
+
4
+ start(Domain, Email, Password, Room) ->
5
+ spawn(fun() ->
6
+ register(tinderl, self()),
7
+ process_flag(trap_exit, true),
8
+ Cmd = lists:flatten(io_lib:format("ruby ./tinderl.rb ~s ~s ~s ~s", [Domain, Email, Password, Room])),
9
+ Port = open_port({spawn, Cmd}, [{packet, 4}, nouse_stdio, exit_status, binary]),
10
+ port_loop(Port)
11
+ end).
12
+
13
+ stop() -> tinderl ! stop.
14
+
15
+ speak(String) when is_list(String) -> speak(list_to_binary(String));
16
+ speak(String) when is_binary(String) -> tinderl ! {speak, self(), String}.
17
+
18
+ paste(String) when is_list(String) -> speak(list_to_binary(String));
19
+ paste(String) when is_binary(String) -> tinderl ! {paste, self(), String}.
20
+
21
+ port_loop(Port) ->
22
+ receive
23
+ {speak, _Caller, String} ->
24
+ Data = term_to_binary({speak, String}),
25
+ Port ! {self(), {command, Data}},
26
+
27
+ port_loop(Port);
28
+
29
+ {paste, _Caller, String} ->
30
+ Data = term_to_binary({paste, String}),
31
+ Port ! {self(), {command, Data}},
32
+
33
+ port_loop(Port);
34
+
35
+ stop ->
36
+ Port ! {self(), close},
37
+ receive
38
+ {Port, closed} -> exit(normal)
39
+ end;
40
+
41
+ {'EXIT', Port, Reason} ->
42
+ exit({port_terminated,Reason})
43
+ end.
@@ -0,0 +1,27 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[../../lib])
2
+
3
+ require 'beambridge'
4
+ require 'tinder'
5
+
6
+ domain, email, password, room_name = *ARGV
7
+ campfire = Tinder::Campfire.new domain
8
+ campfire.login email, password
9
+ room = campfire.find_room_by_name room_name
10
+
11
+ receive do |f|
12
+ f.when([:speak, Any]) do |comment|
13
+ room.speak(comment)
14
+ f.receive_loop
15
+ end
16
+
17
+ f.when([:paste, Any]) do |comment|
18
+ room.paste(comment)
19
+ f.receive_loop
20
+ end
21
+
22
+ f.when(Any) do |obj|
23
+ p obj
24
+ end
25
+ end
26
+
27
+ room.leave if room
@@ -0,0 +1,398 @@
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_REF 101
12
+ #define ERL_NEW_REF 114
13
+ #define ERL_PORT 102
14
+ #define ERL_PID 103
15
+ #define ERL_SMALL_TUPLE 104
16
+ #define ERL_LARGE_TUPLE 105
17
+ #define ERL_NIL 106
18
+ #define ERL_STRING 107
19
+ #define ERL_LIST 108
20
+ #define ERL_BIN 109
21
+ #define ERL_FUN 117
22
+ #define ERL_NEW_FUN 112
23
+
24
+ static VALUE mErlectricity;
25
+ static VALUE cDecoder;
26
+ void Init_decoder();
27
+
28
+ VALUE method_decode(VALUE klass, VALUE rString);
29
+
30
+ VALUE read_any_raw(unsigned char **pData);
31
+
32
+ // checkers
33
+
34
+ void check_int(int num) {
35
+ char buf[17];
36
+ sprintf(buf, "%u", num);
37
+ rb_raise(rb_eStandardError, buf);
38
+ }
39
+
40
+ void check_str(char *str) {
41
+ rb_raise(rb_eStandardError, str);
42
+ }
43
+
44
+ // string peekers/readers
45
+
46
+ unsigned int peek_1(unsigned char **pData) {
47
+ return (unsigned int) **pData;
48
+ }
49
+
50
+ unsigned int peek_2(unsigned char **pData) {
51
+ return (unsigned int) ((**pData << 8) + *(*pData + 1));
52
+ }
53
+
54
+ unsigned int peek_4(unsigned char **pData) {
55
+ return (unsigned int) ((**pData << 24) + (*(*pData + 1) << 16) + (*(*pData + 2) << 8) + *(*pData + 3));
56
+ }
57
+
58
+ unsigned int read_1(unsigned char **pData) {
59
+ unsigned int val = peek_1(pData);
60
+ *pData += 1;
61
+ return val;
62
+ }
63
+
64
+ unsigned int read_2(unsigned char **pData) {
65
+ unsigned int val = peek_2(pData);
66
+ *pData += 2;
67
+ return val;
68
+ }
69
+
70
+ unsigned int read_4(unsigned char **pData) {
71
+ unsigned int val = peek_4(pData);
72
+ *pData += 4;
73
+ return val;
74
+ }
75
+
76
+ // tuples, lists
77
+
78
+ VALUE read_small_tuple(unsigned char **pData) {
79
+ if(read_1(pData) != ERL_SMALL_TUPLE) {
80
+ rb_raise(rb_eStandardError, "Invalid Type, not a small tuple");
81
+ }
82
+
83
+ int arity = read_1(pData);
84
+
85
+ VALUE array = rb_ary_new2(arity);
86
+
87
+ int i;
88
+ for(i = 0; i < arity; ++i) {
89
+ rb_ary_store(array, i, read_any_raw(pData));
90
+ }
91
+
92
+ return array;
93
+ }
94
+
95
+ VALUE read_large_tuple(unsigned char **pData) {
96
+ if(read_1(pData) != ERL_LARGE_TUPLE) {
97
+ rb_raise(rb_eStandardError, "Invalid Type, not a large tuple");
98
+ }
99
+
100
+ unsigned int arity = read_4(pData);
101
+
102
+ VALUE array = rb_ary_new2(arity);
103
+
104
+ int i;
105
+ for(i = 0; i < arity; ++i) {
106
+ rb_ary_store(array, i, read_any_raw(pData));
107
+ }
108
+
109
+ return array;
110
+ }
111
+
112
+ VALUE read_list(unsigned char **pData) {
113
+ if(read_1(pData) != ERL_LIST) {
114
+ rb_raise(rb_eStandardError, "Invalid Type, not an erlang list");
115
+ }
116
+
117
+ unsigned int size = read_4(pData);
118
+
119
+ VALUE newref_class = rb_const_get(mErlectricity, rb_intern("List"));
120
+ VALUE array = rb_funcall(newref_class, rb_intern("new"), 1, INT2NUM(size));
121
+
122
+ int i;
123
+ for(i = 0; i < size; ++i) {
124
+ rb_ary_store(array, i, read_any_raw(pData));
125
+ }
126
+
127
+ read_1(pData);
128
+
129
+ return array;
130
+ }
131
+
132
+ // primitives
133
+
134
+ void read_string_raw(unsigned char *dest, unsigned char **pData, unsigned int length) {
135
+ memcpy((char *) dest, (char *) *pData, length);
136
+ *(dest + length) = (unsigned char) 0;
137
+ *pData += length;
138
+ }
139
+
140
+ VALUE read_bin(unsigned char **pData) {
141
+ if(read_1(pData) != ERL_BIN) {
142
+ rb_raise(rb_eStandardError, "Invalid Type, not an erlang binary");
143
+ }
144
+
145
+ unsigned int length = read_4(pData);
146
+
147
+ VALUE rStr = rb_str_new((char *) *pData, length);
148
+ *pData += length;
149
+
150
+ return rStr;
151
+ }
152
+
153
+ VALUE read_string(unsigned char **pData) {
154
+ if(read_1(pData) != ERL_STRING) {
155
+ rb_raise(rb_eStandardError, "Invalid Type, not an erlang string");
156
+ }
157
+
158
+ int length = read_2(pData);
159
+ VALUE newref_class = rb_const_get(mErlectricity, rb_intern("List"));
160
+ VALUE array = rb_funcall(newref_class, rb_intern("new"), 1, INT2NUM(length));
161
+
162
+ int i = 0;
163
+ for(i; i < length; ++i) {
164
+ rb_ary_store(array, i, INT2NUM(**pData));
165
+ *pData += 1;
166
+ }
167
+
168
+ return array;
169
+ }
170
+
171
+ VALUE read_atom(unsigned char **pData) {
172
+ if(read_1(pData) != ERL_ATOM) {
173
+ rb_raise(rb_eStandardError, "Invalid Type, not an atom");
174
+ }
175
+
176
+ int length = read_2(pData);
177
+
178
+ unsigned char buf[length + 1];
179
+ read_string_raw(buf, pData, length);
180
+
181
+ // Erlang true and false are actually atoms
182
+ if(length == 4 && strncmp((char *) buf, "true", length) == 0) {
183
+ return Qtrue;
184
+ } else if(length == 5 && strncmp((char *) buf, "false", length) == 0) {
185
+ return Qfalse;
186
+ } else {
187
+ return ID2SYM(rb_intern((char *) buf));
188
+ }
189
+ }
190
+
191
+ VALUE read_small_int(unsigned char **pData) {
192
+ if(read_1(pData) != ERL_SMALL_INT) {
193
+ rb_raise(rb_eStandardError, "Invalid Type, not a small int");
194
+ }
195
+
196
+ int value = read_1(pData);
197
+
198
+ return INT2FIX(value);
199
+ }
200
+
201
+ VALUE read_int(unsigned char **pData) {
202
+ if(read_1(pData) != ERL_INT) {
203
+ rb_raise(rb_eStandardError, "Invalid Type, not an int");
204
+ }
205
+
206
+ long long value = read_4(pData);
207
+
208
+ long long negative = ((value >> 31) & 0x1 == 1);
209
+
210
+ if(negative) {
211
+ value = (value - ((long long) 1 << 32));
212
+ }
213
+
214
+ return INT2FIX(value);
215
+ }
216
+
217
+ VALUE read_small_bignum(unsigned char **pData) {
218
+ if(read_1(pData) != ERL_SMALL_BIGNUM) {
219
+ rb_raise(rb_eStandardError, "Invalid Type, not a small bignum");
220
+ }
221
+
222
+ unsigned int size = read_1(pData);
223
+ unsigned int sign = read_1(pData);
224
+
225
+ VALUE num = INT2NUM(0);
226
+ VALUE tmp;
227
+
228
+ unsigned char buf[size + 1];
229
+ read_string_raw(buf, pData, size);
230
+
231
+ unsigned int i;
232
+ for(i = 0; i < size; ++i) {
233
+ tmp = INT2FIX(*(buf + i));
234
+ tmp = rb_funcall(tmp, rb_intern("<<"), 1, INT2NUM(i * 8));
235
+ num = rb_funcall(num, rb_intern("+"), 1, tmp);
236
+ }
237
+
238
+ if(sign) {
239
+ num = rb_funcall(num, rb_intern("*"), 1, INT2NUM(-1));
240
+ }
241
+
242
+ return num;
243
+ }
244
+
245
+ VALUE read_large_bignum(unsigned char **pData) {
246
+ if(read_1(pData) != ERL_LARGE_BIGNUM) {
247
+ rb_raise(rb_eStandardError, "Invalid Type, not a small bignum");
248
+ }
249
+
250
+ unsigned int size = read_4(pData);
251
+ unsigned int sign = read_1(pData);
252
+
253
+ VALUE num = INT2NUM(0);
254
+ VALUE tmp;
255
+
256
+ unsigned char buf[size + 1];
257
+ read_string_raw(buf, pData, size);
258
+
259
+ unsigned int i;
260
+ for(i = 0; i < size; ++i) {
261
+ tmp = INT2FIX(*(buf + i));
262
+ tmp = rb_funcall(tmp, rb_intern("<<"), 1, INT2NUM(i * 8));
263
+
264
+ num = rb_funcall(num, rb_intern("+"), 1, tmp);
265
+ }
266
+
267
+ if(sign) {
268
+ num = rb_funcall(num, rb_intern("*"), 1, INT2NUM(-1));
269
+ }
270
+
271
+ return num;
272
+ }
273
+
274
+ VALUE read_float(unsigned char **pData) {
275
+ if(read_1(pData) != ERL_FLOAT) {
276
+ rb_raise(rb_eStandardError, "Invalid Type, not a float");
277
+ }
278
+
279
+ unsigned char buf[32];
280
+ read_string_raw(buf, pData, 31);
281
+
282
+ VALUE rString = rb_str_new2((char *) buf);
283
+
284
+ return rb_funcall(rString, rb_intern("to_f"), 0);
285
+ }
286
+
287
+ VALUE read_nil(unsigned char **pData) {
288
+ if(read_1(pData) != ERL_NIL) {
289
+ rb_raise(rb_eStandardError, "Invalid Type, not a nil list");
290
+ }
291
+
292
+ VALUE newref_class = rb_const_get(mErlectricity, rb_intern("List"));
293
+ return rb_funcall(newref_class, rb_intern("new"), 0);
294
+ }
295
+
296
+ // specials
297
+
298
+ VALUE read_pid(unsigned char **pData) {
299
+ if(read_1(pData) != ERL_PID) {
300
+ rb_raise(rb_eStandardError, "Invalid Type, not a pid");
301
+ }
302
+
303
+ VALUE node = read_atom(pData);
304
+ VALUE id = INT2NUM(read_4(pData));
305
+ VALUE serial = INT2NUM(read_4(pData));
306
+ VALUE creation = INT2FIX(read_1(pData));
307
+
308
+ VALUE pid_class = rb_const_get(mErlectricity, rb_intern("Pid"));
309
+ return rb_funcall(pid_class, rb_intern("new"), 4, node, id, serial, creation);
310
+ }
311
+
312
+ VALUE read_new_reference(unsigned char **pData) {
313
+ if(read_1(pData) != ERL_NEW_REF) {
314
+ rb_raise(rb_eStandardError, "Invalid Type, not a new-style reference");
315
+ }
316
+
317
+ int size = read_2(pData);
318
+ VALUE node = read_atom(pData);
319
+ VALUE creation = INT2FIX(read_1(pData));
320
+
321
+ VALUE id = rb_ary_new2(size);
322
+ int i;
323
+ for(i = 0; i < size; ++i) {
324
+ rb_ary_store(id, i, INT2NUM(read_4(pData)));
325
+ }
326
+
327
+ VALUE newref_class = rb_const_get(mErlectricity, rb_intern("NewReference"));
328
+ return rb_funcall(newref_class, rb_intern("new"), 3, node, creation, id);
329
+ }
330
+
331
+ // read_any_raw
332
+
333
+ VALUE read_any_raw(unsigned char **pData) {
334
+ switch(peek_1(pData)) {
335
+ case ERL_SMALL_INT:
336
+ return read_small_int(pData);
337
+ break;
338
+ case ERL_INT:
339
+ return read_int(pData);
340
+ break;
341
+ case ERL_FLOAT:
342
+ return read_float(pData);
343
+ break;
344
+ case ERL_ATOM:
345
+ return read_atom(pData);
346
+ break;
347
+ case ERL_PID:
348
+ return read_pid(pData);
349
+ break;
350
+ case ERL_SMALL_TUPLE:
351
+ return read_small_tuple(pData);
352
+ break;
353
+ case ERL_LARGE_TUPLE:
354
+ return read_large_tuple(pData);
355
+ break;
356
+ case ERL_NIL:
357
+ return read_nil(pData);
358
+ break;
359
+ case ERL_STRING:
360
+ return read_string(pData);
361
+ break;
362
+ case ERL_LIST:
363
+ return read_list(pData);
364
+ break;
365
+ case ERL_BIN:
366
+ return read_bin(pData);
367
+ break;
368
+ case ERL_SMALL_BIGNUM:
369
+ return read_small_bignum(pData);
370
+ break;
371
+ case ERL_LARGE_BIGNUM:
372
+ return read_large_bignum(pData);
373
+ break;
374
+ case ERL_NEW_REF:
375
+ return read_new_reference(pData);
376
+ break;
377
+ }
378
+ return Qnil;
379
+ }
380
+
381
+ VALUE method_decode(VALUE klass, VALUE rString) {
382
+ unsigned char *data = (unsigned char *) StringValuePtr(rString);
383
+
384
+ unsigned char **pData = &data;
385
+
386
+ // check protocol version
387
+ if(read_1(pData) != ERL_VERSION) {
388
+ rb_raise(rb_eStandardError, "Bad Magic");
389
+ }
390
+
391
+ return read_any_raw(pData);
392
+ }
393
+
394
+ void Init_decoder() {
395
+ mErlectricity = rb_const_get(rb_cObject, rb_intern("Beambridge"));
396
+ cDecoder = rb_define_class_under(mErlectricity, "Decoder", rb_cObject);
397
+ rb_define_singleton_method(cDecoder, "decode", method_decode, 1);
398
+ }