oj 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
- */