oj 2.18.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }