oj 2.18.5 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -226
  3. data/ext/oj/circarray.c +0 -25
  4. data/ext/oj/circarray.h +0 -25
  5. data/ext/oj/code.c +227 -0
  6. data/ext/oj/code.h +40 -0
  7. data/ext/oj/compat.c +126 -38
  8. data/ext/oj/custom.c +1097 -0
  9. data/ext/oj/dump.c +658 -2376
  10. data/ext/oj/dump.h +92 -0
  11. data/ext/oj/dump_compat.c +937 -0
  12. data/ext/oj/dump_leaf.c +254 -0
  13. data/ext/oj/dump_object.c +810 -0
  14. data/ext/oj/dump_rails.c +329 -0
  15. data/ext/oj/dump_strict.c +416 -0
  16. data/ext/oj/err.c +0 -25
  17. data/ext/oj/err.h +8 -2
  18. data/ext/oj/fast.c +24 -24
  19. data/ext/oj/mimic_json.c +817 -0
  20. data/ext/oj/mimic_rails.c +806 -0
  21. data/ext/oj/mimic_rails.h +17 -0
  22. data/ext/oj/object.c +18 -72
  23. data/ext/oj/odd.c +0 -25
  24. data/ext/oj/odd.h +2 -27
  25. data/ext/oj/oj.c +655 -1503
  26. data/ext/oj/oj.h +93 -40
  27. data/ext/oj/parse.c +99 -46
  28. data/ext/oj/parse.h +12 -26
  29. data/ext/oj/reader.c +1 -25
  30. data/ext/oj/reader.h +3 -25
  31. data/ext/oj/resolve.c +9 -11
  32. data/ext/oj/resolve.h +2 -2
  33. data/ext/oj/rxclass.c +133 -0
  34. data/ext/oj/rxclass.h +27 -0
  35. data/ext/oj/saj.c +4 -25
  36. data/ext/oj/scp.c +3 -25
  37. data/ext/oj/sparse.c +89 -13
  38. data/ext/oj/stream_writer.c +301 -0
  39. data/ext/oj/strict.c +4 -27
  40. data/ext/oj/string_writer.c +480 -0
  41. data/ext/oj/val_stack.h +6 -2
  42. data/lib/oj.rb +1 -23
  43. data/lib/oj/easy_hash.rb +12 -4
  44. data/lib/oj/json.rb +172 -0
  45. data/lib/oj/mimic.rb +123 -18
  46. data/lib/oj/state.rb +131 -0
  47. data/lib/oj/version.rb +1 -1
  48. data/pages/Advanced.md +22 -0
  49. data/pages/Compatibility.md +25 -0
  50. data/pages/Custom.md +23 -0
  51. data/pages/Encoding.md +65 -0
  52. data/pages/JsonGem.md +79 -0
  53. data/pages/Modes.md +140 -0
  54. data/pages/Options.md +250 -0
  55. data/pages/Rails.md +60 -0
  56. data/pages/Security.md +20 -0
  57. data/test/activesupport4/decoding_test.rb +105 -0
  58. data/test/activesupport4/encoding_test.rb +531 -0
  59. data/test/activesupport4/test_helper.rb +41 -0
  60. data/test/activesupport5/decoding_test.rb +125 -0
  61. data/test/activesupport5/encoding_test.rb +483 -0
  62. data/test/activesupport5/encoding_test_cases.rb +90 -0
  63. data/test/activesupport5/test_helper.rb +50 -0
  64. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  65. data/test/json_gem/json_addition_test.rb +216 -0
  66. data/test/json_gem/json_common_interface_test.rb +143 -0
  67. data/test/json_gem/json_encoding_test.rb +109 -0
  68. data/test/json_gem/json_ext_parser_test.rb +20 -0
  69. data/test/json_gem/json_fixtures_test.rb +35 -0
  70. data/test/json_gem/json_generator_test.rb +383 -0
  71. data/test/json_gem/json_generic_object_test.rb +90 -0
  72. data/test/json_gem/json_parser_test.rb +470 -0
  73. data/test/json_gem/json_string_matching_test.rb +42 -0
  74. data/test/json_gem/test_helper.rb +18 -0
  75. data/test/perf_compat.rb +30 -28
  76. data/test/perf_object.rb +1 -1
  77. data/test/perf_strict.rb +18 -1
  78. data/test/sample.rb +0 -1
  79. data/test/test_compat.rb +169 -93
  80. data/test/test_custom.rb +355 -0
  81. data/test/test_file.rb +0 -8
  82. data/test/test_null.rb +376 -0
  83. data/test/test_object.rb +268 -3
  84. data/test/test_scp.rb +22 -1
  85. data/test/test_strict.rb +160 -4
  86. data/test/test_various.rb +52 -620
  87. data/test/tests.rb +14 -0
  88. data/test/tests_mimic.rb +14 -0
  89. data/test/tests_mimic_addition.rb +7 -0
  90. metadata +89 -47
  91. data/test/activesupport_datetime_test.rb +0 -23
  92. data/test/bug.rb +0 -51
  93. data/test/bug2.rb +0 -10
  94. data/test/bug3.rb +0 -46
  95. data/test/bug_fast.rb +0 -32
  96. data/test/bug_load.rb +0 -24
  97. data/test/crash.rb +0 -111
  98. data/test/curl/curl_oj.rb +0 -46
  99. data/test/curl/get_oj.rb +0 -24
  100. data/test/curl/just_curl.rb +0 -31
  101. data/test/curl/just_oj.rb +0 -51
  102. data/test/example.rb +0 -11
  103. data/test/foo.rb +0 -24
  104. data/test/io.rb +0 -48
  105. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  106. data/test/mod.rb +0 -16
  107. data/test/rails.rb +0 -50
  108. data/test/russian.rb +0 -18
  109. data/test/struct.rb +0 -29
  110. data/test/test_serializer.rb +0 -59
  111. data/test/write_timebars.rb +0 -31
@@ -0,0 +1,40 @@
1
+ /* code.h
2
+ * Copyright (c) 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #ifndef __OJ_CODE_H__
7
+ #define __OJ_CODE_H__
8
+
9
+ #include <ruby.h>
10
+
11
+ #include "oj.h"
12
+
13
+ typedef void (*EncodeFunc)(VALUE obj, int depth, Out out);
14
+ typedef VALUE (*DecodeFunc)(VALUE clas, VALUE args);
15
+
16
+ typedef struct _Code {
17
+ const char *name;
18
+ VALUE clas;
19
+ EncodeFunc encode;
20
+ DecodeFunc decode;
21
+ bool active; // For compat mode.
22
+ } *Code;
23
+
24
+ // Used by encode functions.
25
+ typedef struct _Attr {
26
+ const char *name;
27
+ int len;
28
+ VALUE value;
29
+ long num;
30
+ VALUE time;
31
+ } *Attr;
32
+
33
+ extern bool oj_code_dump(Code codes, VALUE obj, int depth, Out out);
34
+ extern VALUE oj_code_load(Code codes, VALUE clas, VALUE args);
35
+ extern void oj_code_set_active(Code codes, VALUE clas, bool active);
36
+ extern bool oj_code_has(Code codes, VALUE clas, bool encode);
37
+
38
+ extern void oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out);
39
+
40
+ #endif /* __OJ_CODE_H__ */
@@ -1,31 +1,6 @@
1
1
  /* compat.c
2
2
  * Copyright (c) 2012, Peter Ohler
3
3
  * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
4
  */
