benofsky-yajl-ruby 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG.md +281 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +320 -0
  5. data/Rakefile +40 -0
  6. data/VERSION.yml +5 -0
  7. data/benchmark/encode.rb +58 -0
  8. data/benchmark/encode_json_and_marshal.rb +42 -0
  9. data/benchmark/encode_json_and_yaml.rb +53 -0
  10. data/benchmark/http.rb +32 -0
  11. data/benchmark/parse.rb +59 -0
  12. data/benchmark/parse_json_and_marshal.rb +50 -0
  13. data/benchmark/parse_json_and_yaml.rb +55 -0
  14. data/benchmark/parse_stream.rb +54 -0
  15. data/benchmark/subjects/item.json +1 -0
  16. data/benchmark/subjects/ohai.json +1216 -0
  17. data/benchmark/subjects/ohai.marshal_dump +0 -0
  18. data/benchmark/subjects/ohai.yml +975 -0
  19. data/benchmark/subjects/twitter_search.json +1 -0
  20. data/benchmark/subjects/twitter_stream.json +430 -0
  21. data/benchmark/subjects/unicode.json +1 -0
  22. data/examples/encoding/chunked_encoding.rb +27 -0
  23. data/examples/encoding/one_shot.rb +13 -0
  24. data/examples/encoding/to_an_io.rb +12 -0
  25. data/examples/http/twitter_search_api.rb +12 -0
  26. data/examples/http/twitter_stream_api.rb +26 -0
  27. data/examples/parsing/from_file.rb +14 -0
  28. data/examples/parsing/from_stdin.rb +9 -0
  29. data/examples/parsing/from_string.rb +13 -0
  30. data/ext/api/yajl_common.h +85 -0
  31. data/ext/api/yajl_gen.h +159 -0
  32. data/ext/api/yajl_parse.h +196 -0
  33. data/ext/extconf.rb +9 -0
  34. data/ext/yajl.c +164 -0
  35. data/ext/yajl_alloc.c +65 -0
  36. data/ext/yajl_alloc.h +50 -0
  37. data/ext/yajl_buf.c +119 -0
  38. data/ext/yajl_buf.h +73 -0
  39. data/ext/yajl_bytestack.h +85 -0
  40. data/ext/yajl_encode.c +188 -0
  41. data/ext/yajl_encode.h +50 -0
  42. data/ext/yajl_ext.c +911 -0
  43. data/ext/yajl_ext.h +128 -0
  44. data/ext/yajl_gen.c +317 -0
  45. data/ext/yajl_lex.c +747 -0
  46. data/ext/yajl_lex.h +135 -0
  47. data/ext/yajl_parser.c +450 -0
  48. data/ext/yajl_parser.h +82 -0
  49. data/lib/yajl/bzip2/stream_reader.rb +32 -0
  50. data/lib/yajl/bzip2/stream_writer.rb +15 -0
  51. data/lib/yajl/bzip2.rb +11 -0
  52. data/lib/yajl/deflate/stream_reader.rb +44 -0
  53. data/lib/yajl/deflate/stream_writer.rb +21 -0
  54. data/lib/yajl/deflate.rb +6 -0
  55. data/lib/yajl/gzip/stream_reader.rb +31 -0
  56. data/lib/yajl/gzip/stream_writer.rb +14 -0
  57. data/lib/yajl/gzip.rb +6 -0
  58. data/lib/yajl/http_stream.rb +197 -0
  59. data/lib/yajl/json_gem/encoding.rb +50 -0
  60. data/lib/yajl/json_gem/parsing.rb +27 -0
  61. data/lib/yajl/json_gem.rb +14 -0
  62. data/lib/yajl.rb +93 -0
  63. data/spec/encoding/encoding_spec.rb +234 -0
  64. data/spec/global/global_spec.rb +55 -0
  65. data/spec/http/fixtures/http.bzip2.dump +0 -0
  66. data/spec/http/fixtures/http.chunked.dump +11 -0
  67. data/spec/http/fixtures/http.deflate.dump +0 -0
  68. data/spec/http/fixtures/http.error.dump +12 -0
  69. data/spec/http/fixtures/http.gzip.dump +0 -0
  70. data/spec/http/fixtures/http.html.dump +1220 -0
  71. data/spec/http/fixtures/http.raw.dump +1226 -0
  72. data/spec/http/http_delete_spec.rb +99 -0
  73. data/spec/http/http_error_spec.rb +33 -0
  74. data/spec/http/http_get_spec.rb +110 -0
  75. data/spec/http/http_post_spec.rb +124 -0
  76. data/spec/http/http_put_spec.rb +106 -0
  77. data/spec/json_gem_compatibility/compatibility_spec.rb +203 -0
  78. data/spec/parsing/active_support_spec.rb +64 -0
  79. data/spec/parsing/chunked_spec.rb +98 -0
  80. data/spec/parsing/fixtures/fail.15.json +1 -0
  81. data/spec/parsing/fixtures/fail.16.json +1 -0
  82. data/spec/parsing/fixtures/fail.17.json +1 -0
  83. data/spec/parsing/fixtures/fail.26.json +1 -0
  84. data/spec/parsing/fixtures/fail11.json +1 -0
  85. data/spec/parsing/fixtures/fail12.json +1 -0
  86. data/spec/parsing/fixtures/fail13.json +1 -0
  87. data/spec/parsing/fixtures/fail14.json +1 -0
  88. data/spec/parsing/fixtures/fail19.json +1 -0
  89. data/spec/parsing/fixtures/fail20.json +1 -0
  90. data/spec/parsing/fixtures/fail21.json +1 -0
  91. data/spec/parsing/fixtures/fail22.json +1 -0
  92. data/spec/parsing/fixtures/fail23.json +1 -0
  93. data/spec/parsing/fixtures/fail24.json +1 -0
  94. data/spec/parsing/fixtures/fail25.json +1 -0
  95. data/spec/parsing/fixtures/fail27.json +2 -0
  96. data/spec/parsing/fixtures/fail28.json +2 -0
  97. data/spec/parsing/fixtures/fail3.json +1 -0
  98. data/spec/parsing/fixtures/fail4.json +1 -0
  99. data/spec/parsing/fixtures/fail5.json +1 -0
  100. data/spec/parsing/fixtures/fail6.json +1 -0
  101. data/spec/parsing/fixtures/fail9.json +1 -0
  102. data/spec/parsing/fixtures/pass.array.json +6 -0
  103. data/spec/parsing/fixtures/pass.codepoints_from_unicode_org.json +1 -0
  104. data/spec/parsing/fixtures/pass.contacts.json +1 -0
  105. data/spec/parsing/fixtures/pass.db100.xml.json +1 -0
  106. data/spec/parsing/fixtures/pass.db1000.xml.json +1 -0
  107. data/spec/parsing/fixtures/pass.dc_simple_with_comments.json +11 -0
  108. data/spec/parsing/fixtures/pass.deep_arrays.json +1 -0
  109. data/spec/parsing/fixtures/pass.difficult_json_c_test_case.json +1 -0
  110. data/spec/parsing/fixtures/pass.difficult_json_c_test_case_with_comments.json +1 -0
  111. data/spec/parsing/fixtures/pass.doubles.json +1 -0
  112. data/spec/parsing/fixtures/pass.empty_array.json +1 -0
  113. data/spec/parsing/fixtures/pass.empty_string.json +1 -0
  114. data/spec/parsing/fixtures/pass.escaped_bulgarian.json +4 -0
  115. data/spec/parsing/fixtures/pass.escaped_foobar.json +1 -0
  116. data/spec/parsing/fixtures/pass.item.json +1 -0
  117. data/spec/parsing/fixtures/pass.json-org-sample1.json +23 -0
  118. data/spec/parsing/fixtures/pass.json-org-sample2.json +11 -0
  119. data/spec/parsing/fixtures/pass.json-org-sample3.json +26 -0
  120. data/spec/parsing/fixtures/pass.json-org-sample4-nows.json +88 -0
  121. data/spec/parsing/fixtures/pass.json-org-sample4.json +89 -0
  122. data/spec/parsing/fixtures/pass.json-org-sample5.json +27 -0
  123. data/spec/parsing/fixtures/pass.map-spain.xml.json +1 -0
  124. data/spec/parsing/fixtures/pass.ns-invoice100.xml.json +1 -0
  125. data/spec/parsing/fixtures/pass.ns-soap.xml.json +1 -0
  126. data/spec/parsing/fixtures/pass.numbers-fp-4k.json +6 -0
  127. data/spec/parsing/fixtures/pass.numbers-fp-64k.json +61 -0
  128. data/spec/parsing/fixtures/pass.numbers-int-4k.json +11 -0
  129. data/spec/parsing/fixtures/pass.numbers-int-64k.json +154 -0
  130. data/spec/parsing/fixtures/pass.twitter-search.json +1 -0
  131. data/spec/parsing/fixtures/pass.twitter-search2.json +1 -0
  132. data/spec/parsing/fixtures/pass.unicode.json +3315 -0
  133. data/spec/parsing/fixtures/pass.yelp.json +1 -0
  134. data/spec/parsing/fixtures/pass1.json +56 -0
  135. data/spec/parsing/fixtures/pass2.json +1 -0
  136. data/spec/parsing/fixtures/pass3.json +6 -0
  137. data/spec/parsing/fixtures_spec.rb +41 -0
  138. data/spec/parsing/one_off_spec.rb +81 -0
  139. data/spec/rcov.opts +3 -0
  140. data/spec/spec.opts +2 -0
  141. data/spec/spec_helper.rb +16 -0
  142. data/yajl-ruby.gemspec +203 -0
  143. metadata +232 -0
