rapidjson 0.2.1 → 0.2.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17354e68c40c730955b333eacdc5232255f5c0e12605a2e35ea0a90dd84def19
4
- data.tar.gz: c3d95c72fb9e4416f33007bf7076ed616e82bd138b2637e0a2e0a57a5b55faed
3
+ metadata.gz: 5e6a9c6ee55bf39622dca0dec0df0edb42de2f121dfd81b6711e3e730c7beb90
4
+ data.tar.gz: 2d3f80f79aa7e61cc0340f253891bfb4c6f67d3c80c1bf8a6e23b132d7dd8334
5
5
  SHA512:
6
- metadata.gz: 5d3dc6b671e1cc4110c457a8551fdf05b62ef03ff355a93531406b32853fd0b9174bc6a9ae3b743f891fd1ff4fe8eb95506b36bff9753ca1ed8f60970e694ed7
7
- data.tar.gz: 26195d47a4c2a68b190f0d46da8e09d2df7125b89ad53478758c9db74bf59e65c5a0a6b9fb10f47372da45fe25e269a0c6e48c44b36af10f3f22222739e70497
6
+ metadata.gz: f0e242022084c035eec3fc8deede180bf390f9275aae83bf923ee7ab2076bb5abc1860eded72bc6c43aa8eb9803d492b7477f1923fa343cc2348d72dff809872
7
+ data.tar.gz: 34092b2851dade4bf8adcec4dcf8e8fe66a561a41910c9ff99ac721d6aad48d6340bde69f586973d1f004583aa5eb5c5d278117375418edfe0365a91f339a963
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  (Maybe) Ruby's fastest JSON library! Built using the [RapidJSON C++ library](https://rapidjson.org/)
4
4
 
5
+ No monkey patches, ActiveSupport integration, `json` gem emulation.
6
+
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
@@ -42,6 +44,58 @@ RapidJSON.pretty_encode(json_string)
42
44
  # }
