oj 2.0.0 → 3.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.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +17 -23
  3. data/README.md +74 -425
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +4 -0
  6. data/ext/oj/circarray.c +68 -0
  7. data/ext/oj/circarray.h +23 -0
  8. data/ext/oj/code.c +227 -0
  9. data/ext/oj/code.h +40 -0
  10. data/ext/oj/compat.c +243 -0
  11. data/ext/oj/custom.c +1097 -0
  12. data/ext/oj/dump.c +766 -1534
  13. data/ext/oj/dump.h +92 -0
  14. data/ext/oj/dump_compat.c +937 -0
  15. data/ext/oj/dump_leaf.c +254 -0
  16. data/ext/oj/dump_object.c +810 -0
  17. data/ext/oj/dump_rails.c +329 -0
  18. data/ext/oj/dump_strict.c +416 -0
  19. data/ext/oj/encode.h +51 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +17 -7
  23. data/ext/oj/fast.c +213 -180
  24. data/ext/oj/hash.c +163 -0
  25. data/ext/oj/hash.h +46 -0
  26. data/ext/oj/hash_test.c +512 -0
  27. data/ext/oj/mimic_json.c +817 -0
  28. data/ext/oj/mimic_rails.c +806 -0
  29. data/ext/oj/mimic_rails.h +17 -0
  30. data/ext/oj/object.c +752 -0
  31. data/ext/oj/odd.c +230 -0
  32. data/ext/oj/odd.h +44 -0
  33. data/ext/oj/oj.c +1288 -929
  34. data/ext/oj/oj.h +240 -69
  35. data/ext/oj/parse.c +1014 -0
  36. data/ext/oj/parse.h +92 -0
  37. data/ext/oj/reader.c +223 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +127 -0
  40. data/ext/oj/{cache.h → resolve.h} +6 -13
  41. data/ext/oj/rxclass.c +133 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +77 -175
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +911 -0
  46. data/ext/oj/stream_writer.c +301 -0
  47. data/ext/oj/strict.c +162 -0
  48. data/ext/oj/string_writer.c +480 -0
  49. data/ext/oj/val_stack.c +98 -0
  50. data/ext/oj/val_stack.h +188 -0
  51. data/lib/oj/active_support_helper.rb +41 -0
  52. data/lib/oj/bag.rb +6 -10
  53. data/lib/oj/easy_hash.rb +52 -0
  54. data/lib/oj/json.rb +172 -0
  55. data/lib/oj/mimic.rb +260 -5
  56. data/lib/oj/saj.rb +13 -10
  57. data/lib/oj/schandler.rb +142 -0
  58. data/lib/oj/state.rb +131 -0
  59. data/lib/oj/version.rb +1 -1
  60. data/lib/oj.rb +11 -23
  61. data/pages/Advanced.md +22 -0
  62. data/pages/Compatibility.md +25 -0
  63. data/pages/Custom.md +23 -0
  64. data/pages/Encoding.md +65 -0
  65. data/pages/JsonGem.md +79 -0
  66. data/pages/Modes.md +140 -0
  67. data/pages/Options.md +250 -0
  68. data/pages/Rails.md +60 -0
  69. data/pages/Security.md +20 -0
  70. data/test/_test_active.rb +76 -0
  71. data/test/_test_active_mimic.rb +96 -0
  72. data/test/_test_mimic_rails.rb +126 -0
  73. data/test/activesupport4/decoding_test.rb +105 -0
  74. data/test/activesupport4/encoding_test.rb +531 -0
  75. data/test/activesupport4/test_helper.rb +41 -0
  76. data/test/activesupport5/decoding_test.rb +125 -0
  77. data/test/activesupport5/encoding_test.rb +483 -0
  78. data/test/activesupport5/encoding_test_cases.rb +90 -0
  79. data/test/activesupport5/test_helper.rb +50 -0
  80. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  81. data/test/helper.rb +27 -0
  82. data/test/isolated/shared.rb +310 -0
  83. data/test/isolated/test_mimic_after.rb +13 -0
  84. data/test/isolated/test_mimic_alone.rb +12 -0
  85. data/test/isolated/test_mimic_as_json.rb +45 -0
  86. data/test/isolated/test_mimic_before.rb +13 -0
  87. data/test/isolated/test_mimic_define.rb +28 -0
  88. data/test/isolated/test_mimic_rails_after.rb +22 -0
  89. data/test/isolated/test_mimic_rails_before.rb +21 -0
  90. data/test/isolated/test_mimic_redefine.rb +15 -0
  91. data/test/json_gem/json_addition_test.rb +216 -0
  92. data/test/json_gem/json_common_interface_test.rb +143 -0
  93. data/test/json_gem/json_encoding_test.rb +109 -0
  94. data/test/json_gem/json_ext_parser_test.rb +20 -0
  95. data/test/json_gem/json_fixtures_test.rb +35 -0
  96. data/test/json_gem/json_generator_test.rb +383 -0
  97. data/test/json_gem/json_generic_object_test.rb +90 -0
  98. data/test/json_gem/json_parser_test.rb +470 -0
  99. data/test/json_gem/json_string_matching_test.rb +42 -0
  100. data/test/json_gem/test_helper.rb +18 -0
  101. data/test/perf_compat.rb +130 -0
  102. data/test/perf_fast.rb +9 -9
  103. data/test/perf_file.rb +64 -0
  104. data/test/{perf_obj.rb → perf_object.rb} +24 -10
  105. data/test/perf_scp.rb +151 -0
  106. data/test/perf_strict.rb +32 -113
  107. data/test/sample.rb +2 -3
  108. data/test/test_compat.rb +474 -0
  109. data/test/test_custom.rb +355 -0
  110. data/test/test_debian.rb +53 -0
  111. data/test/test_fast.rb +66 -16
  112. data/test/test_file.rb +237 -0
  113. data/test/test_gc.rb +49 -0
  114. data/test/test_hash.rb +29 -0
  115. data/test/test_null.rb +376 -0
  116. data/test/test_object.rb +1010 -0
  117. data/test/test_saj.rb +16 -16
  118. data/test/test_scp.rb +417 -0
  119. data/test/test_strict.rb +410 -0
  120. data/test/test_various.rb +815 -0
  121. data/test/test_writer.rb +308 -0
  122. data/test/tests.rb +9 -902
  123. data/test/tests_mimic.rb +14 -0
  124. data/test/tests_mimic_addition.rb +7 -0
  125. metadata +253 -38
  126. data/ext/oj/cache.c +0 -148
  127. data/ext/oj/foo.rb +0 -6
  128. data/ext/oj/load.c +0 -1049
  129. data/test/a.rb +0 -38
  130. data/test/perf1.rb +0 -64
  131. data/test/perf2.rb +0 -76
  132. data/test/perf_obj_old.rb +0 -213
  133. data/test/test_mimic.rb +0 -208
data/ext/oj/oj.c CHANGED
@@ -1,32 +1,6 @@
1
1
  /* oj.c
2
2
  * Copyright (c) 2012, Peter Ohler
3
- *
4
3
  * All rights reserved.
5
- *
6
- * Redistribution and use in source and binary forms, with or without
7
- * modification, are permitted provided that the following conditions are met:
8
- *
9
- * - Redistributions of source code must retain the above copyright notice, this
10
- * list of conditions and the following disclaimer.
11
- *
12
- * - Redistributions in binary form must reproduce the above copyright notice,
13
- * this list of conditions and the following disclaimer in the documentation
14
- * and/or other materials provided with the distribution.
15
- *
16
- * - Neither the name of Peter Ohler nor the names of its contributors may be
17
- * used to endorse or promote products derived from this software without
18
- * specific prior written permission.
19
- *
20
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
4
  */
31
5
 
32
6
  #include <stdlib.h>
@@ -35,8 +9,19 @@
35
9
  #include <string.h>
36
10
  #include <sys/types.h>
37
11
  #include <unistd.h>
12
+ #include <fcntl.h>
38
13
 
39
14
  #include "oj.h"
15
+ #include "parse.h"
16
+ #include "hash.h"
17
+ #include "odd.h"
18
+ #include "dump.h"
19
+ #include "mimic_rails.h"
20
+ #include "encode.h"
21
+
22
+ #if !HAS_ENCODING_SUPPORT || defined(RUBINIUS_RUBY)
23
+ #define rb_eEncodingError rb_eException
24
+ #endif
40
25
 
