erlectricity-funbox 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +24 -0
- data/History.txt +35 -0
- data/LICENSE +20 -0
- data/README.md +130 -0
- data/Rakefile +74 -0
- data/VERSION.yml +4 -0
- data/benchmarks/bench.rb +21 -0
- data/erlectricity.gemspec +105 -0
- data/examples/echo/README.md +12 -0
- data/examples/echo/echo.erl +13 -0
- data/examples/echo/echo.rb +11 -0
- data/examples/gruff/gruff.erl +61 -0
- data/examples/gruff/gruff_provider.rb +31 -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 +111 -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 +28 -0
- data/ext/decoder.c +398 -0
- data/ext/extconf.rb +11 -0
- data/lib/erlectricity.rb +33 -0
- data/lib/erlectricity/condition.rb +66 -0
- data/lib/erlectricity/conditions/boolean.rb +11 -0
- data/lib/erlectricity/conditions/hash.rb +13 -0
- data/lib/erlectricity/conditions/static.rb +34 -0
- data/lib/erlectricity/conditions/type.rb +17 -0
- data/lib/erlectricity/constants.rb +36 -0
- data/lib/erlectricity/decoder.rb +212 -0
- data/lib/erlectricity/encoder.rb +164 -0
- data/lib/erlectricity/errors/decode_error.rb +3 -0
- data/lib/erlectricity/errors/encode_error.rb +3 -0
- data/lib/erlectricity/errors/erlectricity_error.rb +3 -0
- data/lib/erlectricity/matcher.rb +21 -0
- data/lib/erlectricity/port.rb +46 -0
- data/lib/erlectricity/receiver.rb +69 -0
- data/lib/erlectricity/types/function.rb +3 -0
- data/lib/erlectricity/types/list.rb +3 -0
- data/lib/erlectricity/types/new_function.rb +3 -0
- data/lib/erlectricity/types/new_reference.rb +3 -0
- data/lib/erlectricity/types/pid.rb +3 -0
- data/lib/erlectricity/types/reference.rb +3 -0
- data/lib/erlectricity/version.rb +9 -0
- data/test/condition_spec.rb +72 -0
- data/test/decode_spec.rb +145 -0
- data/test/encode_spec.rb +146 -0
- data/test/matcher_spec.rb +81 -0
- data/test/port_spec.rb +34 -0
- data/test/receiver_spec.rb +103 -0
- data/test/spec_suite.rb +2 -0
- data/test/test_helper.rb +46 -0
- metadata +116 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
-module(gruff).
|
2
|
+
-export([start/0, stop/0, plot/4]).
|
3
|
+
|
4
|
+
start() ->
|
5
|
+
spawn(fun() ->
|
6
|
+
register(gruff, self()),
|
7
|
+
process_flag(trap_exit, true),
|
8
|
+
Cmd = "ruby ./gruff_provider.rb",
|
9
|
+
Port = open_port({spawn, Cmd}, [{packet, 4}, nouse_stdio, exit_status, binary]),
|
10
|
+
port_loop(Port)
|
11
|
+
end).
|
12
|
+
|
13
|
+
stop() -> gruff ! stop.
|
14
|
+
|
15
|
+
plot(Name, Font, Data, Labels) ->
|
16
|
+
gruff ! {plot, self(), Name, Font, Data, Labels},
|
17
|
+
receive
|
18
|
+
{result, Bin} -> Bin
|
19
|
+
end.
|
20
|
+
|
21
|
+
send_data(_Port, []) -> ok;
|
22
|
+
send_data(Port, [{Name, Points}|Rest]) ->
|
23
|
+
Data = {data, Name, Points},
|
24
|
+
Port ! {self(), {command, term_to_binary(Data)}},
|
25
|
+
send_data(Port, Rest).
|
26
|
+
|
27
|
+
send_labels(Port, Labels) ->
|
28
|
+
Data = {labels, Labels},
|
29
|
+
Port ! {self(), {command, term_to_binary(Data)}}.
|
30
|
+
|
31
|
+
port_loop(Port) ->
|
32
|
+
receive
|
33
|
+
{plot, Caller, Name, Font, Data, Labels} ->
|
34
|
+
PlotData = term_to_binary({plot, Name, 'Line', Font}),
|
35
|
+
Port ! {self(), {command, PlotData}},
|
36
|
+
|
37
|
+
send_data(Port, Data),
|
38
|
+
send_labels(Port, Labels),
|
39
|
+
|
40
|
+
EndData = term_to_binary('end'),
|
41
|
+
Port ! {self(), {command, EndData}},
|
42
|
+
Result = get_result(Port),
|
43
|
+
Caller ! {result, Result },
|
44
|
+
|
45
|
+
port_loop(Port);
|
46
|
+
|
47
|
+
stop ->
|
48
|
+
Port ! {self(), close},
|
49
|
+
receive
|
50
|
+
{Port, closed} -> exit(normal)
|
51
|
+
end
|
52
|
+
end.
|
53
|
+
|
54
|
+
get_result(Port) ->
|
55
|
+
receive
|
56
|
+
{Port, {data, Data}} ->
|
57
|
+
{result, Bin} = binary_to_term(Data),
|
58
|
+
Bin;
|
59
|
+
{'EXIT', Port, Reason} ->
|
60
|
+
exit({port_terminated,Reason})
|
61
|
+
end.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[../../lib])
|
2
|
+
|
3
|
+
require 'erlectricity'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'gruff'
|
6
|
+
|
7
|
+
receive do |f|
|
8
|
+
f.when([:plot, String, Symbol, String]) do |name, style, font|
|
9
|
+
graph = Gruff.const_get(style).new
|
10
|
+
graph.title = name
|
11
|
+
graph.font = font
|
12
|
+
graph.legend_font_size = 10
|
13
|
+
|
14
|
+
f.receive do |g|
|
15
|
+
g.when([:data, Symbol, Array]) do |name, points|
|
16
|
+
graph.data name, points
|
17
|
+
g.receive_loop
|
18
|
+
end
|
19
|
+
|
20
|
+
g.when([:labels, Erl.hash]) do |label_data|
|
21
|
+
graph.labels = label_data
|
22
|
+
g.receive_loop
|
23
|
+
end
|
24
|
+
|
25
|
+
g.when(:end) { :ok }
|
26
|
+
end
|
27
|
+
|
28
|
+
f.send!([:result, graph.to_blob])
|
29
|
+
f.receive_loop
|
30
|
+
end
|
31
|
+
end
|
@@ -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,111 @@
|
|
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 'rubygems'
|
45
|
+
require 'erlectricity'
|
46
|
+
|
47
|
+
def log arg
|
48
|
+
puts arg
|
49
|
+
# @f ||= File.open('/tmp/rerl.log', 'w')
|
50
|
+
# @f.puts arg
|
51
|
+
# @f.flush
|
52
|
+
end
|
53
|
+
|
54
|
+
def debug meth, got = nil, send = nil
|
55
|
+
log "[ ruby ] received: #{meth}, #{got.inspect}"
|
56
|
+
log "[ ruby ] sending: #{meth}, #{send.inspect}"
|
57
|
+
end
|
58
|
+
|
59
|
+
receive do |f|
|
60
|
+
f.when(:test) do
|
61
|
+
debug(:test)
|
62
|
+
f.send!(:test)
|
63
|
+
f.receive_loop
|
64
|
+
end
|
65
|
+
|
66
|
+
f.when([:atom, Symbol]) do |sym|
|
67
|
+
debug(:atom, sym, sym.to_s.reverse.to_sym)
|
68
|
+
f.send!([:atom, sym.to_s.reverse.to_sym])
|
69
|
+
f.receive_loop
|
70
|
+
end
|
71
|
+
|
72
|
+
f.when([:bool, Erl.boolean]) do |bool|
|
73
|
+
debug(:bool, bool, !bool)
|
74
|
+
f.send!([:bool, !bool])
|
75
|
+
f.receive_loop
|
76
|
+
end
|
77
|
+
|
78
|
+
f.when([:number, Fixnum]) do |num|
|
79
|
+
debug(:number, num, num*2)
|
80
|
+
f.send!([:number, num*2])
|
81
|
+
f.receive_loop
|
82
|
+
end
|
83
|
+
|
84
|
+
f.when([:string, String]) do |str|
|
85
|
+
debug(:string, str, str.reverse)
|
86
|
+
f.send!([:string, str.reverse])
|
87
|
+
f.receive_loop
|
88
|
+
end
|
89
|
+
|
90
|
+
f.when([:array, Array]) do |arr|
|
91
|
+
debug(:array, arr, arr.reverse)
|
92
|
+
f.send!([:array, arr.reverse])
|
93
|
+
f.receive_loop
|
94
|
+
end
|
95
|
+
|
96
|
+
f.when([:hash, Erl.hash]) do |hash|
|
97
|
+
newhash = hash.dup
|
98
|
+
newhash[:ruby] = :true
|
99
|
+
debug(:hash, hash, newhash)
|
100
|
+
f.send!([:hash, newhash.to_a])
|
101
|
+
f.receive_loop
|
102
|
+
end
|
103
|
+
|
104
|
+
f.when([:object, Any]) do |obj|
|
105
|
+
debug(:object, obj, obj)
|
106
|
+
f.send!([:object, obj])
|
107
|
+
f.receive_loop
|
108
|
+
end
|
109
|
+
|
110
|
+
f.send!(:i_am_alive)
|
111
|
+
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,28 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[../../lib])
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'erlectricity'
|
5
|
+
require 'tinder'
|
6
|
+
|
7
|
+
domain, email, password, room_name = *ARGV
|
8
|
+
campfire = Tinder::Campfire.new domain
|
9
|
+
campfire.login email, password
|
10
|
+
room = campfire.find_room_by_name room_name
|
11
|
+
|
12
|
+
receive do |f|
|
13
|
+
f.when([:speak, Any]) do |comment|
|
14
|
+
room.speak(comment)
|
15
|
+
f.receive_loop
|
16
|
+
end
|
17
|
+
|
18
|
+
f.when([:paste, Any]) do |comment|
|
19
|
+
room.paste(comment)
|
20
|
+
f.receive_loop
|
21
|
+
end
|
22
|
+
|
23
|
+
f.when(Any) do |obj|
|
24
|
+
p obj
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
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
|
+
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
|
+
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("Erlectricity"));
|
396
|
+
cDecoder = rb_define_class_under(mErlectricity, "Decoder", rb_cObject);
|
397
|
+
rb_define_singleton_method(cDecoder, "decode", method_decode, 1);
|
398
|
+
}
|