30
5
 
31
6
  #include <stdio.h>
@@ -45,10 +20,12 @@ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *o
45
20
  volatile VALUE rkey = kval->key_val;
46
21
 
47
22
  if (Qundef == rkey &&
48
- 0 != pi->options.create_id &&
23
+ Yes == pi->options.create_ok &&
24
+ NULL != pi->options.create_id &&
49
25
  *pi->options.create_id == *key &&
50
26
  (int)pi->options.create_id_len == klen &&
51
27
  0 == strncmp(pi->options.create_id, key, klen)) {
28
+
52
29
  parent->classname = oj_strndup(str, len);
53
30
  parent->clen = len;
54
31
  } else {
@@ -62,20 +39,51 @@ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *o
62
39
  rkey = rb_str_intern(rkey);
63
40
  }
64
41
  }
65
- rb_hash_aset(parent->val, rkey, rstr);
42
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
43
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
44
+
45
+ if (Qnil != clas) {
46
+ rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
47
+ }
48
+ }
49
+ if (rb_cHash != rb_obj_class(parent->val)) {
50
+ // The rb_hash_set would still work but the unit tests for the
51
+ // json gem require the less efficient []= method be called to set
52
+ // values. Even using the store method to set the values will fail
53
+ // the unit tests.
54
+ rb_funcall(parent->val, rb_intern("[]="), 2, rkey, rstr);
55
+ } else {
56
+ rb_hash_aset(parent->val, rkey, rstr);
57
+ }
66
58
  }
67
59
  }
68
60
 
61
+ static VALUE
62
+ start_hash(ParseInfo pi) {
63
+ volatile VALUE h;
64
+
65
+ if (Qnil != pi->options.hash_class) {
66
+ h = rb_class_new_instance(0, NULL, pi->options.hash_class);
67
+ } else {
68
+ h = rb_hash_new();
69
+ }
70
+ return h;
71
+ }
72
+
69
73
  static void
70
74
  end_hash(struct _ParseInfo *pi) {
71
75
  Val parent = stack_peek(&pi->stack);
72
76
 
73
77
  if (0 != parent->classname) {
74
- VALUE clas;
78
+ volatile VALUE clas;
75
79
 
76
- clas = oj_name2class(pi, parent->classname, parent->clen, 0);
80
+ clas = oj_name2class(pi, parent->classname, parent->clen, 0, rb_eArgError);
77
81
  if (Qundef != clas) { // else an error
78
- parent->val = rb_funcall(clas, oj_json_create_id, 1, parent->val);
82
+ ID creatable = rb_intern("json_creatable?");
83
+
84
+ if (!rb_respond_to(clas, creatable) || Qtrue == rb_funcall(clas, creatable, 0)) {
85
+ parent->val = rb_funcall(clas, oj_json_create_id, 1, parent->val);
86
+ }
79
87
  }
80
88
  if (0 != parent->classname) {
81
89
  xfree((char*)parent->classname);
@@ -98,6 +106,22 @@ calc_hash_key(ParseInfo pi, Val parent) {
98
106
  return rkey;
99
107
  }
100
108
 
109
+ static void
110
+ add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
111
+ volatile VALUE rstr = rb_str_new(str, len);
112
+
113
+ rstr = oj_encode(rstr);
114
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
115
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
116
+
117
+ if (Qnil != clas) {
118
+ pi->stack.head->val = rb_funcall(clas, oj_json_create_id, 1, rstr);
119
+ return;
120
+ }
121
+ }
122
+ pi->stack.head->val = rstr;
123
+ }
124
+
101
125
  static void
102
126
  add_num(ParseInfo pi, NumInfo ni) {
103
127
  pi->stack.head->val = oj_num_as_value(ni);
@@ -105,22 +129,80 @@ add_num(ParseInfo pi, NumInfo ni) {
105
129
 
106
130
  static void
107
131
  hash_set_num(struct _ParseInfo *pi, Val parent, NumInfo ni) {
108
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), oj_num_as_value(ni));
132
+ if (!oj_use_hash_alt && rb_cHash != rb_obj_class(parent->val)) {
133
+ // The rb_hash_set would still work but the unit tests for the
134
+ // json gem require the less efficient []= method be called to set
135
+ // values. Even using the store method to set the values will fail
136
+ // the unit tests.
137
+ rb_funcall(stack_peek(&pi->stack)->val, rb_intern("[]="), 2, calc_hash_key(pi, parent), oj_num_as_value(ni));
138
+ } else {
139
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), oj_num_as_value(ni));
140
+ }
141
+ }
142
+
143
+ static void
144
+ hash_set_value(ParseInfo pi, Val parent, VALUE value) {
145
+ if (rb_cHash != rb_obj_class(parent->val)) {
146
+ // The rb_hash_set would still work but the unit tests for the
147
+ // json gem require the less efficient []= method be called to set
148
+ // values. Even using the store method to set the values will fail
149
+ // the unit tests.
150
+ rb_funcall(stack_peek(&pi->stack)->val, rb_intern("[]="), 2, calc_hash_key(pi, parent), value);
151
+ } else {
152
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
153
+ }
154
+ }
155
+
156
+ static VALUE
157
+ start_array(ParseInfo pi) {
158
+ if (Qnil != pi->options.array_class) {
159
+ return rb_class_new_instance(0, NULL, pi->options.array_class);
160
+ }
161
+ return rb_ary_new();
109
162
  }
110
163
 
111
164
  static void
112
165
  array_append_num(ParseInfo pi, NumInfo ni) {
113
- rb_ary_push(stack_peek(&pi->stack)->val, oj_num_as_value(ni));
166
+ Val parent = stack_peek(&pi->stack);
167
+
168
+ if (!oj_use_array_alt && rb_cArray != rb_obj_class(parent->val)) {
169
+ // The rb_ary_push would still work but the unit tests for the json
170
+ // gem require the less efficient << method be called to push the
171
+ // values.
172
+ rb_funcall(parent->val, rb_intern("<<"), 1, oj_num_as_value(ni));
173
+ } else {
174
+ rb_ary_push(parent->val, oj_num_as_value(ni));
175
+ }
114
176
  }
115
177
 
178
+ static void
179
+ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
180
+ volatile VALUE rstr = rb_str_new(str, len);
181
+
182
+ rstr = oj_encode(rstr);
183
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
184
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
185
+
186
+ if (Qnil != clas) {
187
+ rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
188
+ return;
189
+ }
190
+ }
191
+ rb_ary_push(stack_peek(&pi->stack)->val, rstr);
192
+ }
116
193
 
117
194
  void
118
195
  oj_set_compat_callbacks(ParseInfo pi) {
119
196
  oj_set_strict_callbacks(pi);
197
+ pi->start_hash = start_hash;
120
198
  pi->end_hash = end_hash;
121
199
  pi->hash_set_cstr = hash_set_cstr;
122
- pi->add_num = add_num;
123
200
  pi->hash_set_num = hash_set_num;
201
+ pi->hash_set_value = hash_set_value;
202
+ pi->add_num = add_num;
203
+ pi->add_cstr = add_cstr;
204
+ pi->array_append_cstr = array_append_cstr;
205
+ pi->start_array = start_array;
124
206
  pi->array_append_num = array_append_num;
125
207
  }
