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
@@ -1,31 +1,6 @@
1
1
  /* scp.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 <stdlib.h>
@@ -187,7 +162,9 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
187
162
  struct _ParseInfo pi;
188
163
  VALUE input = argv[1];
189
164
 
165
+ parse_info_init(&pi);
190
166
  pi.err_class = Qnil;
167
+ pi.max_depth = 0;
191
168
  pi.options = oj_default_options;
192
169
  if (3 == argc) {
193
170
  oj_parse_options(argv[2], &pi.options);
@@ -237,6 +214,7 @@ oj_sc_parse(int argc, VALUE *argv, VALUE self) {
237
214
  pi.add_value = noop_add_value;
238
215
  pi.expect_value = 0;
239
216
  }
217
+ pi.has_callbacks = true;
240
218
 
241
219
  if (T_STRING == rb_type(input)) {
242
220
  return oj_pi_parse(argc - 1, argv + 1, &pi, 0, 0, 1);
@@ -35,6 +35,7 @@
35
35
  #include <math.h>
36
36
 
37
37
  #include "oj.h"
38
+ #include "encode.h"
38
39
  #include "parse.h"
39
40
  #include "buf.h"
40
41
  #include "hash.h" // for oj_strndup()
@@ -252,6 +253,17 @@ read_escaped_str(ParseInfo pi) {
252
253
  case '"': buf_append(&buf, '"'); break;
253
254
  case '/': buf_append(&buf, '/'); break;
254
255
  case '\\': buf_append(&buf, '\\'); break;
256
+ case '\'':
257
+ // The json gem claims this is not an error despite the
258
+ // ECMA-404 indicating it is not valid.
259
+ if (CompatMode == pi->options.mode) {
260
+ buf_append(&buf, '\'');
261
+ } else {
262
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
263
+ buf_cleanup(&buf);
264
+ return;
265
+ }
266
+ break;
255
267
  case 'u':
256
268
  if (0 == (code = read_hex(pi)) && err_has(&pi->err)) {
257
269
  buf_cleanup(&buf);
@@ -428,15 +440,22 @@ read_num(ParseInfo pi) {
428
440
  c = reader_get(&pi->rd);
429
441
  }
430
442
  if ('I' == c) {
431
- if (0 != reader_expect(&pi->rd, "nfinity")) {
443
+ if (No == pi->options.allow_nan) {
444
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
445
+ return;
446
+ } else if (0 != reader_expect(&pi->rd, "nfinity")) {
432
447
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
433
448
  return;
434
449
  }
435
450
  ni.infinity = 1;
436
451
  } else {
437
452
  int dec_cnt = 0;
453
+ bool zero1 = false;
438
454
 
439
455
  for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
456
+ if (0 == ni.i && '0' == c) {
457
+ zero1 = true;
458
+ }
440
459
  if (0 < ni.i) {
441
460
  dec_cnt++;
442
461
  }
@@ -445,6 +464,13 @@ read_num(ParseInfo pi) {
445
464
  } else {
446
465
  int d = (c - '0');
447
466
 
467
+ if (0 < d) {
468
+ if (zero1 && CompatMode == pi->options.mode) {
469
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
470
+ return;
471
+ }
472
+ zero1 = false;
473
+ }
448
474
  ni.i = ni.i * 10 + d;
449
475
  if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
450
476
  ni.big = 1;
@@ -453,6 +479,9 @@ read_num(ParseInfo pi) {
453
479
  }
454
480
  if ('.' == c) {
455
481
  c = reader_get(&pi->rd);
482
+ if (c < '0' || '9' < c) {
483
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
484
+ }
456
485
  for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
457
486
  int d = (c - '0');
458
487
 
@@ -616,9 +645,17 @@ void
616
645
  oj_sparse2(ParseInfo pi) {
617
646
  int first = 1;
618
647
  char c;
648
+ long start = 0;
619
649
 
620
650
  err_init(&pi->err);
621
651
  while (1) {
652
+ if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
653
+ VALUE err_clas = oj_get_json_err_class("NestingError");
654
+
655
+ oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
656
+ pi->err_class = err_clas;
657
+ return;
658
+ }
622
659
  c = reader_next_non_white(&pi->rd);
623
660
  if (!first && '\0' != c) {
624
661
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document");
@@ -657,12 +694,25 @@ oj_sparse2(ParseInfo pi) {
657
694
  case '7':
658
695
  case '8':
659
696
  case '9':
660
- case 'I':
661
697
  reader_backup(&pi->rd);
662
698
  read_num(pi);
663
699
  break;
700
+ case 'I':
701
+ if (Yes == pi->options.allow_nan) {
702
+ reader_backup(&pi->rd);
703
+ read_num(pi);
704
+ } else {
705
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
706
+ return;
707
+ }
708
+ break;
664
709
  case 'N':
665
- read_nan(pi);
710
+ if (Yes == pi->options.allow_nan) {
711
+ read_nan(pi);
712
+ } else {
713
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
714
+ return;
715
+ }
666
716
  break;
667
717
  case 't':
668
718
  read_true(pi);
@@ -719,23 +769,28 @@ oj_sparse2(ParseInfo pi) {
719
769
  }
720
770
  if (stack_empty(&pi->stack)) {
721
771
  if (Qundef != pi->proc) {
772
+ VALUE args[3];
773
+ long len = pi->rd.pos - start;
774
+
775
+ *args = stack_head_val(&pi->stack);
776
+ args[1] = LONG2NUM(start);
777
+ args[2] = LONG2NUM(len);
778
+
722
779
  if (Qnil == pi->proc) {
723
- rb_yield(stack_head_val(&pi->stack));
780
+ rb_yield_values2(3, args);
724
781
  } else {
725
782
  #if HAS_PROC_WITH_BLOCK
726
- VALUE args[1];
727
-
728
- *args = stack_head_val(&pi->stack);
729
- rb_proc_call_with_block(pi->proc, 1, args, Qnil);
783
+ rb_proc_call_with_block(pi->proc, 3, args, Qnil);
730
784
  #else
731
785
  oj_set_error_at(pi, rb_eNotImpError, __FILE__, __LINE__,
732
786
  "Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
733
787
  return;
734
788
  #endif
735
789
  }
736
- } else {
790
+ } else if (!pi->has_callbacks) {
737
791
  first = 0;
738
792
  }
793
+ start = pi->rd.pos;
739
794
  }
740
795
  }
741
796
  }
@@ -758,11 +813,19 @@ oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
758
813
  rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
759
814
  }
760
815
  input = argv[0];
761
- if (2 == argc) {
762
- oj_parse_options(argv[1], &pi->options);
816
+ if (2 <= argc) {
817
+ if (T_HASH == rb_type(argv[1])) {
818
+ oj_parse_options(argv[1], &pi->options);
819
+ } else if (3 <= argc && T_HASH == rb_type(argv[2])) {
820
+ oj_parse_options(argv[2], &pi->options);
821
+ }
763
822
  }
764
- if (Qnil == input && Yes == pi->options.nilnil) {
765
- return Qnil;
823
+ if (Qnil == input) {
824
+ if (Yes == pi->options.nilnil) {
825
+ return Qnil;
826
+ } else {
827
+ rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
828
+ }
766
829
  }
767
830
  if (rb_block_given_p()) {
768
831
  pi->proc = Qnil;
@@ -829,6 +892,19 @@ oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
829
892
  if (Qnil != pi->err_class) {
830
893
  pi->err.clas = pi->err_class;
831
894
  }
895
+ if (CompatMode == pi->options.mode) {
896
+ // The json gem requires the error message be UTF-8 encoded. In
897
+ // additional the complete JSON source should be returned but that
898
+ // is not possible without stored all the bytes read and reading
899
+ // the remaining bytes on the stream. Both seem like a very bad
900
+ // idea.
901
+ VALUE args[] = { oj_encode(rb_str_new2(pi->err.msg)) };
902
+
903
+ rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
904
+ } else {
905
+ oj_err_raise(&pi->err);
906
+ }
907
+
832
908
  oj_err_raise(&pi->err);
833
909
  }
834
910
  return result;
@@ -0,0 +1,301 @@
1
+ /* stream_writer.c
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <errno.h>
7
+
8
+ #include <ruby.h>
9
+
10
+ #include "oj.h"
11
+
12
+ extern VALUE Oj;
13
+
14
+ static void
15
+ stream_writer_free(void *ptr) {
16
+ StreamWriter sw;
17
+
18
+ if (0 == ptr) {
19
+ return;
20
+ }
21
+ sw = (StreamWriter)ptr;
22
+ xfree(sw->sw.out.buf);
23
+ xfree(sw->sw.types);
24
+ xfree(ptr);
25
+ }
26
+
27
+ static void
28
+ stream_writer_write(StreamWriter sw) {
29
+ ssize_t size = sw->sw.out.cur - sw->sw.out.buf;
30
+
31
+ switch (sw->type) {
32
+ case STRING_IO:
33
+ rb_funcall(sw->stream, oj_write_id, 1, rb_str_new(sw->sw.out.buf, size));
34
+ break;
35
+ case STREAM_IO:
36
+ rb_funcall(sw->stream, oj_write_id, 1, rb_str_new(sw->sw.out.buf, size));
37
+ break;
38
+ case FILE_IO:
39
+ if (size != write(sw->fd, sw->sw.out.buf, size)) {
40
+ rb_raise(rb_eIOError, "Write failed. [_%d_:%s]\n", errno, strerror(errno));
41
+ }
42
+ break;
43
+ default:
44
+ rb_raise(rb_eArgError, "expected an IO Object.");
45
+ }
46
+ }
47
+
48
+ static void
49
+ stream_writer_reset_buf(StreamWriter sw) {
50
+ sw->sw.out.cur = sw->sw.out.buf;
51
+ *sw->sw.out.cur = '\0';
52
+ }
53
+
54
+ /* Document-method: new
55
+ * call-seq: new(io, options)
56
+ *
57
+ * Creates a new StreamWriter.
58
+ * - *io* [_IO_] stream to write to
59
+ * - *options* [_Hash_] formating options
60
+ */
61
+ static VALUE
62
+ stream_writer_new(int argc, VALUE *argv, VALUE self) {
63
+ StreamWriterType type = STREAM_IO;
64
+ int fd = 0;
65
+ VALUE stream = argv[0];
66
+ VALUE clas = rb_obj_class(stream);
67
+ StreamWriter sw;
68
+ #if !IS_WINDOWS
69
+ VALUE s;
70
+ #endif
71
+
72
+ if (oj_stringio_class == clas) {
73
+ type = STRING_IO;
74
+ #if !IS_WINDOWS
75
+ } else if (rb_respond_to(stream, oj_fileno_id) &&
76
+ Qnil != (s = rb_funcall(stream, oj_fileno_id, 0)) &&
77
+ 0 != (fd = FIX2INT(s))) {
78
+ type = FILE_IO;
79
+ #endif
80
+ } else if (rb_respond_to(stream, oj_write_id)) {
81
+ type = STREAM_IO;
82
+ } else {
83
+ rb_raise(rb_eArgError, "expected an IO Object.");
84
+ }
85
+ sw = ALLOC(struct _StreamWriter);
86
+ oj_str_writer_init(&sw->sw);
87
+ if (2 == argc) {
88
+ oj_parse_options(argv[1], &sw->sw.opts);
89
+ }
90
+ sw->sw.out.indent = sw->sw.opts.indent;
91
+ sw->stream = stream;
92
+ sw->type = type;
93
+ sw->fd = fd;
94
+
95
+ return Data_Wrap_Struct(oj_stream_writer_class, 0, stream_writer_free, sw);
96
+ }
97
+
98
+ /* Document-method: push_key
99
+ * call-seq: push_key(key)
100
+ *
101
+ * Pushes a key onto the JSON document. The key will be used for the next push
102
+ * if currently in a JSON object and ignored otherwise. If a key is provided on
103
+ * the next push then that new key will be ignored.
104
+ * - *key* [_String_] the key pending for the next push
105
+ */
106
+ static VALUE
107
+ stream_writer_push_key(VALUE self, VALUE key) {
108
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
109
+
110
+ rb_check_type(key, T_STRING);
111
+ stream_writer_reset_buf(sw);
112
+ oj_str_writer_push_key(&sw->sw, StringValuePtr(key));
113
+ stream_writer_write(sw);
114
+ return Qnil;
115
+ }
116
+
117
+ /* Document-method: push_object
118
+ * call-seq: push_object(key=nil)
119
+ *
120
+ * Pushes an object onto the JSON document. Future pushes will be to this object
121
+ * until a pop() is called.
122
+ * - *key* [_String_] the key if adding to an object in the JSON document
123
+ */
124
+ static VALUE
125
+ stream_writer_push_object(int argc, VALUE *argv, VALUE self) {
126
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
127
+
128
+ stream_writer_reset_buf(sw);
129
+ switch (argc) {
130
+ case 0:
131
+ oj_str_writer_push_object(&sw->sw, 0);
132
+ break;
133
+ case 1:
134
+ if (Qnil == argv[0]) {
135
+ oj_str_writer_push_object(&sw->sw, 0);
136
+ } else {
137
+ rb_check_type(argv[0], T_STRING);
138
+ oj_str_writer_push_object(&sw->sw, StringValuePtr(argv[0]));
139
+ }
140
+ break;
141
+ default:
142
+ rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
143
+ break;
144
+ }
145
+ stream_writer_write(sw);
146
+ return Qnil;
147
+ }
148
+
149
+ /* Document-method: push_array
150
+ * call-seq: push_array(key=nil)
151
+ *
152
+ * Pushes an array onto the JSON document. Future pushes will be to this object
153
+ * until a pop() is called.
154
+ * - *key* [_String_] the key if adding to an object in the JSON document
155
+ */
156
+ static VALUE
157
+ stream_writer_push_array(int argc, VALUE *argv, VALUE self) {
158
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
159
+
160
+ stream_writer_reset_buf(sw);
161
+ switch (argc) {
162
+ case 0:
163
+ oj_str_writer_push_array(&sw->sw, 0);
164
+ break;
165
+ case 1:
166
+ if (Qnil == argv[0]) {
167
+ oj_str_writer_push_array(&sw->sw, 0);
168
+ } else {
169
+ rb_check_type(argv[0], T_STRING);
170
+ oj_str_writer_push_array(&sw->sw, StringValuePtr(argv[0]));
171
+ }
172
+ break;
173
+ default:
174
+ rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
175
+ break;
176
+ }
177
+ stream_writer_write(sw);
178
+ return Qnil;
179
+ }
180
+
181
+ /* Document-method: push_value
182
+ * call-seq: push_value(value, key=nil)
183
+ *
184
+ * Pushes a value onto the JSON document.
185
+ * - *value* [_Object_] value to add to the JSON document
186
+ * - *key* [_String_] the key if adding to an object in the JSON document
187
+ */
188
+ static VALUE
189
+ stream_writer_push_value(int argc, VALUE *argv, VALUE self) {
190
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
191
+
192
+ stream_writer_reset_buf(sw);
193
+ switch (argc) {
194
+ case 1:
195
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
196
+ break;
197
+ case 2:
198
+ if (Qnil == argv[1]) {
199
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
200
+ } else {
201
+ rb_check_type(argv[1], T_STRING);
202
+ oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, StringValuePtr(argv[1]));
203
+ }
204
+ break;
205
+ default:
206
+ rb_raise(rb_eArgError, "Wrong number of argument to 'push_value'.");
207
+ break;
208
+ }
209
+ stream_writer_write(sw);
210
+ return Qnil;
211
+ }
212
+
213
+ /* Document-method: push_json
214
+ * call-seq: push_json(value, key=nil)
215
+ *
216
+ * Pushes a string onto the JSON document. The String must be a valid JSON
217
+ * encoded string. No additional checking is done to verify the validity of the
218
+ * string.
219
+ * - *value* [_Object_] value to add to the JSON document
220
+ * - *key* [_String_] the key if adding to an object in the JSON document
221
+ */
222
+ static VALUE
223
+ stream_writer_push_json(int argc, VALUE *argv, VALUE self) {
224
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
225
+
226
+ rb_check_type(argv[0], T_STRING);
227
+ stream_writer_reset_buf(sw);
228
+ switch (argc) {
229
+ case 1:
230
+ oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), 0);
231
+ break;
232
+ case 2:
233
+ if (Qnil == argv[0]) {
234
+ oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), 0);
235
+ } else {
236
+ rb_check_type(argv[1], T_STRING);
237
+ oj_str_writer_push_json((StrWriter)DATA_PTR(self), StringValuePtr(*argv), StringValuePtr(argv[1]));
238
+ }
239
+ break;
240
+ default:
241
+ rb_raise(rb_eArgError, "Wrong number of argument to 'push_json'.");
242
+ break;
243
+ }
244
+ stream_writer_write(sw);
245
+ return Qnil;
246
+ }
247
+
248
+ /* Document-method: pop
249
+ * call-seq: pop()
250
+ *
251
+ * Pops up a level in the JSON document closing the array or object that is
252
+ * currently open.
253
+ */
254
+ static VALUE
255
+ stream_writer_pop(VALUE self) {
256
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
257
+
258
+ stream_writer_reset_buf(sw);
259
+ oj_str_writer_pop(&sw->sw);
260
+ stream_writer_write(sw);
261
+ return Qnil;
262
+ }
263
+
264
+ /* Document-method: pop_all
265
+ * call-seq: pop_all()
266
+ *
267
+ * Pops all level in the JSON document closing all the array or object that is
268
+ * currently open.
269
+ */
270
+ static VALUE
271
+ stream_writer_pop_all(VALUE self) {
272
+ StreamWriter sw = (StreamWriter)DATA_PTR(self);
273
+
274
+ stream_writer_reset_buf(sw);
275
+ oj_str_writer_pop_all(&sw->sw);
276
+ stream_writer_write(sw);
277
+
278
+ return Qnil;
279
+ }
280
+
281
+ /* Document-class: Oj::StreamWriter
282
+ *
283
+ * Supports building a JSON document one element at a time. Build the IO stream
284
+ * document by pushing values into the document. Pushing an array or an object
285
+ * will create that element in the JSON document and subsequent pushes will add
286
+ * the elements to that array or object until a pop() is called.
287
+ */
288
+ void
289
+ oj_stream_writer_init() {
290
+ oj_stream_writer_class = rb_define_class_under(Oj, "StreamWriter", rb_cObject);
291
+ rb_define_module_function(oj_stream_writer_class, "new", stream_writer_new, -1);
292
+ rb_define_method(oj_stream_writer_class, "push_key", stream_writer_push_key, 1);
293
+ rb_define_method(oj_stream_writer_class, "push_object", stream_writer_push_object, -1);
294
+ rb_define_method(oj_stream_writer_class, "push_array", stream_writer_push_array, -1);
295
+ rb_define_method(oj_stream_writer_class, "push_value", stream_writer_push_value, -1);
296
+ rb_define_method(oj_stream_writer_class, "push_json", stream_writer_push_json, -1);
297
+ rb_define_method(oj_stream_writer_class, "pop", stream_writer_pop, 0);
298
+ rb_define_method(oj_stream_writer_class, "pop_all", stream_writer_pop_all, 0);
299
+ }
300
+
301
+