tmm1-erlectricity 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CONTRIBUTORS +2 -0
  2. data/History.txt +1 -0
  3. data/Manifest.txt +45 -0
  4. data/README.txt +51 -0
  5. data/Rakefile +71 -0
  6. data/examples/gruff/gruff.erl +62 -0
  7. data/examples/gruff/gruff_provider.rb +36 -0
  8. data/examples/gruff/gruff_run.erl +17 -0
  9. data/examples/gruff/stat_run.erl +18 -0
  10. data/examples/gruff/stat_writer.erl +40 -0
  11. data/examples/tinderl/tinderl.erl +45 -0
  12. data/examples/tinderl/tinderl.rb +27 -0
  13. data/ext/decoder.c +391 -0
  14. data/ext/extconf.rb +11 -0
  15. data/lib/erlectricity.rb +37 -0
  16. data/lib/erlectricity/condition.rb +51 -0
  17. data/lib/erlectricity/conditions/hash.rb +14 -0
  18. data/lib/erlectricity/conditions/static.rb +13 -0
  19. data/lib/erlectricity/conditions/type.rb +17 -0
  20. data/lib/erlectricity/constants.rb +37 -0
  21. data/lib/erlectricity/decoder.rb +204 -0
  22. data/lib/erlectricity/encoder.rb +127 -0
  23. data/lib/erlectricity/errors/decode_error.rb +3 -0
  24. data/lib/erlectricity/errors/encode_error.rb +3 -0
  25. data/lib/erlectricity/errors/erlectricity_error.rb +3 -0
  26. data/lib/erlectricity/matcher.rb +38 -0
  27. data/lib/erlectricity/port.rb +46 -0
  28. data/lib/erlectricity/receiver.rb +78 -0
  29. data/lib/erlectricity/types/function.rb +3 -0
  30. data/lib/erlectricity/types/list.rb +1 -0
  31. data/lib/erlectricity/types/new_function.rb +3 -0
  32. data/lib/erlectricity/types/new_reference.rb +3 -0
  33. data/lib/erlectricity/types/pid.rb +3 -0
  34. data/lib/erlectricity/types/reference.rb +3 -0
  35. data/lib/erlectricity/version.rb +9 -0
  36. data/setup.rb +1585 -0
  37. data/test/condition_spec.rb +73 -0
  38. data/test/decode_spec.rb +129 -0
  39. data/test/encode_spec.rb +132 -0
  40. data/test/matcher_spec.rb +69 -0
  41. data/test/port_spec.rb +35 -0
  42. data/test/receiver_spec.rb +105 -0
  43. data/test/spec_suite.rb +2 -0
  44. data/test/test_erlectricity.rb +2 -0
  45. data/test/test_helper.rb +42 -0
  46. metadata +102 -0
@@ -0,0 +1,2 @@
1
+ Tom Preston-Werner
2
+ - provided the C extension that replaces the ruby decoder
@@ -0,0 +1 @@
1
+
@@ -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
@@ -0,0 +1,51 @@
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
+ -or-
17
+
18
+ $ gem install mojombo-erlectricity -s http://gems.github.com
19
+
20
+ == CONTRIBUTE:
21
+
22
+ Contributions are welcome via GitHub! Fork the code from
23
+ http://github.com/mojombo/erlectricity and send a pull request to mojombo.
24
+
25
+ == USAGE (Ruby side):
26
+
27
+ require 'rubygems'
28
+ require 'erlectricity'
29
+
30
+ receive do |f|
31
+ f.when(:echo, String) do |text|
32
+ f.send!(:result, "You said: #{text}")
33
+ f.receive_loop
34
+ end
35
+ end
36
+
37
+ == USAGE (Erlang side):
38
+
39
+ -module(echo).
40
+ -export([test/0]).
41
+
42
+ test() ->
43
+ Cmd = "ruby echo.rb",
44
+ Port = open_port({spawn, Cmd}, [{packet, 4}, use_stdio, exit_status, binary]),
45
+ Payload = term_to_binary({echo, <<"hello world!">>}),
46
+ port_command(Port, Payload),
47
+ receive
48
+ {Port, {data, Data}} ->
49
+ {result, Text} = binary_to_term(Data),
50
+ io:format("~p~n", [Text])
51
+ end.
@@ -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,36 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../../lib/")
2
+ require 'erlectricity'
3
+ require 'rubygems'
4
+ require 'gruff'
5
+
6
+ receive do |f|
7
+
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
+
15
+ f.receive do |g|
16
+ g.when(:data, Symbol, Array) do |name, points|
17
+ graph.data name, points
18
+ g.receive_loop
19
+ end
20
+
21
+ g.when(:labels, Erl.hash) do |label_data|
22
+ graph.labels = label_data
23
+ g.receive_loop
24
+ end
25
+
26
+ g.when(:end){ :ok }
27
+ end
28
+
29
+
30
+ f.send! :result, graph.to_blob
31
+ f.receive_loop
32
+ end
33
+
34
+ end
35
+
36
+
@@ -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
@@ -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
+ }