41
26
  typedef struct _YesNoOpt {
42
27
  VALUE sym;
@@ -48,18 +33,35 @@ void Init_oj();
48
33
  VALUE Oj = Qnil;
49
34
 
50
35
  ID oj_add_value_id;
36
+ ID oj_array_append_id;
51
37
  ID oj_array_end_id;
52
38
  ID oj_array_start_id;
53
39
  ID oj_as_json_id;
40
+ ID oj_begin_id;
41
+ ID oj_end_id;
42
+ ID oj_exclude_end_id;
54
43
  ID oj_error_id;
44
+ ID oj_file_id;
55
45
  ID oj_fileno_id;
46
+ ID oj_ftype_id;
47
+ ID oj_has_key_id;
56
48
  ID oj_hash_end_id;
49
+ ID oj_hash_key_id;
50
+ ID oj_hash_set_id;
57
51
  ID oj_hash_start_id;
52
+ ID oj_iconv_id;
58
53
  ID oj_instance_variables_id;
59
54
  ID oj_json_create_id;
55
+ ID oj_length_id;
60
56
  ID oj_new_id;
57
+ ID oj_parse_id;
58
+ ID oj_pos_id;
61
59
  ID oj_read_id;
60
+ ID oj_readpartial_id;
61
+ ID oj_replace_id;
62
+ ID oj_stat_id;
62
63
  ID oj_string_id;
64
+ ID oj_to_h_id;
63
65
  ID oj_to_hash_id;
64
66
  ID oj_to_json_id;
65
67
  ID oj_to_s_id;
@@ -68,480 +70,978 @@ ID oj_to_time_id;
68
70
  ID oj_tv_nsec_id;
69
71
  ID oj_tv_sec_id;
70
72
  ID oj_tv_usec_id;
73
+ ID oj_utc_id;
71
74
  ID oj_utc_offset_id;
75
+ ID oj_utcq_id;
72
76
  ID oj_write_id;
73
77
 
78
+
74
79
  VALUE oj_bag_class;
75
- VALUE oj_parse_error_class;
76
80
  VALUE oj_bigdecimal_class;
81
+ VALUE oj_cstack_class;
82
+ VALUE oj_date_class;
83
+ VALUE oj_datetime_class;
84
+ VALUE oj_enumerable_class;
85
+ VALUE oj_parse_error_class;
86
+ VALUE oj_stream_writer_class;
87
+ VALUE oj_string_writer_class;
77
88
  VALUE oj_stringio_class;
78
89
  VALUE oj_struct_class;
79
- VALUE oj_time_class;
80
90
 
81
91
  VALUE oj_slash_string;
82
92
 
83
- static VALUE ascii_only_sym;
93
+ VALUE oj_allow_nan_sym;
94
+ VALUE oj_array_class_sym;
95
+ VALUE oj_create_additions_sym;
96
+ VALUE oj_hash_class_sym;
97
+ VALUE oj_indent_sym;
98
+ VALUE oj_object_class_sym;
99
+ VALUE oj_quirks_mode_sym;
100
+
101
+ static VALUE allow_blank_sym;
102
+ static VALUE allow_gc_sym;
103
+ static VALUE allow_invalid_unicode_sym;
104
+ static VALUE ascii_sym;
84
105
  static VALUE auto_define_sym;
106
+ static VALUE auto_sym;
107
+ static VALUE bigdecimal_as_decimal_sym;
108
+ static VALUE bigdecimal_load_sym;
109
+ static VALUE bigdecimal_sym;
85
110
  static VALUE circular_sym;
111
+ static VALUE class_cache_sym;
86
112
  static VALUE compat_sym;
87
113
  static VALUE create_id_sym;
88
- static VALUE indent_sym;
89
- static VALUE max_stack_sym;
114
+ static VALUE custom_sym;
115
+ static VALUE empty_string_sym;
116
+ static VALUE escape_mode_sym;
117
+ static VALUE float_prec_sym;
118
+ static VALUE float_sym;
119
+ static VALUE huge_sym;
120
+ static VALUE json_sym;
121
+ static VALUE match_string_sym;
90
122
  static VALUE mode_sym;
123
+ static VALUE nan_sym;
124
+ static VALUE newline_sym;
125
+ static VALUE nilnil_sym;
91
126
  static VALUE null_sym;
92
127
  static VALUE object_sym;
128
+ static VALUE omit_nil_sym;
129
+ static VALUE rails_sym;
130
+ static VALUE raise_sym;
93
131
  static VALUE ruby_sym;
132
+ static VALUE sec_prec_sym;
94
133
  static VALUE strict_sym;
95
134
  static VALUE symbol_keys_sym;
96
135
  static VALUE time_format_sym;
136
+ static VALUE unicode_xss_sym;
97
137
  static VALUE unix_sym;
138
+ static VALUE unix_zone_sym;
139
+ static VALUE use_as_json_sym;
140
+ static VALUE use_to_hash_sym;
141
+ static VALUE use_to_json_sym;
142
+ static VALUE word_sym;
98
143
  static VALUE xmlschema_sym;
99
-
100
- static VALUE array_nl_sym;
101
- static VALUE create_additions_sym;
102
- static VALUE object_nl_sym;
103
- static VALUE space_before_sym;
104
- static VALUE space_sym;
105
- static VALUE symbolize_names_sym;
106
-
107
- static VALUE mimic = Qnil;
108
-
109
- Cache oj_class_cache = 0;
110
- Cache oj_attr_cache = 0;
144
+ static VALUE xss_safe_sym;
111
145
 
112
146
  #if HAS_ENCODING_SUPPORT
113
147
  rb_encoding *oj_utf8_encoding = 0;
148
+ #else
149
+ VALUE oj_utf8_encoding = Qnil;
114
150
  #endif
115
151
 
116
- #if SAFE_CACHE
117
- pthread_mutex_t oj_cache_mutex; // only used if SAFE_CACHE defined
152
+ #if USE_PTHREAD_MUTEX
153
+ pthread_mutex_t oj_cache_mutex;
154
+ #elif USE_RB_MUTEX
155
+ VALUE oj_cache_mutex = Qnil;
118
156
  #endif
157
+
119
158
  static const char json_class[] = "json_class";
120
159
 
121
160
  struct _Options oj_default_options = {
122
- 0, // indent
123
- No, // circular
124
- Yes, // auto_define
125
- No, // sym_key
126
- No, // ascii_only
127
- ObjectMode, // mode
128
- UnixTime, // time_format
129
- json_class, // create_id
130
- 65536, // max_stack
131
- 0, // dump_opts
132
- };
133
-
134
- static VALUE define_mimic_json(int argc, VALUE *argv, VALUE self);
135
- static struct _Odd odds[5]; // bump up if new Odd classes are added
136
-
137
- Odd
138
- oj_get_odd(VALUE clas) {
139
- Odd odd = odds;
140
-
141
- for (; Qundef != odd->clas; odd++) {
142
- if (clas == odd->clas) {
143
- return odd;
144
- }
161
+ 0, // indent
162
+ No, // circular
163
+ No, // auto_define
164
+ No, // sym_key
165
+ JSONEsc, // escape_mode
166
+ ObjectMode, // mode
167
+ Yes, // class_cache
168
+ UnixTime, // time_format
169
+ Yes, // bigdec_as_num
170
+ AutoDec, // bigdec_load
171
+ No, // to_hash
172
+ No, // to_json
173
+ No, // as_json
174
+ No, // nilnil
175
+ Yes, // empty_string
176
+ Yes, // allow_gc
177
+ Yes, // quirks_mode
178
+ No, // allow_invalid
179
+ No, // create_ok
180
+ Yes, // allow_nan
181
+ json_class, // create_id
182
+ 10, // create_id_len
183
+ 9, // sec_prec
184
+ 16, // float_prec
185
+ "%0.15g", // float_fmt
186
+ Qnil, // hash_class
187
+ Qnil, // array_class
188
+ { // dump_opts
189
+ false, //use
190
+ "", // indent
191
+ "", // before_sep
192
+ "", // after_sep
193
+ "", // hash_nl
194
+ "", // array_nl
195
+ 0, // indent_size
196
+ 0, // before_size
197
+ 0, // after_size
198
+ 0, // hash_size
199
+ 0, // array_size
200
+ AutoNan,// nan_dump
201
+ false, // omit_nil
202
+ MAX_DEPTH, // max_depth
203
+ },
204
+ { // str_rx
205
+ NULL, // head
206
+ NULL, // tail
207
+ { '\0' }, // err
145
208
  }
146
- return 0;
147
- }
209
+ };
148
210
 
149
- /* call-seq: default_options() => Hash
211
+ /* Document-method: default_options()
212
+ * call-seq: default_options()
150
213
  *
151
214
  * Returns the default load and dump options as a Hash. The options are
152
- * - indent: [Fixnum] number of spaces to indent each element in an JSON document
153
- * - circular: [true|false|nil] support circular references while dumping
154
- * - auto_define: [true|false|nil] automatically define classes if they do not exist
155
- * - symbol_keys: [true|false|nil] use symbols instead of strings for hash keys
156
- * - mode: [:object|:strict|:compat|:null] load and dump modes to use for JSON
157
- * - time_format: [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
158
- * - create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
159
- * - max_stack: [Fixnum|nil] maximum json size to allocate on the stack, default is 65536
160
- * @return [Hash] all current option settings.
215
+ * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element in an JSON document, zero or nil is no newline between JSON elements, negative indicates no newline between top level JSON elements in a stream, a String indicates the string should be used for indentation
216
+ * - *:circular* [_Boolean_|_nil_] support circular references while dumping
217
+ * - *:auto_define* [_Boolean_|_nil_] automatically define classes if they do not exist
218
+ * - *:symbol_keys* [_Boolean_|_nil_] use symbols instead of strings for hash keys
219
+ * - *:escape_mode* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] determines the characters to escape
220
+ * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing (if dynamically modifying classes or reloading classes then don't use this)
221
+ * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_] load and dump modes to use for JSON
222
+ * - *:time_format* [_:unix_|_:unix_zone_|_:xmlschema_|_:ruby_] time format when dumping in :compat and :object mode
223
+ * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String
224
+ * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
225
+ * - *:create_id* [_String_|_nil_] create id for json compatible object encoding, default is 'json_create'
226
+ * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the seconds portion of time
227
+ * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0 indicates use Ruby
228
+ * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
229
+ * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
230
+ * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and not raise an Exception
231
+ * - *:empty_string* [_Boolean_|_nil_] if true an empty input will not raise an Exception
232
+ * - *:allow_gc* [_Boolean_|_nil_] allow or prohibit GC during parsing, default is true (allow)
233
+ * - *:quirks_mode* [_true,_|_false_|_nil_] Allow single JSON values instead of documents, default is true (allow)
234
+ * - *:allow_invalid_unicode* [_true,_|_false_|_nil_] Allow invalid unicode, default is false (don't allow)
235
+ * - *:allow_nan* [_true,_|_false_|_nil_] Allow Nan, Infinity, and -Infinity to be parsed, default is true (allow)
236
+ * - *:indent_str* [_String_|_nil_] String to use for indentation, overriding the indent option is not nil
237
+ * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields
238
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields
239
+ * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value
240
+ * - *:array_nl* [_String_|_nil_] String to use after a JSON array value
241
+ * - *:nan* [_:null_|_:huge_|_:word_|_:raise_|_:auto_] how to dump Infinity and NaN in null, strict, and compat mode. :null places a null, :huge places a huge number, :word places Infinity or NaN, :raise raises and exception, :auto uses default for each mode.
242
+ * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load, :object_class can also be used
243
+ * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load
244
+ * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted
245
+ *
246
+ * Return [_Hash_] all current option settings.
161
247
  */
162
248
  static VALUE
163
249
  get_def_opts(VALUE self) {
164
250
  VALUE opts = rb_hash_new();
165
-
166
- rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
167
- rb_hash_aset(opts, max_stack_sym, INT2FIX(oj_default_options.max_stack));
251
+
252
+ if (0 == oj_default_options.dump_opts.indent_size) {
253
+ rb_hash_aset(opts, oj_indent_sym, INT2FIX(oj_default_options.indent));
254
+ } else {
255
+ rb_hash_aset(opts, oj_indent_sym, rb_str_new2(oj_default_options.dump_opts.indent_str));
256
+ }
257
+ rb_hash_aset(opts, sec_prec_sym, INT2FIX(oj_default_options.sec_prec));
168
258
  rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
259
+ rb_hash_aset(opts, class_cache_sym, (Yes == oj_default_options.class_cache) ? Qtrue : ((No == oj_default_options.class_cache) ? Qfalse : Qnil));
169
260
  rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
170
- rb_hash_aset(opts, ascii_only_sym, (Yes == oj_default_options.ascii_only) ? Qtrue : ((No == oj_default_options.ascii_only) ? Qfalse : Qnil));
171
261
  rb_hash_aset(opts, symbol_keys_sym, (Yes == oj_default_options.sym_key) ? Qtrue : ((No == oj_default_options.sym_key) ? Qfalse : Qnil));
262
+ rb_hash_aset(opts, bigdecimal_as_decimal_sym, (Yes == oj_default_options.bigdec_as_num) ? Qtrue : ((No == oj_default_options.bigdec_as_num) ? Qfalse : Qnil));
263
+ rb_hash_aset(opts, use_to_json_sym, (Yes == oj_default_options.to_json) ? Qtrue : ((No == oj_default_options.to_json) ? Qfalse : Qnil));
264
+ rb_hash_aset(opts, use_to_hash_sym, (Yes == oj_default_options.to_hash) ? Qtrue : ((No == oj_default_options.to_hash) ? Qfalse : Qnil));
265
+ rb_hash_aset(opts, use_as_json_sym, (Yes == oj_default_options.as_json) ? Qtrue : ((No == oj_default_options.as_json) ? Qfalse : Qnil));
266
+ rb_hash_aset(opts, nilnil_sym, (Yes == oj_default_options.nilnil) ? Qtrue : ((No == oj_default_options.nilnil) ? Qfalse : Qnil));
267
+ rb_hash_aset(opts, empty_string_sym, (Yes == oj_default_options.empty_string) ? Qtrue : ((No == oj_default_options.empty_string) ? Qfalse : Qnil));
268
+ rb_hash_aset(opts, allow_gc_sym, (Yes == oj_default_options.allow_gc) ? Qtrue : ((No == oj_default_options.allow_gc) ? Qfalse : Qnil));
269
+ rb_hash_aset(opts, oj_quirks_mode_sym, (Yes == oj_default_options.quirks_mode) ? Qtrue : ((No == oj_default_options.quirks_mode) ? Qfalse : Qnil));
270
+ rb_hash_aset(opts, allow_invalid_unicode_sym, (Yes == oj_default_options.allow_invalid) ? Qtrue : ((No == oj_default_options.allow_invalid) ? Qfalse : Qnil));
271
+ rb_hash_aset(opts, oj_allow_nan_sym, (Yes == oj_default_options.allow_nan) ? Qtrue : ((No == oj_default_options.allow_nan) ? Qfalse : Qnil));
272
+ rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
172
273
  switch (oj_default_options.mode) {
173
274
  case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
174
275
  case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
175
276
  case NullMode: rb_hash_aset(opts, mode_sym, null_sym); break;
176
277
  case ObjectMode:
278
+ case CustomMode: rb_hash_aset(opts, mode_sym, custom_sym); break;
279
+ case RailsMode: rb_hash_aset(opts, mode_sym, rails_sym); break;
177
280
  default: rb_hash_aset(opts, mode_sym, object_sym); break;
178
281
  }
282
+ switch (oj_default_options.escape_mode) {
283
+ case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
284
+ case JSONEsc: rb_hash_aset(opts, escape_mode_sym, json_sym); break;
285
+ case XSSEsc: rb_hash_aset(opts, escape_mode_sym, xss_safe_sym); break;
286
+ case ASCIIEsc: rb_hash_aset(opts, escape_mode_sym, ascii_sym); break;
287
+ case JXEsc: rb_hash_aset(opts, escape_mode_sym, unicode_xss_sym); break;
288
+ default: rb_hash_aset(opts, escape_mode_sym, json_sym); break;
289
+ }
179
290
  switch (oj_default_options.time_format) {
180
291
  case XmlTime: rb_hash_aset(opts, time_format_sym, xmlschema_sym); break;
181
292
  case RubyTime: rb_hash_aset(opts, time_format_sym, ruby_sym); break;
293
+ case UnixZTime: rb_hash_aset(opts, time_format_sym, unix_zone_sym); break;
182
294
  case UnixTime:
183
295
  default: rb_hash_aset(opts, time_format_sym, unix_sym); break;
184
296
  }
297
+ switch (oj_default_options.bigdec_load) {
298
+ case BigDec: rb_hash_aset(opts, bigdecimal_load_sym, bigdecimal_sym);break;
299
+ case FloatDec: rb_hash_aset(opts, bigdecimal_load_sym, float_sym); break;
300
+ case AutoDec:
301
+ default: rb_hash_aset(opts, bigdecimal_load_sym, auto_sym); break;
302
+ }
185
303
  rb_hash_aset(opts, create_id_sym, (0 == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
186
-
304
+ rb_hash_aset(opts, oj_space_sym, (0 == oj_default_options.dump_opts.after_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.after_sep));
305
+ rb_hash_aset(opts, oj_space_before_sym, (0 == oj_default_options.dump_opts.before_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.before_sep));
306
+ rb_hash_aset(opts, oj_object_nl_sym, (0 == oj_default_options.dump_opts.hash_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.hash_nl));
307
+ rb_hash_aset(opts, oj_array_nl_sym, (0 == oj_default_options.dump_opts.array_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.array_nl));
308
+
309
+ switch (oj_default_options.dump_opts.nan_dump) {
310
+ case NullNan: rb_hash_aset(opts, nan_sym, null_sym); break;
311
+ case RaiseNan: rb_hash_aset(opts, nan_sym, raise_sym); break;
312
+ case WordNan: rb_hash_aset(opts, nan_sym, word_sym); break;
313
+ case HugeNan: rb_hash_aset(opts, nan_sym, huge_sym); break;
314
+ case AutoNan:
315
+ default: rb_hash_aset(opts, nan_sym, auto_sym); break;
316
+ }
317
+ rb_hash_aset(opts, omit_nil_sym, oj_default_options.dump_opts.omit_nil ? Qtrue : Qfalse);
318
+ rb_hash_aset(opts, oj_hash_class_sym, oj_default_options.hash_class);
319
+ rb_hash_aset(opts, oj_array_class_sym, oj_default_options.array_class);
320
+
187
321
  return opts;
188
322
  }
189
323
 
190
- /* call-seq: default_options=(opts)
324
+ /* Document-method: default_options=
325
+ * call-seq: default_options=(opts)
191
326
  *
192
327
  * Sets the default options for load and dump.
193
- * @param [Hash] opts options to change
194
- * @param [Fixnum] :indent number of spaces to indent each element in an JSON document
195
- * @param [true|false|nil] :circular support circular references while dumping
196
- * @param [true|false|nil] :auto_define automatically define classes if they do not exist
197
- * @param [true|false|nil] :symbol_keys convert hash keys to symbols
198
- * @param [true|false|nil] :ascii_only encode all high-bit characters as escaped sequences if true
199
- * @param [:object|:strict|:compat|:null] load and dump mode to use for JSON
200
- * :strict raises an exception when a non-supported Object is
201
- * encountered. :compat attempts to extract variable values from an
202
- * Object using to_json() or to_hash() then it walks the Object's
203
- * variables if neither is found. The :object mode ignores to_hash()
204
- * and to_json() methods and encodes variables using code internal to
205
- * the Oj gem. The :null mode ignores non-supported Objects and
206
- * replaces them with a null.
207
- * @param [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
208
- * :unix decimal number denoting the number of seconds since 1/1/1970,
209
- * :xmlschema date-time format taken from XML Schema as a String,
210
- * :ruby Time.to_s formatted String
211
- * @param [String|nil] :create_id create id for json compatible object encoding
212
- * @param [Fixnum|nil] :max_stack maximum size to allocate on the stack for a JSON String
213
- * @return [nil]
328
+ * - *opts* [_Hash_] options to change
329
+ * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element in a JSON document or the String to use for identation.
330
+ * - :circular [_Boolean_|_nil_] support circular references while dumping.
331
+ * - *:auto_define* [_Boolean_|_nil_] automatically define classes if they do not exist.
332
+ * - *:symbol_keys* [_Boolean_|_nil_] convert hash keys to symbols.
333
+ * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing.
334
+ * - *:escape* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] mode encodes all high-bit characters as escaped sequences if :ascii, :json is standand UTF-8 JSON encoding, :newline is the same as :json but newlines are not escaped, :unicode_xss allows unicode but escapes &, <, and >, and any \u20xx characters along with some others, and :xss_safe escapes &, <, and >, and some others.
335
+ * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String.
336
+ * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_nil_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
337
+ * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_] load and dump mode to use for JSON :strict raises an exception when a non-supported Object is encountered. :compat attempts to extract variable values from an Object using to_json() or to_hash() then it walks the Object's variables if neither is found. The :object mode ignores to_hash() and to_json() methods and encodes variables using code internal to the Oj gem. The :null mode ignores non-supported Objects and replaces them with a null. The :custom mode honors all dump options. The :rails more mimics rails and Active behavior.
338
+ * - *:time_format* [_:unix_|_:xmlschema_|_:ruby_] time format when dumping in :compat mode :unix decimal number denoting the number of seconds since 1/1/1970, :unix_zone decimal number denoting the number of seconds since 1/1/1970 plus the utc_offset in the exponent, :xmlschema date-time format taken from XML Schema as a String, :ruby Time.to_s formatted String.
339
+ * - *:create_id* [_String_|_nil_] create id for json compatible object encoding
340
+ * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the seconds portion of time.
341
+ * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0 indicates use Ruby.
342
+ * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
343
+ * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false.
344
+ * - *:use_to_hash* [_Boolean_|_nil_] call to_hash() methods on dump, default is false.
345
+ * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and not raise an Exception.
346
+ * - *:allow_gc* [_Boolean_|_nil_] allow or prohibit GC during parsing, default is true (allow).
347
+ * - *:quirks_mode* [_Boolean_|_nil_] allow single JSON values instead of documents, default is true (allow).
348
+ * - *:allow_invalid_unicode* [_Boolean_|_nil_] allow invalid unicode, default is false (don't allow).
349
+ * - *:allow_nan* [_Boolean_|_nil_] allow Nan, Infinity, and -Infinity, default is true (allow).
350
+ * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields.
351
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields.
352
+ * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value.
353
+ * - *:array_nl* [_String_|_nil_] String to use after a JSON array value
354
+ * - *:nan* [_:null_|_:huge_|_:word_|_:raise_] how to dump Infinity and NaN in null, strict, and compat mode. :null places a null, :huge places a huge number, :word places Infinity or NaN, :raise raises and exception, :auto uses default for each mode.
355
+ * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load, :object_class can also be used.
356
+ * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load.
357
+ * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted.
214
358
  */
215
359
  static VALUE
216
360
  set_def_opts(VALUE self, VALUE opts) {
361
+ Check_Type(opts, T_HASH);
362
+ oj_parse_options(opts, &oj_default_options);
363
+
364
+ return Qnil;
365
+ }
366
+
367
+ void
368
+ oj_parse_options(VALUE ropts, Options copts) {
217
369
  struct _YesNoOpt ynos[] = {
218
- { circular_sym, &oj_default_options.circular },
219
- { auto_define_sym, &oj_default_options.auto_define },
220
- { symbol_keys_sym, &oj_default_options.sym_key },
221
- { ascii_only_sym, &oj_default_options.ascii_only },
370
+ { circular_sym, &copts->circular },
371
+ { auto_define_sym, &copts->auto_define },
372
+ { symbol_keys_sym, &copts->sym_key },
373
+ { class_cache_sym, &copts->class_cache },
374
+ { bigdecimal_as_decimal_sym, &copts->bigdec_as_num },
375
+ { use_to_hash_sym, &copts->to_hash },
376
+ { use_to_json_sym, &copts->to_json },
377
+ { use_as_json_sym, &copts->as_json },
378
+ { nilnil_sym, &copts->nilnil },
379
+ { allow_blank_sym, &copts->nilnil }, // same as nilnil
380
+ { empty_string_sym, &copts->empty_string },
381
+ { allow_gc_sym, &copts->allow_gc },
382
+ { oj_quirks_mode_sym, &copts->quirks_mode },
383
+ { allow_invalid_unicode_sym, &copts->allow_invalid },
384
+ { oj_allow_nan_sym, &copts->allow_nan },
385
+ { oj_create_additions_sym, &copts->create_ok },
222
386
  { Qnil, 0 }
223
387
  };
224
- YesNoOpt o;
225
- VALUE v;
388
+ YesNoOpt o;
389
+ volatile VALUE v;
390
+ size_t len;
226
391
 
227
- Check_Type(opts, T_HASH);
228
- v = rb_hash_aref(opts, indent_sym);
229
- if (Qnil != v) {
230
- Check_Type(v, T_FIXNUM);
231
- oj_default_options.indent = FIX2INT(v);
392
+ if (T_HASH != rb_type(ropts)) {
393
+ return;
232
394
  }
233
- v = rb_hash_aref(opts, max_stack_sym);
234
- if (Qnil != v) {
235
- int i;
236
-
237
- Check_Type(v, T_FIXNUM);
238
- i = FIX2INT(v);
239
- if (0 > i) {
240
- i = 0;
395
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_indent_sym)) {
396
+ v = rb_hash_lookup(ropts, oj_indent_sym);
397
+ switch (rb_type(v)) {
398
+ case T_NIL:
399
+ copts->dump_opts.indent_size = 0;
400
+ *copts->dump_opts.indent_str = '\0';
401
+ copts->indent = 0;
402
+ break;
403
+ case T_FIXNUM:
404
+ copts->dump_opts.indent_size = 0;
405
+ *copts->dump_opts.indent_str = '\0';
406
+ copts->indent = FIX2INT(v);
407
+ break;
408
+ case T_STRING:
409
+ if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
410
+ rb_raise(rb_eArgError, "indent string is limited to %lu characters.", sizeof(copts->dump_opts.indent_str));
411
+ }
412
+ strcpy(copts->dump_opts.indent_str, StringValuePtr(v));
413
+ copts->dump_opts.indent_size = (uint8_t)len;
414
+ copts->indent = 0;
415
+ break;
416
+ default:
417
+ rb_raise(rb_eTypeError, "indent must be a Fixnum, String, or nil.");
418
+ break;
241
419
  }
242
- oj_default_options.max_stack = (size_t)i;
243
420
  }
421
+ if (Qnil != (v = rb_hash_lookup(ropts, float_prec_sym))) {
422
+ int n;
244
423
 
245
- v = rb_hash_lookup(opts, mode_sym);
246
- if (Qnil == v) {
247
- // ignore
248
- } else if (object_sym == v) {
249
- oj_default_options.mode = ObjectMode;
250
- } else if (strict_sym == v) {
251
- oj_default_options.mode = StrictMode;
252
- } else if (compat_sym == v) {
253
- oj_default_options.mode = CompatMode;
254
- } else if (null_sym == v) {
255
- oj_default_options.mode = NullMode;
256
- } else {
257
- rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
424
+ #ifdef RUBY_INTEGER_UNIFICATION
425
+ if (rb_cInteger != rb_obj_class(v)) {
426
+ rb_raise(rb_eArgError, ":float_precision must be a Integer.");
427
+ }
428
+ #else
429
+ if (T_FIXNUM != rb_type(v)) {
430
+ rb_raise(rb_eArgError, ":float_precision must be a Fixnum.");
431
+ }
432
+ #endif
433
+ n = FIX2INT(v);
434
+ if (0 >= n) {
435
+ *copts->float_fmt = '\0';
436
+ copts->float_prec = 0;
437
+ } else {
438
+ if (20 < n) {
439
+ n = 20;
440
+ }
441
+ sprintf(copts->float_fmt, "%%0.%dg", n);
442
+ copts->float_prec = n;
443
+ }
258
444
  }
445
+ if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
446
+ int n;
259
447
 
260
- v = rb_hash_lookup(opts, time_format_sym);
261
- if (Qnil == v) {
262
- // ignore
263
- } else if (unix_sym == v) {
264
- oj_default_options.time_format = UnixTime;
265
- } else if (xmlschema_sym == v) {
266
- oj_default_options.time_format = XmlTime;
267
- } else if (ruby_sym == v) {
268
- oj_default_options.time_format = RubyTime;
269
- } else {
270
- rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.");
448
+ #ifdef RUBY_INTEGER_UNIFICATION
449
+ if (rb_cInteger != rb_obj_class(v)) {
450
+ rb_raise(rb_eArgError, ":second_precision must be a Integer.");
451
+ }
452
+ #else
453
+ if (T_FIXNUM != rb_type(v)) {
454
+ rb_raise(rb_eArgError, ":second_precision must be a Fixnum.");
455
+ }
456
+ #endif
457
+ n = NUM2INT(v);
458
+ if (0 > n) {
459
+ n = 0;
460
+ } else if (9 < n) {
461
+ n = 9;
462
+ }
463
+ copts->sec_prec = n;
271
464
  }
272
-
273
- if (Qtrue == rb_funcall(opts, rb_intern("has_key?"), 1, create_id_sym)) {
274
- if (0 != oj_default_options.create_id) {
465
+ if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
466
+ if (object_sym == v) {
467
+ copts->mode = ObjectMode;
468
+ } else if (strict_sym == v) {
469
+ copts->mode = StrictMode;
470
+ } else if (compat_sym == v || json_sym == v) {
471
+ copts->mode = CompatMode;
472
+ } else if (null_sym == v) {
473
+ copts->mode = NullMode;
474
+ } else if (custom_sym == v) {
475
+ copts->mode = CustomMode;
476
+ } else if (rails_sym == v) {
477
+ copts->mode = RailsMode;
478
+ } else {
479
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, or :rails.");
480
+ }
481
+ }
482
+ if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
483
+ if (unix_sym == v) {
484
+ copts->time_format = UnixTime;
485
+ } else if (unix_zone_sym == v) {
486
+ copts->time_format = UnixZTime;
487
+ } else if (xmlschema_sym == v) {
488
+ copts->time_format = XmlTime;
489
+ } else if (ruby_sym == v) {
490
+ copts->time_format = RubyTime;
491
+ } else {
492
+ rb_raise(rb_eArgError, ":time_format must be :unix, :unix_zone, :xmlschema, or :ruby.");
493
+ }
494
+ }
495
+ if (Qnil != (v = rb_hash_lookup(ropts, escape_mode_sym))) {
496
+ if (newline_sym == v) {
497
+ copts->escape_mode = NLEsc;
498
+ } else if (json_sym == v) {
499
+ copts->escape_mode = JSONEsc;
500
+ } else if (xss_safe_sym == v) {
501
+ copts->escape_mode = XSSEsc;
502
+ } else if (ascii_sym == v) {
503
+ copts->escape_mode = ASCIIEsc;
504
+ } else if (unicode_xss_sym == v) {
505
+ copts->escape_mode = JXEsc;
506
+ } else {
507
+ rb_raise(rb_eArgError, ":encoding must be :newline, :json, :xss_safe, :unicode_xss, or :ascii.");
508
+ }
509
+ }
510
+ if (Qnil != (v = rb_hash_lookup(ropts, bigdecimal_load_sym))) {
511
+ if (bigdecimal_sym == v || Qtrue == v) {
512
+ copts->bigdec_load = BigDec;
513
+ } else if (float_sym == v) {
514
+ copts->bigdec_load = FloatDec;
515
+ } else if (auto_sym == v || Qfalse == v) {
516
+ copts->bigdec_load = AutoDec;
517
+ } else {
518
+ rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
519
+ }
520
+ }
521
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, create_id_sym)) {
522
+ v = rb_hash_lookup(ropts, create_id_sym);
523
+ if (Qnil == v) {
275
524
  if (json_class != oj_default_options.create_id) {
276
525
  xfree((char*)oj_default_options.create_id);
277
526
  }
278
- oj_default_options.create_id = 0;
279
- }
280
- v = rb_hash_lookup(opts, create_id_sym);
281
- if (Qnil != v) {
282
- size_t len = RSTRING_LEN(v) + 1;
283
-
284
- oj_default_options.create_id = ALLOC_N(char, len);
285
- strcpy((char*)oj_default_options.create_id, StringValuePtr(v));
527
+ copts->create_id = NULL;
528
+ copts->create_id_len = 0;
529
+ } else if (T_STRING == rb_type(v)) {
530
+ const char *str = StringValuePtr(v);
531
+
532
+ len = RSTRING_LEN(v);
533
+ if (len != copts->create_id_len ||
534
+ 0 != strcmp(copts->create_id, str)) {
535
+ copts->create_id = ALLOC_N(char, len + 1);
536
+ strcpy((char*)copts->create_id, str);
537
+ copts->create_id_len = len;
538
+ }
539
+ } else {
540
+ rb_raise(rb_eArgError, ":create_id must be string.");
286
541
  }
287
542
  }
288
-
289
543
  for (o = ynos; 0 != o->attr; o++) {
290
- if (Qtrue != rb_funcall(opts, rb_intern("has_key?"), 1, o->sym)) {
291
- continue;
292
- }
293
- if (Qnil != (v = rb_hash_lookup(opts, o->sym))) {
544
+ if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
294
545
  if (Qtrue == v) {
295
546
  *o->attr = Yes;
296
547
  } else if (Qfalse == v) {
297
548
  *o->attr = No;
298
549
  } else {
299
- rb_raise(rb_eArgError, "%s must be true, false, or nil.", rb_id2name(SYM2ID(o->sym)));
550
+ rb_raise(rb_eArgError, "%s must be true or false.", rb_id2name(SYM2ID(o->sym)));
300
551
  }
301
552
  }
302
553
  }
303
- return Qnil;
304
- }
305
-
306
- static void
307
- parse_options(VALUE ropts, Options copts) {
308
- struct _YesNoOpt ynos[] = {
309
- { circular_sym, &copts->circular },
310
- { auto_define_sym, &copts->auto_define },
311
- { symbol_keys_sym, &copts->sym_key },
312
- { ascii_only_sym, &copts->ascii_only },
313
- { Qnil, 0 }
314
- };
315
- YesNoOpt o;
316
-
317
- if (rb_cHash == rb_obj_class(ropts)) {
318
- VALUE v;
319
-
320
- if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
321
- if (rb_cFixnum != rb_obj_class(v)) {
322
- rb_raise(rb_eArgError, ":indent must be a Fixnum.");
554
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_space_sym)) {
555
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_space_sym))) {
556
+ copts->dump_opts.after_size = 0;
557
+ *copts->dump_opts.after_sep = '\0';
558
+ } else {
559
+ rb_check_type(v, T_STRING);
560
+ if (sizeof(copts->dump_opts.after_sep) <= (len = RSTRING_LEN(v))) {
561
+ rb_raise(rb_eArgError, "space string is limited to %lu characters.", sizeof(copts->dump_opts.after_sep));
323
562
  }
324
- copts->indent = NUM2INT(v);
563
+ strcpy(copts->dump_opts.after_sep, StringValuePtr(v));
564
+ copts->dump_opts.after_size = (uint8_t)len;
325
565
  }
326
- if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
327
- if (object_sym == v) {
328
- copts->mode = ObjectMode;
329
- } else if (strict_sym == v) {
330
- copts->mode = StrictMode;
331
- } else if (compat_sym == v) {
332
- copts->mode = CompatMode;
333
- } else if (null_sym == v) {
334
- copts->mode = NullMode;
335
- } else {
336
- rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
566
+ }
567
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_space_before_sym)) {
568
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_space_before_sym))) {
569
+ copts->dump_opts.before_size = 0;
570
+ *copts->dump_opts.before_sep = '\0';
571
+ } else {
572
+ rb_check_type(v, T_STRING);
573
+ if (sizeof(copts->dump_opts.before_sep) <= (len = RSTRING_LEN(v))) {
574
+ rb_raise(rb_eArgError, "sapce_before string is limited to %lu characters.", sizeof(copts->dump_opts.before_sep));
337
575
  }
576
+ strcpy(copts->dump_opts.before_sep, StringValuePtr(v));
577
+ copts->dump_opts.before_size = (uint8_t)len;
338
578
  }
