oj 3.7.11 → 3.9.1

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