tmm1-erlectricity 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }