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.
- checksums.yaml +15 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/History.txt +35 -0
- data/LICENSE +20 -0
- data/README.md +130 -0
- data/Rakefile +44 -0
- data/VERSION.yml +4 -0
- data/beambridge.gemspec +29 -0
- data/benchmarks/bench.rb +21 -0
- data/examples/echo/README.md +12 -0
- data/examples/echo/echo.erl +13 -0
- data/examples/echo/echo.rb +10 -0
- data/examples/gruff/gruff.erl +61 -0
- data/examples/gruff/gruff_provider.rb +30 -0
- data/examples/gruff/gruff_run.sh +19 -0
- data/examples/gruff/stat_run.sh +20 -0
- data/examples/gruff/stat_writer.erl +40 -0
- data/examples/simple/README.md +5 -0
- data/examples/simple/rerl.rb +110 -0
- data/examples/simple/rerl.sh +37 -0
- data/examples/tinderl/README.md +14 -0
- data/examples/tinderl/tinderl.erl +43 -0
- data/examples/tinderl/tinderl.rb +27 -0
- data/ext/decoder.c +398 -0
- data/ext/extconf.rb +11 -0
- data/lib/beambridge.rb +34 -0
- data/lib/beambridge/condition.rb +66 -0
- data/lib/beambridge/conditions/boolean.rb +11 -0
- data/lib/beambridge/conditions/hash.rb +13 -0
- data/lib/beambridge/conditions/static.rb +34 -0
- data/lib/beambridge/conditions/type.rb +17 -0
- data/lib/beambridge/constants.rb +36 -0
- data/lib/beambridge/decoder.rb +212 -0
- data/lib/beambridge/encoder.rb +164 -0
- data/lib/beambridge/errors/beambridge_error.rb +3 -0
- data/lib/beambridge/errors/decode_error.rb +3 -0
- data/lib/beambridge/errors/encode_error.rb +3 -0
- data/lib/beambridge/matcher.rb +21 -0
- data/lib/beambridge/port.rb +48 -0
- data/lib/beambridge/receiver.rb +69 -0
- data/lib/beambridge/types/function.rb +3 -0
- data/lib/beambridge/types/list.rb +3 -0
- data/lib/beambridge/types/new_function.rb +3 -0
- data/lib/beambridge/types/new_reference.rb +3 -0
- data/lib/beambridge/types/pid.rb +3 -0
- data/lib/beambridge/types/reference.rb +3 -0
- data/lib/beambridge/version.rb +3 -0
- data/spec/condition_spec.rb +72 -0
- data/spec/decode_spec.rb +143 -0
- data/spec/encode_spec.rb +152 -0
- data/spec/matcher_spec.rb +81 -0
- data/spec/port_spec.rb +34 -0
- data/spec/receiver_spec.rb +103 -0
- data/spec/spec_helper.rb +47 -0
- 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,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
|
data/ext/decoder.c
ADDED
@@ -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
|
+
}
|