339
- if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
340
- if (unix_sym == v) {
341
- copts->time_format = UnixTime;
342
- } else if (xmlschema_sym == v) {
343
- copts->time_format = XmlTime;
344
- } else if (ruby_sym == v) {
345
- copts->time_format = RubyTime;
346
- } else {
347
- rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.");
579
+ }
580
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_nl_sym)) {
581
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_object_nl_sym))) {
582
+ copts->dump_opts.hash_size = 0;
583
+ *copts->dump_opts.hash_nl = '\0';
584
+ } else {
585
+ rb_check_type(v, T_STRING);
586
+ if (sizeof(copts->dump_opts.hash_nl) <= (len = RSTRING_LEN(v))) {
587
+ rb_raise(rb_eArgError, "object_nl string is limited to %lu characters.", sizeof(copts->dump_opts.hash_nl));
348
588
  }
589
+ strcpy(copts->dump_opts.hash_nl, StringValuePtr(v));
590
+ copts->dump_opts.hash_size = (uint8_t)len;
349
591
  }
350
- for (o = ynos; 0 != o->attr; o++) {
351
- if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
352
- if (Qtrue == v) {
353
- *o->attr = Yes;
354
- } else if (Qfalse == v) {
355
- *o->attr = No;
356
- } else {
357
- rb_raise(rb_eArgError, "%s must be true or false.", rb_id2name(SYM2ID(o->sym)));
358
- }
592
+ }
593
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_nl_sym)) {
594
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_array_nl_sym))) {
595
+ copts->dump_opts.array_size = 0;
596
+ *copts->dump_opts.array_nl = '\0';
597
+ } else {
598
+ rb_check_type(v, T_STRING);
599
+ if (sizeof(copts->dump_opts.array_nl) <= (len = RSTRING_LEN(v))) {
600
+ rb_raise(rb_eArgError, "array_nl string is limited to %lu characters.", sizeof(copts->dump_opts.array_nl));
359
601
  }
602
+ strcpy(copts->dump_opts.array_nl, StringValuePtr(v));
603
+ copts->dump_opts.array_size = (uint8_t)len;
360
604
  }
361
605
  }
362
- }
363
-
364
- static VALUE
365
- load_with_opts(VALUE input, Options copts) {
366
- char *json;
367
- size_t len;
368
- VALUE obj;
369
-
370
- if (rb_type(input) == T_STRING) {
371
- // the json string gets modified so make a copy of it
372
- len = RSTRING_LEN(input) + 1;
373
- if (copts->max_stack < len) {
374
- json = ALLOC_N(char, len);
606
+ if (Qnil != (v = rb_hash_lookup(ropts, nan_sym))) {
607
+ if (null_sym == v) {
608
+ copts->dump_opts.nan_dump = NullNan;
609
+ } else if (huge_sym == v) {
610
+ copts->dump_opts.nan_dump = HugeNan;
611
+ } else if (word_sym == v) {
612
+ copts->dump_opts.nan_dump = WordNan;
613
+ } else if (raise_sym == v) {
614
+ copts->dump_opts.nan_dump = RaiseNan;
615
+ } else if (auto_sym == v) {
616
+ copts->dump_opts.nan_dump = AutoNan;
375
617
  } else {
376
- json = ALLOCA_N(char, len);
618
+ rb_raise(rb_eArgError, ":nan must be :null, :huge, :word, :raise, or :auto.");
377
619
  }
378
- strcpy(json, StringValuePtr(input));
379
- } else {
380
- VALUE clas = rb_obj_class(input);
381
- VALUE s;
382
-
383
- if (oj_stringio_class == clas) {
384
- s = rb_funcall2(input, oj_string_id, 0, 0);
385
- len = RSTRING_LEN(s) + 1;
386
- if (copts->max_stack < len) {
387
- json = ALLOC_N(char, len);
388
- } else {
389
- json = ALLOCA_N(char, len);
390
- }
391
- strcpy(json, StringValuePtr(s));
392
- #ifndef JRUBY_RUBY
393
- #if !IS_WINDOWS
394
- // JRuby gets confused with what is the real fileno.
395
- } else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
396
- int fd = FIX2INT(s);
397
- ssize_t cnt;
398
-
399
- len = lseek(fd, 0, SEEK_END);
400
- lseek(fd, 0, SEEK_SET);
401
- if (copts->max_stack < len) {
402
- json = ALLOC_N(char, len + 1);
403
- } else {
404
- json = ALLOCA_N(char, len + 1);
405
- }
406
- if (0 >= (cnt = read(fd, json, len)) || cnt != (ssize_t)len) {
407
- rb_raise(rb_eIOError, "failed to read from IO Object.");
408
- }
409
- json[len] = '\0';
410
- #endif
411
- #endif
412
- } else if (rb_respond_to(input, oj_read_id)) {
413
- s = rb_funcall2(input, oj_read_id, 0, 0);
414
- len = RSTRING_LEN(s) + 1;
415
- if (copts->max_stack < len) {
416
- json = ALLOC_N(char, len);
417
- } else {
418
- json = ALLOCA_N(char, len);
419
- }
420
- strcpy(json, StringValuePtr(s));
620
+ }
621
+ copts->dump_opts.use = (0 < copts->dump_opts.indent_size ||
622
+ 0 < copts->dump_opts.after_size ||
623
+ 0 < copts->dump_opts.before_size ||
624
+ 0 < copts->dump_opts.hash_size ||
625
+ 0 < copts->dump_opts.array_size);
626
+ if (Qnil != (v = rb_hash_lookup(ropts, omit_nil_sym))) {
627
+ if (Qtrue == v) {
628
+ copts->dump_opts.omit_nil = true;
629
+ } else if (Qfalse == v) {
630
+ copts->dump_opts.omit_nil = false;
421
631
  } else {
422
- rb_raise(rb_eArgError, "load() expected a String or IO Object.");
632
+ rb_raise(rb_eArgError, ":omit_nil must be true or false.");
423
633
  }
424
634
  }
425
- obj = oj_parse(json, copts);
426
- if (copts->max_stack < len) {
427
- xfree(json);
635
+ // This is here only for backwards compatibility with the original Oj.
636
+ v = rb_hash_lookup(ropts, oj_ascii_only_sym);
637
+ if (Qtrue == v) {
638
+ copts->escape_mode = ASCIIEsc;
639
+ } else if (Qfalse == v) {
640
+ copts->escape_mode = JSONEsc;
428
641
  }
429
- return obj;
642
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_hash_class_sym)) {
643
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
644
+ copts->hash_class = Qnil;
645
+ } else {
646
+ rb_check_type(v, T_CLASS);
647
+ copts->hash_class = v;
648
+ }
649
+ }
650
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_class_sym)) {
651
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
652
+ copts->hash_class = Qnil;
653
+ } else {
654
+ rb_check_type(v, T_CLASS);
655
+ copts->hash_class = v;
656
+ }
657
+ }
658
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_class_sym)) {
659
+ if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
660
+ copts->array_class = Qnil;
661
+ } else {
662
+ rb_check_type(v, T_CLASS);
663
+ copts->array_class = v;
664
+ }
665
+ }
666
+ oj_parse_opt_match_string(&copts->str_rx, ropts);
430
667
  }
431
668
 
432
- /* call-seq: load(json, options) => Hash, Array, String, Fixnum, Float, true, false, or nil
669
+ static int
670
+ match_string_cb(VALUE key, VALUE value, RxClass rc) {
671
+ if (T_CLASS != rb_type(value)) {
672
+ rb_raise(rb_eArgError, "for :match_string, the hash values must be a Class.");
673
+ }
674
+ switch (rb_type(key)) {
675
+ case T_REGEXP:
676
+ oj_rxclass_rappend(rc, key, value);
677
+ break;
678
+ case T_STRING:
679
+ if (0 != oj_rxclass_append(rc, StringValuePtr(key), value)) {
680
+ rb_raise(rb_eArgError, "%s", rc->err);
681
+ }
682
+ break;
683
+ default:
684
+ rb_raise(rb_eArgError, "for :match_string, keys must either a String or RegExp.");
685
+ break;
686
+ }
687
+ return ST_CONTINUE;
688
+ }
689
+
690
+ void
691
+ oj_parse_opt_match_string(RxClass rc, VALUE ropts) {
692
+ VALUE v;
693
+
694
+ if (Qnil != (v = rb_hash_lookup(ropts, match_string_sym))) {
695
+ rb_check_type(v, T_HASH);
696
+ // Zero out rc. Pattern are not appended but override.
697
+ rc->head = NULL;
698
+ rc->tail = NULL;
699
+ *rc->err = '\0';
700
+ rb_hash_foreach(v, match_string_cb, (VALUE)rc);
701
+ }
702
+ }
703
+
704
+ /* Document-method: load
705
+ * call-seq: load(json, options) { _|_obj, start, len_|_ }
706
+ *
707
+ * Parses a JSON document String into a Object, Hash, Array, String, Fixnum,
708
+ * Float, true, false, or nil according to the default mode or the mode
709
+ * specified. Raises an exception if the JSON is malformed or the classes
710
+ * specified are not valid. If the string input is not a valid JSON document (an
711
+ * empty string is not a valid JSON document) an exception is raised.
712
+ *
713
+ * When used with a document that has multiple JSON elements the block, if
714
+ * any, will be yielded to. If no block then the last element read will be
715
+ * returned.
716
+ *
717
+ * This parser operates on string and will attempt to load files into memory if
718
+ * a file object is passed as the first argument. A stream input will be parsed
719
+ * using a stream parser but others use the slightly faster string parser.
720
+ *
721
+ * A block can be provided with a single argument. That argument will be the
722
+ * parsed JSON document. This is useful when parsing a string that includes
723
+ * multiple JSON documents. The block can take up to 3 arguments, the parsed
724
+ * object, the position in the string or stream of the start of the JSON for
725
+ * that object, and the length of the JSON for that object plus trailing
726
+ * whitespace.
433
727
  *
434
- * Parses a JSON document String into a Hash, Array, String, Fixnum, Float,
435
- * true, false, or nil. Raises an exception if the JSON is malformed or the
436
- * classes specified are not valid.
437
- * @param [String] json JSON String
438
- * @param [Hash] options load options (same as default_options)
728
+ * - *json* [_String_|_IO_] JSON String or an Object that responds to read()
729
+ * - *options* [_Hash_] load options (same as default_options)
730
+ * - -
731
+ * - *obj* [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_] parsed object.
732
+ * - *start* [_optional, _Integer_] start position of parsed JSON for obj.
733
+ * - *len* [_optional, _Integer_] length of parsed JSON for obj.
734
+ *
735
+ * Returns [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_]
439
736
  */
440
737
  static VALUE
441
738
  load(int argc, VALUE *argv, VALUE self) {
442
- struct _Options options = oj_default_options;
739
+ Mode mode = oj_default_options.mode;
443
740
 
444
741
  if (1 > argc) {
445
742
  rb_raise(rb_eArgError, "Wrong number of arguments to load().");
446
743
  }
447
744
  if (2 <= argc) {
448
- parse_options(argv[1], &options);
745
+ VALUE ropts = argv[1];
746
+ VALUE v;
747
+
748
+ Check_Type(ropts, T_HASH);
749
+ if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
750
+ if (object_sym == v) {
751
+ mode = ObjectMode;
752
+ } else if (strict_sym == v) {
753
+ mode = StrictMode;
754
+ } else if (compat_sym == v || json_sym == v) {
755
+ mode = CompatMode;
756
+ } else if (null_sym == v) {
757
+ mode = NullMode;
758
+ } else if (custom_sym == v) {
759
+ mode = CustomMode;
760
+ } else if (rails_sym == v) {
761
+ mode = RailsMode;
762
+ } else {
763
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, or :rails.");
764
+ }
765
+ }
766
+ }
767
+ switch (mode) {
768
+ case StrictMode:
769
+ case NullMode:
770
+ return oj_strict_parse(argc, argv, self);
771
+ case CompatMode:
772
+ case RailsMode:
773
+ return oj_compat_parse(argc, argv, self);
774
+ case CustomMode:
775
+ return oj_custom_parse(argc, argv, self);
776
+ case ObjectMode:
777
+ default:
778
+ break;
449
779
  }
450
- return load_with_opts(*argv, &options);
780
+ return oj_object_parse(argc, argv, self);
451
781
  }
452
782
 
453
783
  /* Document-method: load_file
454
- * call-seq: load_file(path, options) => Hash, Array, String, Fixnum, Float, true, false, or nil
784
+ * call-seq: load_file(path, options) { _|_obj, start, len_|_ }
785
+ *
786
+ * Parses a JSON document String into a Object, Hash, Array, String, Fixnum,
787
+ * Float, true, false, or nil according to the default mode or the mode
788
+ * specified. Raises an exception if the JSON is malformed or the classes
789
+ * specified are not valid. If the string input is not a valid JSON document (an
790
+ * empty string is not a valid JSON document) an exception is raised.
455
791
  *
456
- * Parses a JSON document from a file into a Hash, Array, String, Fixnum,
457
- * Float, true, false, or nil. Raises an exception if the JSON is malformed or
458
- * the classes specified are not valid.
792
+ * When used with a document that has multiple JSON elements the block, if
793
+ * any, will be yielded to. If no block then the last element read will be
794
+ * returned.
459
795
  *
460
- * @param [String] path path to a file containing a JSON document
461
- * @param [Hash] options load options (same as default_options)
796
+ * If the input file is not a valid JSON document (an empty file is not a valid
797
+ * JSON document) an exception is raised.
798
+ *
799
+ * This is a stream based parser which allows a large or huge file to be loaded
800
+ * without pulling the whole file into memory.
801
+ *
802
+ * A block can be provided with a single argument. That argument will be the
803
+ * parsed JSON document. This is useful when parsing a string that includes
804
+ * multiple JSON documents. The block can take up to 3 arguments, the parsed
805
+ * object, the position in the string or stream of the start of the JSON for
806
+ * that object, and the length of the JSON for that object plus trailing
807
+ * whitespace.
808
+ *
809
+ * - *path* [_String_] to a file containing a JSON document
810
+ * - *options* [_Hash_] load options (same as default_options)
811
+ * - -
812
+ * - *obj* [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_] parsed object.
813
+ * - *start* [_optional, _Integer_] start position of parsed JSON for obj.
814
+ * - *len* [_optional, _Integer_] length of parsed JSON for obj.
815
+ *
816
+ * Returns [_Object_|_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_]
462
817
  */
463
818
  static VALUE
464
819
  load_file(int argc, VALUE *argv, VALUE self) {
465
820
  char *path;
466
- char *json;
467
- FILE *f;
468
- unsigned long len;
469
- VALUE obj;
470
- struct _Options options = oj_default_options;
471
- size_t max_stack = oj_default_options.max_stack;
821
+ int fd;
822
+ Mode mode = oj_default_options.mode;
823
+ struct _ParseInfo pi;
472
824
 
825
+ if (1 > argc) {
826
+ rb_raise(rb_eArgError, "Wrong number of arguments to load().");
827
+ }
473
828
  Check_Type(*argv, T_STRING);
829
+ parse_info_init(&pi);
830
+ pi.options = oj_default_options;
831
+ pi.handler = Qnil;
832
+ pi.err_class = Qnil;
833
+ pi.max_depth = 0;
834
+ if (2 <= argc) {
835
+ VALUE ropts = argv[1];
836
+ VALUE v;
837
+
838
+ Check_Type(ropts, T_HASH);
839
+ if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
840
+ if (object_sym == v) {
841
+ mode = ObjectMode;
842
+ } else if (strict_sym == v) {
843
+ mode = StrictMode;
844
+ } else if (compat_sym == v || json_sym == v) {
845
+ mode = CompatMode;
846
+ } else if (null_sym == v) {
847
+ mode = NullMode;
848
+ } else if (custom_sym == v) {
849
+ mode = CustomMode;
850
+ } else if (rails_sym == v) {
851
+ mode = RailsMode;
852
+ } else {
853
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, :rails.");
854
+ }
855
+ }
856
+ }
474
857
  path = StringValuePtr(*argv);
