beambridge 0.9.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.
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
+ }