mojombo-erlectricity 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTORS +2 -0
- data/History.txt +1 -0
- data/Manifest.txt +45 -0
- data/README.txt +43 -0
- data/Rakefile +71 -0
- data/examples/gruff/gruff.erl +62 -0
- data/examples/gruff/gruff_provider.rb +38 -0
- data/examples/gruff/gruff_run.erl +17 -0
- data/examples/gruff/stat_run.erl +18 -0
- data/examples/gruff/stat_writer.erl +40 -0
- data/examples/tinderl/tinderl.erl +45 -0
- data/examples/tinderl/tinderl.rb +27 -0
- data/ext/decoder.c +391 -0
- data/ext/extconf.rb +11 -0
- data/lib/erlectricity.rb +36 -0
- data/lib/erlectricity/condition.rb +51 -0
- data/lib/erlectricity/conditions/hash.rb +14 -0
- data/lib/erlectricity/conditions/static.rb +13 -0
- data/lib/erlectricity/conditions/type.rb +17 -0
- data/lib/erlectricity/constants.rb +37 -0
- data/lib/erlectricity/decoder.rb +204 -0
- data/lib/erlectricity/encoder.rb +127 -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 +38 -0
- data/lib/erlectricity/port.rb +46 -0
- data/lib/erlectricity/receiver.rb +79 -0
- data/lib/erlectricity/types/function.rb +3 -0
- data/lib/erlectricity/types/list.rb +1 -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/setup.rb +1585 -0
- data/test/condition_spec.rb +73 -0
- data/test/decode_spec.rb +129 -0
- data/test/encode_spec.rb +132 -0
- data/test/matcher_spec.rb +69 -0
- data/test/port_spec.rb +35 -0
- data/test/receiver_spec.rb +105 -0
- data/test/spec_suite.rb +2 -0
- data/test/test_erlectricity.rb +2 -0
- data/test/test_helper.rb +42 -0
- metadata +110 -0
data/CONTRIBUTORS
ADDED
data/History.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
CONTRIBUTORS
|
2
|
+
History.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
examples/gruff/gruff.erl
|
7
|
+
examples/gruff/gruff_provider.rb
|
8
|
+
examples/gruff/gruff_run.erl
|
9
|
+
examples/gruff/stat_run.erl
|
10
|
+
examples/gruff/stat_writer.erl
|
11
|
+
examples/tinderl/tinderl.erl
|
12
|
+
examples/tinderl/tinderl.rb
|
13
|
+
ext/decoder.c
|
14
|
+
ext/extconf.rb
|
15
|
+
lib/erlectricity.rb
|
16
|
+
lib/erlectricity/condition.rb
|
17
|
+
lib/erlectricity/conditions/hash.rb
|
18
|
+
lib/erlectricity/conditions/static.rb
|
19
|
+
lib/erlectricity/conditions/type.rb
|
20
|
+
lib/erlectricity/constants.rb
|
21
|
+
lib/erlectricity/decoder.rb
|
22
|
+
lib/erlectricity/encoder.rb
|
23
|
+
lib/erlectricity/errors/decode_error.rb
|
24
|
+
lib/erlectricity/errors/encode_error.rb
|
25
|
+
lib/erlectricity/errors/erlectricity_error.rb
|
26
|
+
lib/erlectricity/matcher.rb
|
27
|
+
lib/erlectricity/port.rb
|
28
|
+
lib/erlectricity/receiver.rb
|
29
|
+
lib/erlectricity/types/function.rb
|
30
|
+
lib/erlectricity/types/list.rb
|
31
|
+
lib/erlectricity/types/new_function.rb
|
32
|
+
lib/erlectricity/types/new_reference.rb
|
33
|
+
lib/erlectricity/types/pid.rb
|
34
|
+
lib/erlectricity/types/reference.rb
|
35
|
+
lib/erlectricity/version.rb
|
36
|
+
setup.rb
|
37
|
+
test/condition_spec.rb
|
38
|
+
test/decode_spec.rb
|
39
|
+
test/encode_spec.rb
|
40
|
+
test/matcher_spec.rb
|
41
|
+
test/port_spec.rb
|
42
|
+
test/receiver_spec.rb
|
43
|
+
test/spec_suite.rb
|
44
|
+
test/test_erlectricity.rb
|
45
|
+
test/test_helper.rb
|
data/README.txt
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
erlectricity
|
2
|
+
by Scott Fleckenstein
|
3
|
+
Tom Preston-Werner
|
4
|
+
|
5
|
+
http://github.com/mojombo/erlectricity
|
6
|
+
|
7
|
+
== DESCRIPTION:
|
8
|
+
|
9
|
+
Erlectricity allows a Ruby program to receive and respond to Erlang messages
|
10
|
+
sent over the Erlang binary protocol.
|
11
|
+
|
12
|
+
== INSTALL:
|
13
|
+
|
14
|
+
$ gem install erlectricity
|
15
|
+
|
16
|
+
== USAGE (Ruby side):
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
require 'erlectricity'
|
20
|
+
require 'stringio'
|
21
|
+
|
22
|
+
receive do |f|
|
23
|
+
f.when(:echo, String) do |text|
|
24
|
+
f.send!(:result, "You said: #{text}")
|
25
|
+
f.receive_loop
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
== USAGE (Erlang side):
|
30
|
+
|
31
|
+
-module(echo).
|
32
|
+
-export([test/0]).
|
33
|
+
|
34
|
+
test() ->
|
35
|
+
Cmd = "ruby echo.rb",
|
36
|
+
Port = open_port({spawn, Cmd}, [{packet, 4}, use_stdio, exit_status, binary]),
|
37
|
+
Payload = term_to_binary({echo, <<"hello world!">>}),
|
38
|
+
port_command(Port, Payload),
|
39
|
+
receive
|
40
|
+
{Port, {data, Data}} ->
|
41
|
+
{result, Text} = binary_to_term(Data),
|
42
|
+
io:format("~p~n", [Text])
|
43
|
+
end.
|
data/Rakefile
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'hoe'
|
11
|
+
include FileUtils
|
12
|
+
require File.join(File.dirname(__FILE__), 'lib', 'erlectricity', 'version')
|
13
|
+
|
14
|
+
AUTHOR = 'Scott Fleckenstein' # can also be an array of Authors
|
15
|
+
EMAIL = "nullstyle@gmail.com"
|
16
|
+
DESCRIPTION = "A library to interface erlang and ruby through the erlang port system"
|
17
|
+
GEM_NAME = 'erlectricity' # what ppl will type to install your gem
|
18
|
+
RUBYFORGE_PROJECT = 'erlectricity' # The unix name for your project
|
19
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
20
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
21
|
+
|
22
|
+
NAME = "erlectricity"
|
23
|
+
REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
24
|
+
VERS = Erlectricity::VERSION::STRING + (REV ? ".#{REV}" : "")
|
25
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
|
26
|
+
RDOC_OPTS = ['--quiet', '--title', 'erlectricity documentation',
|
27
|
+
"--opname", "index.html",
|
28
|
+
"--line-numbers",
|
29
|
+
"--main", "README",
|
30
|
+
"--inline-source"]
|
31
|
+
|
32
|
+
class Hoe
|
33
|
+
def extra_deps
|
34
|
+
@extra_deps.reject { |x| Array(x).first == 'hoe' }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate all the Rake tasks
|
39
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
40
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
41
|
+
p.author = AUTHOR
|
42
|
+
p.description = DESCRIPTION
|
43
|
+
p.email = EMAIL
|
44
|
+
p.summary = DESCRIPTION
|
45
|
+
p.url = HOMEPATH
|
46
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
47
|
+
p.test_globs = ["test/**/test_*.rb"]
|
48
|
+
p.clean_globs = CLEAN #An array of file patterns to delete on clean.
|
49
|
+
|
50
|
+
# == Optional
|
51
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
52
|
+
p.extra_deps = [
|
53
|
+
['concurrent', '0.2.2'],
|
54
|
+
] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
55
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
56
|
+
p.spec_extras = {:extensions => ['ext/extconf.rb']}
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'Release the website and new gem version'
|
60
|
+
task :deploy => [:check_version, :release]
|
61
|
+
|
62
|
+
task :check_version do
|
63
|
+
unless ENV['VERSION']
|
64
|
+
puts 'Must pass a VERSION=x.y.z release version'
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
unless ENV['VERSION'] == VERS
|
68
|
+
puts "Please update your version.rb to match the release version, currently #{VERS}"
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,62 @@
|
|
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}, use_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
|
+
|
32
|
+
port_loop(Port) ->
|
33
|
+
receive
|
34
|
+
{plot, Caller, Name, Font, Data, Labels} ->
|
35
|
+
PlotData = term_to_binary({plot, Name, 'Line', Font}),
|
36
|
+
Port ! {self(), {command, PlotData}},
|
37
|
+
|
38
|
+
send_data(Port, Data),
|
39
|
+
send_labels(Port, Labels),
|
40
|
+
|
41
|
+
EndData = term_to_binary('end'),
|
42
|
+
Port ! {self(), {command, EndData}},
|
43
|
+
Result = get_result(Port),
|
44
|
+
Caller ! {result, Result },
|
45
|
+
|
46
|
+
port_loop(Port);
|
47
|
+
|
48
|
+
stop ->
|
49
|
+
Port ! {self(), close},
|
50
|
+
receive
|
51
|
+
{Port, closed} -> exit(normal)
|
52
|
+
end
|
53
|
+
end.
|
54
|
+
|
55
|
+
get_result(Port) ->
|
56
|
+
receive
|
57
|
+
{Port, {data, Data}} ->
|
58
|
+
{result, Bin} = binary_to_term(Data),
|
59
|
+
Bin;
|
60
|
+
{'EXIT', Port, Reason} ->
|
61
|
+
exit({port_terminated,Reason})
|
62
|
+
end.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + "/../../lib/")
|
2
|
+
require 'erlectricity'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'gruff'
|
5
|
+
|
6
|
+
receive do |f|
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
f.when(:plot, String, Symbol, String) do |name, style, font|
|
11
|
+
graph = Gruff.const_get(style).new
|
12
|
+
graph.title = name
|
13
|
+
graph.font = font
|
14
|
+
graph.legend_font_size = 10
|
15
|
+
|
16
|
+
|
17
|
+
f.receive do |g|
|
18
|
+
g.when(:data, Symbol, Array) do |name, points|
|
19
|
+
graph.data name, points
|
20
|
+
g.receive_loop
|
21
|
+
end
|
22
|
+
|
23
|
+
g.when(:labels, Erl.hash) do |label_data|
|
24
|
+
graph.labels = label_data
|
25
|
+
g.receive_loop
|
26
|
+
end
|
27
|
+
|
28
|
+
g.when(:end){ :ok }
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
f.send! :result, graph.to_blob
|
33
|
+
f.receive_loop
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env escript
|
2
|
+
-export([main/1]).
|
3
|
+
main(_Any) ->
|
4
|
+
Data = [
|
5
|
+
{apples, [10, 2, 3, 4, 4, 3]},
|
6
|
+
{oranges, [4, 8, 7, 9, 8, 9]},
|
7
|
+
{watermelons, [2, 3, 1, 5, 6, 8]},
|
8
|
+
{peaches, [9, 9, 10, 8, 7, 9]}
|
9
|
+
],
|
10
|
+
gruff:start(),
|
11
|
+
Result = gruff:plot(
|
12
|
+
<<"My Charts">>,
|
13
|
+
<<"/Users/scott/Library/Fonts/Arial">>,
|
14
|
+
Data,
|
15
|
+
[{0, <<"2003">>}, {2, <<"2004">>}, {4, <<"2005">>}]
|
16
|
+
),
|
17
|
+
file:write_file("out.png", Result).
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env escript
|
2
|
+
-export([main/1]).
|
3
|
+
main(_Any) ->
|
4
|
+
gruff:start(),
|
5
|
+
MemoryWriter = stat_writer:start(<<"Memory Info">>, fun() -> erlang:memory() end),
|
6
|
+
ProcessWriter = stat_writer:start(<<"Process Info">>,
|
7
|
+
fun() ->
|
8
|
+
{_, QueueLength} = erlang:process_info(erlang:whereis(gruff), message_queue_len),
|
9
|
+
[{processes, erlang:system_info(process_count)},
|
10
|
+
{gruff_queue_length, QueueLength}]
|
11
|
+
end
|
12
|
+
),
|
13
|
+
receive
|
14
|
+
after 20000 ->
|
15
|
+
MemoryWriter ! stop,
|
16
|
+
ProcessWriter ! stop,
|
17
|
+
elang:halt()
|
18
|
+
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,45 @@
|
|
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}, use_stdio, exit_status, binary]),
|
10
|
+
port_loop(Port)
|
11
|
+
end).
|
12
|
+
|
13
|
+
stop() -> tinderl ! stop.
|
14
|
+
|
15
|
+
|
16
|
+
speak(String) when is_list(String) -> speak(list_to_binary(String));
|
17
|
+
speak(String) when is_binary(String) -> tinderl ! {speak, self(), String}.
|
18
|
+
|
19
|
+
paste(String) when is_list(String) -> speak(list_to_binary(String));
|
20
|
+
paste(String) when is_binary(String) -> tinderl ! {paste, self(), String}.
|
21
|
+
|
22
|
+
port_loop(Port) ->
|
23
|
+
receive
|
24
|
+
{speak, _Caller, String} ->
|
25
|
+
Data = term_to_binary({speak, String}),
|
26
|
+
Port ! {self(), {command, Data}},
|
27
|
+
|
28
|
+
port_loop(Port);
|
29
|
+
|
30
|
+
{paste, _Caller, String} ->
|
31
|
+
Data = term_to_binary({paste, String}),
|
32
|
+
Port ! {self(), {command, Data}},
|
33
|
+
|
34
|
+
port_loop(Port);
|
35
|
+
|
36
|
+
stop ->
|
37
|
+
Port ! {self(), close},
|
38
|
+
receive
|
39
|
+
{Port, closed} -> exit(normal)
|
40
|
+
end;
|
41
|
+
|
42
|
+
{'EXIT', Port, Reason} ->
|
43
|
+
exit({port_terminated,Reason})
|
44
|
+
end.
|
45
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + "/../../lib/")
|
2
|
+
require 'erlectricity'
|
3
|
+
require 'rubygems'
|
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
|
+
STDERR.write obj.inspect
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
room.leave if room
|
data/ext/decoder.c
ADDED
@@ -0,0 +1,391 @@
|
|
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_read_any_from(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
|
+
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
|
+
int size = read_4(pData);
|
118
|
+
|
119
|
+
VALUE array = rb_ary_new2(size);
|
120
|
+
|
121
|
+
int i;
|
122
|
+
for(i = 0; i < size; ++i) {
|
123
|
+
rb_ary_store(array, i, read_any_raw(pData));
|
124
|
+
}
|
125
|
+
|
126
|
+
read_1(pData);
|
127
|
+
|
128
|
+
return array;
|
129
|
+
}
|
130
|
+
|
131
|
+
// primitives
|
132
|
+
|
133
|
+
void read_string_raw(unsigned char *dest, unsigned char **pData, int length) {
|
134
|
+
memcpy((char *) dest, (char *) *pData, length);
|
135
|
+
*(dest + length) = (unsigned char) 0;
|
136
|
+
*pData += length;
|
137
|
+
}
|
138
|
+
|
139
|
+
VALUE read_bin(unsigned char **pData) {
|
140
|
+
if(read_1(pData) != ERL_BIN) {
|
141
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an erlang binary");
|
142
|
+
}
|
143
|
+
|
144
|
+
int length = read_4(pData);
|
145
|
+
|
146
|
+
unsigned char buf[length + 1];
|
147
|
+
read_string_raw(buf, pData, length);
|
148
|
+
|
149
|
+
return rb_str_new2((char *) buf);
|
150
|
+
}
|
151
|
+
|
152
|
+
VALUE read_string(unsigned char **pData) {
|
153
|
+
if(read_1(pData) != ERL_STRING) {
|
154
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an erlang string");
|
155
|
+
}
|
156
|
+
|
157
|
+
int length = read_2(pData);
|
158
|
+
|
159
|
+
unsigned char buf[length + 1];
|
160
|
+
read_string_raw(buf, pData, length);
|
161
|
+
|
162
|
+
VALUE array = rb_ary_new2(length);
|
163
|
+
|
164
|
+
int i = 0;
|
165
|
+
for(i; i < length; ++i) {
|
166
|
+
rb_ary_store(array, i, INT2NUM(*(buf + i)));
|
167
|
+
}
|
168
|
+
|
169
|
+
return array;
|
170
|
+
}
|
171
|
+
|
172
|
+
VALUE read_atom(unsigned char **pData) {
|
173
|
+
if(read_1(pData) != ERL_ATOM) {
|
174
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an atom");
|
175
|
+
}
|
176
|
+
|
177
|
+
int length = read_2(pData);
|
178
|
+
|
179
|
+
unsigned char buf[length + 1];
|
180
|
+
read_string_raw(buf, pData, length);
|
181
|
+
|
182
|
+
return ID2SYM(rb_intern((char *) buf));
|
183
|
+
}
|
184
|
+
|
185
|
+
VALUE read_small_int(unsigned char **pData) {
|
186
|
+
if(read_1(pData) != ERL_SMALL_INT) {
|
187
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small int");
|
188
|
+
}
|
189
|
+
|
190
|
+
int value = read_1(pData);
|
191
|
+
|
192
|
+
return INT2FIX(value);
|
193
|
+
}
|
194
|
+
|
195
|
+
VALUE read_int(unsigned char **pData) {
|
196
|
+
if(read_1(pData) != ERL_INT) {
|
197
|
+
rb_raise(rb_eStandardError, "Invalid Type, not an int");
|
198
|
+
}
|
199
|
+
|
200
|
+
long long value = read_4(pData);
|
201
|
+
|
202
|
+
long long negative = ((value >> 31) & 0x1 == 1);
|
203
|
+
|
204
|
+
if(negative) {
|
205
|
+
value = (value - ((long long) 1 << 32));
|
206
|
+
}
|
207
|
+
|
208
|
+
return INT2FIX(value);
|
209
|
+
}
|
210
|
+
|
211
|
+
VALUE read_small_bignum(unsigned char **pData) {
|
212
|
+
if(read_1(pData) != ERL_SMALL_BIGNUM) {
|
213
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small bignum");
|
214
|
+
}
|
215
|
+
|
216
|
+
unsigned int size = read_1(pData);
|
217
|
+
unsigned int sign = read_1(pData);
|
218
|
+
|
219
|
+
VALUE num = INT2NUM(0);
|
220
|
+
VALUE tmp;
|
221
|
+
|
222
|
+
unsigned char buf[size + 1];
|
223
|
+
read_string_raw(buf, pData, size);
|
224
|
+
|
225
|
+
int i;
|
226
|
+
for(i = 0; i < size; ++i) {
|
227
|
+
tmp = INT2FIX(*(buf + i));
|
228
|
+
tmp = rb_funcall(tmp, rb_intern("<<"), 1, INT2NUM(i * 8));
|
229
|
+
num = rb_funcall(num, rb_intern("+"), 1, tmp);
|
230
|
+
}
|
231
|
+
|
232
|
+
if(sign) {
|
233
|
+
num = rb_funcall(num, rb_intern("*"), 1, INT2NUM(-1));
|
234
|
+
}
|
235
|
+
|
236
|
+
return num;
|
237
|
+
}
|
238
|
+
|
239
|
+
VALUE read_large_bignum(unsigned char **pData) {
|
240
|
+
if(read_1(pData) != ERL_LARGE_BIGNUM) {
|
241
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a small bignum");
|
242
|
+
}
|
243
|
+
|
244
|
+
unsigned int size = read_4(pData);
|
245
|
+
unsigned int sign = read_1(pData);
|
246
|
+
|
247
|
+
VALUE num = INT2NUM(0);
|
248
|
+
VALUE tmp;
|
249
|
+
|
250
|
+
unsigned char buf[size + 1];
|
251
|
+
read_string_raw(buf, pData, size);
|
252
|
+
|
253
|
+
int i;
|
254
|
+
for(i = 0; i < size; ++i) {
|
255
|
+
tmp = INT2FIX(*(buf + i));
|
256
|
+
tmp = rb_funcall(tmp, rb_intern("<<"), 1, INT2NUM(i * 8));
|
257
|
+
|
258
|
+
num = rb_funcall(num, rb_intern("+"), 1, tmp);
|
259
|
+
}
|
260
|
+
|
261
|
+
if(sign) {
|
262
|
+
num = rb_funcall(num, rb_intern("*"), 1, INT2NUM(-1));
|
263
|
+
}
|
264
|
+
|
265
|
+
return num;
|
266
|
+
}
|
267
|
+
|
268
|
+
VALUE read_float(unsigned char **pData) {
|
269
|
+
if(read_1(pData) != ERL_FLOAT) {
|
270
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a float");
|
271
|
+
}
|
272
|
+
|
273
|
+
unsigned char buf[32];
|
274
|
+
read_string_raw(buf, pData, 31);
|
275
|
+
|
276
|
+
VALUE rString = rb_str_new2((char *) buf);
|
277
|
+
|
278
|
+
return rb_funcall(rString, rb_intern("to_f"), 0);
|
279
|
+
}
|
280
|
+
|
281
|
+
VALUE read_nil(unsigned char **pData) {
|
282
|
+
if(read_1(pData) != ERL_NIL) {
|
283
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a nil list");
|
284
|
+
}
|
285
|
+
|
286
|
+
return rb_ary_new2(0);
|
287
|
+
}
|
288
|
+
|
289
|
+
// specials
|
290
|
+
|
291
|
+
VALUE read_pid(unsigned char **pData) {
|
292
|
+
if(read_1(pData) != ERL_PID) {
|
293
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a pid");
|
294
|
+
}
|
295
|
+
|
296
|
+
VALUE node = read_atom(pData);
|
297
|
+
VALUE id = INT2NUM(read_4(pData));
|
298
|
+
VALUE serial = INT2NUM(read_4(pData));
|
299
|
+
VALUE creation = INT2FIX(read_1(pData));
|
300
|
+
|
301
|
+
VALUE pid_class = rb_const_get(mErlectricity, rb_intern("Pid"));
|
302
|
+
return rb_funcall(pid_class, rb_intern("new"), 4, node, id, serial, creation);
|
303
|
+
}
|
304
|
+
|
305
|
+
VALUE read_new_reference(unsigned char **pData) {
|
306
|
+
if(read_1(pData) != ERL_NEW_REF) {
|
307
|
+
rb_raise(rb_eStandardError, "Invalid Type, not a new-style reference");
|
308
|
+
}
|
309
|
+
|
310
|
+
int size = read_2(pData);
|
311
|
+
VALUE node = read_atom(pData);
|
312
|
+
VALUE creation = INT2FIX(read_1(pData));
|
313
|
+
|
314
|
+
VALUE id = rb_ary_new2(size);
|
315
|
+
int i;
|
316
|
+
for(i = 0; i < size; ++i) {
|
317
|
+
rb_ary_store(id, i, INT2NUM(read_4(pData)));
|
318
|
+
}
|
319
|
+
|
320
|
+
VALUE newref_class = rb_const_get(mErlectricity, rb_intern("NewReference"));
|
321
|
+
return rb_funcall(newref_class, rb_intern("new"), 3, node, creation, id);
|
322
|
+
}
|
323
|
+
|
324
|
+
// read_any_raw
|
325
|
+
|
326
|
+
VALUE read_any_raw(unsigned char **pData) {
|
327
|
+
switch(peek_1(pData)) {
|
328
|
+
case ERL_SMALL_INT:
|
329
|
+
return read_small_int(pData);
|
330
|
+
break;
|
331
|
+
case ERL_INT:
|
332
|
+
return read_int(pData);
|
333
|
+
break;
|
334
|
+
case ERL_FLOAT:
|
335
|
+
return read_float(pData);
|
336
|
+
break;
|
337
|
+
case ERL_ATOM:
|
338
|
+
return read_atom(pData);
|
339
|
+
break;
|
340
|
+
case ERL_PID:
|
341
|
+
return read_pid(pData);
|
342
|
+
break;
|
343
|
+
case ERL_SMALL_TUPLE:
|
344
|
+
return read_small_tuple(pData);
|
345
|
+
break;
|
346
|
+
case ERL_LARGE_TUPLE:
|
347
|
+
return read_large_tuple(pData);
|
348
|
+
break;
|
349
|
+
case ERL_NIL:
|
350
|
+
return read_nil(pData);
|
351
|
+
break;
|
352
|
+
case ERL_STRING:
|
353
|
+
return read_string(pData);
|
354
|
+
break;
|
355
|
+
case ERL_LIST:
|
356
|
+
return read_list(pData);
|
357
|
+
break;
|
358
|
+
case ERL_BIN:
|
359
|
+
return read_bin(pData);
|
360
|
+
break;
|
361
|
+
case ERL_SMALL_BIGNUM:
|
362
|
+
return read_small_bignum(pData);
|
363
|
+
break;
|
364
|
+
case ERL_LARGE_BIGNUM:
|
365
|
+
return read_large_bignum(pData);
|
366
|
+
break;
|
367
|
+
case ERL_NEW_REF:
|
368
|
+
return read_new_reference(pData);
|
369
|
+
break;
|
370
|
+
}
|
371
|
+
return Qnil;
|
372
|
+
}
|
373
|
+
|
374
|
+
VALUE method_read_any_from(VALUE klass, VALUE rString) {
|
375
|
+
unsigned char *data = (unsigned char *) StringValuePtr(rString);
|
376
|
+
|
377
|
+
unsigned char **pData = &data;
|
378
|
+
|
379
|
+
// check protocol version
|
380
|
+
if(read_1(pData) != ERL_VERSION) {
|
381
|
+
rb_raise(rb_eStandardError, "Bad Magic");
|
382
|
+
}
|
383
|
+
|
384
|
+
return read_any_raw(pData);
|
385
|
+
}
|
386
|
+
|
387
|
+
void Init_decoder() {
|
388
|
+
mErlectricity = rb_const_get(rb_cObject, rb_intern("Erlectricity"));
|
389
|
+
cDecoder = rb_define_class_under(mErlectricity, "Decoder", rb_cObject);
|
390
|
+
rb_define_singleton_method(cDecoder, "read_any_from", method_read_any_from, 1);
|
391
|
+
}
|