475
- if (0 == (f = fopen(path, "r"))) {
858
+ if (0 == (fd = open(path, O_RDONLY))) {
476
859
  rb_raise(rb_eIOError, "%s", strerror(errno));
477
860
  }
478
- fseek(f, 0, SEEK_END);
479
- len = ftell(f);
480
- if (max_stack < len) {
481
- json = ALLOC_N(char, len + 1);
482
- } else {
483
- json = ALLOCA_N(char, len + 1);
484
- }
485
- fseek(f, 0, SEEK_SET);
486
- if (len != fread(json, 1, len, f)) {
487
- fclose(f);
488
- rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")), "Failed to read %ld bytes from %s.", len, path);
489
- }
490
- fclose(f);
491
- json[len] = '\0';
492
- if (2 <= argc) {
493
- parse_options(argv[1], &options);
494
- }
495
- obj = oj_parse(json, &options);
496
- if (max_stack < len) {
497
- xfree(json);
861
+ switch (mode) {
862
+ case StrictMode:
863
+ oj_set_strict_callbacks(&pi);
864
+ return oj_pi_sparse(argc, argv, &pi, fd);
865
+ case NullMode:
866
+ case CompatMode:
867
+ case CustomMode:
868
+ case RailsMode:
869
+ oj_set_compat_callbacks(&pi);
870
+ return oj_pi_sparse(argc, argv, &pi, fd);
871
+ case ObjectMode:
872
+ default:
873
+ break;
498
874
  }
499
- return obj;
875
+ oj_set_object_callbacks(&pi);
876
+
877
+ return oj_pi_sparse(argc, argv, &pi, fd);
500
878
  }
501
879
 
502
- /* call-seq: dump(obj, options) => json-string
880
+ /* Document-method: safe_load
881
+ * call-seq: safe_load(doc)
882
+ *
883
+ * Loads a JSON document in strict mode with :auto_define and :symbol_keys
884
+ * turned off. This function should be safe to use with JSON received on an
885
+ * unprotected public interface.
886
+ *
887
+ * - *doc* [_String__|_IO_] JSON String or IO to load.
888
+ *
889
+ * Returns [_Hash_|_Array_|_String_|_Fixnum_|_Bignum_|_BigDecimal_|_nil_|_True_|_False_]
890
+ */
891
+ static VALUE
892
+ safe_load(VALUE self, VALUE doc) {
893
+ struct _ParseInfo pi;
894
+ VALUE args[1];
895
+
896
+ parse_info_init(&pi);
897
+ pi.err_class = Qnil;
898
+ pi.max_depth = 0;
899
+ pi.options = oj_default_options;
900
+ pi.options.auto_define = No;
901
+ pi.options.sym_key = No;
902
+ pi.options.mode = StrictMode;
903
+ oj_set_strict_callbacks(&pi);
904
+ *args = doc;
905
+
906
+ return oj_pi_parse(1, args, &pi, 0, 0, 1);
907
+ }
908
+
909
+ /* Document-method: saj_parse
910
+ * call-seq: saj_parse(handler, io)
911
+ *
912
+ * Parses an IO stream or file containing a JSON document. Raises an exception
913
+ * if the JSON is malformed. This is a callback parser that calls the methods in
914
+ * the handler if they exist. A sample is the Oj::Saj class which can be used as
915
+ * a base class for the handler.
916
+ *
917
+ * - *handler* [_Oj::Saj_] responds to Oj::Saj methods
918
+ * - *io* [_IO_|_String_] IO Object to read from
919
+ */
920
+
921
+ /* Document-method: sc_parse
922
+ * call-seq: sc_parse(handler, io)
923
+ *
924
+ * Parses an IO stream or file containing a JSON document. Raises an exception
925
+ * if the JSON is malformed. This is a callback parser (Simple Callback Parser)
926
+ * that calls the methods in the handler if they exist. A sample is the
927
+ * Oj::ScHandler class which can be used as a base class for the handler. This
928
+ * callback parser is slightly more efficient than the Saj callback parser and
929
+ * requires less argument checking.
930
+ *
931
+ * - *handler* [_Oj_::ScHandler_] responds to Oj::ScHandler methods
932
+ * - *io* [_IO__|_String_] IO Object to read from
933
+ */
934
+
935
+ /* Document-method: dump
936
+ * call-seq: dump(obj, options)
503
937
  *
504
938
  * Dumps an Object (obj) to a string.
505
- * @param [Object] obj Object to serialize as an JSON document String
506
- * @param [Hash] options same as default_options
939
+ * - *obj* [_Object_] Object to serialize as an JSON document String
940
+ * - *options* [_Hash_] same as default_options
507
941
  */
508
942
  static VALUE
509
943
  dump(int argc, VALUE *argv, VALUE self) {
510
- char *json;
944
+ char buf[4096];
945
+ struct _Out out;
511
946
  struct _Options copts = oj_default_options;
512
947
  VALUE rstr;
513
-
948
+
949
+ if (1 > argc) {
950
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
951
+ }
514
952
  if (2 == argc) {
515
- parse_options(argv[1], &copts);
953
+ oj_parse_options(argv[1], &copts);
516
954
  }
517
- if (0 == (json = oj_write_obj_to_str(*argv, &copts))) {
955
+ if (CompatMode == copts.mode) {
956
+ copts.to_json = No;
957
+ copts.dump_opts.nan_dump = true;
958
+ }
959
+ out.buf = buf;
960
+ out.end = buf + sizeof(buf) - 10;
961
+ out.allocated = 0;
962
+ out.omit_nil = copts.dump_opts.omit_nil;
963
+ out.caller = CALLER_DUMP;
964
+ oj_dump_obj_to_json(*argv, &copts, &out);
965
+ if (0 == out.buf) {
518
966
  rb_raise(rb_eNoMemError, "Not enough memory.");
519
967
  }
520
- rstr = rb_str_new2(json);
521
- #if HAS_ENCODING_SUPPORT
522
- rb_enc_associate(rstr, oj_utf8_encoding);
523
- #endif
524
- xfree(json);
525
-
968
+ rstr = rb_str_new2(out.buf);
969
+ rstr = oj_encode(rstr);
970
+ if (out.allocated) {
971
+ xfree(out.buf);
972
+ }
526
973
  return rstr;
527
974
  }
528
975
 
976
+ /* Document-method: to_json
977
+ * call-seq: to_json(obj, options)
978
+ *
979
+ * Dumps an Object (obj) to a string. If the object has a to_json method that
980
+ * will be called. The mode is set to :compat.
981
+ * - *obj* [_Object_] Object to serialize as an JSON document String
982
+ * - *options* [_Hash_]
983
+ * - *:max_nesting* [_boolean_] It true nesting is limited to 100. The option to detect circular references is available but is not compatible with the json gem., default is false
984
+ * - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and Infinity will be used as appropriate, default is true.
985
+ * - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true (allow).
986
+ * - *:indent_str* [_String_|_nil_] String to use for indentation, overriding the indent option if not nil.
987
+ * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields.
988
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields.
989
+ * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value.
990
+ * - *:array_nl* [_String_|_nil_] String to use after a JSON array value.
991
+ *
992
+ * Returns [_String_] the encoded JSON.
993
+ */
994
+ static VALUE
995
+ to_json(int argc, VALUE *argv, VALUE self) {
996
+ char buf[4096];
997
+ struct _Out out;
998
+ struct _Options copts = oj_default_options;
999
+ VALUE rstr;
1000
+
1001
+ if (1 > argc) {
1002
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
1003
+ }
1004
+ copts.dump_opts.nan_dump = false;
1005
+ if (2 == argc) {
1006
+ oj_parse_mimic_dump_options(argv[1], &copts);
1007
+ }
1008
+ copts.mode = CompatMode;
1009
+ copts.to_json = Yes;
1010
+ out.buf = buf;
1011
+ out.end = buf + sizeof(buf) - 10;
1012
+ out.allocated = 0;
1013
+ out.omit_nil = copts.dump_opts.omit_nil;
1014
+ // For obj.to_json or generate nan is not allowed but if called from dump
1015
+ // it is.
1016
+ copts.dump_opts.nan_dump = false;
1017
+ oj_dump_obj_to_json(*argv, &copts, &out);
1018
+ if (0 == out.buf) {
1019
+ rb_raise(rb_eNoMemError, "Not enough memory.");
1020
+ }
1021
+ rstr = rb_str_new2(out.buf);
1022
+ rstr = oj_encode(rstr);
1023
+ if (out.allocated) {
1024
+ xfree(out.buf);
1025
+ }
1026
+ return rstr;
1027
+ }
529
1028
 
530
- /* call-seq: to_file(file_path, obj, options)
1029
+ /* Document-method: to_file
1030
+ * call-seq: to_file(file_path, obj, options)
531
1031
  *
532
1032
  * Dumps an Object to the specified file.
533
- * @param [String] file_path file path to write the JSON document to
534
- * @param [Object] obj Object to serialize as an JSON document String
535
- * @param [Hash] options formating options
536
- * @param [Fixnum] :indent format expected
537
- * @param [true|false] :circular allow circular references, default: false
1033
+ * - *file* [_String_] _path file path to write the JSON document to
1034
+ * - *obj* [_Object_] Object to serialize as an JSON document String
1035
+ * - *options* [_Hash_] formating options
1036
+ * - *:indent* [_Fixnum_] format expected
1037
+ * - *:circular* [_Boolean_] allow circular references, default: false
538
1038
  */
539
1039
  static VALUE
540
1040
  to_file(int argc, VALUE *argv, VALUE self) {
541
1041
  struct _Options copts = oj_default_options;
542
1042
 
543
1043
  if (3 == argc) {
544
- parse_options(argv[2], &copts);
1044
+ oj_parse_options(argv[2], &copts);
545
1045
  }
546
1046
  Check_Type(*argv, T_STRING);
547
1047
  oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
@@ -549,438 +1049,444 @@ to_file(int argc, VALUE *argv, VALUE self) {
549
1049
  return Qnil;
550
1050
  }
551
1051
 
552
- /* call-seq: saj_parse(handler, io)
1052
+ /* Document-method: to_stream
1053
+ * call-seq: to_stream(io, obj, options)
553
1054
  *
554
- * Parses an IO stream or file containing an JSON document. Raises an exception
555
- * if the JSON is malformed.
556
- * @param [Oj::Saj] handler SAJ (responds to Oj::Saj methods) like handler
557
- * @param [IO|String] io IO Object to read from
1055
+ * Dumps an Object to the specified IO stream.
1056
+ * - *io* [_IO_] IO stream to write the JSON document to
1057
+ * - *obj* [_Object_] Object to serialize as an JSON document String
1058
+ * - *options* [_Hash_] formating options
1059
+ * - *:indent* [_Fixnum_] format expected
1060
+ * - *:circular* [_Boolean_] allow circular references, default: false
558
1061
  */
559
1062
  static VALUE
560
- saj_parse(int argc, VALUE *argv, VALUE self) {
1063
+ to_stream(int argc, VALUE *argv, VALUE self) {
561
1064
  struct _Options copts = oj_default_options;
562
- char *json;
563
- size_t len;
564
- VALUE input = argv[1];
565
-
566
- if (argc < 2) {
567
- rb_raise(rb_eArgError, "Wrong number of arguments to saj_parse.\n");
568
- }
569
- if (rb_type(input) == T_STRING) {
570
- // the json string gets modified so make a copy of it
571
- len = RSTRING_LEN(input) + 1;
572
- if (copts.max_stack < len) {
573
- json = ALLOC_N(char, len);
574
- } else {
575
- json = ALLOCA_N(char, len);
576
- }
577
- strcpy(json, StringValuePtr(input));
578
- } else {
579
- VALUE clas = rb_obj_class(input);
580
- VALUE s;
581
-
582
- if (oj_stringio_class == clas) {
583
- s = rb_funcall2(input, oj_string_id, 0, 0);
584
- len = RSTRING_LEN(s) + 1;
585
- if (copts.max_stack < len) {
586
- json = ALLOC_N(char, len);
587
- } else {
588
- json = ALLOCA_N(char, len);
589
- }
590
- strcpy(json, StringValuePtr(s));
591
- #ifndef JRUBY_RUBY
592
- #if !IS_WINDOWS
593
- // JRuby gets confused with what is the real fileno.
594
- } else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
595
- int fd = FIX2INT(s);
596
- ssize_t cnt;
597
-
598
- len = lseek(fd, 0, SEEK_END);
599
- lseek(fd, 0, SEEK_SET);
600
- if (copts.max_stack < len) {
601
- json = ALLOC_N(char, len + 1);
602
- } else {
603
- json = ALLOCA_N(char, len + 1);
604
- }
605
- if (0 >= (cnt = read(fd, json, len)) || cnt != (ssize_t)len) {
606
- rb_raise(rb_eIOError, "failed to read from IO Object.");
607
- }
608
- json[len] = '\0';
609
- #endif
610
- #endif
611
- } else if (rb_respond_to(input, oj_read_id)) {
612
- s = rb_funcall2(input, oj_read_id, 0, 0);
613
- len = RSTRING_LEN(s) + 1;
614
- if (copts.max_stack < len) {
615
- json = ALLOC_N(char, len);
616
- } else {
617
- json = ALLOCA_N(char, len);
618
- }
619
- strcpy(json, StringValuePtr(s));
620
- } else {
621
- rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
622
- }
623
- }
624
- oj_saj_parse(*argv, json);
625
- if (copts.max_stack < len) {
626
- xfree(json);
1065
+
1066
+ if (3 == argc) {
1067
+ oj_parse_options(argv[2], &copts);
627
1068
  }
1069
+ oj_write_obj_to_stream(argv[1], *argv, &copts);
1070
+
628
1071
  return Qnil;
629
1072
  }
630
1073
 
631
- // Mimic JSON section
632
-
1074
+ /* Document-method: register_odd
1075
+ * call-seq: register_odd(clas, create_object, create_method, *members)
1076
+ *
1077
+ * Registers a class as special. This is useful for working around subclasses of
1078
+ * primitive types as is done with ActiveSupport classes. The use of this
1079
+ * function should be limited to just classes that can not be handled in the
1080
+ * normal way. It is not intended as a hook for changing the output of all
1081
+ * classes as it is not optimized for large numbers of classes.
1082
+ *
1083
+ * - *clas* [_Class__|_Module_] Class or Module to be made special
1084
+ * - *create_object* [_Object_] object to call the create method on
1085
+ * - *create_method* [_Symbol_] method on the clas that will create a new instance of the clas when given all the member values in the order specified.
1086
+ * - *members* [_Symbol__|_String_] methods used to get the member values from instances of the clas.
1087
+ */
633
1088
  static VALUE
634
- mimic_dump(int argc, VALUE *argv, VALUE self) {
635
- char *json;
636
- struct _Options copts = oj_default_options;
637
- VALUE rstr;
638
-
639
- if (0 == (json = oj_write_obj_to_str(*argv, &copts))) {
640
- rb_raise(rb_eNoMemError, "Not enough memory.");
1089
+ register_odd(int argc, VALUE *argv, VALUE self) {
1090
+ if (3 > argc) {
1091
+ rb_raise(rb_eArgError, "incorrect number of arguments.");
641
1092
  }
642
- rstr = rb_str_new2(json);
643
- #if HAS_ENCODING_SUPPORT
644
- rb_enc_associate(rstr, oj_utf8_encoding);
645
- #endif
646
- if (2 <= argc && Qnil != argv[1]) {
647
- VALUE io = argv[1];
648
- VALUE args[1];
649
-
650
- *args = rstr;
651
- rb_funcall2(io, oj_write_id, 1, args);
652
- rstr = io;
1093
+ switch (rb_type(*argv)) {
1094
+ case T_CLASS:
1095
+ case T_MODULE:
1096
+ break;
1097
+ default:
1098
+ rb_raise(rb_eTypeError, "expected a class or module.");
1099
+ break;
653
1100
  }
654
- xfree(json);
1101
+ Check_Type(argv[2], T_SYMBOL);
1102
+ if (MAX_ODD_ARGS < argc - 2) {
1103
+ rb_raise(rb_eArgError, "too many members.");
1104
+ }
1105
+ oj_reg_odd(argv[0], argv[1], argv[2], argc - 3, argv + 3, false);
655
1106
 
656
- return rstr;
1107
+ return Qnil;
657
1108
  }
658
1109
 
659
- // This is the signature for the hash_foreach callback also.
660
- static int
661
- mimic_walk(VALUE key, VALUE obj, VALUE proc) {
662
- switch (rb_type(obj)) {
663
- case T_HASH:
664
- rb_hash_foreach(obj, mimic_walk, proc);
1110
+ /* Document-method: register_odd_raw
1111
+ * call-seq: register_odd_raw(clas, create_object, create_method, dump_method)
1112
+ *
1113
+ * Registers a class as special and expect the output to be a string that can be
1114
+ * included in the dumped JSON directly. This is useful for working around
1115
+ * subclasses of primitive types as is done with ActiveSupport classes. The use
1116
+ * of this function should be limited to just classes that can not be handled in
1117
+ * the normal way. It is not intended as a hook for changing the output of all
1118
+ * classes as it is not optimized for large numbers of classes. Be careful with
1119
+ * this option as the JSON may be incorrect if invalid JSON is returned.
1120
+ *
1121
+ * - *clas* [_Class_|_Module_] Class or Module to be made special
1122
+ * - *create_object* [_Object_] object to call the create method on
1123
+ * - *create_method* [_Symbol_] method on the clas that will create a new instance of the clas when given all the member values in the order specified.
1124
+ * - *dump_method* [_Symbol_|_String_] method to call on the object being serialized to generate the raw JSON.
1125
+ */
1126
+ static VALUE
1127
+ register_odd_raw(int argc, VALUE *argv, VALUE self) {
1128
+ if (3 > argc) {
1129
+ rb_raise(rb_eArgError, "incorrect number of arguments.");
1130
+ }
1131
+ switch (rb_type(*argv)) {
1132
+ case T_CLASS:
1133
+ case T_MODULE:
665
1134
  break;
666
- case T_ARRAY:
667
- {
668
- VALUE *np = RARRAY_PTR(obj);
669
- size_t cnt = RARRAY_LEN(obj);
670
-
671
- for (; 0 < cnt; cnt--, np++) {
672
- mimic_walk(Qnil, *np, proc);
673
- }
674
- break;
675
- }
676
1135
  default:
1136
+ rb_raise(rb_eTypeError, "expected a class or module.");
677
1137
  break;
678
1138
  }
679
- if (Qnil == proc) {
680
- if (rb_block_given_p()) {
681
- rb_yield(obj);
682
- }
683
- } else {
684
- #if HAS_PROC_WITH_BLOCK
685
- VALUE args[1];
686
-
687
- *args = obj;
688
- rb_proc_call_with_block(proc, 1, args, Qnil);
689
- #else
690
- rb_raise(rb_eNotImpError, "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
691
- #endif
1139
+ Check_Type(argv[2], T_SYMBOL);
1140
+ if (MAX_ODD_ARGS < argc - 2) {
1141
+ rb_raise(rb_eArgError, "too many members.");
692
1142
  }
693
- return ST_CONTINUE;
1143
+ oj_reg_odd(argv[0], argv[1], argv[2], 1, argv + 3, true);
1144
+
1145
+ return Qnil;
694
1146
  }
695
1147
 
696
- static VALUE
697
- mimic_load(int argc, VALUE *argv, VALUE self) {
698
- VALUE obj = load(1, argv, self);
699
- VALUE p = Qnil;
1148
+ ////////////////////////////////////////////////////////////////////////////////
1149
+ // RDoc entries must be in the same file as the rb_define_method and must be
1150
+ // directly above the C method function. The extern declaration is enough to
1151
+ // get it to work.
1152
+ ////////////////////////////////////////////////////////////////////////////////
700
1153
 
701
- if (2 <= argc) {
702
- p = argv[1];
703
- }
704
- mimic_walk(Qnil, obj, p);
1154
+ /* Document-method: strict_load
1155
+ * call-seq: strict_load(json, options) { _|_obj, start, len_|_ }
1156
+ *
1157
+ * Parses a JSON document String into an Hash, Array, String, Fixnum, Float,
1158
+ * true, false, or nil. It parses using a mode that is strict in that it maps
1159
+ * each primitive JSON type to a similar Ruby type. The :create_id is not
1160
+ * honored in this mode. Note that a Ruby Hash is used to represent the JSON
1161
+ * Object type. These two are not the same since the JSON Object type can have
1162
+ * repeating entries with the same key and Ruby Hash can not.
1163
+ *
1164
+ * When used with a document that has multiple JSON elements the block, if
1165
+ * any, will be yielded to. If no block then the last element read will be
1166
+ * returned.
1167
+ *
1168
+ * Raises an exception if the JSON is malformed or the classes specified are not
1169
+ * valid. If the input is not a valid JSON document (an empty string is not a
1170
+ * valid JSON document) an exception is raised.
1171
+ *
1172
+ * A block can be provided with a single argument. That argument will be the
1173
+ * parsed JSON document. This is useful when parsing a string that includes
1174
+ * multiple JSON documents. The block can take up to 3 arguments, the parsed
1175
+ * object, the position in the string or stream of the start of the JSON for
1176
+ * that object, and the length of the JSON for that object plus trailing
1177
+ * whitespace.
1178
+ *
1179
+ * - *json* [_String_|_IO_] JSON String or an Object that responds to read().
1180
+ * - *options* [_Hash_] load options (same as default_options).
1181
+ * - -
1182
+ * - *obj* [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_] parsed object.
1183
+ * - *start* [_optional, _Integer_] start position of parsed JSON for obj.
1184
+ * - *len* [_optional, _Integer_] length of parsed JSON for obj.
1185
+ *
1186
+ * Returns [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_]
1187
+ */
1188
+ extern VALUE oj_strict_parse(int argc, VALUE *argv, VALUE self);
705
1189
 
706
- return obj;
707
- }
1190
+ /* Document-method: compat_load
1191
+ * call-seq: compat_load(json, options) { _|_obj, start, len_|_ }
1192
+ *
1193
+ * Parses a JSON document String into an Object, Hash, Array, String, Fixnum,
1194
+ * Float, true, false, or nil. It parses using a mode that is generally
1195
+ * compatible with other Ruby JSON parsers in that it will create objects based
1196
+ * on the :create_id value. It is not compatible in every way to every other
1197
+ * parser though as each parser has it's own variations.
1198
+ *
1199
+ * When used with a document that has multiple JSON elements the block, if
1200
+ * any, will be yielded to. If no block then the last element read will be
1201
+ * returned.
1202
+ *
1203
+ * Raises an exception if the JSON is malformed or the classes specified are not
1204
+ * valid. If the input is not a valid JSON document (an empty string is not a
1205
+ * valid JSON document) an exception is raised.
1206
+ *
1207
+ * A block can be provided with a single argument. That argument will be the
1208
+ * parsed JSON document. This is useful when parsing a string that includes
1209
+ * multiple JSON documents. The block can take up to 3 arguments, the parsed
1210
+ * object, the position in the string or stream of the start of the JSON for
1211
+ * that object, and the length of the JSON for that object plus trailing
1212
+ * whitespace.
1213
+ *
1214
+ * - *json* [_String_|_IO_] JSON String or an Object that responds to read().
1215
+ * - *options* [_Hash_] load options (same as default_options).
1216
+ * - -
1217
+ * - *obj* [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_] parsed object.
1218
+ * - *start* [_optional, _Integer_] start position of parsed JSON for obj.
1219
+ * - *len* [_optional, _Integer_] length of parsed JSON for obj.
1220
+ *
1221
+ * Returns [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_]
1222
+ */
1223
+ extern VALUE oj_compat_parse(int argc, VALUE *argv, VALUE self);
708
1224
 
709
- static VALUE
710
- mimic_dump_load(int argc, VALUE *argv, VALUE self) {
711
- if (1 > argc) {
712
- rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
713
- } else if (T_STRING == rb_type(*argv)) {
714
- return mimic_load(argc, argv, self);
715
- } else {
716
- return mimic_dump(argc, argv, self);
717
- }
718
- return Qnil;
719
- }
1225
+ /* Document-method: object_load
1226
+ * call-seq: object_load(json, options) { _|_obj, start, len_|_ }
1227
+ *
1228
+ * Parses a JSON document String into an Object, Hash, Array, String, Fixnum,
1229
+ * Float, true, false, or nil. In the :object mode the JSON should have been
1230
+ * generated by Oj.dump(). The parser will reconstitute the original marshalled
1231
+ * or dumped Object. The :auto_define and :circular options have meaning with
1232
+ * this parsing mode.
1233
+ *
1234
+ * Raises an exception if the JSON is malformed or the classes specified are not
1235
+ * valid. If the input is not a valid JSON document (an empty string is not a
1236
+ * valid JSON document) an exception is raised.
1237
+ *
1238
+ * Note: Oj is not able to automatically deserialize all classes that are a
1239
+ * subclass of a Ruby Exception. Only exception that take one required string
1240
+ * argument in the initialize() method are supported. This is an example of how
1241
+ * to write an Exception subclass that supports both a single string intializer
1242
+ * and an Exception as an argument. Additional optional arguments can be added
1243
+ * as well.
1244
+ *
1245
+ * The reason for this restriction has to do with a design decision on the part
1246
+ * of the Ruby developers. Exceptions are special Objects. They do not follow the
1247
+ * rules of other Objects. Exceptions have 'mesg' and a 'bt' attribute. Note that
1248
+ * these are not '@mesg' and '@bt'. They can not be set using the normal C or
1249
+ * Ruby calls. The only way I have found to set the 'mesg' attribute is through
1250
+ * the initializer. Unfortunately that means any subclass that provides a
1251
+ * different initializer can not be automatically decoded. A way around this is
1252
+ * to use a create function but this example shows an alternative.
1253
+ *
1254
+ * A block can be provided with a single argument. That argument will be the
1255
+ * parsed JSON document. This is useful when parsing a string that includes
1256
+ * multiple JSON documents. The block can take up to 3 arguments, the parsed
1257
+ * object, the position in the string or stream of the start of the JSON for
1258
+ * that object, and the length of the JSON for that object plus trailing
1259
+ * whitespace.
1260
+ *
1261
+ * - *json* [_String_|_IO_] JSON String or an Object that responds to read().
1262
+ * - *options* [_Hash_] load options (same as default_options).
1263
+ * - -
1264
+ * - *obj* [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_] parsed object.
1265
+ * - *start* [_optional, _Integer_] start position of parsed JSON for obj.
1266
+ * - *len* [_optional, _Integer_] length of parsed JSON for obj.
1267
+ *
1268
+ * Returns [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_]
1269
+ */
1270
+ extern VALUE oj_object_parse(int argc, VALUE *argv, VALUE self);
720
1271
 
721
- static VALUE
722
- mimic_generate_core(int argc, VALUE *argv, Options copts) {
723
- char *json;
724
- VALUE rstr;
725
-
726
- if (2 == argc && Qnil != argv[1]) {
727
- struct _DumpOpts dump_opts;
728
- VALUE ropts = argv[1];
729
- VALUE v;
730
-
731
- memset(&dump_opts, 0, sizeof(dump_opts)); // may not be needed
732
- if (T_HASH != rb_type(ropts)) {
733
- rb_raise(rb_eArgError, "options must be a hash.");
734
- }
735
- if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
736
- rb_check_type(v, T_STRING);
737
- if (0 == copts->dump_opts) {
738
- copts->dump_opts = &dump_opts;
739
- }
740
- copts->dump_opts->indent = StringValuePtr(v);
741
- copts->dump_opts->indent_size = (uint8_t)strlen(copts->dump_opts->indent);
742
- }
743
- if (Qnil != (v = rb_hash_lookup(ropts, space_sym))) {
744
- rb_check_type(v, T_STRING);
745
- if (0 == copts->dump_opts) {
746
- copts->dump_opts = &dump_opts;
747
- }
748
- copts->dump_opts->after_sep = StringValuePtr(v);
749
- copts->dump_opts->after_size = (uint8_t)strlen(copts->dump_opts->after_sep);
750
- }
751
- if (Qnil != (v = rb_hash_lookup(ropts, space_before_sym))) {
752
- rb_check_type(v, T_STRING);
753
- if (0 == copts->dump_opts) {
754
- copts->dump_opts = &dump_opts;
755
- }
756
- copts->dump_opts->before_sep = StringValuePtr(v);
757
- copts->dump_opts->before_size = (uint8_t)strlen(copts->dump_opts->before_sep);
758
- }
759
- if (Qnil != (v = rb_hash_lookup(ropts, object_nl_sym))) {
760
- rb_check_type(v, T_STRING);
761
- if (0 == copts->dump_opts) {
762
- copts->dump_opts = &dump_opts;
763
- }
764
- copts->dump_opts->hash_nl = StringValuePtr(v);
765
- copts->dump_opts->hash_size = (uint8_t)strlen(copts->dump_opts->hash_nl);
766
- }
767
- if (Qnil != (v = rb_hash_lookup(ropts, array_nl_sym))) {
768
- rb_check_type(v, T_STRING);
769
- if (0 == copts->dump_opts) {
770
- copts->dump_opts = &dump_opts;
771
- }
772
- copts->dump_opts->array_nl = StringValuePtr(v);
773
- copts->dump_opts->array_size = (uint8_t)strlen(copts->dump_opts->array_nl);
774
- }
775
- // :allow_nan is not supported as Oj always allows_nan
776
- // :max_nesting is always set to 100
777
- }
778
- if (0 == (json = oj_write_obj_to_str(*argv, copts))) {
779
- rb_raise(rb_eNoMemError, "Not enough memory.");
780
- }
781
- rstr = rb_str_new2(json);
782
- #if HAS_ENCODING_SUPPORT
783
- rb_enc_associate(rstr, oj_utf8_encoding);
784
- #endif
785
- xfree(json);
1272
+ /* Document-method: add_to_json
1273
+ * call-seq: add_to_json(*args)
1274
+ *
1275
+ * Override simple to_s dump behavior in :compat mode to instead use an
1276
+ * optimized dump that includes the classname and attributes so that the
1277
+ * object can be re-created on load. The format is the same as the json gem
1278
+ * but does not use the ruby methods for encoding.
1279
+ *
1280
+ * The classes supported for optimization are: Array, BigDecimal, Complex,
1281
+ * Date, DateTime, Exception, Hash, Integer, OpenStruct, Range, Rational,
1282
+ * Regexp, Struct, and Time. Providing no classes will result in all those
1283
+ * classes being optimized.q
1284
+ *
1285
+ * - *args( [_Class_] zero or more classes to optimize.
1286
+ */
1287
+ extern VALUE oj_add_to_json(int argc, VALUE *argv, VALUE self);
786
1288
 
787
- return rstr;
788
- }
1289
+ /* @!method remove_to_json(*args)
1290
+ *
1291
+ * Reverts back to the to_s dump behavior in :compat mode to instead use an
1292
+ * optimized dump that includes the classname and attributes so that the
1293
+ * object can be re-created on load. The format is the same as the json gem
1294
+ * but does not use the ruby methods for encoding.
1295
+ *
1296
+ * The classes supported for optimization are: Array, BigDecimal, Complex,
1297
+ * Date, DateTime, Exception, Hash, Integer, OpenStruct, Range, Rational,
1298
+ * Regexp, Struct, and Time. Providing no classes will result in all those
1299
+ * classes being reverted from the optimized mode.
1300
+ *
1301
+ * - *args* [_Class_] zero or more classes to optimize.
1302
+ */
1303
+ extern VALUE oj_remove_to_json(int argc, VALUE *argv, VALUE self);
789
1304
 
790
- static VALUE
791
- mimic_generate(int argc, VALUE *argv, VALUE self) {
792
- struct _Options copts = oj_default_options;
1305
+ /* Document-method: mimic_JSON
1306
+ * call-seq: mimic_JSON()
1307
+ *
1308
+ * Creates the JSON module with methods and classes to mimic the JSON gem. After
1309
+ * this method is invoked calls that expect the JSON module will use Oj instead
1310
+ * and be faster than the original JSON. Most options that could be passed to
1311
+ * the JSON methods are supported. The calls to set parser or generator will not
1312
+ * raise an Exception but will not have any effect. The method can also be
1313
+ * called after the json gem is loaded. The necessary methods on the json gem
1314
+ * will be replaced with Oj methods.
1315
+ *
1316
+ * Note that this also sets the default options of :mode to :compat and
1317
+ * :encoding to :ascii.
1318
+ *
1319
+ * Returns [_Module_] the JSON module.
1320
+ */
1321
+ extern VALUE oj_define_mimic_json(int argc, VALUE *argv, VALUE self);
793
1322
 
794
- return mimic_generate_core(argc, argv, &copts);
795
- }
1323
+ /* Document-method: generate
1324
+ * call-seq: generate(obj, opts=nil)
1325
+ *
1326
+ * Encode obj as a JSON String. The obj argument must be a Hash, Array, or
1327
+ * respond to to_h or to_json. Options other than those listed such as
1328
+ * +:allow_nan+ or +:max_nesting+ are ignored.
1329
+ *
1330
+ * - *obj* [_Object__|_Hash_|_Array_] object to convert to a JSON String
1331
+ * - *opts* [_Hash_] options
1332
+ * - - *:indent* [_String_] String to use for indentation.
1333
+ * - *:space* [_String_] String placed after a , or : delimiter
1334
+ * - *:space * _before [_String_] String placed before a : delimiter
1335
+ * - *:object_nl* [_String_] String placed after a JSON object
1336
+ * - *:array_nl* [_String_] String placed after a JSON array
1337
+ * - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters in the output. Note JSON.generate does support this even if it is not documented.
1338
+ *
1339
+ * Returns [_String_]generated JSON.
1340
+ */
1341
+ extern VALUE oj_mimic_generate(int argc, VALUE *argv, VALUE self);
1342
+
1343
+ /*
1344
+ extern void oj_hash_test();
796
1345
 
797
1346
  static VALUE
798
- mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
799
- struct _Options copts = oj_default_options;
800
- struct _DumpOpts dump_opts;
801
-
802
- dump_opts.indent = " ";
803
- dump_opts.indent_size = (uint8_t)strlen(dump_opts.indent);
804
- dump_opts.before_sep = " ";
805
- dump_opts.before_size = (uint8_t)strlen(dump_opts.before_sep);
806
- dump_opts.after_sep = " ";
807
- dump_opts.after_size = (uint8_t)strlen(dump_opts.after_sep);
808
- dump_opts.hash_nl = "\n";
809
- dump_opts.hash_size = (uint8_t)strlen(dump_opts.hash_nl);
810
- dump_opts.array_nl = "\n";
811
- dump_opts.array_size = (uint8_t)strlen(dump_opts.array_nl);
812
- copts.dump_opts = &dump_opts;
813
-
814
- return mimic_generate_core(argc, argv, &copts);
1347
+ hash_test(VALUE self) {
1348
+ oj_hash_test();
1349
+ return Qnil;
815
1350
  }
1351
+ */
816
1352
 
1353
+ #if !HAS_ENCODING_SUPPORT
817
1354
  static VALUE
818
- mimic_parse(int argc, VALUE *argv, VALUE self) {
819
- struct _Options options = oj_default_options;
1355
+ iconv_encoder(VALUE x) {
1356
+ VALUE iconv;
820
1357
 
821
- if (1 > argc) {
822
- rb_raise(rb_eArgError, "Wrong number of arguments to parse().");
823
- }
824
- if (2 <= argc && Qnil != argv[1]) {
825
- VALUE ropts = argv[1];
826
- VALUE v;
1358
+ rb_require("iconv");
1359
+ iconv = rb_const_get(rb_cObject, rb_intern("Iconv"));
827
1360
 
828
- if (T_HASH != rb_type(ropts)) {
829
- rb_raise(rb_eArgError, "options must be a hash.");
830
- }
831
- if (Qnil != (v = rb_hash_lookup(ropts, symbolize_names_sym))) {
832
- options.sym_key = (Qtrue == v) ? Yes : No;
833
- }
834
- if (Qnil != (v = rb_hash_lookup(ropts, create_additions_sym))) {
835
- options.mode = (Qtrue == v) ? CompatMode : StrictMode;
836
- }
837
- // :allow_nan is not supported as Oj always allows nan
838
- // :max_nesting is always set to 100
839
- // :object_class is always Hash
840
- // :array_class is always Array
841
- }
842
- return load_with_opts(*argv, &options);
1361
+ return rb_funcall(iconv, rb_intern("new"), 2, rb_str_new2("ASCII//TRANSLIT"), rb_str_new2("UTF-8"));
843
1362
  }
844
1363
 
845
1364
  static VALUE
846
- mimic_recurse_proc(VALUE self, VALUE obj) {
847
- rb_need_block();
848
- mimic_walk(Qnil, obj, Qnil);
849
-
1365
+ iconv_rescue(VALUE x) {
850
1366
  return Qnil;
851
1367
  }
1368
+ #endif
852
1369
 
853
1370
  static VALUE
854
- no_op1(VALUE self, VALUE obj) {
1371
+ protect_require(VALUE x) {
1372
+ rb_require("time");
1373
+ rb_require("bigdecimal");
855
1374
  return Qnil;
856
1375
  }
857
1376
 
858
- static VALUE
859
- mimic_create_id(VALUE self, VALUE id) {
860
- Check_Type(id, T_STRING);
861
-
862
- if (0 != oj_default_options.create_id) {
863
- if (json_class != oj_default_options.create_id) {
864
- xfree((char*)oj_default_options.create_id);
865
- }
866
- oj_default_options.create_id = 0;
867
- }
868
- if (Qnil != id) {
869
- size_t len = RSTRING_LEN(id) + 1;
870
-
871
- oj_default_options.create_id = ALLOC_N(char, len);
872
- strcpy((char*)oj_default_options.create_id, StringValuePtr(id));
873
- }
874
- return id;
875
- }
876
-
877
- /* Document-method: mimic_JSON
878
- * call-seq: mimic_JSON() => Module
1377
+ /* Document-module: Oj
1378
+ *
1379
+ * Optimized JSON (Oj), as the name implies was written to provide speed
1380
+ * optimized JSON handling.
1381
+ *
1382
+ * Oj uses modes to control how object are encoded and decoded. In addition
1383
+ * global and options to methods allow additional behavior modifications. The
1384
+ * modes are:
1385
+ *
1386
+ * - *:strict* mode will only allow the 7 basic JSON types to be serialized. Any other Object
1387
+ * will raise an Exception.
1388
+ *
1389
+ * - *:null* mode is similar to the :strict mode except any Object that is not
1390
+ * one of the JSON base types is replaced by a JSON null.
1391
+ *
1392
+ * - *:object* mode will dump any Object as a JSON Object with keys that match
1393
+ * the Ruby Object's variable names without the '@' character. This is the
1394
+ * highest performance mode.
1395
+ *
1396
+ * - *:compat* or *:json* mode is the compatible mode for the json gem. It mimics
1397
+ * the json gem including the options, defaults, and restrictions.
1398
+ *
1399
+ * - *:rails* is the compatibility mode for Rails or Active support.
879
1400
  *
880
- * Creates the JSON module with methods and classes to mimic the JSON
881
- * gem. After this method is invoked calls that expect the JSON module will
882
- * use Oj instead and be faster than the original JSON. Most options that
883
- * could be passed to the JSON methods are supported. The calls to set parser
884
- * or generator will not raise an Exception but will not have any effect.
1401
+ * - *:custom* is the most configurable mode.
885
1402
  */
886
- static VALUE
887
- define_mimic_json(int argc, VALUE *argv, VALUE self) {
888
- if (Qnil == mimic) {
889
- VALUE ext;
890
- VALUE dummy;
891
-
892
- if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
893
- rb_raise(rb_const_get_at(Oj, rb_intern("MimicError")),
894
- "JSON module already exists. Can not mimic. Do not require 'json' before calling mimic_JSON.");
895
- }
896
- mimic = rb_define_module("JSON");
897
- ext = rb_define_module_under(mimic, "Ext");
898
- dummy = rb_define_class_under(ext, "Parser", rb_cObject);
899
- dummy = rb_define_class_under(ext, "Generator", rb_cObject);
900
- // convince Ruby that the json gem has already been loaded
901
- dummy = rb_gv_get("$LOADED_FEATURES");
902
- if (rb_type(dummy) == T_ARRAY) {
903
- rb_ary_push(dummy, rb_str_new2("json"));
904
- if (0 < argc) {
905
- VALUE mimic_args[1];
906
-
907
- *mimic_args = *argv;
908
- rb_funcall2(Oj, rb_intern("mimic_loaded"), 1, mimic_args);
909
- } else {
910
- rb_funcall2(Oj, rb_intern("mimic_loaded"), 0, 0);
911
- }
912
- }
1403
+ void
1404
+ Init_oj() {
1405
+ int err = 0;
913
1406
 
914
- rb_define_module_function(mimic, "parser=", no_op1, 1);
915
- rb_define_module_function(mimic, "generator=", no_op1, 1);
916
- rb_define_module_function(mimic, "create_id=", mimic_create_id, 1);
917
-
918
- rb_define_module_function(mimic, "dump", mimic_dump, -1);
919
- rb_define_module_function(mimic, "load", mimic_load, -1);
920
- rb_define_module_function(mimic, "restore", mimic_load, -1);
921
- rb_define_module_function(mimic, "recurse_proc", mimic_recurse_proc, 1);
922
- rb_define_module_function(mimic, "[]", mimic_dump_load, -1);
923
-
924
- rb_define_module_function(mimic, "generate", mimic_generate, -1);
925
- rb_define_module_function(mimic, "fast_generate", mimic_generate, -1);
926
- rb_define_module_function(mimic, "pretty_generate", mimic_pretty_generate, -1);
927
- /* for older versions of JSON, the deprecated unparse methods */
928
- rb_define_module_function(mimic, "unparse", mimic_generate, -1);
929
- rb_define_module_function(mimic, "fast_unparse", mimic_generate, -1);
930
- rb_define_module_function(mimic, "pretty_unparse", mimic_pretty_generate, -1);
931
-
932
- rb_define_module_function(mimic, "parse", mimic_parse, -1);
933
- rb_define_module_function(mimic, "parse!", mimic_parse, -1);
934
-
935
- array_nl_sym = ID2SYM(rb_intern("array_nl")); rb_gc_register_address(&array_nl_sym);
936
- create_additions_sym = ID2SYM(rb_intern("create_additions")); rb_gc_register_address(&create_additions_sym);
937
- object_nl_sym = ID2SYM(rb_intern("object_nl")); rb_gc_register_address(&object_nl_sym);
938
- space_before_sym = ID2SYM(rb_intern("space_before")); rb_gc_register_address(&space_before_sym);
939
- space_sym = ID2SYM(rb_intern("space")); rb_gc_register_address(&space_sym);
940
- symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
941
-
942
- oj_default_options.mode = CompatMode;
943
- oj_default_options.ascii_only = Yes;
944
- }
945
- return mimic;
946
- }
1407
+ Oj = rb_define_module("Oj");
947
1408
 
948
- void Init_oj() {
949
- Odd odd;
950
- ID *idp;
1409
+ oj_cstack_class = rb_define_class_under(Oj, "CStack", rb_cObject);
951
1410
 
952
- Oj = rb_define_module("Oj");
1411
+ oj_string_writer_init();
1412
+ oj_stream_writer_init();
953
1413
 
954
- rb_require("time");
955
1414
  rb_require("date");
956
- rb_require("bigdecimal");
1415
+ // On Rubinius the require fails but can be done from a ruby file.
1416
+ rb_protect(protect_require, Qnil, &err);
1417
+ #if NEEDS_RATIONAL
1418
+ rb_require("rational");
1419
+ #endif
957
1420
  rb_require("stringio");
1421
+ #if HAS_ENCODING_SUPPORT
1422
+ oj_utf8_encoding = rb_enc_find("UTF-8");
1423
+ #else
1424
+ // need an option to turn this on
1425
+ oj_utf8_encoding = rb_rescue(iconv_encoder, Qnil, iconv_rescue, Qnil);
1426
+ oj_utf8_encoding = Qnil;
1427
+ #endif
1428
+
1429
+ //rb_define_module_function(Oj, "hash_test", hash_test, 0);
958
1430
 
959
1431
  rb_define_module_function(Oj, "default_options", get_def_opts, 0);
960
1432
  rb_define_module_function(Oj, "default_options=", set_def_opts, 1);
961
1433
 
962
- rb_define_module_function(Oj, "mimic_JSON", define_mimic_json, -1);
1434
+ rb_define_module_function(Oj, "mimic_JSON", oj_define_mimic_json, -1);
963
1435
  rb_define_module_function(Oj, "load", load, -1);
964
1436
  rb_define_module_function(Oj, "load_file", load_file, -1);
1437
+ rb_define_module_function(Oj, "safe_load", safe_load, 1);
1438
+ rb_define_module_function(Oj, "strict_load", oj_strict_parse, -1);
1439
+ rb_define_module_function(Oj, "compat_load", oj_compat_parse, -1);
1440
+ rb_define_module_function(Oj, "object_load", oj_object_parse, -1);
1441
+
965
1442
  rb_define_module_function(Oj, "dump", dump, -1);
1443
+
966
1444
  rb_define_module_function(Oj, "to_file", to_file, -1);
1445
+ rb_define_module_function(Oj, "to_stream", to_stream, -1);
1446
+ // JSON gem compatibility
1447
+ rb_define_module_function(Oj, "to_json", to_json, -1);
1448
+ rb_define_module_function(Oj, "generate", oj_mimic_generate, -1);
1449
+ rb_define_module_function(Oj, "fast_generate", oj_mimic_generate, -1);
967
1450
 
968
- rb_define_module_function(Oj, "saj_parse", saj_parse, -1);
1451
+ rb_define_module_function(Oj, "add_to_json", oj_add_to_json, -1);
1452
+ rb_define_module_function(Oj, "remove_to_json", oj_remove_to_json, -1);
1453
+
1454
+ rb_define_module_function(Oj, "register_odd", register_odd, -1);
1455
+ rb_define_module_function(Oj, "register_odd_raw", register_odd_raw, -1);
1456
+
1457
+ rb_define_module_function(Oj, "saj_parse", oj_saj_parse, -1);
1458
+ rb_define_module_function(Oj, "sc_parse", oj_sc_parse, -1);
969
1459
 
970
1460
  oj_add_value_id = rb_intern("add_value");
1461
+ oj_array_append_id = rb_intern("array_append");
971
1462
  oj_array_end_id = rb_intern("array_end");
972
1463
  oj_array_start_id = rb_intern("array_start");
973
1464
  oj_as_json_id = rb_intern("as_json");
974
1465
  oj_error_id = rb_intern("error");
1466
+ oj_begin_id = rb_intern("begin");
1467
+ oj_end_id = rb_intern("end");
1468
+ oj_exclude_end_id = rb_intern("exclude_end?");
1469
+ oj_file_id = rb_intern("file?");
975
1470
  oj_fileno_id = rb_intern("fileno");
1471
+ oj_ftype_id = rb_intern("ftype");
976
1472
  oj_hash_end_id = rb_intern("hash_end");
1473
+ oj_hash_key_id = rb_intern("hash_key");
1474
+ oj_hash_set_id = rb_intern("hash_set");
977
1475
  oj_hash_start_id = rb_intern("hash_start");
1476
+ oj_iconv_id = rb_intern("iconv");
978
1477
  oj_instance_variables_id = rb_intern("instance_variables");
979
1478
  oj_json_create_id = rb_intern("json_create");
1479
+ oj_length_id = rb_intern("length");
980
1480
  oj_new_id = rb_intern("new");
1481
+ oj_parse_id = rb_intern("parse");
1482
+ oj_pos_id = rb_intern("pos");
981
1483
  oj_read_id = rb_intern("read");
1484
+ oj_readpartial_id = rb_intern("readpartial");
1485
+ oj_replace_id = rb_intern("replace");
1486
+ oj_stat_id = rb_intern("stat");
982
1487
  oj_string_id = rb_intern("string");
983
1488
  oj_to_hash_id = rb_intern("to_hash");
1489
+ oj_to_h_id = rb_intern("to_h");
984
1490
  oj_to_json_id = rb_intern("to_json");
985
1491
  oj_to_s_id = rb_intern("to_s");
986
1492
  oj_to_sym_id = rb_intern("to_sym");
@@ -988,248 +1494,101 @@ void Init_oj() {
988
1494
  oj_tv_nsec_id = rb_intern("tv_nsec");
989
1495
  oj_tv_sec_id = rb_intern("tv_sec");
990
1496
  oj_tv_usec_id = rb_intern("tv_usec");
1497
+ oj_utc_id = rb_intern("utc");
991
1498
  oj_utc_offset_id = rb_intern("utc_offset");
1499
+ oj_utcq_id = rb_intern("utc?");
992
1500
  oj_write_id = rb_intern("write");
1501
+ oj_has_key_id = rb_intern("has_key?");
1502
+
1503
+ rb_require("oj/bag");
1504
+ rb_require("oj/error");
1505
+ rb_require("oj/mimic");
1506
+ rb_require("oj/saj");
1507
+ rb_require("oj/schandler");
993
1508
 
994
1509
  oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
995
- oj_parse_error_class = rb_const_get_at(Oj, rb_intern("ParseError"));
996
- oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
997
- oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
998
1510
  oj_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1511
+ oj_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
1512
+ oj_datetime_class = rb_const_get(rb_cObject, rb_intern("DateTime"));
1513
+ oj_enumerable_class = rb_const_get(rb_cObject, rb_intern("Enumerable"));
1514
+ oj_parse_error_class = rb_const_get_at(Oj, rb_intern("ParseError"));
999
1515
  oj_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
1000
-
1001
- ascii_only_sym = ID2SYM(rb_intern("ascii_only")); rb_gc_register_address(&ascii_only_sym);
1002
- auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
1003
- circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
1004
- compat_sym = ID2SYM(rb_intern("compat")); rb_gc_register_address(&compat_sym);
1005
- create_id_sym = ID2SYM(rb_intern("create_id")); rb_gc_register_address(&create_id_sym);
1006
- indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&indent_sym);
1007
- max_stack_sym = ID2SYM(rb_intern("max_stack")); rb_gc_register_address(&max_stack_sym);
1008
- mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
1009
- null_sym = ID2SYM(rb_intern("null")); rb_gc_register_address(&null_sym);
1010
- object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
1011
- ruby_sym = ID2SYM(rb_intern("ruby")); rb_gc_register_address(&ruby_sym);
1012
- strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
1013
- symbol_keys_sym = ID2SYM(rb_intern("symbol_keys")); rb_gc_register_address(&symbol_keys_sym);
1014
- time_format_sym = ID2SYM(rb_intern("time_format")); rb_gc_register_address(&time_format_sym);
1015
- unix_sym = ID2SYM(rb_intern("unix")); rb_gc_register_address(&unix_sym);
1016
- xmlschema_sym = ID2SYM(rb_intern("xmlschema")); rb_gc_register_address(&xmlschema_sym);
1017
-
1018
- oj_slash_string = rb_str_new2("/"); rb_gc_register_address(&oj_slash_string);
1516
+ oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
1517
+ oj_json_parser_error_class = rb_eEncodingError; // replaced if mimic is called
1518
+ oj_json_generator_error_class = rb_eEncodingError; // replaced if mimic is called
1519
+
1520
+ allow_blank_sym = ID2SYM(rb_intern("allow_blank")); rb_gc_register_address(&allow_blank_sym);
1521
+ allow_gc_sym = ID2SYM(rb_intern("allow_gc")); rb_gc_register_address(&allow_gc_sym);
1522
+ allow_invalid_unicode_sym = ID2SYM(rb_intern("allow_invalid_unicode"));rb_gc_register_address(&allow_invalid_unicode_sym);
1523
+ ascii_sym = ID2SYM(rb_intern("ascii")); rb_gc_register_address(&ascii_sym);
1524
+ auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
1525
+ auto_sym = ID2SYM(rb_intern("auto")); rb_gc_register_address(&auto_sym);
1526
+ bigdecimal_as_decimal_sym = ID2SYM(rb_intern("bigdecimal_as_decimal"));rb_gc_register_address(&bigdecimal_as_decimal_sym);
1527
+ bigdecimal_load_sym = ID2SYM(rb_intern("bigdecimal_load")); rb_gc_register_address(&bigdecimal_load_sym);
1528
+ bigdecimal_sym = ID2SYM(rb_intern("bigdecimal")); rb_gc_register_address(&bigdecimal_sym);
1529
+ circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
1530
+ class_cache_sym = ID2SYM(rb_intern("class_cache")); rb_gc_register_address(&class_cache_sym);
1531
+ compat_sym = ID2SYM(rb_intern("compat")); rb_gc_register_address(&compat_sym);
1532
+ create_id_sym = ID2SYM(rb_intern("create_id")); rb_gc_register_address(&create_id_sym);
1533
+ custom_sym = ID2SYM(rb_intern("custom")); rb_gc_register_address(&custom_sym);
1534
+ empty_string_sym = ID2SYM(rb_intern("empty_string")); rb_gc_register_address(&empty_string_sym);
1535
+ escape_mode_sym = ID2SYM(rb_intern("escape_mode")); rb_gc_register_address(&escape_mode_sym);
1536
+ float_prec_sym = ID2SYM(rb_intern("float_precision")); rb_gc_register_address(&float_prec_sym);
1537
+ float_sym = ID2SYM(rb_intern("float")); rb_gc_register_address(&float_sym);
1538
+ huge_sym = ID2SYM(rb_intern("huge")); rb_gc_register_address(&huge_sym);
1539
+ json_sym = ID2SYM(rb_intern("json")); rb_gc_register_address(&json_sym);
1540
+ match_string_sym = ID2SYM(rb_intern("match_string")); rb_gc_register_address(&match_string_sym);
1541
+ mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
1542
+ nan_sym = ID2SYM(rb_intern("nan")); rb_gc_register_address(&nan_sym);
1543
+ newline_sym = ID2SYM(rb_intern("newline")); rb_gc_register_address(&newline_sym);
1544
+ nilnil_sym = ID2SYM(rb_intern("nilnil")); rb_gc_register_address(&nilnil_sym);
1545
+ null_sym = ID2SYM(rb_intern("null")); rb_gc_register_address(&null_sym);
1546
+ object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
1547
+ oj_allow_nan_sym = ID2SYM(rb_intern("allow_nan")); rb_gc_register_address(&oj_allow_nan_sym);
1548
+ oj_array_class_sym = ID2SYM(rb_intern("array_class")); rb_gc_register_address(&oj_array_class_sym);
1549
+ oj_array_nl_sym = ID2SYM(rb_intern("array_nl")); rb_gc_register_address(&oj_array_nl_sym);
1550
+ oj_ascii_only_sym = ID2SYM(rb_intern("ascii_only")); rb_gc_register_address(&oj_ascii_only_sym);
1551
+ oj_create_additions_sym = ID2SYM(rb_intern("create_additions"));rb_gc_register_address(&oj_create_additions_sym);
1552
+ oj_hash_class_sym = ID2SYM(rb_intern("hash_class")); rb_gc_register_address(&oj_hash_class_sym);
1553
+ oj_indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&oj_indent_sym);
1554
+ oj_max_nesting_sym = ID2SYM(rb_intern("max_nesting")); rb_gc_register_address(&oj_max_nesting_sym);
1555
+ oj_object_class_sym = ID2SYM(rb_intern("object_class")); rb_gc_register_address(&oj_object_class_sym);
1556
+ oj_object_nl_sym = ID2SYM(rb_intern("object_nl")); rb_gc_register_address(&oj_object_nl_sym);
1557
+ oj_quirks_mode_sym = ID2SYM(rb_intern("quirks_mode")); rb_gc_register_address(&oj_quirks_mode_sym);
1558
+ oj_space_before_sym = ID2SYM(rb_intern("space_before")); rb_gc_register_address(&oj_space_before_sym);
1559
+ oj_space_sym = ID2SYM(rb_intern("space")); rb_gc_register_address(&oj_space_sym);
1560
+ omit_nil_sym = ID2SYM(rb_intern("omit_nil")); rb_gc_register_address(&omit_nil_sym);
1561
+ rails_sym = ID2SYM(rb_intern("rails")); rb_gc_register_address(&rails_sym);
1562
+ raise_sym = ID2SYM(rb_intern("raise")); rb_gc_register_address(&raise_sym);
1563
+ ruby_sym = ID2SYM(rb_intern("ruby")); rb_gc_register_address(&ruby_sym);
1564
+ sec_prec_sym = ID2SYM(rb_intern("second_precision")); rb_gc_register_address(&sec_prec_sym);
1565
+ strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
1566
+ symbol_keys_sym = ID2SYM(rb_intern("symbol_keys")); rb_gc_register_address(&symbol_keys_sym);
1567
+ time_format_sym = ID2SYM(rb_intern("time_format")); rb_gc_register_address(&time_format_sym);
1568
+ unicode_xss_sym = ID2SYM(rb_intern("unicode_xss")); rb_gc_register_address(&unicode_xss_sym);
1569
+ unix_sym = ID2SYM(rb_intern("unix")); rb_gc_register_address(&unix_sym);
1570
+ unix_zone_sym = ID2SYM(rb_intern("unix_zone")); rb_gc_register_address(&unix_zone_sym);
1571
+ use_as_json_sym = ID2SYM(rb_intern("use_as_json")); rb_gc_register_address(&use_as_json_sym);
1572
+ use_to_hash_sym = ID2SYM(rb_intern("use_to_hash")); rb_gc_register_address(&use_to_hash_sym);
1573
+ use_to_json_sym = ID2SYM(rb_intern("use_to_json")); rb_gc_register_address(&use_to_json_sym);
1574
+ word_sym = ID2SYM(rb_intern("word")); rb_gc_register_address(&word_sym);
1575
+ xmlschema_sym = ID2SYM(rb_intern("xmlschema")); rb_gc_register_address(&xmlschema_sym);
1576
+ xss_safe_sym = ID2SYM(rb_intern("xss_safe")); rb_gc_register_address(&xss_safe_sym);
1577
+
1578
+ oj_slash_string = rb_str_new2("/"); rb_gc_register_address(&oj_slash_string);
1019
1579
 
1020
1580
  oj_default_options.mode = ObjectMode;
1021
- #if HAS_ENCODING_SUPPORT
1022
- oj_utf8_encoding = rb_enc_find("UTF-8");
1023
- #endif
1024
1581
 
1025
- oj_cache_new(&oj_class_cache);
1026
- oj_cache_new(&oj_attr_cache);
1027
-
1028
- odd = odds;
1029
- // Rational
1030
- idp = odd->attrs;
1031
- odd->clas = rb_const_get(rb_cObject, rb_intern("Rational"));
1032
- odd->create_obj = rb_cObject;
1033
- odd->create_op = rb_intern("Rational");
1034
- odd->attr_cnt = 2;
1035
- *idp++ = rb_intern("numerator");
1036
- *idp++ = rb_intern("denominator");
1037
- *idp++ = 0;
1038
- // Date
1039
- odd++;
1040
- idp = odd->attrs;
1041
- odd->clas = rb_const_get(rb_cObject, rb_intern("Date"));
1042
- odd->create_obj = odd->clas;
1043
- odd->create_op = oj_new_id;
1044
- odd->attr_cnt = 4;
1045
- *idp++ = rb_intern("year");
1046
- *idp++ = rb_intern("month");
1047
- *idp++ = rb_intern("day");
1048
- *idp++ = rb_intern("start");
1049
- *idp++ = 0;
1050
- // DateTime
1051
- odd++;
1052
- idp = odd->attrs;
1053
- odd->clas = rb_const_get(rb_cObject, rb_intern("DateTime"));
1054
- odd->create_obj = odd->clas;
1055
- odd->create_op = oj_new_id;
1056
- odd->attr_cnt = 8;
1057
- *idp++ = rb_intern("year");
1058
- *idp++ = rb_intern("month");
1059
- *idp++ = rb_intern("day");
1060
- *idp++ = rb_intern("hour");
1061
- *idp++ = rb_intern("min");
1062
- *idp++ = rb_intern("sec");
1063
- *idp++ = rb_intern("offset");
1064
- *idp++ = rb_intern("start");
1065
- *idp++ = 0;
1066
- // Range
1067
- odd++;
1068
- idp = odd->attrs;
1069
- odd->clas = rb_const_get(rb_cObject, rb_intern("Range"));
1070
- odd->create_obj = odd->clas;
1071
- odd->create_op = oj_new_id;
1072
- odd->attr_cnt = 3;
1073
- *idp++ = rb_intern("begin");
1074
- *idp++ = rb_intern("end");
1075
- *idp++ = rb_intern("exclude_end?");
1076
- *idp++ = 0;
1077
- // The end. bump up the size of odds if a new class is added.
1078
- odd++;
1079
- odd->clas = Qundef;
1080
-
1081
- #if SAFE_CACHE
1582
+ oj_hash_init();
1583
+ oj_odd_init();
1584
+ oj_mimic_rails_init();
1585
+
1586
+ #if USE_PTHREAD_MUTEX
1082
1587
  pthread_mutex_init(&oj_cache_mutex, 0);
1588
+ #elif USE_RB_MUTEX
1589
+ oj_cache_mutex = rb_mutex_new();
1590
+ rb_gc_register_address(&oj_cache_mutex);
1083
1591
  #endif
1084
1592
  oj_init_doc();
1085
1593
  }
1086
1594
 
1087
- void
1088
- _oj_raise_error(const char *msg, const char *json, const char *current, const char* file, int line) {
1089
- int jline = 1;
1090
- int col = 1;
1091
-
1092
- for (; json < current && '\n' != *current; current--) {
1093
- col++;
1094
- }
1095
- for (; json < current; current--) {
1096
- if ('\n' == *current) {
1097
- jline++;
1098
- }
1099
- }
1100
- rb_raise(oj_parse_error_class, "%s at line %d, column %d [%s:%d]", msg, jline, col, file, line);
1101
- }
1102
-
1103
- // mimic JSON documentation
1104
-
1105
- /* Document-module: JSON
1106
- *
1107
- * JSON is a JSON parser. This module when defined by the Oj module is a
1108
- * faster replacement for the original.
1109
- */
1110
- /* Document-module: JSON::Ext
1111
- *
1112
- * The Ext module is a placeholder in the mimic JSON module used for
1113
- * compatibility only.
1114
- */
1115
- /* Document-class: JSON::Ext::Parser
1116
- *
1117
- * The JSON::Ext::Parser is a placeholder in the mimic JSON module used for
1118
- * compatibility only.
1119
- */
1120
- /* Document-class: JSON::Ext::Generator
1121
- *
1122
- * The JSON::Ext::Generator is a placeholder in the mimic JSON module used for
1123
- * compatibility only.
1124
- */
1125
-
1126
- /* Document-method: create_id=
1127
- * call-seq: create_id=(id) -> String
1128
- *
1129
- * Sets the create_id tag to look for in JSON document. That key triggers the
1130
- * creation of a class with the same name.
1131
- *
1132
- * @param [nil|String] id new create_id
1133
- * @return the id
1134
- */
1135
- /* Document-method: parser=
1136
- * call-seq: parser=(parser) -> nil
1137
- *
1138
- * Does nothing other than provide compatibiltiy.
1139
- * @param [Object] parser ignored
1140
- */
1141
- /* Document-method: generator=
1142
- * call-seq: generator=(generator) -> nil
1143
- *
1144
- * Does nothing other than provide compatibiltiy.
1145
- * @param [Object] generator ignored
1146
- */
1147
- /* Document-method: dump
1148
- * call-seq: dump(obj, anIO=nil, limit = nil) -> String
1149
- *
1150
- * Encodes an object as a JSON String.
1151
- *
1152
- * @param [Object] obj object to convert to encode as JSON
1153
- * @param [IO] anIO an IO that allows writing
1154
- * @param [Fixnum] limit ignored
1155
- */
1156
- /* Document-method: load
1157
- * call-seq: load(source, proc=nil) -> Object
1158
- *
1159
- * Loads a Ruby Object from a JSON source that can be either a String or an
1160
- * IO. If Proc is given or a block is providedit is called with each nested
1161
- * element of the loaded Object.
1162
- *
1163
- * @param [String|IO] source JSON source
1164
- * @param [Proc] proc to yield to on each element or nil
1165
- */
1166
- /* Document-method: restore
1167
- * call-seq: restore(source, proc=nil) -> Object
1168
- *
1169
- * Loads a Ruby Object from a JSON source that can be either a String or an
1170
- * IO. If Proc is given or a block is providedit is called with each nested
1171
- * element of the loaded Object.
1172
- *
1173
- * @param [String|IO] source JSON source
1174
- * @param [Proc] proc to yield to on each element or nil
1175
- */
1176
- /* Document-method: recurse_proc
1177
- * call-seq: recurse_proc(obj, &proc) -> nil
1178
- *
1179
- * Yields to the proc for every element in the obj recursivly.
1180
- *
1181
- * @param [Hash|Array] obj object to walk
1182
- * @param [Proc] proc to yield to on each element
1183
- */
1184
- /* Document-method: []
1185
- * call-seq: [](obj, opts={}) -> Object
1186
- *
1187
- * If the obj argument is a String then it is assumed to be a JSON String and
1188
- * parsed otherwise the obj is encoded as a JSON String.
1189
- *
1190
- * @param [String|Hash|Array] obj object to convert
1191
- * @param [Hash] opts same options as either generate or parse
1192
- */
1193
- /* Document-method: generate
1194
- * call-seq: generate(obj, opts=nil) -> String
1195
- *
1196
- * Encode obj as a JSON String. The obj argument must be a Hash, Array, or
1197
- * respond to to_h or to_json. Options other than those listed such as
1198
- * +:allow_nan+ or +:max_nesting+ are ignored.
1199
- *
1200
- * @param [Object|Hash|Array] obj object to convert to a JSON String
1201
- * @param [Hash] opts options
1202
- * @param [String] :indent String to use for indentation
1203
- * @param [String] :space String placed after a , or : delimiter
1204
- * @param [String] :space_before String placed before a : delimiter
1205
- * @param [String] :object_nl String placed after a JSON object
1206
- * @param [String] :array_nl String placed after a JSON array
1207
- */
1208
- /* Document-method: fast_generate
1209
- * call-seq: fast_generate(obj, opts=nil) -> String
1210
- * Same as generate().
1211
- * @see generate
1212
- */
1213
- /* Document-method: pretty_generate
1214
- * call-seq: pretty_generate(obj, opts=nil) -> String
1215
- * Same as generate() but with different defaults for the spacing options.
1216
- * @see generate
1217
- */
1218
- /* Document-method: parse
1219
- * call-seq: parse(source, opts=nil) -> Object
1220
- *
1221
- * Parses a JSON String or IO into a Ruby Object. Options other than those
1222
- * listed such as +:allow_nan+ or +:max_nesting+ are ignored. +:object_class+ and
1223
- * +:array_object+ are not supported.
1224
- *
1225
- * @param [String|IO] source source to parse
1226
- * @param [Hash] opts options
1227
- * @param [true|false] :symbolize_names flag indicating JSON object keys should be Symbols instead of Strings
1228
- * @param [true|false] :create_additions flag indicating a key matching +create_id+ in a JSON object should trigger the creation of Ruby Object
1229
- * @see create_id=
1230
- */
1231
- /* Document-method: parse!
1232
- * call-seq: parse!(source, opts=nil) -> Object
1233
- * Same as parse().
1234
- * @see parse
1235
- */