data/ext/yajl_ext.c ADDED
@@ -0,0 +1,911 @@
1
+ /*
2
+ * Copyright (c) 2008-2009 Brian Lopez - http://github.com/brianmario
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining
5
+ * a copy of this software and associated documentation files (the
6
+ * "Software"), to deal in the Software without restriction, including
7
+ * without limitation the rights to use, copy, modify, merge, publish,
8
+ * distribute, sublicense, and/or sell copies of the Software, and to
9
+ * permit persons to whom the Software is furnished to do so, subject to
10
+ * the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be
13
+ * included in all copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ */
23
+
24
+ #include "yajl_ext.h"
25
+
26
+ /* Helpers for building objects */
27
+ inline void yajl_check_and_fire_callback(void * ctx) {
28
+ yajl_parser_wrapper * wrapper;
29
+ GetParser((VALUE)ctx, wrapper);
30
+
31
+ /* No need to do any of this if the callback isn't even setup */
32
+ if (wrapper->parse_complete_callback != Qnil) {
33
+ int len = RARRAY_LEN(wrapper->builderStack);
34
+ if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) {
35
+ rb_funcall(wrapper->parse_complete_callback, intern_call, 1, rb_ary_pop(wrapper->builderStack));
36
+ }
37
+ } else {
38
+ int len = RARRAY_LEN(wrapper->builderStack);
39
+ if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) {
40
+ wrapper->objectsFound++;
41
+ if (wrapper->objectsFound > 1) {
42
+ rb_raise(cParseError, "%s", "Found multiple JSON objects in the stream but no block or the on_parse_complete callback was assigned to handle them.");
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ inline void yajl_set_static_value(void * ctx, VALUE val) {
49
+ yajl_parser_wrapper * wrapper;
50
+ VALUE lastEntry, hash;
51
+ int len;
52
+
53
+ GetParser((VALUE)ctx, wrapper);
54
+
55
+ len = RARRAY_LEN(wrapper->builderStack);
56
+ if (len > 0) {
57
+ lastEntry = rb_ary_entry(wrapper->builderStack, len-1);
58
+ switch (TYPE(lastEntry)) {
59
+ case T_ARRAY:
60
+ rb_ary_push(lastEntry, val);
61
+ if (TYPE(val) == T_HASH || TYPE(val) == T_ARRAY) {
62
+ rb_ary_push(wrapper->builderStack, val);
63
+ }
64
+ break;
65
+ case T_HASH:
66
+ rb_hash_aset(lastEntry, val, Qnil);
67
+ rb_ary_push(wrapper->builderStack, val);
68
+ break;
69
+ case T_STRING:
70
+ case T_SYMBOL:
71
+ hash = rb_ary_entry(wrapper->builderStack, len-2);
72
+ if (TYPE(hash) == T_HASH) {
73
+ rb_hash_aset(hash, lastEntry, val);
74
+ rb_ary_pop(wrapper->builderStack);
75
+ if (TYPE(val) == T_HASH || TYPE(val) == T_ARRAY) {
76
+ rb_ary_push(wrapper->builderStack, val);
77
+ }
78
+ }
79
+ break;
80
+ }
81
+ } else {
82
+ rb_ary_push(wrapper->builderStack, val);
83
+ }
84
+ }
85
+
86
+ static void yajl_encoder_wrapper_free(void * wrapper) {
87
+ yajl_encoder_wrapper * w = wrapper;
88
+ if (w) {
89
+ yajl_gen_free(w->encoder);
90
+ free(w);
91
+ }
92
+ }
93
+
94
+ static void yajl_encoder_wrapper_mark(void * wrapper) {
95
+ yajl_encoder_wrapper * w = wrapper;
96
+ if (w) {
97
+ rb_gc_mark(w->on_progress_callback);
98
+ rb_gc_mark(w->terminator);
99
+ }
100
+ }
101
+
102
+ void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) {
103
+ VALUE str, outBuff, otherObj;
104
+ yajl_encoder_wrapper * w = wrapper;
105
+ yajl_gen_status status;
106
+ int idx = 0;
107
+ const unsigned char * buffer;
108
+ const char * cptr;
109
+ unsigned int len;
110
+
111
+ if (io != Qnil || w->on_progress_callback != Qnil) {
112
+ status = yajl_gen_get_buf(w->encoder, &buffer, &len);
113
+ if (len >= WRITE_BUFSIZE) {
114
+ outBuff = rb_str_new((const char *)buffer, len);
115
+ if (io != Qnil) {
116
+ rb_io_write(io, outBuff);
117
+ } else if (w->on_progress_callback != Qnil) {
118
+ rb_funcall(w->on_progress_callback, intern_call, 1, outBuff);
119
+ }
120
+ yajl_gen_clear(w->encoder);
121
+ }
122
+ }
123
+
124
+ switch (TYPE(obj)) {
125
+ case T_HASH:
126
+ status = yajl_gen_map_open(w->encoder);
127
+
128
+ /* TODO: itterate through keys in the hash */
129
+ VALUE keys = rb_funcall(obj, intern_keys, 0);
130
+ VALUE entry, keyStr;
131
+ for(idx=0; idx<RARRAY_LEN(keys); idx++) {
132
+ entry = rb_ary_entry(keys, idx);
133
+ keyStr = rb_funcall(entry, intern_to_s, 0); /* key must be a string */
134
+ /* the key */
135
+ yajl_encode_part(w, keyStr, io);
136
+ /* the value */
137
+ yajl_encode_part(w, rb_hash_aref(obj, entry), io);
138
+ }
139
+
140
+ status = yajl_gen_map_close(w->encoder);
141
+ break;
142
+ case T_ARRAY:
143
+ status = yajl_gen_array_open(w->encoder);
144
+ for(idx=0; idx<RARRAY_LEN(obj); idx++) {
145
+ otherObj = rb_ary_entry(obj, idx);
146
+ yajl_encode_part(w, otherObj, io);
147
+ }
148
+ status = yajl_gen_array_close(w->encoder);
149
+ break;
150
+ case T_NIL:
151
+ status = yajl_gen_null(w->encoder);
152
+ break;
153
+ case T_TRUE:
154
+ status = yajl_gen_bool(w->encoder, 1);
155
+ break;
156
+ case T_FALSE:
157
+ status = yajl_gen_bool(w->encoder, 0);
158
+ break;
159
+ case T_FIXNUM:
160
+ case T_FLOAT:
161
+ case T_BIGNUM:
162
+ str = rb_funcall(obj, intern_to_s, 0);
163
+ cptr = RSTRING_PTR(str);
164
+ len = RSTRING_LEN(str);
165
+ if (memcmp(cptr, "NaN", 3) == 0 || memcmp(cptr, "Infinity", 8) == 0 || memcmp(cptr, "-Infinity", 9) == 0) {
166
+ rb_raise(cEncodeError, "'%s' is an invalid number", cptr);
167
+ }
168
+ status = yajl_gen_number(w->encoder, cptr, len);
169
+ break;
170
+ case T_STRING:
171
+ cptr = RSTRING_PTR(obj);
172
+ len = RSTRING_LEN(obj);
173
+ status = yajl_gen_string(w->encoder, (const unsigned char *)cptr, len);
174
+ break;
175
+ default:
176
+ if (rb_respond_to(obj, intern_to_json)) {
177
+ str = rb_funcall(obj, intern_to_json, 0);
178
+ cptr = RSTRING_PTR(str);
179
+ len = RSTRING_LEN(str);
180
+ status = yajl_gen_number(w->encoder, cptr, len);
181
+ } else {
182
+ str = rb_funcall(obj, intern_to_s, 0);
183
+ cptr = RSTRING_PTR(str);
184
+ len = RSTRING_LEN(str);
185
+ status = yajl_gen_string(w->encoder, (const unsigned char *)cptr, len);
186
+ }
187
+ break;
188
+ }
189
+ }
190
+
191
+ void yajl_parser_wrapper_free(void * wrapper) {
192
+ yajl_parser_wrapper * w = wrapper;
193
+ if (w) {
194
+ yajl_free(w->parser);
195
+ free(w);
196
+ }
197
+ }
198
+
199
+ void yajl_parser_wrapper_mark(void * wrapper) {
200
+ yajl_parser_wrapper * w = wrapper;
201
+ if (w) {
202
+ rb_gc_mark(w->builderStack);
203
+ rb_gc_mark(w->parse_complete_callback);
204
+ }
205
+ }
206
+
207
+ void yajl_parse_chunk(const unsigned char * chunk, unsigned int len, yajl_handle parser) {
208
+ yajl_status stat;
209
+
210
+ stat = yajl_parse(parser, chunk, len);
211
+
212
+ if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) {
213
+ unsigned char * str = yajl_get_error(parser, 1, chunk, len);
214
+ rb_raise(cParseError, "%s", (const char *) str);
215
+ yajl_free_error(parser, str);
216
+ }
217
+ }
218
+
219
+ /* YAJL Callbacks */
220
+ static int yajl_found_null(void * ctx) {
221
+ yajl_set_static_value(ctx, Qnil);
222
+ yajl_check_and_fire_callback(ctx);
223
+ return 1;
224
+ }
225
+
226
+ static int yajl_found_boolean(void * ctx, int boolean) {
227
+ yajl_set_static_value(ctx, boolean ? Qtrue : Qfalse);
228
+ yajl_check_and_fire_callback(ctx);
229
+ return 1;
230
+ }
231
+
232
+ static int yajl_found_number(void * ctx, const char * numberVal, unsigned int numberLen) {
233
+ char buf[numberLen+1];
234
+ buf[numberLen] = 0;
235
+ memcpy(buf, numberVal, numberLen);
236
+
237
+ if (memchr(buf, '.', numberLen) ||
238
+ memchr(buf, 'e', numberLen) ||
239
+ memchr(buf, 'E', numberLen)) {
240
+ yajl_set_static_value(ctx, rb_float_new(strtod(buf, NULL)));
241
+ } else {
242
+ yajl_set_static_value(ctx, rb_cstr2inum(buf, 10));
243
+ }
244
+ return 1;
245
+ }
246
+
247
+ static int yajl_found_string(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
248
+ VALUE str = rb_str_new((const char *)stringVal, stringLen);
249
+ #ifdef HAVE_RUBY_ENCODING_H
250
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
251
+ rb_enc_associate(str, utf8Encoding);
252
+ if (default_internal_enc) {
253
+ str = rb_str_export_to_enc(str, default_internal_enc);
254
+ }
255
+ #endif
256
+ yajl_set_static_value(ctx, str);
257
+ yajl_check_and_fire_callback(ctx);
258
+ return 1;
259
+ }
260
+
261
+ static int yajl_found_hash_key(void * ctx, const unsigned char * stringVal, unsigned int stringLen) {
262
+ yajl_parser_wrapper * wrapper;
263
+ GetParser((VALUE)ctx, wrapper);
264
+ VALUE keyStr;
265
+ #ifdef HAVE_RUBY_ENCODING_H
266
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
267
+ #endif
268
+
269
+ if (wrapper->symbolizeKeys) {
270
+ char buf[stringLen+1];
271
+ memcpy(buf, stringVal, stringLen);
272
+ buf[stringLen] = 0;
273
+ yajl_set_static_value(ctx, ID2SYM(rb_intern(buf)));
274
+ } else {
275
+ keyStr = rb_str_new((const char *)stringVal, stringLen);
276
+ #ifdef HAVE_RUBY_ENCODING_H
277
+ rb_enc_associate(keyStr, utf8Encoding);
278
+ if (default_internal_enc) {
279
+ keyStr = rb_str_export_to_enc(keyStr, default_internal_enc);
280
+ }
281
+ #endif
282
+ yajl_set_static_value(ctx, keyStr);
283
+ }
284
+ yajl_check_and_fire_callback(ctx);
285
+ return 1;
286
+ }
287
+
288
+ static int yajl_found_start_hash(void * ctx) {
289
+ yajl_parser_wrapper * wrapper;
290
+ GetParser((VALUE)ctx, wrapper);
291
+ wrapper->nestedHashLevel++;
292
+ yajl_set_static_value(ctx, rb_hash_new());
293
+ return 1;
294
+ }
295
+
296
+ static int yajl_found_end_hash(void * ctx) {
297
+ yajl_parser_wrapper * wrapper;
298
+ GetParser((VALUE)ctx, wrapper);
299
+ wrapper->nestedHashLevel--;
300
+ if (RARRAY_LEN(wrapper->builderStack) > 1) {
301
+ rb_ary_pop(wrapper->builderStack);
302
+ }
303
+ yajl_check_and_fire_callback(ctx);
304
+ return 1;
305
+ }
306
+
307
+ static int yajl_found_start_array(void * ctx) {
308
+ yajl_parser_wrapper * wrapper;
309
+ GetParser((VALUE)ctx, wrapper);
310
+ wrapper->nestedArrayLevel++;
311
+ yajl_set_static_value(ctx, rb_ary_new());
312
+ return 1;
313
+ }
314
+
315
+ static int yajl_found_end_array(void * ctx) {
316
+ yajl_parser_wrapper * wrapper;
317
+ GetParser((VALUE)ctx, wrapper);
318
+ wrapper->nestedArrayLevel--;
319
+ if (RARRAY_LEN(wrapper->builderStack) > 1) {
320
+ rb_ary_pop(wrapper->builderStack);
321
+ }
322
+ yajl_check_and_fire_callback(ctx);
323
+ return 1;
324
+ }
325
+
326
+
327
+ /* Ruby Interface */
328
+
329
+ /*
330
+ * Document-class: Yajl::Parser
331
+ *
332
+ * This class contains methods for parsing JSON directly from an IO object.
333
+ * The only basic requirment currently is that the IO object respond to #read(len) and #eof?
334
+ * The IO is parsed until a complete JSON object has been read and a ruby object will be returned.
335
+ */
336
+
337
+ /*
338
+ * Document-method: new
339
+ *
340
+ * call-seq: new([:symbolize_keys => true, [:allow_comments => false[, :check_utf8 => false]]])
341
+ *
342
+ * :symbolize_keys will turn hash keys into Ruby symbols, defaults to false.
343
+ *
344
+ * :allow_comments will turn on/off the check for comments inside the JSON stream, defaults to true.
345
+ *
346
+ * :check_utf8 will validate UTF8 characters found in the JSON stream, defaults to true.
347
+ */
348
+ static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) {
349
+ yajl_parser_wrapper * wrapper;
350
+ yajl_parser_config cfg;
351
+ VALUE opts, obj;
352
+ int allowComments = 1, checkUTF8 = 1, symbolizeKeys = 0;
353
+
354
+ /* Scan off config vars */
355
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
356
+ Check_Type(opts, T_HASH);
357
+
358
+ if (rb_hash_aref(opts, sym_allow_comments) == Qfalse) {
359
+ allowComments = 0;
360
+ }
361
+ if (rb_hash_aref(opts, sym_check_utf8) == Qfalse) {
362
+ checkUTF8 = 0;
363
+ }
364
+ if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
365
+ symbolizeKeys = 1;
366
+ }
367
+ }
368
+ cfg = (yajl_parser_config){allowComments, checkUTF8};
369
+
370
+ obj = Data_Make_Struct(klass, yajl_parser_wrapper, yajl_parser_wrapper_mark, yajl_parser_wrapper_free, wrapper);
371
+ wrapper->parser = yajl_alloc(&callbacks, &cfg, NULL, (void *)obj);
372
+ wrapper->nestedArrayLevel = 0;
373
+ wrapper->nestedHashLevel = 0;
374
+ wrapper->objectsFound = 0;
375
+ wrapper->symbolizeKeys = symbolizeKeys;
376
+ wrapper->builderStack = rb_ary_new();
377
+ wrapper->parse_complete_callback = Qnil;
378
+ rb_obj_call_init(obj, 0, 0);
379
+ return obj;
380
+ }
381
+
382
+ /*
383
+ * Document-method: initialize
384
+ *
385
+ * call-seq: new([:symbolize_keys => true, [:allow_comments => false[, :check_utf8 => false]]])
386
+ *
387
+ * :symbolize_keys will turn hash keys into Ruby symbols, defaults to false.
388
+ *
389
+ * :allow_comments will turn on/off the check for comments inside the JSON stream, defaults to true.
390
+ *
391
+ * :check_utf8 will validate UTF8 characters found in the JSON stream, defaults to true.
392
+ */
393
+ static VALUE rb_yajl_parser_init(int argc, VALUE * argv, VALUE self) {
394
+ return self;
395
+ }
396
+
397
+ /*
398
+ * Document-method: parse
399
+ *
400
+ * call-seq:
401
+ * parse(input, buffer_size=8092)
402
+ * parse(input, buffer_size=8092) { |obj| ... }
403
+ *
404
+ * +input+ can either be a string or an IO to parse JSON from
405
+ *
406
+ * +buffer_size+ is the size of chunk that will be parsed off the input (if it's an IO) for each loop of the parsing process.
407
+ * 8092 is a good balance between the different types of streams (off disk, off a socket, etc...), but this option
408
+ * is here so the caller can better tune their parsing depending on the type of stream being passed.
409
+ * A larger read buffer will perform better for files off disk, where as a smaller size may be more efficient for
410
+ * reading off of a socket directly.
411
+ *
412
+ * If a block was passed, it's called when an object has been parsed off the stream. This is especially
413
+ * usefull when parsing a stream of multiple JSON objects.
414
+ *
415
+ * NOTE: you can optionally assign the +on_parse_complete+ callback, and it will be called the same way the optional
416
+ * block is for this method.
417
+ */
418
+ static VALUE rb_yajl_parser_parse(int argc, VALUE * argv, VALUE self) {
419
+ yajl_status stat;
420
+ yajl_parser_wrapper * wrapper;
421
+ VALUE rbufsize, input, blk;
422
+ unsigned int len;
423
+ const char * cptr;
424
+
425
+ GetParser(self, wrapper);
426
+
427
+ /* setup our parameters */
428
+ rb_scan_args(argc, argv, "11&", &input, &rbufsize, &blk);
429
+ if (NIL_P(rbufsize)) {
430
+ rbufsize = INT2FIX(READ_BUFSIZE);
431
+ } else {
432
+ Check_Type(rbufsize, T_FIXNUM);
433
+ }
434
+ if (!NIL_P(blk)) {
435
+ rb_yajl_parser_set_complete_cb(self, blk);
436
+ }
437
+
438
+ if (TYPE(input) == T_STRING) {
439
+ cptr = RSTRING_PTR(input);
440
+ len = RSTRING_LEN(input);
441
+ yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser);
442
+ } else if (rb_respond_to(input, intern_io_read)) {
443
+ VALUE parsed = rb_str_new(0, FIX2LONG(rbufsize));
444
+ while (rb_funcall(input, intern_io_read, 2, rbufsize, parsed) != Qnil) {
445
+ cptr = RSTRING_PTR(parsed);
446
+ len = RSTRING_LEN(parsed);
447
+ yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser);
448
+ }
449
+ } else {
450
+ rb_raise(cParseError, "input must be a string or IO");
451
+ }
452
+
453
+ /* parse any remaining buffered data */
454
+ stat = yajl_parse_complete(wrapper->parser);
455
+
456
+ if (wrapper->parse_complete_callback != Qnil) {
457
+ yajl_check_and_fire_callback((void *)self);
458
+ return Qnil;
459
+ }
460
+
461
+ return rb_ary_pop(wrapper->builderStack);
462
+ }
463
+
464
+ /*
465
+ * Document-method: parse_chunk
466
+ *
467
+ * call-seq: parse_chunk(string_chunk)
468
+ *
469
+ * +string_chunk+ can be a partial or full JSON string to push on the parser.
470
+ *
471
+ * This method will throw an exception if the +on_parse_complete+ callback hasn't been assigned yet.
472
+ * The +on_parse_complete+ callback assignment is required so the user can handle objects that have been
473
+ * parsed off the stream as they're found.
474
+ */
475
+ static VALUE rb_yajl_parser_parse_chunk(VALUE self, VALUE chunk) {
476
+ yajl_parser_wrapper * wrapper;
477
+ unsigned int len;
478
+
479
+ GetParser(self, wrapper);
480
+ if (NIL_P(chunk)) {
481
+ rb_raise(cParseError, "Can't parse a nil string.");
482
+ return Qnil;
483
+ }
484
+
485
+ if (wrapper->parse_complete_callback != Qnil) {
486
+ const char * cptr = RSTRING_PTR(chunk);
487
+ len = RSTRING_LEN(chunk);
488
+ yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser);
489
+ } else {
490
+ rb_raise(cParseError, "The on_parse_complete callback isn't setup, parsing useless.");
491
+ }
492
+
493
+ return Qnil;
494
+ }
495
+
496
+ /*
497
+ * Document-method: on_parse_complete=
498
+ *
499
+ * call-seq: on_parse_complete = Proc.new { |obj| ... }
500
+ *
501
+ * This callback setter allows you to pass a Proc/lambda or any other object that responds to #call.
502
+ *
503
+ * It will pass a single parameter, the ruby object built from the last parsed JSON object
504
+ */
505
+ static VALUE rb_yajl_parser_set_complete_cb(VALUE self, VALUE callback) {
506
+ yajl_parser_wrapper * wrapper;
507
+ GetParser(self, wrapper);
508
+ wrapper->parse_complete_callback = callback;
509
+ return Qnil;
510
+ }
511
+
512
+ /*
513
+ * Document-class: Yajl::Encoder
514
+ *
515
+ * This class contains methods for encoding a Ruby object into JSON, streaming it's output into an IO object.
516
+ * The IO object need only respond to #write(str)
517
+ * The JSON stream created is written to the IO in chunks, as it's being created.
518
+ */
519
+
520
+ /*
521
+ * Document-method: new
522
+ *
523
+ * call-seq: initialize([:pretty => false[, :indent => ' '][, :terminator => "\n"]])
524
+ *
525
+ * :pretty will enable/disable beautifying or "pretty priting" the output string.
526
+ *
527
+ * :indent is the character(s) used to indent the output string.
528
+ *
529
+ * :terminator allows you to specify a character to be used as the termination character after a full JSON string has been generated by
530
+ * the encoder. This would be especially useful when encoding in chunks (via a block or callback during the encode process), to be able to
531
+ * determine when the last chunk of the current encode is sent.
532
+ * If you specify this option to be nil, it will be ignored if encoding directly to an IO or simply returning a string. But if a block is used,
533
+ * the encoder will still pass it - I hope that makes sense ;).
534
+ */
535
+ static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) {
536
+ yajl_encoder_wrapper * wrapper;
537
+ yajl_gen_config cfg;
538
+ VALUE opts, obj, indent;
539
+ const char * indentString = " ";
540
+ int beautify = 0;
541
+
542
+ /* Scan off config vars */
543
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
544
+ Check_Type(opts, T_HASH);
545
+
546
+ if (rb_hash_aref(opts, sym_pretty) == Qtrue) {
547
+ beautify = 1;
548
+ indent = rb_hash_aref(opts, sym_indent);
549
+ if (indent != Qnil) {
550
+ #ifdef HAVE_RUBY_ENCODING_H
551
+ indent = rb_str_export_to_enc(indent, utf8Encoding);
552
+ #endif
553
+ Check_Type(indent, T_STRING);
554
+ indentString = RSTRING_PTR(indent);
555
+ }
556
+ }
557
+ }
558
+ cfg = (yajl_gen_config){beautify, indentString};
559
+
560
+ obj = Data_Make_Struct(klass, yajl_encoder_wrapper, yajl_encoder_wrapper_mark, yajl_encoder_wrapper_free, wrapper);
561
+ wrapper->encoder = yajl_gen_alloc(&cfg, NULL);
562
+ wrapper->on_progress_callback = Qnil;
563
+ if (opts != Qnil && rb_funcall(opts, intern_has_key, 1, sym_terminator) == Qtrue) {
564
+ wrapper->terminator = rb_hash_aref(opts, sym_terminator);
565
+ #ifdef HAVE_RUBY_ENCODING_H
566
+ if (TYPE(wrapper->terminator) == T_STRING) {
567
+ wrapper->terminator = rb_str_export_to_enc(wrapper->terminator, utf8Encoding);
568
+ }
569
+ #endif
570
+ } else {
571
+ wrapper->terminator = 0;
572
+ }
573
+ rb_obj_call_init(obj, 0, 0);
574
+ return obj;
575
+ }
576
+
577
+ /*
578
+ * Document-method: initialize
579
+ *
580
+ * call-seq: initialize([:pretty => false[, :indent => ' '][, :terminator => "\n"]])
581
+ *
582
+ * :pretty will enable/disable beautifying or "pretty priting" the output string.
583
+ *
584
+ * :indent is the character(s) used to indent the output string.
585
+ *
586
+ * :terminator allows you to specify a character to be used as the termination character after a full JSON string has been generated by
587
+ * the encoder. This would be especially useful when encoding in chunks (via a block or callback during the encode process), to be able to
588
+ * determine when the last chunk of the current encode is sent.
589
+ * If you specify this option to be nil, it will be ignored if encoding directly to an IO or simply returning a string. But if a block is used,
590
+ * the encoder will still pass it - I hope that makes sense ;).
591
+ */
592
+ static VALUE rb_yajl_encoder_init(int argc, VALUE * argv, VALUE self) {
593
+ return self;
594
+ }
595
+
596
+ /*
597
+ * Document-method: encode
598
+ *
599
+ * call-seq: encode(obj[, io[, &block]])
600
+ *
601
+ * +obj+ is the Ruby object to encode to JSON
602
+ *
603
+ * +io+ is an optional IO used to stream the encoded JSON string to.
604
+ * If +io+ isn't specified, this method will return the resulting JSON string. If +io+ is specified, this method returns nil
605
+ *
606
+ * If an optional block is passed, it's called when encoding is complete and passed the resulting JSON string
607
+ *
608
+ * It should be noted that you can reuse an instance of this class to continue encoding multiple JSON
609
+ * to the same stream. Just continue calling this method, passing it the same IO object with new/different
610
+ * ruby objects to encode. This is how streaming is accomplished.
611
+ */
612
+ static VALUE rb_yajl_encoder_encode(int argc, VALUE * argv, VALUE self) {
613
+ yajl_encoder_wrapper * wrapper;
614
+ const unsigned char * buffer;
615
+ unsigned int len;
616
+ VALUE obj, io, blk, outBuff;
617
+
618
+ GetEncoder(self, wrapper);
619
+
620
+ rb_scan_args(argc, argv, "11&", &obj, &io, &blk);
621
+
622
+ if (blk != Qnil) {
623
+ wrapper->on_progress_callback = blk;
624
+ }
625
+
626
+ /* begin encode process */
627
+ yajl_encode_part(wrapper, obj, io);
628
+
629
+ /* just make sure we output the remaining buffer */
630
+ yajl_gen_get_buf(wrapper->encoder, &buffer, &len);
631
+ outBuff = rb_str_new((const char *)buffer, len);
632
+ #ifdef HAVE_RUBY_ENCODING_H
633
+ rb_enc_associate(outBuff, utf8Encoding);
634
+ #endif
635
+ yajl_gen_clear(wrapper->encoder);
636
+
637
+ if (io != Qnil) {
638
+ rb_io_write(io, outBuff);
639
+ if (wrapper->terminator != 0 && wrapper->terminator != Qnil) {
640
+ rb_io_write(io, wrapper->terminator);
641
+ }
642
+ return Qnil;
643
+ } else if (blk != Qnil) {
644
+ rb_funcall(blk, intern_call, 1, outBuff);
645
+ if (wrapper->terminator != 0) {
646
+ rb_funcall(blk, intern_call, 1, wrapper->terminator);
647
+ }
648
+ return Qnil;
649
+ } else {
650
+ if (wrapper->terminator != 0 && wrapper->terminator != Qnil) {
651
+ rb_str_concat(outBuff, wrapper->terminator);
652
+ }
653
+ return outBuff;
654
+ }
655
+ return Qnil;
656
+ }
657
+
658
+ /*
659
+ * Document-method: on_progress
660
+ *
661
+ * call-seq: on_progress = Proc.new {|str| ...}
662
+ *
663
+ * This callback setter allows you to pass a Proc/lambda or any other object that responds to #call.
664
+ *
665
+ * It will pass the caller a chunk of the encode buffer after it's reached it's internal max buffer size (defaults to 8kb).
666
+ * For example, encoding a large object that would normally result in 24288 bytes of data will result in 3 calls to this callback (assuming the 8kb default encode buffer).
667
+ */
668
+ static VALUE rb_yajl_encoder_set_progress_cb(VALUE self, VALUE callback) {
669
+ yajl_encoder_wrapper * wrapper;
670
+ GetEncoder(self, wrapper);
671
+ wrapper->on_progress_callback = callback;
672
+ return Qnil;
673
+ }
674
+
675
+
676
+ /* JSON Gem compatibility */
677
+
678
+ /*
679
+ * Document-class: Hash
680
+ */
681
+ /*
682
+ * Document-method: to_json
683
+ *
684
+ * call-seq: to_json(encoder=Yajl::Encoder.new)
685
+ *
686
+ * +encoder+ is an existing Yajl::Encoder used to encode JSON
687
+ *
688
+ * Encodes an instance of Hash to JSON
689
+ */
690
+ static VALUE rb_yajl_json_ext_hash_to_json(int argc, VALUE * argv, VALUE self) {
691
+ VALUE rb_encoder;
692
+ rb_scan_args(argc, argv, "01", &rb_encoder);
693
+ if (rb_encoder == Qnil) {
694
+ rb_encoder = rb_yajl_encoder_new(0, NULL, cEncoder);
695
+ }
696
+ return rb_yajl_encoder_encode(1, &self, rb_encoder);
697
+ }
698
+
699
+ /*
700
+ * Document-class: Array
701
+ */
702
+ /*
703
+ * Document-method: to_json
704
+ *
705
+ * call-seq: to_json(encoder=Yajl::Encoder.new)
706
+ *
707
+ * +encoder+ is an existing Yajl::Encoder used to encode JSON
708
+ *
709
+ * Encodes an instance of Array to JSON
710
+ */
711
+ static VALUE rb_yajl_json_ext_array_to_json(int argc, VALUE * argv, VALUE self) {
712
+ VALUE rb_encoder;
713
+ rb_scan_args(argc, argv, "01", &rb_encoder);
714
+ if (rb_encoder == Qnil) {
715
+ rb_encoder = rb_yajl_encoder_new(0, NULL, cEncoder);
716
+ }
717
+ return rb_yajl_encoder_encode(1, &self, rb_encoder);
718
+ }
719
+
720
+ /*
721
+ * Document-class: Fixnum
722
+ */
723
+ /*
724
+ * Document-method: to_json
725
+ *
726
+ * call-seq: to_json(encoder=Yajl::Encoder.new)
727
+ *
728
+ * +encoder+ is an existing Yajl::Encoder used to encode JSON
729
+ *
730
+ * Encodes an instance of Fixnum to JSON
731
+ */
732
+ static VALUE rb_yajl_json_ext_fixnum_to_json(int argc, VALUE * argv, VALUE self) {
733
+ VALUE rb_encoder;
734
+ rb_scan_args(argc, argv, "01", &rb_encoder);
735
+ if (rb_encoder == Qnil) {
736
+ rb_encoder = rb_yajl_encoder_new(0, NULL, cEncoder);
737
+ }
738
+ return rb_yajl_encoder_encode(1, &self, rb_encoder);
739
+ }
740
+
741
+ /*
742
+ * Document-class: Float
743
+ */
744
+ /*
745
+ * Document-method: to_json
746
+ *
747
+ * call-seq: to_json(encoder=Yajl::Encoder.new)
748
+ *
749
+ * +encoder+ is an existing Yajl::Encoder used to encode JSON
750
+ *
751
+ * Encodes an instance of Float to JSON
752
+ */
753
+ static VALUE rb_yajl_json_ext_float_to_json(int argc, VALUE * argv, VALUE self) {
754
+ VALUE rb_encoder;
755
+ rb_scan_args(argc, argv, "01", &rb_encoder);
756
+ if (rb_encoder == Qnil) {
757
+ rb_encoder = rb_yajl_encoder_new(0, NULL, cEncoder);
758
+ }
759
+ return rb_yajl_encoder_encode(1, &self, rb_encoder);
760
+ }
761
+
762
+ /*
763
+ * Document-class: String
764
+ */
765
+ /*
766
+ * Document-method: to_json
767
+ *
768
+ * call-seq: to_json(encoder=Yajl::Encoder.new)
769
+ *
770
+ * +encoder+ is an existing Yajl::Encoder used to encode JSON
771
+ *
772
+ * Encodes an instance of TrueClass to JSON
773
+ */
774
+ static VALUE rb_yajl_json_ext_string_to_json(int argc, VALUE * argv, VALUE self) {
775
+ VALUE rb_encoder;
776
+ rb_scan_args(argc, argv, "01", &rb_encoder);
777
+ if (rb_encoder == Qnil) {
778
+ rb_encoder = rb_yajl_encoder_new(0, NULL, cEncoder);
779
+ }
780
+ return rb_yajl_encoder_encode(1, &self, rb_encoder);
781
+ }
782
+
783
+ /*
784
+ * Document-class: TrueClass
785
+ */
786
+ /*
787
+ * Document-method: to_json
788
+ *
789
+ * call-seq: to_json(encoder=Yajl::Encoder.new)
790
+ *
791
+ * +encoder+ is an existing Yajl::Encoder used to encode JSON
792
+ *
793
+ * Encodes an instance of TrueClass to JSON
794
+ */
795
+ static VALUE rb_yajl_json_ext_true_to_json(int argc, VALUE * argv, VALUE self) {
796
+ VALUE rb_encoder;
797
+ rb_scan_args(argc, argv, "01", &rb_encoder);
798
+ if (rb_encoder == Qnil) {
799
+ rb_encoder = rb_yajl_encoder_new(0, NULL, cEncoder);
800
+ }
801
+ return rb_yajl_encoder_encode(1, &self, rb_encoder);
802
+ }
803
+
804
+ /*
805
+ * Document-class: FalseClass
806
+ */
807
+ /*
808
+ * Document-method: to_json
809
+ *
810
+ * call-seq: to_json(encoder=Yajl::Encoder.new)
811
+ *
812
+ * +encoder+ is an existing Yajl::Encoder used to encode JSON
813
+ *
814
+ * Encodes an instance of FalseClass to JSON
815
+ */
816
+ static VALUE rb_yajl_json_ext_false_to_json(int argc, VALUE * argv, VALUE self) {
817
+ VALUE rb_encoder;
818
+ rb_scan_args(argc, argv, "01", &rb_encoder);
819
+ if (rb_encoder == Qnil) {
820
+ rb_encoder = rb_yajl_encoder_new(0, NULL, cEncoder);
821
+ }
822
+ return rb_yajl_encoder_encode(1, &self, rb_encoder);
823
+ }
824
+
825
+ /*
826
+ * Document-class: NilClass
827
+ */
828
+ /*
829
+ * Document-method: to_json
830
+ *
831
+ * call-seq: to_json(encoder=Yajl::Encoder.new)
832
+ *
833
+ * +encoder+ is an existing Yajl::Encoder used to encode JSON
834
+ *
835
+ * Encodes an instance of NilClass to JSON
836
+ */
837
+ static VALUE rb_yajl_json_ext_nil_to_json(int argc, VALUE * argv, VALUE self) {
838
+ VALUE rb_encoder;
839
+ rb_scan_args(argc, argv, "01", &rb_encoder);
840
+ if (rb_encoder == Qnil) {
841
+ rb_encoder = rb_yajl_encoder_new(0, NULL, cEncoder);
842
+ }
843
+ return rb_yajl_encoder_encode(1, &self, rb_encoder);
844
+ }
845
+
846
+ /*
847
+ * Document-class: Yajl::Encoder
848
+ */
849
+ /*
850
+ * Document-method: enable_json_gem_compatability
851
+ *
852
+ * call-seq: enable_json_gem_compatability
853
+ *
854
+ * Enables the JSON gem compatibility API
855
+ */
856
+ static VALUE rb_yajl_encoder_enable_json_gem_ext(VALUE klass) {
857
+ rb_define_method(rb_cHash, "to_json", rb_yajl_json_ext_hash_to_json, -1);
858
+ rb_define_method(rb_cArray, "to_json", rb_yajl_json_ext_array_to_json, -1);
859
+ rb_define_method(rb_cFixnum, "to_json", rb_yajl_json_ext_fixnum_to_json, -1);
860
+ rb_define_method(rb_cFloat, "to_json", rb_yajl_json_ext_float_to_json, -1);
861
+ rb_define_method(rb_cString, "to_json", rb_yajl_json_ext_string_to_json, -1);
862
+ rb_define_method(rb_cTrueClass, "to_json", rb_yajl_json_ext_true_to_json, -1);
863
+ rb_define_method(rb_cFalseClass, "to_json", rb_yajl_json_ext_false_to_json, -1);
864
+ rb_define_method(rb_cNilClass, "to_json", rb_yajl_json_ext_nil_to_json, -1);
865
+ return Qnil;
866
+ }
867
+
868
+
869
+ /* Ruby Extension initializer */
870
+ void Init_yajl_ext() {
871
+ mYajl = rb_define_module("Yajl");
872
+
873
+ cParseError = rb_define_class_under(mYajl, "ParseError", rb_eStandardError);
874
+ cEncodeError = rb_define_class_under(mYajl, "EncodeError", rb_eStandardError);
875
+
876
+ cParser = rb_define_class_under(mYajl, "Parser", rb_cObject);
877
+ rb_define_singleton_method(cParser, "new", rb_yajl_parser_new, -1);
878
+ rb_define_method(cParser, "initialize", rb_yajl_parser_init, -1);
879
+ rb_define_method(cParser, "parse", rb_yajl_parser_parse, -1);
880
+ rb_define_method(cParser, "parse_chunk", rb_yajl_parser_parse_chunk, -1);
881
+ rb_define_method(cParser, "<<", rb_yajl_parser_parse_chunk, 1);
882
+ rb_define_method(cParser, "on_parse_complete=", rb_yajl_parser_set_complete_cb, 1);
883
+
884
+ cEncoder = rb_define_class_under(mYajl, "Encoder", rb_cObject);
885
+ rb_define_singleton_method(cEncoder, "new", rb_yajl_encoder_new, -1);
886
+ rb_define_method(cEncoder, "initialize", rb_yajl_encoder_init, -1);
887
+ rb_define_method(cEncoder, "encode", rb_yajl_encoder_encode, -1);
888
+ rb_define_method(cEncoder, "on_progress=", rb_yajl_encoder_set_progress_cb, 1);
889
+
890
+ rb_define_singleton_method(cEncoder, "enable_json_gem_compatability", rb_yajl_encoder_enable_json_gem_ext, 0);
891
+
892
+ intern_io_read = rb_intern("read");
893
+ intern_call = rb_intern("call");
894
+ intern_keys = rb_intern("keys");
895
+ intern_to_s = rb_intern("to_s");
896
+ intern_to_json = rb_intern("to_json");
897
+ intern_to_sym = rb_intern("to_sym");
898
+ intern_has_key = rb_intern("has_key?");
899
+ intern_as_json = rb_intern("as_json");
900
+
901
+ sym_allow_comments = ID2SYM(rb_intern("allow_comments"));
902
+ sym_check_utf8 = ID2SYM(rb_intern("check_utf8"));
903
+ sym_pretty = ID2SYM(rb_intern("pretty"));
904
+ sym_indent = ID2SYM(rb_intern("indent"));
905
+ sym_terminator = ID2SYM(rb_intern("terminator"));
906
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
907
+
908
+ #ifdef HAVE_RUBY_ENCODING_H
909
+ utf8Encoding = rb_utf8_encoding();
910
+ #endif
911
+ }