43
45
  ```
44
46
 
47
+ By default the encoder is "strict" and will raise an exception
48
+
49
+ ## ActiveSupport
50
+
51
+ RapidJSON provides a drop-in replacement ActiveSupport encoder, with very good compatibility.
52
+ Add the following to an initializer to opt-in.
53
+
54
+ ```
55
+ # config/initializers/rapidjson.rb
56
+
57
+ ActiveSupport::JSON::Encoding.json_encoder = RapidJSON::ActiveSupportEncoder
58
+ ```
59
+
60
+ This makes `model.to_json` ~15x faster, and `nested_hash.to_json` ~27x faster (compred using Rails 7.0)
61
+
62
+ ## JSON gem compatibility
63
+
64
+ Contrary to some other JSON libraries, `RapidJSON` doesn't provice a monkey patch to entirely replace the stdlib JSON gem.
65
+
66
+ However it does provide a module that behave like the stdlib JSON gem and that can be used to monkey patch existing code.
67
+
68
+ ```ruby
69
+ module SomeLibrary
70
+ def do_stuff(payload)
71
+ JSON.parse(payload)
72
+ end
73
+ end
74
+ ```
75
+
76
+ ```ruby
77
+ SomeLibrary::JSON = RapidJSON::JSONGem
78
+ ```
79
+
80
+ Note that this module only use `RapidJSON` when it's certain it is safe to do so. If the JSON gem is called with
81
+ some options that `RapidJSON` doesn't support, it automatically fallbacks to calling the JSON gem.
82
+
83
+ ## Advanced usage
84
+
85
+ By default RapidJSON will only encode "JSON-ready" types: `Hash`, `Array`, `Integer`, `Float`, `String`, `Symbol`, `true`, `false`, and `nil`.
86
+
87
+ RapidJSON::Coder can be initialized with a block which allows the behaviour to be customized. This is how the ActiveSupport encoder and JSON compatibility above are implemented! Just using Ruby :heart:.
88
+
89
+ ```
90
+ RapidJSON::Coder.new do |object, is_key|
91
+ object.to_s # Convert any unknown object to string
92
+ end
93
+ ```
94
+
95
+ The block is called only for unrecognized types. The return value is expected to be a "JSON ready" type which will then be encoded.
96
+
97
+ One additional special type is `RapidJSON::Fragment`, which is interpreted as an existing JSON-encoded string. This can be used to efficiently embed an existing JSON document, or to provide compatibility.
98
+
45
99
  ## Performance
46
100
 
47
101
  Your current JSON parser/encoder is probably fine.
@@ -88,27 +142,6 @@ I spent a week working on YAJL/yajl-ruby, and though I really liked the library,
88
142
 
89
143
  However, if you're happy with your current Ruby JSON library (including `json`) you should keep using it. They're all very good.
90
144
 
91
- ## JSON gem compatibility
92
-
93
- Contrary to some other JSON libraries, `RapidJSON` doesn't provice a monkey patch to entirely replace the stdlib JSON gem.
94
-
95
- However it does provide a module that behave like the stdlib JSON gem and that can be used to monkey patch existing code.
96
-
97
- ```ruby
98
- module SomeLibrary
99
- def do_stuff(payload)
100
- JSON.parse(payload)
101
- end
102
- end
103
- ```
104
-
105
- ```ruby
106
- SomeLibrary::JSON = RapidJSON::JSONGem
107
- ```
108
-
109
- Note that this module only use `RapidJSON` when it's certain it is safe to do so. If the JSON gem is called with
110
- some options that `RapidJSON` doesn't support, it automatically fallbacks to calling the JSON gem.
111
-
112
145
  ## Development
113
146
 
114
147
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -12,9 +12,16 @@ class RubyStringBuffer {
12
12
  mem = RSTRING_PTR(ruby_string);
13
13
  }
14
14
 
15
- void Reserve(size_t size) {
16
- if (capacity - used < size) {
17
- resize(capacity * 2);
15
+ void Reserve(size_t want) {
16
+ if (capacity - used < want) {
17
+ size_t new_capacity = capacity;
18
+ while (new_capacity - used < want) {
19
+ if (new_capacity >= SIZE_MAX / 2) {
20
+ ruby_malloc_size_overflow(capacity, 2);
21
+ }
22
+ new_capacity <<= 1;
23
+ }
24
+ resize(new_capacity);
18
25
  }
19
26
  }
20
27
 
@@ -19,26 +19,44 @@ typedef RubyStringBuffer DefaultBuffer;
19
19
  static VALUE
20
20
  dump(VALUE _self, VALUE obj, VALUE pretty, VALUE as_json, VALUE allow_nan) {
21
21
  // NB: as_json here is not marked by the extension, but is always on the stack
22
+ VALUE result;
23
+ int state;
24
+
22
25
  if (RTEST(pretty)) {
23
26
  RubyObjectEncoder<DefaultBuffer, PrettyWriter<DefaultBuffer> > encoder(as_json, RTEST(allow_nan));
24
- return encoder.encode(obj);
27
+ encoder.writer.SetIndent(' ', 2);
28
+ result = encoder.encode_protected(obj, &state);
25
29
  } else {
26
30
  RubyObjectEncoder<DefaultBuffer, Writer<DefaultBuffer> > encoder(as_json, RTEST(allow_nan));
27
- return encoder.encode(obj);
31
+ result = encoder.encode_protected(obj, &state);
32
+ }
33
+
34
+ if (state) {
35
+ rb_jump_tag(state);
28
36
  }
37
+ return result;
29
38
  }
30
39
 
31
40
  static VALUE
32
- load(VALUE _self, VALUE string) {
33
- RubyObjectHandler handler;
34
- Reader reader;
35
- char *cstring = StringValueCStr(string); // fixme?
36
- StringStream ss(cstring);
37
- ParseResult ok = reader.Parse(ss, handler);
41
+ load(VALUE _self, VALUE string, VALUE allow_nan) {
42
+ RubyObjectHandler handler(RTEST(allow_nan));
43
+ ParseResult ok;
44
+
45
+ {
46
+ char *cstring = StringValueCStr(string); // fixme?
47
+ StringStream ss(cstring);
48
+ Reader reader;
49
+ ok = reader.Parse<kParseNanAndInfFlag>(ss, handler);
50
+ }
38
51
 
39
52
  if (!ok) {
40
- rb_raise(rb_eParseError, "JSON parse error: %s (%lu)",
41
- GetParseError_En(ok.Code()), ok.Offset());
53
+ VALUE err = handler.GetErr();
54
+ if (RTEST(err)) {
55
+ rb_exc_raise(err);
56
+ } else {
57
+ rb_raise(rb_eParseError, "JSON parse error: %s (%lu)",
58
+ GetParseError_En(ok.Code()), ok.Offset());
59
+ }
42
60
  }
43
61
 
44
62
  return handler.GetRoot();
@@ -68,7 +86,7 @@ Init_rapidjson(void)
68
86
  rb_global_variable(&rb_cRapidJSONFragment);
69
87
 
70
88
  rb_define_private_method(rb_cCoder, "_dump", dump, 4);
71
- rb_define_method(rb_cCoder, "load", load, 1);
89
+ rb_define_method(rb_cCoder, "_load", load, 2);
72
90
  rb_define_method(rb_cCoder, "valid_json?", valid_json_p, 1);
73
91
 
74
92
  VALUE rb_eRapidJSONError = rb_const_get(rb_mRapidJSON, rb_intern("Error"));
@@ -8,7 +8,6 @@ using namespace rapidjson;
8
8
  template <typename B = RubyStringBuffer, typename W=Writer<B> >
9
9
  class RubyObjectEncoder {
10
10
  B buf;
11
- W writer;
12
11
  VALUE as_json;
13
12
  bool allow_nan;
14
13
 
@@ -200,10 +199,26 @@ class RubyObjectEncoder {
200
199
  allow_nan = allow_nan_;
201
200
  };
202
201
 
202
+ W writer;
203
203
  int depth;
204
204
 
205
205
  VALUE encode(VALUE obj) {
206
206
  encode_any(obj, true);
207
207
  return buf.GetRubyString();
208
208
  }
209
+
210
+ struct protected_args {
211
+ RubyObjectEncoder *encoder;
212
+ VALUE obj;
213
+ };
214
+
215
+ static VALUE encode_protected_cb(VALUE data) {
216
+ struct protected_args *args = (struct protected_args *)data;
217
+ return args->encoder->encode(args->obj);
218
+ }
219
+
220
+ VALUE encode_protected(VALUE obj, int *state) {
221
+ struct protected_args args = { this, obj };
222
+ return rb_protect(encode_protected_cb, (VALUE)&args, state);
223
+ }
209
224
  };
@@ -50,6 +50,10 @@ struct RubyObjectHandler : public BaseReaderHandler<UTF8<>, RubyObjectHandler> {
50
50
  }
51
51
 
52
52
  bool Double(double d) {
53
+ if (!isfinite(d) && !allow_nan) {
54
+ err = rb_exc_new_cstr(rb_eParseError, "JSON parse error: Invalid float value");
55
+ return false;
56
+ }
53
57
  return PutValue(rb_float_new(d));
54
58
  }
55
59
 
@@ -92,7 +96,7 @@ struct RubyObjectHandler : public BaseReaderHandler<UTF8<>, RubyObjectHandler> {
92
96
  depth++;
93
97
  return true;
94
98
  } else {
95
- rb_raise(rb_eParseError, "JSON parse error: input too deep");
99
+ err = rb_exc_new_cstr(rb_eParseError, "JSON parse error: input too deep");
96
100
  return false;
97
101
  }
98
102
  }
@@ -140,12 +144,18 @@ struct RubyObjectHandler : public BaseReaderHandler<UTF8<>, RubyObjectHandler> {
140
144
  return stack[0];
141
145
  }
142
146
 
143
- RubyObjectHandler(): depth(0) {
147
+ VALUE GetErr() {
148
+ return err;
149
+ }
150
+
151
+ RubyObjectHandler(bool allow_nan): err(Qfalse), depth(0), allow_nan(allow_nan) {
144
152
  stack[0] = Qundef;
145
153
  }
146
154
 
147
155
  static const int MAX_DEPTH = 256;
148
- int depth;
149
156
  VALUE stack[MAX_DEPTH];
150
157
  VALUE last_key[MAX_DEPTH];
158
+ VALUE err;
159
+ int depth;
160
+ bool allow_nan;
151
161
  };
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RapidJSON
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  end
data/lib/rapidjson.rb CHANGED
@@ -17,6 +17,10 @@ module RapidJSON
17
17
  def dump(object)
18
18
  _dump(object, @pretty, @to_json_proc, @allow_nan)
19
19
  end
20
+
21
+ def load(string)
22
+ _load(string, @allow_nan)
23
+ end
20
24
  end
21
25
  end
22
26
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rapidjson
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hawthorn
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-06-27 00:00:00.000000000 Z
11
+ date: 2023-12-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Fast JSON encoder/decoder based using RapidJSON
14
14
  email:
@@ -93,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0'
95
95
  requirements: []
96
- rubygems_version: 3.3.26
96
+ rubygems_version: 3.4.10
97
97
  signing_key:
98
98
  specification_version: 4
99
99
  summary: Fast JSON encoder/decoder based using RapidJSON