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 +4 -4
- data/README.md +54 -21
- data/ext/rapidjson/buffer.hh +10 -3
- data/ext/rapidjson/cext.cc +29 -11
- data/ext/rapidjson/encoder.hh +16 -1
- data/ext/rapidjson/parser.hh +13 -3
- data/lib/rapidjson/version.rb +1 -1
- data/lib/rapidjson.rb +4 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e6a9c6ee55bf39622dca0dec0df0edb42de2f121dfd81b6711e3e730c7beb90
|
4
|
+
data.tar.gz: 2d3f80f79aa7e61cc0340f253891bfb4c6f67d3c80c1bf8a6e23b132d7dd8334
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
data/ext/rapidjson/buffer.hh
CHANGED
@@ -12,9 +12,16 @@ class RubyStringBuffer {
|
|
12
12
|
mem = RSTRING_PTR(ruby_string);
|
13
13
|
}
|
14
14
|
|
15
|
-
void Reserve(size_t
|
16
|
-
if (capacity - used <
|
17
|
-
|
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
|
|
data/ext/rapidjson/cext.cc
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
41
|
-
|
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, "
|
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"));
|
data/ext/rapidjson/encoder.hh
CHANGED
@@ -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
|
};
|
data/ext/rapidjson/parser.hh
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
};
|
data/lib/rapidjson/version.rb
CHANGED
data/lib/rapidjson.rb
CHANGED
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.
|
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-
|
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.
|
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
|