126
208
 
@@ -128,13 +210,17 @@ VALUE
128
210
  oj_compat_parse(int argc, VALUE *argv, VALUE self) {
129
211
  struct _ParseInfo pi;
130
212
 
213
+ parse_info_init(&pi);
131
214
  pi.options = oj_default_options;
132
215
  pi.handler = Qnil;
133
216
  pi.err_class = Qnil;
217
+ pi.max_depth = 0;
218
+ pi.options.allow_nan = Yes;
219
+ pi.options.nilnil = Yes;
134
220
  oj_set_compat_callbacks(&pi);
135
221
 
136
222
  if (T_STRING == rb_type(*argv)) {
137
- return oj_pi_parse(argc, argv, &pi, 0, 0, 1);
223
+ return oj_pi_parse(argc, argv, &pi, 0, 0, false);
138
224
  } else {
139
225
  return oj_pi_sparse(argc, argv, &pi, 0);
140
226
  }
@@ -144,12 +230,14 @@ VALUE
144
230
  oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
145
231
  struct _ParseInfo pi;
146
232
 
233
+ parse_info_init(&pi);
147
234
  pi.options = oj_default_options;
148
235
  pi.handler = Qnil;
149
236
  pi.err_class = Qnil;
150
- oj_set_strict_callbacks(&pi);
151
- pi.end_hash = end_hash;
152
- pi.hash_set_cstr = hash_set_cstr;
237
+ pi.max_depth = 0;
238
+ pi.options.allow_nan = Yes;
239
+ pi.options.nilnil = Yes;
240
+ oj_set_compat_callbacks(&pi);
153
241
 
154
- return oj_pi_parse(argc, argv, &pi, json, len, 1);
242
+ return oj_pi_parse(argc, argv, &pi, json, len, false);
155
243
  }
