stomp_parser 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5a04a9e96b78143103517610d026fe12603b2ebe
4
+ data.tar.gz: 660c92162ef6ad5cdc05f321ced1f36ce52999ab
5
+ SHA512:
6
+ metadata.gz: f20b1607fbba7e6f3a6be6a736c0bf5a63e90ef4e2f76d086cc617a3f564166b0b61523bdecb7d5a5748bfef0f164d1ccb8fbddd0c744da9991bf9e51c3a1096
7
+ data.tar.gz: c4f7189b20e941cb84547c970d9a3d4b8c3e118d15539ef3387c8bbda60ebd6cc98f7a1efab3560f65f624cf778e9b30515bb435f564818ceb74258bd1c3d3c3
@@ -0,0 +1,33 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spec/profile
19
+
20
+ # C extension
21
+ *.o
22
+ *.bundle
23
+ mkmf.log
24
+ Makefile
25
+ tmp/
26
+ spec/spec.log
27
+
28
+ # Ragel
29
+ lib/stomp_parser/ruby_parser.rb
30
+ lib/stomp_parser/c_parser.rb
31
+ ext/stomp_parser/c_parser.c
32
+ ext/java/stomp_parser/JavaParser.java
33
+ lib/stomp_parser/java_parser.jar
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ -r spec_helper
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - rbx-2
7
+ - jruby
@@ -0,0 +1,2 @@
1
+ install ragel
2
+ install graphviz
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem "perftools.rb", platform: :mri
6
+ gem "benchmark-ips"
7
+
8
+ platforms :rbx do
9
+ gem "rb-readline"
10
+ gem "rubysl-singleton"
11
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kim Burgestrand, Jonas Nicklas
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,59 @@
1
+ # StompParser
2
+
3
+ [![Build Status](https://travis-ci.org/stompede/stomp_parser.png?branch=master)](https://travis-ci.org/stompede/stomp_parser)
4
+ [![Gem Version](https://badge.fury.io/rb/stomp_parser.png)](http://badge.fury.io/rb/stomp_parser)
5
+
6
+ Fast STOMP parser and serializer for Ruby with native extensions in C for MRI
7
+ and Rubinius and in Java for JRuby, as well as a pure Ruby parser.
8
+
9
+ ## Parsing
10
+
11
+ ``` ruby
12
+ parser = StompParser::Parser.new
13
+ parser.parse(chunk) do |frame|
14
+ puts "We received #{frame.command} frame with #{frame.body} and headers #{frame.headers}!"
15
+ end
16
+ ```
17
+
18
+ The chunks do not have to be complete STOMP frames. The callback will be called
19
+ whenever a complete frame has been parsed. Additionally, StompParser handles
20
+ escape sequences in headers and body encoding for you.
21
+
22
+ ## Serializing
23
+
24
+ ``` ruby
25
+ StompParser::Frame.new("SEND", { some: "header" }, "Hello").to_str
26
+ ```
27
+
28
+ ## Development
29
+
30
+ Development should be ez.
31
+
32
+ ``` bash
33
+ git clone git@github.com:stompede/stomp_parser.git # git, http://git-scm.com/
34
+ cd stomp_parser
35
+ brew bundle # Homebrew, http://brew.sh/
36
+ bundle install # Bundler, http://bundler.io/
37
+ rake # compile state machine, run test suite
38
+ ```
39
+
40
+ A few notes:
41
+
42
+ - Native dependencies are listed in the Brewfile.
43
+ - The stomp message parser is written in [Ragel](http://www.complang.org/ragel/), see [parser_common.rl](parser_common.rl).
44
+ - Graphviz is used to visualize the Ragel state machine.
45
+ - Most rake tasks will compile the state machine anew if needed.
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork it on GitHub (<http://github.com/stompede/stomp_parser/fork>).
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
51
+ 3. Follow the [Development](#development) instructions in this README.
52
+ 4. Create your changes, please add tests.
53
+ 5. Commit your changes (`git commit -am 'Add some feature'`).
54
+ 6. Push to the branch (`git push origin my-new-feature`).
55
+ 7. Create new pull request on GitHub.
56
+
57
+ ## License
58
+
59
+ [MIT](MIT-LICENSE.txt)
@@ -0,0 +1,85 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ def ragel(*args)
4
+ sh "ragel", "-I.", *args
5
+ end
6
+
7
+ # Build state machine before building gem.
8
+ task :build => :compile
9
+
10
+ file "parser_common.rl"
11
+
12
+ rule ".rb" => %w[.rb.rl parser_common.rl] do |t|
13
+ ragel "-F1", "-R", t.source, "-o", t.name
14
+ end
15
+
16
+ rule ".c" => %w[.c.rl parser_common.rl] do |t|
17
+ ragel "-G2", "-C", t.source, "-o", t.name
18
+ end
19
+
20
+ rule ".java" => %w[.java.rl parser_common.rl] do |t|
21
+ ragel "-T0", "-J", t.source, "-o", t.name
22
+ end
23
+
24
+ desc "ragel machines"
25
+ task :compile => %w[lib/stomp_parser/ruby_parser.rb]
26
+
27
+ case RUBY_ENGINE
28
+ when "rbx", "ruby"
29
+ require "rake/extensiontask"
30
+ task :compile => %w[ext/stomp_parser/c_parser.c]
31
+
32
+ Rake::ExtensionTask.new do |ext|
33
+ ext.name = "c_parser"
34
+ ext.ext_dir = "ext/stomp_parser"
35
+ ext.lib_dir = "lib/stomp_parser"
36
+ end
37
+ when "jruby"
38
+ require "rake/javaextensiontask"
39
+ task :compile => %w[ext/java/stomp_parser/JavaParser.java]
40
+
41
+ Rake::JavaExtensionTask.new do |ext|
42
+ ext.name = "java_parser"
43
+ ext.lib_dir = "lib/stomp_parser"
44
+ end
45
+ end
46
+
47
+ desc "ragel machines"
48
+ task :clean do |t|
49
+ source_tasks = Rake::Task[:compile].prerequisite_tasks.grep(Rake::FileTask)
50
+ rm_f source_tasks.map(&:name)
51
+ end
52
+
53
+ namespace :ragel do
54
+ desc "Show stomp parser state machine as an image"
55
+ task :show => "lib/stomp_parser/ruby_parser.rb" do |t|
56
+ mkdir_p "tmp"
57
+ ragel "-V", "-p", t.prerequisite_tasks[0].source, "-o", "tmp/parser.dot"
58
+ sh "dot -Tpng -O tmp/parser.dot"
59
+ rm "tmp/parser.dot"
60
+ sh "open tmp/parser.dot.png"
61
+ end
62
+ end
63
+
64
+ desc "Start a pry session with the gem loaded."
65
+ task :console => :compile do
66
+ exec "pry", "-rbundler/setup", "-rstomp_parser"
67
+ end
68
+
69
+ require "rspec/core/rake_task"
70
+ RSpec::Core::RakeTask.new(:spec)
71
+ task :spec => :compile
72
+
73
+ desc "Run all benchmarks."
74
+ task :bench => :compile do
75
+ sh "ruby", "-I.", *FileList["spec/benchmarks/**/*.rb"].flat_map { |x| ["-r", x] }, "-e", "''"
76
+ end
77
+
78
+ desc "Run the profiler and show a gif, requires perftools.rb"
79
+ task :profile => :compile do
80
+ # CPUPROFILE_METHODS=0 CPUPROFILE_OBJECTS=0 CPUPROFILE_REALTIME=1
81
+ sh "CPUPROFILE_REALTIME=1 ruby spec/profile.rb"
82
+ sh "pprof.rb --text spec/profile/parser.profile"
83
+ end
84
+
85
+ task :default => :spec
@@ -0,0 +1,179 @@
1
+ package stomp_parser;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyModule;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyObject;
7
+ import org.jruby.RubyFixnum;
8
+ import org.jruby.RubyString;
9
+ import org.jruby.RubyNumeric;
10
+ import org.jruby.RubyException;
11
+ import org.jruby.exceptions.RaiseException;
12
+
13
+ import org.jruby.runtime.ThreadContext;
14
+ import org.jruby.runtime.builtin.IRubyObject;
15
+ import org.jruby.runtime.Block;
16
+
17
+ import org.jruby.anno.JRubyClass;
18
+ import org.jruby.anno.JRubyMethod;
19
+
20
+ %%{
21
+ machine frame;
22
+ alphtype byte;
23
+
24
+ action mark {
25
+ mark = p;
26
+ }
27
+
28
+ action mark_frame {
29
+ mark_frame = context.runtime.getClassFromPath("StompParser::Frame").callMethod("new", context.nil, context.nil);
30
+ mark_frame_size = 0;
31
+ }
32
+
33
+ action write_command {
34
+ mark_frame.callMethod(context, "write_command", RubyString.newString(context.runtime, data, mark, p - mark));
35
+ mark = -1;
36
+ }
37
+
38
+ action mark_key {
39
+ mark_key = RubyString.newString(context.runtime, data, mark, p - mark);
40
+ mark = -1;
41
+ }
42
+
43
+ action write_header {
44
+ IRubyObject args[] = { mark_key, RubyString.newString(context.runtime, data, mark, p - mark) };
45
+ mark_frame.callMethod(context, "write_header", args);
46
+ mark_key = null;
47
+ mark = -1;
48
+ }
49
+
50
+ action finish_headers {
51
+ IRubyObject content_length = mark_frame.callMethod(context, "content_length");
52
+
53
+ if ( ! content_length.isNil()) {
54
+ mark_content_length = RubyNumeric.num2int(content_length);
55
+ } else {
56
+ mark_content_length = -1;
57
+ }
58
+ }
59
+
60
+ action write_body {
61
+ mark_frame.callMethod(context, "write_body", RubyString.newString(context.runtime, data, mark, p - mark));
62
+ mark = -1;
63
+ }
64
+
65
+ action consume_null {
66
+ ((mark_content_length != -1) && ((p - mark) < mark_content_length))
67
+ }
68
+
69
+ action consume_octet {
70
+ ((mark_content_length == -1) || ((p - mark) < mark_content_length))
71
+ }
72
+
73
+ action check_frame_size {
74
+ mark_frame_size += 1;
75
+ if (mark_frame_size > maxFrameSize) {
76
+ RubyModule frameSizeExceeded = context.runtime.getClassFromPath("StompParser::FrameSizeExceeded");
77
+ RubyException error = (RubyException) frameSizeExceeded.callMethod("new");
78
+ throw new RaiseException(error);
79
+ }
80
+ }
81
+
82
+ action finish_frame {
83
+ block.yield(context, mark_frame);
84
+ mark_frame = null;
85
+ }
86
+
87
+ include frame_common "parser_common.rl";
88
+ }%%
89
+
90
+ @JRubyClass(name="JavaParser", parent="Object")
91
+ public class JavaParser extends RubyObject {
92
+ %% write data noprefix;
93
+
94
+ private class State {
95
+ public int cs = JavaParser.start;
96
+ public byte[] chunk;
97
+ public int mark = -1;
98
+ public RubyString mark_key;
99
+ public IRubyObject mark_frame;
100
+ public int mark_frame_size = -1;
101
+ public int mark_content_length = -1;
102
+ }
103
+
104
+ private RubyException parseError;
105
+ private long maxFrameSize;
106
+ private State state;
107
+
108
+ public JavaParser(Ruby runtime, RubyClass klass) {
109
+ super(runtime, klass);
110
+ state = new State();
111
+ parseError = null;
112
+ }
113
+
114
+ @JRubyMethod
115
+ public IRubyObject initialize(ThreadContext context) {
116
+ RubyModule mStompParser = context.runtime.getClassFromPath("StompParser");
117
+ return initialize(context, mStompParser.callMethod("max_frame_size"));
118
+ }
119
+
120
+ @JRubyMethod(argTypes = {RubyFixnum.class})
121
+ public IRubyObject initialize(ThreadContext context, IRubyObject maxFrameSize) {
122
+ this.maxFrameSize = ((RubyFixnum) maxFrameSize).getLongValue();
123
+ return context.nil;
124
+ }
125
+
126
+ @JRubyMethod(argTypes = {RubyString.class})
127
+ public IRubyObject parse(ThreadContext context, IRubyObject chunk, Block block) {
128
+ if (parseError == null) {
129
+ int p;
130
+ byte data[] = null;
131
+ byte bytes[] = ((RubyString) chunk).getBytes();
132
+
133
+ if (state.chunk != null) {
134
+ p = state.chunk.length;
135
+ data = new byte[state.chunk.length + bytes.length];
136
+ System.arraycopy(state.chunk, 0, data, 0, state.chunk.length);
137
+ System.arraycopy(bytes, 0, data, state.chunk.length, bytes.length);
138
+ } else {
139
+ p = 0;
140
+ data = bytes;
141
+ }
142
+
143
+ int pe = data.length;
144
+
145
+ int cs = state.cs;
146
+ int mark = state.mark;
147
+ RubyString mark_key = state.mark_key;
148
+ IRubyObject mark_frame = state.mark_frame;
149
+ int mark_frame_size = state.mark_frame_size;
150
+ int mark_content_length = state.mark_content_length;
151
+
152
+ %% write exec;
153
+
154
+ if (mark != -1) {
155
+ state.chunk = data;
156
+ } else {
157
+ state.chunk = null;
158
+ }
159
+
160
+ state.cs = cs;
161
+ state.mark = mark;
162
+ state.mark_key = mark_key;
163
+ state.mark_frame = mark_frame;
164
+ state.mark_frame_size = mark_frame_size;
165
+ state.mark_content_length = mark_content_length;
166
+
167
+ if (cs == error) {
168
+ IRubyObject args[] = { RubyString.newString(context.runtime, data), RubyFixnum.newFixnum(context.runtime, (long) p) };
169
+ parseError = (RubyException) context.runtime.getClassFromPath("StompParser").callMethod(context, "build_parse_error", args);
170
+ }
171
+ }
172
+
173
+ if (parseError != null) {
174
+ throw new RaiseException(parseError);
175
+ }
176
+
177
+ return context.nil;
178
+ }
179
+ }
@@ -0,0 +1,23 @@
1
+ package stomp_parser;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyClass;
5
+ import org.jruby.RubyModule;
6
+ import org.jruby.runtime.load.BasicLibraryService;
7
+ import org.jruby.runtime.builtin.IRubyObject;
8
+ import org.jruby.runtime.ObjectAllocator;
9
+
10
+ public class JavaParserService implements BasicLibraryService {
11
+ public boolean basicLoad(Ruby ruby) {
12
+ RubyModule mStomp = ruby.getClassFromPath("StompParser");
13
+ RubyClass cJavaParser = ruby.defineClassUnder("JavaParser", ruby.getObject(), JAVA_PARSER_ALLOCATOR, mStomp);
14
+ cJavaParser.defineAnnotatedMethods(JavaParser.class);
15
+ return true;
16
+ }
17
+
18
+ private static final ObjectAllocator JAVA_PARSER_ALLOCATOR = new ObjectAllocator() {
19
+ public IRubyObject allocate(Ruby ruby, RubyClass klass) {
20
+ return new JavaParser(ruby, klass);
21
+ }
22
+ };
23
+ }
@@ -0,0 +1,225 @@
1
+ #include <ruby.h>
2
+
3
+ #if DEBUG_H
4
+ # define DEBUG(fmt, ...) do { fprintf(stderr, fmt "\n", ##__VA_ARGS__); } while(0)
5
+ #else
6
+ # define DEBUG(...)
7
+ #endif
8
+
9
+ #define UNUSED(x) (void)(x)
10
+ #define MARK_LEN (p - mark)
11
+ #define MARK_STR_NEW() rb_str_new(mark, MARK_LEN)
12
+
13
+ #define true 1
14
+ #define false 0
15
+
16
+ typedef struct {
17
+ VALUE error;
18
+ long max_frame_size;
19
+
20
+ VALUE chunk;
21
+ const char *p;
22
+ int cs;
23
+ const char *mark;
24
+ VALUE mark_key;
25
+ VALUE mark_frame;
26
+ long mark_frame_size;
27
+ long mark_content_length;
28
+ } parser_state_t;
29
+
30
+ VALUE mStompParser = Qnil;
31
+ VALUE cFrame = Qnil;
32
+ VALUE eFrameSizeExceeded = Qnil;
33
+ ID g_new;
34
+ ID g_write_command;
35
+ ID g_write_header;
36
+ ID g_write_body;
37
+ ID g_content_length;
38
+ ID g_build_parse_error;
39
+ ID g_max_frame_size;
40
+
41
+ %%{
42
+ machine frame;
43
+
44
+ action mark {
45
+ mark = p;
46
+ }
47
+
48
+ action mark_frame {
49
+ mark_frame = rb_funcall(cFrame, g_new, 2, Qnil, Qnil);
50
+ mark_frame_size = 0;
51
+ }
52
+
53
+ action write_command {
54
+ rb_funcall(mark_frame, g_write_command, 1, MARK_STR_NEW());
55
+ mark = NULL;
56
+ }
57
+
58
+ action mark_key {
59
+ mark_key = MARK_STR_NEW();
60
+ mark = NULL;
61
+ }
62
+
63
+ action write_header {
64
+ rb_funcall(mark_frame, g_write_header, 2, mark_key, MARK_STR_NEW());
65
+ mark_key = Qnil;
66
+ mark = NULL;
67
+ }
68
+
69
+ action finish_headers {
70
+ VALUE length = rb_funcall(mark_frame, g_content_length, 0);
71
+ if ( ! NIL_P(length)) {
72
+ mark_content_length = NUM2LONG(length);
73
+ } else {
74
+ mark_content_length = -1;
75
+ }
76
+ }
77
+
78
+ action write_body {
79
+ rb_funcall(mark_frame, g_write_body, 1, MARK_STR_NEW());
80
+ mark = NULL;
81
+ }
82
+
83
+ action consume_null {
84
+ ((mark_content_length != -1) && (MARK_LEN < mark_content_length))
85
+ }
86
+
87
+ action consume_octet {
88
+ ((mark_content_length == -1) || (MARK_LEN < mark_content_length))
89
+ }
90
+
91
+ action check_frame_size {
92
+ mark_frame_size += 1;
93
+ if (mark_frame_size > max_frame_size) {
94
+ rb_raise(eFrameSizeExceeded, "");
95
+ }
96
+ }
97
+
98
+ action finish_frame {
99
+ rb_yield(mark_frame);
100
+ mark_frame = Qnil;
101
+ }
102
+
103
+ include frame_common "parser_common.rl";
104
+
105
+ write data noprefix;
106
+ }%%
107
+
108
+ static void parser_free(parser_state_t *state) {
109
+ // TODO: free memory inside struct!
110
+ xfree(state);
111
+ }
112
+
113
+ static void parser_mark(parser_state_t *state) {
114
+ rb_gc_mark(state->error);
115
+ rb_gc_mark(state->mark_key);
116
+ rb_gc_mark(state->mark_frame);
117
+ rb_gc_mark(state->chunk);
118
+ }
119
+
120
+ static VALUE parser_alloc(VALUE klass) {
121
+ parser_state_t *state = ALLOC(parser_state_t);
122
+ return Data_Wrap_Struct(klass, parser_mark, parser_free, state);
123
+ }
124
+
125
+ static VALUE parser_initialize(int argc, VALUE *argv, VALUE self) {
126
+ parser_state_t *state;
127
+ Data_Get_Struct(self, parser_state_t, state);
128
+
129
+ VALUE max_frame_size;
130
+ rb_scan_args(argc, argv, "01", &max_frame_size);
131
+
132
+ if (max_frame_size == Qnil) {
133
+ max_frame_size = rb_funcall(mStompParser, g_max_frame_size, 0);
134
+ }
135
+
136
+ state->error = Qnil;
137
+ state->max_frame_size = FIX2LONG(max_frame_size);
138
+ state->chunk = Qnil;
139
+ state->cs = start;
140
+ state->mark = NULL;
141
+ state->mark_key = Qnil;
142
+ state->mark_frame = Qnil;
143
+ state->mark_frame_size = 0;
144
+ state->mark_content_length = 0;
145
+
146
+ return self;
147
+ }
148
+
149
+ static VALUE parser_parse(VALUE self, VALUE new_chunk) {
150
+ parser_state_t *state;
151
+ Data_Get_Struct(self, parser_state_t, state);
152
+
153
+ if (NIL_P(state->error)) {
154
+ VALUE chunk = Qnil;
155
+ const char *p = NULL;
156
+ const char *mark = NULL;
157
+
158
+ if ( ! NIL_P(state->chunk)) {
159
+ long offset = RSTRING_LEN(state->chunk);
160
+ long mark_offset = state->mark - RSTRING_PTR(state->chunk);
161
+
162
+ chunk = rb_str_append(state->chunk, new_chunk);
163
+ p = RSTRING_PTR(chunk) + offset;
164
+ mark = RSTRING_PTR(chunk) + mark_offset;
165
+ } else {
166
+ chunk = new_chunk;
167
+ p = RSTRING_PTR(chunk);
168
+ }
169
+
170
+ const char *pe = RSTRING_END(chunk);
171
+ long max_frame_size = state->max_frame_size;
172
+
173
+ int cs = state->cs;
174
+ VALUE mark_key = state->mark_key;
175
+ VALUE mark_frame = state->mark_frame;
176
+ long mark_frame_size = state->mark_frame_size;
177
+ long mark_content_length = state->mark_content_length;
178
+
179
+ %% write exec;
180
+
181
+ if (mark != NULL) {
182
+ state->chunk = chunk;
183
+ } else {
184
+ state->chunk = Qnil;
185
+ }
186
+
187
+ state->cs = cs;
188
+ state->mark = mark;
189
+ state->mark_key = mark_key;
190
+ state->mark_frame = mark_frame;
191
+ state->mark_frame_size = mark_frame_size;
192
+ state->mark_content_length = mark_content_length;
193
+
194
+ if (cs == error) {
195
+ long index = p - RSTRING_PTR(chunk);
196
+ state->error = rb_funcall(mStompParser, g_build_parse_error, 2, chunk, LONG2NUM(index));
197
+ }
198
+ }
199
+
200
+ if ( ! NIL_P(state->error)) {
201
+ rb_exc_raise(state->error);
202
+ }
203
+
204
+ return Qnil;
205
+ }
206
+
207
+ void Init_c_parser(void) {
208
+ mStompParser = rb_const_get(rb_cObject, rb_intern("StompParser"));
209
+ cFrame = rb_const_get(mStompParser, rb_intern("Frame"));
210
+ eFrameSizeExceeded = rb_const_get(mStompParser, rb_intern("FrameSizeExceeded"));
211
+
212
+ g_new = rb_intern("new");
213
+ g_write_command = rb_intern("write_command");
214
+ g_write_header = rb_intern("write_header");
215
+ g_write_body = rb_intern("write_body");
216
+ g_content_length = rb_intern("content_length");
217
+ g_build_parse_error = rb_intern("build_parse_error");
218
+ g_max_frame_size = rb_intern("max_frame_size");
219
+
220
+ VALUE cParser = rb_define_class_under(mStompParser, "CParser", rb_cObject);
221
+ rb_define_alloc_func(cParser, parser_alloc);
222
+
223
+ rb_define_method(cParser, "initialize", parser_initialize, -1);
224
+ rb_define_method(cParser, "parse", parser_parse, 1);
225
+ }