oj 3.7.11 → 3.9.1

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.
@@ -21,7 +21,7 @@ extern "C" {
21
21
  #include <stdint.h>
22
22
  #include <stdbool.h>
23
23
 
24
- #if HAVE_LIBPTHREAD
24
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
25
25
  #include <pthread.h>
26
26
  #endif
27
27
  #include "cache8.h"
@@ -139,6 +139,7 @@ typedef struct _options {
139
139
  char to_hash; // YesNo
140
140
  char to_json; // YesNo
141
141
  char as_json; // YesNo
142
+ char raw_json; // YesNo
142
143
  char nilnil; // YesNo
143
144
  char empty_string; // YesNo
144
145
  char allow_gc; // allow GC during parse
@@ -147,6 +148,7 @@ typedef struct _options {
147
148
  char create_ok; // YesNo allow create_id
148
149
  char allow_nan; // YEsyNo for parsing only
149
150
  char trace; // YesNo
151
+ char safe; // YesNo
150
152
  int64_t integer_range_min; // dump numbers outside range as string
151
153
  int64_t integer_range_max;
152
154
  const char *create_id; // 0 or string
@@ -202,7 +204,7 @@ typedef struct _strWriter {
202
204
  char *types; // DumpType
203
205
  char *types_end;
204
206
  int keyWritten;
205
-
207
+
206
208
  } *StrWriter;
207
209
 
208
210
  typedef struct _streamWriter {
@@ -219,7 +221,7 @@ enum {
219
221
  COL_VAL = 0x02,
220
222
  RUBY_VAL = 0x03
221
223
  };
222
-
224
+
223
225
  typedef struct _leaf {
224
226
  struct _leaf *next;
225
227
  union {
@@ -349,6 +351,7 @@ extern ID oj_readpartial_id;
349
351
  extern ID oj_replace_id;
350
352
  extern ID oj_stat_id;
351
353
  extern ID oj_string_id;
354
+ extern ID oj_raw_json_id;
352
355
  extern ID oj_to_h_id;
353
356
  extern ID oj_to_hash_id;
354
357
  extern ID oj_to_json_id;
@@ -365,8 +368,9 @@ extern ID oj_write_id;
365
368
 
366
369
  extern bool oj_use_hash_alt;
367
370
  extern bool oj_use_array_alt;
371
+ extern bool string_writer_optimized;
368
372
 
369
- #if HAVE_LIBPTHREAD
373
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
370
374
  extern pthread_mutex_t oj_cache_mutex;
371
375
  #else
372
376
  extern VALUE oj_cache_mutex;
@@ -8,6 +8,7 @@
8
8
  #include <string.h>
9
9
  #include <unistd.h>
10
10
  #include <math.h>
11
+ #include <ruby/util.h>
11
12
 
12
13
  #include "oj.h"
13
14
  #include "encode.h"
@@ -1036,7 +1037,7 @@ CLEANUP:
1036
1037
  if (Qnil != pi->err_class) {
1037
1038
  pi->err.clas = pi->err_class;
1038
1039
  }
1039
- if (CompatMode == pi->options.mode) {
1040
+ if (CompatMode == pi->options.mode && Yes != pi->options.safe) {
1040
1041
  // The json gem requires the error message be UTF-8 encoded. In
1041
1042
  // additional the complete JSON source must be returned. There
1042
1043
  // does not seem to be a size limit.
@@ -117,7 +117,7 @@ dump_attr_cb(ID key, VALUE value, Out out) {
117
117
  dump_rails_val(value, depth, out, true);
118
118
  out->depth = depth;
119
119
  *out->cur++ = ',';
120
-
120
+
121
121
  return ST_CONTINUE;
122
122
  }
123
123
 
@@ -230,7 +230,7 @@ dump_sec_nano(VALUE obj, int64_t sec, long nsec, Out out) {
230
230
  int tzhour, tzmin;
231
231
  char tzsign = '+';
232
232
  int len;
233
-
233
+
234
234
  if (out->end - out->cur <= 36) {
235
235
  assure_size(out, 36);
236
236
  }
@@ -353,7 +353,7 @@ columns_array(VALUE rcols, int *ccnt) {
353
353
  StrLen cols;
354
354
  int i;
355
355
  int cnt = (int)RARRAY_LEN(rcols);
356
-
356
+
357
357
  *ccnt = cnt;
358
358
  cols = ALLOC_N(struct _strLen, cnt);
359
359
  for (i = 0, cp = cols; i < cnt; i++, cp++) {
@@ -372,7 +372,7 @@ dump_row(VALUE row, StrLen cols, int ccnt, int depth, Out out) {
372
372
  size_t size;
373
373
  int d2 = depth + 1;
374
374
  int i;
375
-
375
+
376
376
  assure_size(out, 2);
377
377
  *out->cur++ = '{';
378
378
  size = depth * out->indent + 3;
@@ -432,7 +432,7 @@ dump_activerecord_result(VALUE obj, int depth, Out out, bool as_ok) {
432
432
  int i, rcnt;
433
433
  size_t size;
434
434
  int d2 = depth + 1;
435
-
435
+
436
436
  if (0 == rows_id) {
437
437
  rows_id = rb_intern("@rows");
438
438
  columns_id = rb_intern("@columns");
@@ -597,7 +597,7 @@ create_opt(ROptTable rot, VALUE clas) {
597
597
  ro = &rot->table[olen];
598
598
  } else {
599
599
  int i;
600
-
600
+
601
601
  for (i = 0, ro = rot->table; i < olen; i++, ro++) {
602
602
  if (clas < ro->clas) {
603
603
  memmove(ro + 1, ro, sizeof(struct _rOpt) * (olen - i));
@@ -672,7 +672,7 @@ encoder_new(int argc, VALUE *argv, VALUE self) {
672
672
  e->opts = oj_default_options;
673
673
  e->arg = Qnil;
674
674
  copy_opts(&ropts, &e->ropts);
675
-
675
+
676
676
  if (1 <= argc && Qnil != *argv) {
677
677
  oj_parse_options(*argv, &e->opts);
678
678
  e->arg = *argv;
@@ -727,7 +727,7 @@ optimize(int argc, VALUE *argv, ROptTable rot, bool on) {
727
727
  int i;
728
728
  NamedFunc nf;
729
729
  VALUE clas;
730
-
730
+
731
731
  oj_rails_hash_opt = on;
732
732
  oj_rails_array_opt = on;
733
733
  oj_rails_float_opt = on;
@@ -750,6 +750,8 @@ optimize(int argc, VALUE *argv, ROptTable rot, bool on) {
750
750
  oj_rails_array_opt = on;
751
751
  } else if (rb_cFloat == *argv) {
752
752
  oj_rails_float_opt = on;
753
+ } else if (oj_string_writer_class == *argv) {
754
+ string_writer_optimized = on;
753
755
  } else if (NULL != (ro = oj_rails_get_opt(rot, *argv)) ||
754
756
  NULL != (ro = create_opt(rot, *argv))) {
755
757
  ro->on = on;
@@ -759,14 +761,14 @@ optimize(int argc, VALUE *argv, ROptTable rot, bool on) {
759
761
 
760
762
  /* Document-method optimize
761
763
  * call-seq: optimize(*classes)
762
- *
764
+ *
763
765
  * Use Oj rails optimized routines to encode the specified classes. This
764
766
  * ignores the as_json() method on the class and uses an internal encoding
765
767
  * instead. Passing in no classes indicates all should use the optimized
766
768
  * version of encoding for all previously optimized classes. Passing in the
767
769
  * Object class set a global switch that will then use the optimized behavior
768
770
  * for all classes.
769
- *
771
+ *
770
772
  * - *classes* [_Class_] a list of classes to optimize
771
773
  */
772
774
  static VALUE
@@ -780,19 +782,20 @@ encoder_optimize(int argc, VALUE *argv, VALUE self) {
780
782
 
781
783
  /* Document-method: optimize
782
784
  * call-seq: optimize(*classes)
783
- *
785
+ *
784
786
  * Use Oj rails optimized routines to encode the specified classes. This
785
787
  * ignores the as_json() method on the class and uses an internal encoding
786
788
  * instead. Passing in no classes indicates all should use the optimized
787
789
  * version of encoding for all previously optimized classes. Passing in the
788
790
  * Object class set a global switch that will then use the optimized behavior
789
791
  * for all classes.
790
- *
792
+ *
791
793
  * - *classes* [_Class_] a list of classes to optimize
792
794
  */
793
795
  static VALUE
794
796
  rails_optimize(int argc, VALUE *argv, VALUE self) {
795
797
  optimize(argc, argv, &ropts, true);
798
+ string_writer_optimized = true;
796
799
 
797
800
  return Qnil;
798
801
  }
@@ -806,7 +809,7 @@ rails_optimize(int argc, VALUE *argv, VALUE self) {
806
809
  VALUE
807
810
  rails_mimic_json(VALUE self) {
808
811
  VALUE json;
809
-
812
+
810
813
  if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
811
814
  json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
812
815
  } else {
@@ -820,7 +823,7 @@ rails_mimic_json(VALUE self) {
820
823
 
821
824
  /* Document-method: deoptimize
822
825
  * call-seq: deoptimize(*classes)
823
- *
826
+ *
824
827
  * Turn off Oj rails optimization on the specified classes.
825
828
  *
826
829
  * - *classes* [_Class_] a list of classes to deoptimize
@@ -836,7 +839,7 @@ encoder_deoptimize(int argc, VALUE *argv, VALUE self) {
836
839
 
837
840
  /* Document-method: deoptimize
838
841
  * call-seq: deoptimize(*classes)
839
- *
842
+ *
840
843
  * Turn off Oj rails optimization on the specified classes.
841
844
  *
842
845
  * - *classes* [_Class_] a list of classes to deoptimize
@@ -844,13 +847,14 @@ encoder_deoptimize(int argc, VALUE *argv, VALUE self) {
844
847
  static VALUE
845
848
  rails_deoptimize(int argc, VALUE *argv, VALUE self) {
846
849
  optimize(argc, argv, &ropts, false);
850
+ string_writer_optimized = false;
847
851
 
848
852
  return Qnil;
849
853
  }
850
854
 
851
855
  /* Document-method:optimized?
852
856
  * call-seq: optimized?(clas)
853
- *
857
+ *
854
858
  * - *clas* [_Class_] Class to check
855
859
  *
856
860
  * @return true if the class is being optimized for rails and false otherwise
@@ -868,7 +872,7 @@ encoder_optimized(VALUE self, VALUE clas) {
868
872
 
869
873
  /* Document-method: optimized?
870
874
  * call-seq: optimized?(clas)
871
- *
875
+ *
872
876
  * Returns true if the specified Class is being optimized.
873
877
  */
874
878
  static VALUE
@@ -966,7 +970,7 @@ encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *argv) {
966
970
 
967
971
  /* Document-method: encode
968
972
  * call-seq: encode(obj)
969
- *
973
+ *
970
974
  * - *obj* [_Object_] object to encode
971
975
  *
972
976
  * Returns encoded object as a JSON string.
@@ -977,7 +981,7 @@ encoder_encode(VALUE self, VALUE obj) {
977
981
 
978
982
  if (Qnil != e->arg) {
979
983
  VALUE argv[1] = { e->arg };
980
-
984
+
981
985
  return encode(obj, &e->ropts, &e->opts, 1, argv);
982
986
  }
983
987
  return encode(obj, &e->ropts, &e->opts, 0, NULL);
@@ -985,9 +989,9 @@ encoder_encode(VALUE self, VALUE obj) {
985
989
 
986
990
  /* Document-method: encode
987
991
  * call-seq: encode(obj, opts=nil)
988
- *
992
+ *
989
993
  * Encode obj as a JSON String.
990
- *
994
+ *
991
995
  * - *obj* [_Object_|Hash|Array] object to convert to a JSON String
992
996
  * - *opts* [_Hash_] options
993
997
  *
@@ -1037,7 +1041,7 @@ rails_time_precision(VALUE self, VALUE prec) {
1037
1041
 
1038
1042
  /* Document-method: set_encoder
1039
1043
  * call-seq: set_encoder()
1040
- *
1044
+ *
1041
1045
  * Sets the ActiveSupport.encoder to Oj::Rails::Encoder and wraps some of the
1042
1046
  * formatting globals used by ActiveSupport to allow the use of those globals
1043
1047
  * in the Oj::Rails optimizations.
@@ -1049,7 +1053,7 @@ rails_set_encoder(VALUE self) {
1049
1053
  VALUE encoding;
1050
1054
  VALUE pv;
1051
1055
  VALUE verbose;
1052
-
1056
+
1053
1057
  if (rb_const_defined_at(rb_cObject, rb_intern("ActiveSupport"))) {
1054
1058
  active = rb_const_get_at(rb_cObject, rb_intern("ActiveSupport"));
1055
1059
  } else {
@@ -1067,6 +1071,8 @@ rails_set_encoder(VALUE self) {
1067
1071
  rb_undef_method(encoding, "use_standard_json_time_format=");
1068
1072
  rb_define_module_function(encoding, "use_standard_json_time_format=", rails_use_standard_json_time_format, 1);
1069
1073
 
1074
+ pv = rb_iv_get(encoding, "@escape_html_entities_in_json");
1075
+ escape_html = Qtrue == pv;
1070
1076
  rb_undef_method(encoding, "escape_html_entities_in_json=");
1071
1077
  rb_define_module_function(encoding, "escape_html_entities_in_json=", rails_escape_html_entities_in_json, 1);
1072
1078
 
@@ -1090,7 +1096,7 @@ rails_set_decoder(VALUE self) {
1090
1096
  VALUE json;
1091
1097
  VALUE json_error;
1092
1098
  VALUE verbose;
1093
-
1099
+
1094
1100
  if (rb_const_defined_at(rb_cObject, rb_intern("JSON"))) {
1095
1101
  json = rb_const_get_at(rb_cObject, rb_intern("JSON"));
1096
1102
  } else {
@@ -1113,7 +1119,7 @@ rails_set_decoder(VALUE self) {
1113
1119
  rb_undef_method(json, "parse");
1114
1120
  rb_define_module_function(json, "parse", oj_mimic_parse, -1);
1115
1121
  rb_gv_set("$VERBOSE", verbose);
1116
-
1122
+
1117
1123
  return Qnil;
1118
1124
  }
1119
1125
 
@@ -1133,7 +1139,7 @@ oj_optimize_rails(VALUE self) {
1133
1139
  }
1134
1140
 
1135
1141
  /* Document-module: Oj::Rails
1136
- *
1142
+ *
1137
1143
  * Module that provides rails and active support compatibility.
1138
1144
  */
1139
1145
  /* Document-class: Oj::Rails::Encoder
@@ -1143,7 +1149,7 @@ oj_optimize_rails(VALUE self) {
1143
1149
  void
1144
1150
  oj_mimic_rails_init() {
1145
1151
  VALUE rails = rb_define_module_under(Oj, "Rails");
1146
-
1152
+
1147
1153
  rb_define_module_function(rails, "encode", rails_encode, -1);
1148
1154
 
1149
1155
  encoder_class = rb_define_class_under(rails, "Encoder", rb_cObject);
@@ -1283,7 +1289,10 @@ hash_cb(VALUE key, VALUE value, Out out) {
1283
1289
  int depth = out->depth;
1284
1290
  long size;
1285
1291
  int rtype = rb_type(key);
1286
-
1292
+
1293
+ if (out->omit_nil && Qnil == value) {
1294
+ return ST_CONTINUE;
1295
+ }
1287
1296
  if (rtype != T_STRING && rtype != T_SYMBOL) {
1288
1297
  key = rb_funcall(key, oj_to_s_id, 0);
1289
1298
  rtype = rb_type(key);
@@ -1398,6 +1407,8 @@ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
1398
1407
 
1399
1408
  if (NULL != (ro = oj_rails_get_opt(out->ropts, rb_obj_class(obj))) && ro->on) {
1400
1409
  ro->dump(obj, depth, out, as_ok);
1410
+ } else if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
1411
+ oj_dump_raw_json(obj, depth, out);
1401
1412
  } else if (rb_respond_to(obj, oj_as_json_id)) {
1402
1413
  dump_as_json(obj, depth, out, true);
1403
1414
  } else if (rb_respond_to(obj, oj_to_hash_id)) {
@@ -1405,6 +1416,8 @@ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
1405
1416
  } else {
1406
1417
  oj_dump_obj_to_s(obj, out);
1407
1418
  }
1419
+ } else if (Yes == out->opts->raw_json && rb_respond_to(obj, oj_raw_json_id)) {
1420
+ oj_dump_raw_json(obj, depth, out);
1408
1421
  } else if (rb_respond_to(obj, oj_to_hash_id)) {
1409
1422
  // Always attempt to_hash.
1410
1423
  dump_to_hash(obj, depth, out);
@@ -6,7 +6,7 @@
6
6
  #include <stdlib.h>
7
7
  #include <stdio.h>
8
8
  #include <string.h>
9
- #if HAVE_LIBPTHREAD
9
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
10
10
  #include <pthread.h>
11
11
  #endif
12
12
 
@@ -75,7 +75,7 @@ oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE
75
75
  if (No == pi->options.class_cache) {
76
76
  return resolve_classpath(pi, name, len, auto_define, error_class);
77
77
  }
78
- #if HAVE_LIBPTHREAD
78
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
79
79
  pthread_mutex_lock(&oj_cache_mutex);
80
80
  #else
81
81
  rb_mutex_lock(oj_cache_mutex);
@@ -85,7 +85,7 @@ oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE
85
85
  *slot = clas;
86
86
  }
87
87
  }
88
- #if HAVE_LIBPTHREAD
88
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
89
89
  pthread_mutex_unlock(&oj_cache_mutex);
90
90
  #else
91
91
  rb_mutex_unlock(oj_cache_mutex);
@@ -632,7 +632,7 @@ oj_sparse2(ParseInfo pi) {
632
632
  while (1) {
633
633
  if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
634
634
  VALUE err_clas = oj_get_json_err_class("NestingError");
635
-
635
+
636
636
  oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
637
637
  pi->err_class = err_clas;
638
638
  return;
@@ -890,7 +890,7 @@ CLEANUP:
890
890
  if (Qnil != pi->err_class && 0 != pi->err_class) {
891
891
  pi->err.clas = pi->err_class;
892
892
  }
893
- if (CompatMode == pi->options.mode) {
893
+ if (CompatMode == pi->options.mode && Yes != pi->options.safe) {
894
894
  // The json gem requires the error message be UTF-8 encoded. In
895
895
  // additional the complete JSON source should be returned but that
896
896
  // is not possible without stored all the bytes read and reading
@@ -8,6 +8,8 @@
8
8
 
9
9
  extern VALUE Oj;
10
10
 
11
+ bool string_writer_optimized = false;
12
+
11
13
  static void
12
14
  key_check(StrWriter sw, const char *key) {
13
15
  DumpType type = sw->types[sw->depth];
@@ -152,7 +154,7 @@ oj_str_writer_push_array(StrWriter sw, const char *key) {
152
154
  void
153
155
  oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
154
156
  Out out = &sw->out;
155
-
157
+
156
158
  if (sw->keyWritten) {
157
159
  sw->keyWritten = 0;
158
160
  } else {
@@ -273,7 +275,7 @@ str_writer_free(void *ptr) {
273
275
  static VALUE
274
276
  str_writer_new(int argc, VALUE *argv, VALUE self) {
275
277
  StrWriter sw = ALLOC(struct _strWriter);
276
-
278
+
277
279
  oj_str_writer_init(sw, 0);
278
280
  if (1 == argc) {
279
281
  oj_parse_options(argv[0], &sw->opts);
@@ -487,8 +489,26 @@ str_writer_to_s(VALUE self) {
487
489
  return oj_encode(rstr);
488
490
  }
489
491
 
492
+ /* Document-method: as_json
493
+ * call-seq: as_json()
494
+ *
495
+ * Returns the contents of the writer as a JSON element. If called from inside
496
+ * an array or hash by Oj the raw buffer will be used othersize a more
497
+ * inefficient parse of the contents and a return of the result is
498
+ * completed. The parse uses the trict mode.
499
+ *
500
+ * *return* [_Hash_|_Array_|_String_|_Integer_|_Float_|_True_|_False_|_nil|)
501
+ */
502
+ static VALUE
503
+ str_writer_as_json(VALUE self) {
504
+ if (string_writer_optimized) {
505
+ return self;
506
+ }
507
+ return rb_hash_new();
508
+ }
509
+
490
510
  /* Document-class: Oj::StringWriter
491
- *
511
+ *
492
512
  * Supports building a JSON document one element at a time. Build the document
493
513
  * by pushing values into the document. Pushing an array or an object will
494
514
  * create that element in the JSON document and subsequent pushes will add the
@@ -509,4 +529,6 @@ oj_string_writer_init() {
509
529
  rb_define_method(oj_string_writer_class, "pop_all", str_writer_pop_all, 0);
510
530
  rb_define_method(oj_string_writer_class, "reset", str_writer_reset, 0);
511
531
  rb_define_method(oj_string_writer_class, "to_s", str_writer_to_s, 0);
532
+ rb_define_method(oj_string_writer_class, "raw_json", str_writer_to_s, 0);
533
+ rb_define_method(oj_string_writer_class, "as_json", str_writer_as_json, 0);
512
534
  }