@@ -0,0 +1,1097 @@
1
+ /* custom.c
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <stdio.h>
7
+
8
+ #include "code.h"
9
+ #include "dump.h"
10
+ #include "encode.h"
11
+ #include "err.h"
12
+ #include "hash.h"
13
+ #include "odd.h"
14
+ #include "oj.h"
15
+ #include "parse.h"
16
+ #include "resolve.h"
17
+
18
+ extern void oj_set_obj_ivar(Val parent, Val kval, VALUE value);
19
+ extern VALUE oj_parse_xml_time(const char *str, int len); // from object.c
20
+
21
+ static void
22
+ dump_obj_str(VALUE obj, int depth, Out out) {
23
+ struct _Attr attrs[] = {
24
+ { "s", 1, Qnil },
25
+ { NULL, 0, Qnil },
26
+ };
27
+ attrs->value = rb_funcall(obj, oj_to_s_id, 0);
28
+
29
+ oj_code_attrs(obj, attrs, depth, out);
30
+ }
31
+
32
+
33
+ static void
34
+ bigdecimal_dump(VALUE obj, int depth, Out out) {
35
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
36
+ const char *str = rb_string_value_ptr((VALUE*)&rstr);
37
+ int len = RSTRING_LEN(rstr);
38
+
39
+ if (0 == strcasecmp("Infinity", str)) {
40
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
41
+ oj_dump_raw(str, len, out);
42
+ } else if (0 == strcasecmp("-Infinity", str)) {
43
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
44
+ oj_dump_raw(str, len, out);
45
+ } else {
46
+ oj_dump_raw(str, len, out);
47
+ }
48
+ }
49
+
50
+ static ID real_id = 0;
51
+ static ID imag_id = 0;
52
+
53
+ static void
54
+ complex_dump(VALUE obj, int depth, Out out) {
55
+ struct _Attr attrs[] = {
56
+ { "real", 4, Qnil },
57
+ { "imag", 4, Qnil },
58
+ { NULL, 0, Qnil },
59
+ };
60
+ if (0 == real_id) {
61
+ real_id = rb_intern("real");
62
+ imag_id = rb_intern("imag");
63
+ }
64
+ attrs[0].value = rb_funcall(obj, real_id, 0);
65
+ attrs[1].value = rb_funcall(obj, imag_id, 0);
66
+
67
+ oj_code_attrs(obj, attrs, depth, out);
68
+ }
69
+
70
+ static VALUE
71
+ complex_load(VALUE clas, VALUE args) {
72
+ if (0 == real_id) {
73
+ real_id = rb_intern("real");
74
+ imag_id = rb_intern("imag");
75
+ }
76
+ return rb_complex_new(rb_hash_aref(args, rb_id2str(real_id)), rb_hash_aref(args, rb_id2str(imag_id)));
77
+ }
78
+
79
+ static void
80
+ date_dump(VALUE obj, int depth, Out out) {
81
+ struct _Attr attrs[] = {
82
+ { "s", 1, Qnil },
83
+ { NULL, 0, Qnil },
84
+ };
85
+ attrs->value = rb_funcall(obj, rb_intern("iso8601"), 0);
86
+
87
+ oj_code_attrs(obj, attrs, depth, out);
88
+ }
89
+
90
+ static VALUE
91
+ date_load(VALUE clas, VALUE args) {
92
+ volatile VALUE v;
93
+
94
+ if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
95
+ return rb_funcall(oj_date_class, rb_intern("parse"), 1, v);
96
+ }
97
+ return Qnil;
98
+ }
99
+
100
+ static VALUE
101
+ datetime_load(VALUE clas, VALUE args) {
102
+ volatile VALUE v;
103
+
104
+ if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
105
+ return rb_funcall(oj_datetime_class, rb_intern("parse"), 1, v);
106
+ }
107
+ return Qnil;
108
+ }
109
+
110
+ static ID table_id = 0;
111
+
112
+ static void
113
+ openstruct_dump(VALUE obj, int depth, Out out) {
114
+ struct _Attr attrs[] = {
115
+ { "table", 5, Qnil },
116
+ { NULL, 0, Qnil },
117
+ };
118
+ if (0 == table_id) {
119
+ table_id = rb_intern("table");
120
+ }
121
+ attrs->value = rb_funcall(obj, table_id, 0);
122
+
123
+ oj_code_attrs(obj, attrs, depth, out);
124
+ }
125
+
126
+ static VALUE
127
+ openstruct_load(VALUE clas, VALUE args) {
128
+ if (0 == table_id) {
129
+ table_id = rb_intern("table");
130
+ }
131
+ return rb_funcall(clas, oj_new_id, 1, rb_hash_aref(args, rb_id2str(table_id)));
132
+ }
133
+
134
+ static void
135
+ range_dump(VALUE obj, int depth, Out out) {
136
+ struct _Attr attrs[] = {
137
+ { "begin", 5, Qnil },
138
+ { "end", 3, Qnil },
139
+ { "exclude", 7, Qnil },
140
+ { NULL, 0, Qnil },
141
+ };
142
+ attrs[0].value = rb_funcall(obj, oj_begin_id, 0);
143
+ attrs[1].value = rb_funcall(obj, oj_end_id, 0);
144
+ attrs[2].value = rb_funcall(obj, oj_exclude_end_id, 0);
145
+
146
+ oj_code_attrs(obj, attrs, depth, out);
147
+ }
148
+
149
+ static VALUE
150
+ range_load(VALUE clas, VALUE args) {
151
+ VALUE nargs[3];
152
+
153
+ nargs[0] = rb_hash_aref(args, rb_id2str(oj_begin_id));
154
+ nargs[1] = rb_hash_aref(args, rb_id2str(oj_end_id));
155
+ nargs[2] = rb_hash_aref(args, rb_id2str(oj_exclude_end_id));
156
+
157
+ return rb_class_new_instance(3, nargs, rb_cRange);
158
+ }
159
+
160
+ static ID numerator_id = 0;
161
+ static ID denominator_id = 0;
162
+
163
+ static void
164
+ rational_dump(VALUE obj, int depth, Out out) {
165
+ struct _Attr attrs[] = {
166
+ { "numerator", 9, Qnil },
167
+ { "denominator", 11, Qnil },
168
+ { NULL, 0, Qnil },
169
+ };
170
+ if (0 == numerator_id) {
171
+ numerator_id = rb_intern("numerator");
172
+ denominator_id = rb_intern("denominator");
173
+ }
174
+ attrs[0].value = rb_funcall(obj, numerator_id, 0);
175
+ attrs[1].value = rb_funcall(obj, denominator_id, 0);
176
+
177
+ oj_code_attrs(obj, attrs, depth, out);
178
+ }
179
+
180
+ static VALUE
181
+ rational_load(VALUE clas, VALUE args) {
182
+ if (0 == numerator_id) {
183
+ numerator_id = rb_intern("numerator");
184
+ denominator_id = rb_intern("denominator");
185
+ }
186
+ return rb_rational_new(rb_hash_aref(args, rb_id2str(numerator_id)),
187
+ rb_hash_aref(args, rb_id2str(denominator_id)));
188
+ }
189
+
190
+ static VALUE
191
+ regexp_load(VALUE clas, VALUE args) {
192
+ volatile VALUE v;
193
+
194
+ if (Qnil != (v = rb_hash_aref(args, rb_str_new2("s")))) {
195
+ return rb_funcall(rb_cRegexp, oj_new_id, 1, v);
196
+ }
197
+ return Qnil;
198
+ }
199
+
200
+ static void
201
+ time_dump(VALUE obj, int depth, Out out) {
202
+ struct _Attr attrs[] = {
203
+ { "time", 4, Qundef, 0, Qundef },
204
+ { NULL, 0, Qnil },
205
+ };
206
+ attrs->time = obj;
207
+
208
+ oj_code_attrs(obj, attrs, depth, out);
209
+ }
210
+
211
+ static VALUE
212
+ time_load(VALUE clas, VALUE args) {
213
+ // Value should have already been replaced in one of the hash_set_xxx
214
+ // functions.
215
+ return args;
216
+ }
217
+
218
+ static struct _Code codes[] = {
219
+ { "BigDecimal", Qnil, bigdecimal_dump, NULL, true },
220
+ { "Complex", Qnil, complex_dump, complex_load, true },
221
+ { "Date", Qnil, date_dump, date_load, true },
222
+ { "DateTime", Qnil, date_dump, datetime_load, true },
223
+ { "OpenStruct", Qnil, openstruct_dump, openstruct_load, true },
224
+ { "Range", Qnil, range_dump, range_load, true },
225
+ { "Rational", Qnil, rational_dump, rational_load, true },
226
+ { "Regexp", Qnil, dump_obj_str, regexp_load, true },
227
+ { "Time", Qnil, time_dump, time_load, true },
228
+ { NULL, Qundef, NULL, NULL, false },
229
+ };
230
+
231
+ static int
232
+ hash_cb(VALUE key, VALUE value, Out out) {
233
+ int depth = out->depth;
234
+
235
+ if (out->omit_nil && Qnil == value) {
236
+ return ST_CONTINUE;
237
+ }
238
+ if (!out->opts->dump_opts.use) {
239
+ assure_size(out, depth * out->indent + 1);
240
+ fill_indent(out, depth);
241
+ } else {
242
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
243
+ if (0 < out->opts->dump_opts.hash_size) {
244
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
245
+ out->cur += out->opts->dump_opts.hash_size;
246
+ }
247
+ if (0 < out->opts->dump_opts.indent_size) {
248
+ int i;
249
+
250
+ for (i = depth; 0 < i; i--) {
251
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
252
+ out->cur += out->opts->dump_opts.indent_size;
253
+ }
254
+ }
255
+ }
256
+ switch (rb_type(key)) {
257
+ case T_STRING:
258
+ oj_dump_str(key, 0, out, false);
259
+ break;
260
+ case T_SYMBOL:
261
+ oj_dump_sym(key, 0, out, false);
262
+ break;
263
+ default:
264
+ oj_dump_str(rb_funcall(key, oj_to_s_id, 0), 0, out, false);
265
+ break;
266
+ }
267
+ if (!out->opts->dump_opts.use) {
268
+ *out->cur++ = ':';
269
+ } else {
270
+ assure_size(out, out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2);
271
+ if (0 < out->opts->dump_opts.before_size) {
272
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
273
+ out->cur += out->opts->dump_opts.before_size;
274
+ }
275
+ *out->cur++ = ':';
276
+ if (0 < out->opts->dump_opts.after_size) {
277
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
278
+ out->cur += out->opts->dump_opts.after_size;
279
+ }
280
+ }
281
+ oj_dump_custom_val(value, depth, out, true);
282
+ out->depth = depth;
283
+ *out->cur++ = ',';
284
+
285
+ return ST_CONTINUE;
286
+ }
287
+
288
+ static void
289
+ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
290
+ int cnt;
291
+ long id = oj_check_circular(obj, out);
292
+
293
+ if (0 > id) {
294
+ oj_dump_nil(Qnil, depth, out, false);
295
+ return;
296
+ }
297
+ cnt = (int)RHASH_SIZE(obj);
298
+ assure_size(out, 2);
299
+ if (0 == cnt) {
300
+ *out->cur++ = '{';
301
+ *out->cur++ = '}';
302
+ } else {
303
+ *out->cur++ = '{';
304
+ out->depth = depth + 1;
305
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
306
+ if (',' == *(out->cur - 1)) {
307
+ out->cur--; // backup to overwrite last comma
308
+ }
309
+ if (!out->opts->dump_opts.use) {
310
+ assure_size(out, depth * out->indent + 2);
311
+ fill_indent(out, depth);
312
+ } else {
313
+ assure_size(out, depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1);
314
+ if (0 < out->opts->dump_opts.hash_size) {
315
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
316
+ out->cur += out->opts->dump_opts.hash_size;
317
+ }
318
+ if (0 < out->opts->dump_opts.indent_size) {
319
+ int i;
320
+
321
+ for (i = depth; 0 < i; i--) {
322
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
323
+ out->cur += out->opts->dump_opts.indent_size;
324
+ }
325
+ }
326
+ }
327
+ *out->cur++ = '}';
328
+ }
329
+ *out->cur = '\0';
330
+ }
331
+
332
+ static void
333
+ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
334
+ ID *idp;
335
+ AttrGetFunc *fp;
336
+ volatile VALUE v;
337
+ const char *name;
338
+ size_t size;
339
+ int d2 = depth + 1;
340
+
341
+ assure_size(out, 2);
342
+ *out->cur++ = '{';
343
+ if (NULL != out->opts->create_id) {
344
+ const char *classname = rb_class2name(clas);
345
+ int clen = (int)strlen(classname);
346
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
347
+
348
+ size = d2 * out->indent + 10 + clen + out->opts->create_id_len + sep_len;
349
+ assure_size(out, size);
350
+ fill_indent(out, d2);
351
+ *out->cur++ = '"';
352
+ memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
353
+ out->cur += out->opts->create_id_len;
354
+ *out->cur++ = '"';
355
+ if (0 < out->opts->dump_opts.before_size) {
356
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
357
+ out->cur += out->opts->dump_opts.before_size;
358
+ }
359
+ *out->cur++ = ':';
360
+ if (0 < out->opts->dump_opts.after_size) {
361
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
362
+ out->cur += out->opts->dump_opts.after_size;
363
+ }
364
+ *out->cur++ = '"';
365
+ memcpy(out->cur, classname, clen);
366
+ out->cur += clen;
367
+ *out->cur++ = '"';
368
+ *out->cur++ = ',';
369
+ }
370
+ if (odd->raw) {
371
+ v = rb_funcall(obj, *odd->attrs, 0);
372
+ if (Qundef == v || T_STRING != rb_type(v)) {
373
+ rb_raise(rb_eEncodingError, "Invalid type for raw JSON.\n");
374
+ } else {
375
+ const char *s = rb_string_value_ptr((VALUE*)&v);
376
+ int len = RSTRING_LEN(v);
377
+ const char *name = rb_id2name(*odd->attrs);
378
+ size_t nlen = strlen(name);
379
+
380
+ size = len + d2 * out->indent + nlen + 10;
381
+ assure_size(out, size);
382
+ fill_indent(out, d2);
383
+ *out->cur++ = '"';
384
+ memcpy(out->cur, name, nlen);
385
+ out->cur += nlen;
386
+ *out->cur++ = '"';
387
+ *out->cur++ = ':';
388
+ memcpy(out->cur, s, len);
389
+ out->cur += len;
390
+ *out->cur = '\0';
391
+ }
392
+ } else {
393
+ size = d2 * out->indent + 1;
394
+ for (idp = odd->attrs, fp = odd->attrFuncs; 0 != *idp; idp++, fp++) {
395
+ size_t nlen;
396
+
397
+ assure_size(out, size);
398
+ name = rb_id2name(*idp);
399
+ nlen = strlen(name);
400
+ if (0 != *fp) {
401
+ v = (*fp)(obj);
402
+ } else if (0 == strchr(name, '.')) {
403
+ v = rb_funcall(obj, *idp, 0);
404
+ } else {
405
+ char nbuf[256];
406
+ char *n2 = nbuf;
407
+ char *n;
408
+ char *end;
409
+ ID i;
410
+
411
+ if (sizeof(nbuf) <= nlen) {
412
+ n2 = strdup(name);
413
+ } else {
414
+ strcpy(n2, name);
415
+ }
416
+ n = n2;
417
+ v = obj;
418
+ while (0 != (end = strchr(n, '.'))) {
419
+ *end = '\0';
420
+ i = rb_intern(n);
421
+ v = rb_funcall(v, i, 0);
422
+ n = end + 1;
423
+ }
424
+ i = rb_intern(n);
425
+ v = rb_funcall(v, i, 0);
426
+ if (nbuf != n2) {
427
+ free(n2);
428
+ }
429
+ }
430
+ fill_indent(out, d2);
431
+ oj_dump_cstr(name, nlen, 0, 0, out);
432
+ *out->cur++ = ':';
433
+ oj_dump_custom_val(v, d2, out, true);
434
+ assure_size(out, 2);
435
+ *out->cur++ = ',';
436
+ }
437
+ out->cur--;
438
+ }
439
+ *out->cur++ = '}';
440
+ *out->cur = '\0';
441
+ }
442
+
443
+ // Return class if still needs dumping.
444
+ static VALUE
445
+ dump_common(VALUE obj, int depth, Out out) {
446
+ if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
447
+ volatile VALUE rs;
448
+ const char *s;
449
+ int len;
450
+
451
+ rs = rb_funcall(obj, oj_to_json_id, 0);
452
+ s = rb_string_value_ptr((VALUE*)&rs);
453
+ len = (int)RSTRING_LEN(rs);
454
+
455
+ assure_size(out, len + 1);
456
+ memcpy(out->cur, s, len);
457
+ out->cur += len;
458
+ *out->cur = '\0';
459
+ } else if (Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
460
+ volatile VALUE aj;
461
+ int arity;
462
+
463
+ #if HAS_METHOD_ARITY
464
+ arity = rb_obj_method_arity(obj, oj_as_json_id);
465
+ #else
466
+ arity = out->argc;
467
+ #endif
468
+ // Some classes elect to not take an options argument so check the arity
469
+ // of as_json.
470
+ switch (arity) {
471
+ case 0:
472
+ aj = rb_funcall(obj, oj_as_json_id, 0);
473
+ break;
474
+ case 1:
475
+ if (1 <= out->argc) {
476
+ aj = rb_funcall2(obj, oj_as_json_id, 1, (VALUE*)out->argv);
477
+ } else {
478
+ aj = rb_funcall(obj, oj_as_json_id, 1, Qnil);
479
+ }
480
+ break;
481
+ default:
482
+ aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
483
+ break;
484
+ }
485
+ // Catch the obvious brain damaged recursive dumping.
486
+ if (aj == obj) {
487
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
488
+
489
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), false, false, out);
490
+ } else {
491
+ oj_dump_custom_val(aj, depth, out, true);
492
+ }
493
+ } else if (Yes == out->opts->to_hash && rb_respond_to(obj, oj_to_hash_id)) {
494
+ volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);
495
+
496
+ if (T_HASH != rb_type(h)) {
497
+ // It seems that ActiveRecord implemented to_hash so that it returns
498
+ // an Array and not a Hash. To get around that any value returned
499
+ // will be dumped.
500
+
501
+ //rb_raise(rb_eTypeError, "%s.to_hash() did not return a Hash.\n", rb_class2name(rb_obj_class(obj)));
502
+ oj_dump_custom_val(h, depth, out, false);
503
+ } else {
504
+ dump_hash(h, depth, out, true);
505
+ }
506
+ } else if (!oj_code_dump(codes, obj, depth, out)) {
507
+ VALUE clas = rb_obj_class(obj);
508
+ Odd odd = oj_get_odd(clas);
509
+
510
+ if (NULL == odd) {
511
+ return clas;
512
+ }
513
+ dump_odd(obj, odd, clas, depth + 1, out);
514
+ }
515
+ return Qnil;
516
+ }
517
+
518
+ static int
519
+ dump_attr_cb(ID key, VALUE value, Out out) {
520
+ int depth = out->depth;
521
+ size_t size;
522
+ const char *attr;
523
+
524
+ if (out->omit_nil && Qnil == value) {
525
+ return ST_CONTINUE;
526
+ }
527
+ size = depth * out->indent + 1;
528
+ attr = rb_id2name(key);
529
+
530
+ #if HAS_EXCEPTION_MAGIC
531
+ if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
532
+ return ST_CONTINUE;
533
+ }
534
+ #endif
535
+ assure_size(out, size);
536
+ fill_indent(out, depth);
537
+ if ('@' == *attr) {
538
+ attr++;
539
+ oj_dump_cstr(attr, strlen(attr), 0, 0, out);
540
+ } else {
541
+ char buf[32];
542
+
543
+ *buf = '~';
544
+ strncpy(buf + 1, attr, sizeof(buf) - 2);
545
+ buf[sizeof(buf) - 1] = '\0';
546
+ oj_dump_cstr(buf, strlen(buf), 0, 0, out);
547
+ }
548
+ *out->cur++ = ':';
549
+ oj_dump_custom_val(value, depth, out, true);
550
+ out->depth = depth;
551
+ *out->cur++ = ',';
552
+
553
+ return ST_CONTINUE;
554
+ }
555
+
556
+ static void
557
+ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
558
+ size_t size = 0;
559
+ int d2 = depth + 1;
560
+ int cnt;
561
+
562
+ assure_size(out, 2);
563
+ *out->cur++ = '{';
564
+ if (Qundef != clas) {
565
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
566
+ const char *classname = rb_obj_classname(obj);
567
+ size_t len = strlen(classname);
568
+
569
+ size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len;
570
+ assure_size(out, size);
571
+ fill_indent(out, d2);
572
+ *out->cur++ = '"';
573
+ memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
574
+ out->cur += out->opts->create_id_len;
575
+ *out->cur++ = '"';
576
+ if (0 < out->opts->dump_opts.before_size) {
577
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
578
+ out->cur += out->opts->dump_opts.before_size;
579
+ }
580
+ *out->cur++ = ':';
581
+ if (0 < out->opts->dump_opts.after_size) {
582
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
583
+ out->cur += out->opts->dump_opts.after_size;
584
+ }
585
+ *out->cur++ = '"';
586
+ memcpy(out->cur, classname, len);
587
+ out->cur += len;
588
+ *out->cur++ = '"';
589
+ }
590
+ cnt = (int)rb_ivar_count(obj);
591
+ if (Qundef != clas && 0 < cnt) {
592
+ *out->cur++ = ',';
593
+ }
594
+ if (0 == cnt && Qundef == clas) {
595
+ // Might be something special like an Enumerable.
596
+ if (Qtrue == rb_obj_is_kind_of(obj, oj_enumerable_class)) {
597
+ out->cur--;
598
+ oj_dump_custom_val(rb_funcall(obj, rb_intern("entries"), 0), depth, out, false);
599
+ return;
600
+ }
601
+ }
602
+ out->depth = depth + 1;
603
+ rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
604
+ if (',' == *(out->cur - 1)) {
605
+ out->cur--; // backup to overwrite last comma
606
+ }
607
+ #if HAS_EXCEPTION_MAGIC
608
+ if (rb_obj_is_kind_of(obj, rb_eException)) {
609
+ volatile VALUE rv;
610
+
611
+ if (',' != *(out->cur - 1)) {
612
+ *out->cur++ = ',';
613
+ }
614
+ // message
615
+ assure_size(out, 2);
616
+ fill_indent(out, d2);
617
+ oj_dump_cstr("~mesg", 5, 0, 0, out);
618
+ *out->cur++ = ':';
619
+ rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
620
+ oj_dump_custom_val(rv, d2, out, true);
621
+ assure_size(out, size + 2);
622
+ *out->cur++ = ',';
623
+ // backtrace
624
+ fill_indent(out, d2);
625
+ oj_dump_cstr("~bt", 3, 0, 0, out);
626
+ *out->cur++ = ':';
627
+ rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
628
+ oj_dump_custom_val(rv, d2, out, true);
629
+ assure_size(out, 2);
630
+ }
631
+ #endif
632
+ out->depth = depth;
633
+
634
+ fill_indent(out, depth);
635
+ *out->cur++ = '}';
636
+ *out->cur = '\0';
637
+ }
638
+
639
+ static void
640
+ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
641
+ long id = oj_check_circular(obj, out);
642
+ VALUE clas;
643
+
644
+ if (0 > id) {
645
+ oj_dump_nil(Qnil, depth, out, false);
646
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
647
+ dump_obj_attrs(obj, clas, 0, depth, out);
648
+ }
649
+ *out->cur = '\0';
650
+ }
651
+
652
+ static void
653
+ dump_array(VALUE a, int depth, Out out, bool as_ok) {
654
+ size_t size;
655
+ int i, cnt;
656
+ int d2 = depth + 1;
657
+ long id = oj_check_circular(a, out);
658
+
659
+ if (0 > id) {
660
+ oj_dump_nil(Qnil, depth, out, false);
661
+ return;
662
+ }
663
+ cnt = (int)RARRAY_LEN(a);
664
+ *out->cur++ = '[';
665
+ assure_size(out, 2);
666
+ if (0 == cnt) {
667
+ *out->cur++ = ']';
668
+ } else {
669
+ if (out->opts->dump_opts.use) {
670
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
671
+ } else {
672
+ size = d2 * out->indent + 2;
673
+ }
674
+ cnt--;
675
+ for (i = 0; i <= cnt; i++) {
676
+ assure_size(out, size);
677
+ if (out->opts->dump_opts.use) {
678
+ if (0 < out->opts->dump_opts.array_size) {
679
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
680
+ out->cur += out->opts->dump_opts.array_size;
681
+ }
682
+ if (0 < out->opts->dump_opts.indent_size) {
683
+ int i;
684
+ for (i = d2; 0 < i; i--) {
685
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
686
+ out->cur += out->opts->dump_opts.indent_size;
687
+ }
688
+ }
689
+ } else {
690
+ fill_indent(out, d2);
691
+ }
692
+ oj_dump_custom_val(rb_ary_entry(a, i), d2, out, true);
693
+ if (i < cnt) {
694
+ *out->cur++ = ',';
695
+ }
696
+ }
697
+ size = depth * out->indent + 1;
698
+ assure_size(out, size);
699
+ if (out->opts->dump_opts.use) {
700
+ if (0 < out->opts->dump_opts.array_size) {
701
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
702
+ out->cur += out->opts->dump_opts.array_size;
703
+ }
704
+ if (0 < out->opts->dump_opts.indent_size) {
705
+ int i;
706
+
707
+ for (i = depth; 0 < i; i--) {
708
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
709
+ out->cur += out->opts->dump_opts.indent_size;
710
+ }
711
+ }
712
+ } else {
713
+ fill_indent(out, depth);
714
+ }
715
+ *out->cur++ = ']';
716
+ }
717
+ *out->cur = '\0';
718
+ }
719
+
720
+ static void
721
+ dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
722
+ long id = oj_check_circular(obj, out);
723
+ VALUE clas;
724
+
725
+ if (0 > id) {
726
+ oj_dump_nil(Qnil, depth, out, false);
727
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
728
+ VALUE ma = Qnil;
729
+ VALUE v;
730
+ char num_id[32];
731
+ int i;
732
+ int d2 = depth + 1;
733
+ int d3 = d2 + 1;
734
+ size_t size = d2 * out->indent + d3 * out->indent + 3;
735
+ const char *name;
736
+ int cnt;
737
+ size_t len;
738
+
739
+ assure_size(out, size);
740
+ if (clas == rb_cRange) {
741
+ *out->cur++ = '"';
742
+ oj_dump_custom_val(rb_funcall(obj, oj_begin_id, 0), d3, out, false);
743
+ assure_size(out, 3);
744
+ *out->cur++ = '.';
745
+ *out->cur++ = '.';
746
+ if (Qtrue == rb_funcall(obj, oj_exclude_end_id, 0)) {
747
+ *out->cur++ = '.';
748
+ }
749
+ oj_dump_custom_val(rb_funcall(obj, oj_end_id, 0), d3, out, false);
750
+ *out->cur++ = '"';
751
+
752
+ return;
753
+ }
754
+ *out->cur++ = '{';
755
+ fill_indent(out, d2);
756
+ size = d3 * out->indent + 2;
757
+ #if HAS_STRUCT_MEMBERS
758
+ ma = rb_struct_s_members(clas);
759
+ #endif
760
+
761
+ #ifdef RSTRUCT_LEN
762
+ #if UNIFY_FIXNUM_AND_BIGNUM
763
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
764
+ #else // UNIFY_FIXNUM_AND_INTEGER
765
+ cnt = (int)RSTRUCT_LEN(obj);
766
+ #endif // UNIFY_FIXNUM_AND_INTEGER
767
+ #else
768
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
769
+ // class in interpreted Ruby so length() may not be defined.
770
+ cnt = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
771
+ #endif
772
+ for (i = 0; i < cnt; i++) {
773
+ #ifdef RSTRUCT_LEN
774
+ v = RSTRUCT_GET(obj, i);
775
+ #else
776
+ v = rb_struct_aref(obj, INT2FIX(i));
777
+ #endif
778
+ if (ma != Qnil) {
779
+ name = rb_id2name(SYM2ID(rb_ary_entry(ma, i)));
780
+ len = strlen(name);
781
+ } else {
782
+ len = snprintf(num_id, sizeof(num_id), "%d", i);
783
+ name = num_id;
784
+ }
785
+ assure_size(out, size + len + 3);
786
+ fill_indent(out, d3);
787
+ *out->cur++ = '"';
788
+ memcpy(out->cur, name, len);
789
+ out->cur += len;
790
+ *out->cur++ = '"';
791
+ *out->cur++ = ':';
792
+ oj_dump_custom_val(v, d3, out, true);
793
+ *out->cur++ = ',';
794
+ }
795
+ out->cur--;
796
+ *out->cur++ = '}';
797
+ *out->cur = '\0';
798
+ }
799
+ }
800
+
801
+ static void
802
+ dump_data(VALUE obj, int depth, Out out, bool as_ok) {
803
+ long id = oj_check_circular(obj, out);
804
+ VALUE clas;
805
+
806
+ if (0 > id) {
807
+ oj_dump_nil(Qnil, depth, out, false);
808
+ } else if (Qnil != (clas = dump_common(obj, depth, out))) {
809
+ dump_obj_attrs(obj, clas, id, depth, out);
810
+ }
811
+ }
812
+
813
+ static void
814
+ dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
815
+ dump_obj_str(obj, depth, out);
816
+ }
817
+
818
+ static void
819
+ dump_complex(VALUE obj, int depth, Out out, bool as_ok) {
820
+ complex_dump(obj, depth, out);
821
+ }
822
+
823
+ static void
824
+ dump_rational(VALUE obj, int depth, Out out, bool as_ok) {
825
+ rational_dump(obj, depth, out);
826
+ }
827
+
828
+ static DumpFunc custom_funcs[] = {
829
+ NULL, // RUBY_T_NONE = 0x00,
830
+ dump_obj, // RUBY_T_OBJECT = 0x01,
831
+ oj_dump_class, // RUBY_T_CLASS = 0x02,
832
+ oj_dump_class, // RUBY_T_MODULE = 0x03,
833
+ oj_dump_float, // RUBY_T_FLOAT = 0x04,
834
+ oj_dump_str, // RUBY_T_STRING = 0x05,
835
+ dump_regexp, // RUBY_T_REGEXP = 0x06,
836
+ dump_array, // RUBY_T_ARRAY = 0x07,
837
+ dump_hash, // RUBY_T_HASH = 0x08,
838
+ dump_struct, // RUBY_T_STRUCT = 0x09,
839
+ oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
840
+ NULL, // RUBY_T_FILE = 0x0b,
841
+ dump_data, // RUBY_T_DATA = 0x0c,
842
+ NULL, // RUBY_T_MATCH = 0x0d,
843
+ dump_complex, // RUBY_T_COMPLEX = 0x0e,
844
+ dump_rational, // RUBY_T_RATIONAL = 0x0f,
845
+ NULL, // 0x10
846
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
847
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
848
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
849
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
850
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
851
+ };
852
+
853
+ void
854
+ oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
855
+ int type = rb_type(obj);
856
+
857
+ if (MAX_DEPTH < depth) {
858
+ rb_raise(rb_eNoMemError, "Too deeply nested.\n");
859
+ }
860
+ if (0 < type && type <= RUBY_T_FIXNUM) {
861
+ DumpFunc f = custom_funcs[type];
862
+
863
+ if (NULL != f) {
864
+ f(obj, depth, out, true);
865
+ return;
866
+ }
867
+ }
868
+ oj_dump_nil(Qnil, depth, out, false);
869
+ }
870
+
871
+ ///// load functions /////
872
+
873
+ static void
874
+ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
875
+ const char *key = kval->key;
876
+ int klen = kval->klen;
877
+ Val parent = stack_peek(&pi->stack);
878
+ volatile VALUE rkey = kval->key_val;
879
+
880
+ if (Qundef == rkey &&
881
+ Yes == pi->options.create_ok &&
882
+ NULL != pi->options.create_id &&
883
+ *pi->options.create_id == *key &&
884
+ (int)pi->options.create_id_len == klen &&
885
+ 0 == strncmp(pi->options.create_id, key, klen)) {
886
+
887
+ parent->clas = oj_name2class(pi, str, len, false, rb_eArgError);
888
+ if (2 == klen && '^' == *key && 'o' == key[1]) {
889
+ if (Qundef != parent->clas) {
890
+ if (!oj_code_has(codes, parent->clas, false)) {
891
+ parent->val = rb_obj_alloc(parent->clas);
892
+ }
893
+ }
894
+ }
895
+ } else {
896
+ volatile VALUE rstr = rb_str_new(str, len);
897
+
898
+ if (Qundef == rkey) {
899
+ rkey = rb_str_new(key, klen);
900
+ rstr = oj_encode(rstr);
901
+ rkey = oj_encode(rkey);
902
+ if (Yes == pi->options.sym_key) {
903
+ rkey = rb_str_intern(rkey);
904
+ }
905
+ }
906
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
907
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
908
+
909
+ if (Qnil != clas) {
910
+ rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
911
+ }
912
+ }
913
+ switch (rb_type(parent->val)) {
914
+ case T_OBJECT:
915
+ oj_set_obj_ivar(parent, kval, rstr);
916
+ break;
917
+ case T_HASH:
918
+ if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 == strncmp("time", parent->key, 4)) {
919
+ if (Qnil == (parent->val = oj_parse_xml_time(str, len))) {
920
+ parent->val = rb_funcall(rb_cTime, rb_intern("parse"), 1, rb_str_new(str, len));
921
+ }
922
+ } else {
923
+ rb_hash_aset(parent->val, rkey, rstr);
924
+ }
925
+ break;
926
+ default:
927
+ break;
928
+ }
929
+ }
930
+ }
931
+
932
+ static void
933
+ end_hash(struct _ParseInfo *pi) {
934
+ Val parent = stack_peek(&pi->stack);
935
+
936
+ if (Qundef != parent->clas && parent->clas != rb_obj_class(parent->val)) {
937
+ volatile VALUE obj = oj_code_load(codes, parent->clas, parent->val);
938
+
939
+ if (Qnil != obj) {
940
+ parent->val = obj;
941
+ } else {
942
+ parent->val = rb_funcall(parent->clas, oj_json_create_id, 1, parent->val);
943
+ }
944
+ parent->clas = Qundef;
945
+ }
946
+ }
947
+
948
+ static VALUE
949
+ calc_hash_key(ParseInfo pi, Val parent) {
950
+ volatile VALUE rkey = parent->key_val;
951
+
952
+ if (Qundef == rkey) {
953
+ rkey = rb_str_new(parent->key, parent->klen);
954
+ }
955
+ rkey = oj_encode(rkey);
956
+ if (Yes == pi->options.sym_key) {
957
+ rkey = rb_str_intern(rkey);
958
+ }
959
+ return rkey;
960
+ }
961
+
962
+ static void
963
+ hash_set_num(struct _ParseInfo *pi, Val kval, NumInfo ni) {
964
+ Val parent = stack_peek(&pi->stack);
965
+
966
+ switch (rb_type(parent->val)) {
967
+ case T_OBJECT:
968
+ oj_set_obj_ivar(parent, kval, oj_num_as_value(ni));
969
+ break;
970
+ case T_HASH:
971
+ if (4 == parent->klen && NULL != parent->key && rb_cTime == parent->clas && 0 == strncmp("time", parent->key, 4)) {
972
+ int64_t nsec = ni->num * 1000000000LL / ni->div;
973
+
974
+ if (ni->neg) {
975
+ ni->i = -ni->i;
976
+ if (0 < nsec) {
977
+ ni->i--;
978
+ nsec = 1000000000LL - nsec;
979
+ }
980
+ }
981
+ if (86400 == ni->exp) { // UTC time
982
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
983
+ // Since the ruby C routines alway create local time, the
984
+ // offset and then a convertion to UTC keeps makes the time
985
+ // match the expected value.
986
+ parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
987
+ } else if (ni->hasExp) {
988
+ time_t t = (time_t)(ni->i + ni->exp);
989
+ struct tm *st = gmtime(&t);
990
+ VALUE args[8];
991
+
992
+ args[0] = LONG2NUM(1900 + st->tm_year);
993
+ args[1] = LONG2NUM(1 + st->tm_mon);
994
+ args[2] = LONG2NUM(st->tm_mday);
995
+ args[3] = LONG2NUM(st->tm_hour);
996
+ args[4] = LONG2NUM(st->tm_min);
997
+ args[5] = rb_float_new((double)st->tm_sec + ((double)nsec + 0.5) / 1000000000.0);
998
+ args[6] = LONG2NUM(ni->exp);
999
+ parent->val = rb_funcall2(rb_cTime, oj_new_id, 7, args);
1000
+ } else {
1001
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
1002
+ }
1003
+ } else {
1004
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval), oj_num_as_value(ni));
1005
+ }
1006
+ break;
1007
+ default:
1008
+ break;
1009
+ }
1010
+ }
1011
+
1012
+ static void
1013
+ hash_set_value(ParseInfo pi, Val kval, VALUE value) {
1014
+ Val parent = stack_peek(&pi->stack);
1015
+
1016
+ switch (rb_type(parent->val)) {
1017
+ case T_OBJECT:
1018
+ oj_set_obj_ivar(parent, kval, value);
1019
+ break;
1020
+ case T_HASH:
1021
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval), value);
1022
+ break;
1023
+ default:
1024
+ break;
1025
+ }
1026
+ }
1027
+
1028
+ static void
1029
+ array_append_num(ParseInfo pi, NumInfo ni) {
1030
+ Val parent = stack_peek(&pi->stack);
1031
+
1032
+ rb_ary_push(parent->val, oj_num_as_value(ni));
1033
+ }
1034
+
1035
+ static void
1036
+ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
1037
+ volatile VALUE rstr = rb_str_new(str, len);
1038
+
1039
+ rstr = oj_encode(rstr);
1040
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
1041
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
1042
+
1043
+ if (Qnil != clas) {
1044
+ rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
1045
+ return;
1046
+ }
1047
+ }
1048
+ rb_ary_push(stack_peek(&pi->stack)->val, rstr);
1049
+ }
1050
+
1051
+ void
1052
+ oj_set_custom_callbacks(ParseInfo pi) {
1053
+ oj_set_compat_callbacks(pi);
1054
+ pi->hash_set_cstr = hash_set_cstr;
1055
+ pi->end_hash = end_hash;
1056
+ pi->hash_set_num = hash_set_num;
1057
+ pi->hash_set_value = hash_set_value;
1058
+ pi->array_append_cstr = array_append_cstr;
1059
+ pi->array_append_num = array_append_num;
1060
+ }
1061
+
1062
+ VALUE
1063
+ oj_custom_parse(int argc, VALUE *argv, VALUE self) {
1064
+ struct _ParseInfo pi;
1065
+
1066
+ parse_info_init(&pi);
1067
+ pi.options = oj_default_options;
1068
+ pi.handler = Qnil;
1069
+ pi.err_class = Qnil;
1070
+ pi.max_depth = 0;
1071
+ pi.options.allow_nan = Yes;
1072
+ pi.options.nilnil = Yes;
1073
+ oj_set_custom_callbacks(&pi);
1074
+
1075
+ if (T_STRING == rb_type(*argv)) {
1076
+ return oj_pi_parse(argc, argv, &pi, 0, 0, false);
1077
+ } else {
1078
+ return oj_pi_sparse(argc, argv, &pi, 0);
1079
+ }
1080
+ }
1081
+
1082
+ VALUE
1083
+ oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
1084
+ struct _ParseInfo pi;
1085
+
1086
+ parse_info_init(&pi);
1087
+ pi.options = oj_default_options;
1088
+ pi.handler = Qnil;
1089
+ pi.err_class = Qnil;
1090
+ pi.max_depth = 0;
1091
+ pi.options.allow_nan = Yes;
1092
+ pi.options.nilnil = Yes;
1093
+ oj_set_custom_callbacks(&pi);
1094
+ pi.end_hash = end_hash;
1095
+
1096
+ return oj_pi_parse(argc, argv, &pi, json, len, false);
1097
+ }