The purpose of this change is to exploit `fbuffer_append_char` that is
faster than `fbuffer_append`.
`array_delim` was a buffer that concatenated a single comma with
`array_nl`. However, in the typical use case (`JSON.generate(data)`),
`array_nl` is empty. This means that `array_delim` was a
single-character buffer in many cases.
`fbuffer_append(buffer, array_delim)` used `memcpy` to copy one byte,
which was not so efficient.
Rather, this change uses `fbuffer_append_char(buffer, ',')` and then
`fbuffer_append(buffer, array_nl)` only when `array_nl` is not NULL.
This speeds up `JSON.generate` by about 9% in a benchmark.
445de6e459
... instead of `generate_json`.
Since the object key is already confirmed to be a string, using a
generic dispatch function brings an unnecessary overhead.
This speeds up `JSON.generate` by about 3% in a benchmark.
e125072130
Dispatching based on Ruby's VALUE structure is more efficient than
simply cascaded "if ... else if ..." checks.
This speeds up `JSON.generate` by about 5% in a benchmark.
4f9180debb
It is safe to use `RARRAY_AREF` here because no Ruby code is executed
between `RARRAY_LEN` and `RARRAY_AREF`.
This speeds up `JSON.generate` by about 4% in a benchmark.
c5d80f9fd4
```
generator.c:69:27: warning: comparison of integers of different signs: 'short' and 'unsigned long' [-Wsign-compare]
for (i = 1; i < ch_len; i++) {
```
ff8edcd47c
I, Luke T. Shumaker, am the sole author of the added code.
I did not reference CVTUTF when writing it. I did reference the
Unicode standard (15.0.0), the Wikipedia article on UTF-8, and the
Wikipedia article on UTF-16. When I saw some tests fail, I did
reference the old deleted code (but a JSON-specific part, inherently
not as based on CVTUTF) to determine that script_safe should also
escape U+2028 and U+2029.
I targeted simplicity and clarity when writing the code--it can likely
be optimized. In my mind, the obvious next optimization is to have it
combine contiguous non-escaped characters into just one call to
fbuffer_append(), instead of calling fbuffer_append() for each
character.
Regarding the use of the "modern" types `uint32_t`, `uint16_t`, and
`bool`:
- ruby.h is guaranteed to give us uint32_t and uint16_t.
- Since Ruby 3.0.0, ruby.h is guaranteed to give us bool... but we
support down to Ruby 2.3. But, ruby.h is guaranteed to give us
HAVE_STDBOOL_H for the C99 stdbool.h; so use that to include
stdbool.h if we can, and if not then fall back to a copy of the
same bool definition that Ruby 3.0.5 uses with C89.
c96351f874
Rather than checking the class we can check the type.
This is very subtly different for String subclasses, but I think it's
OK.
We also save on checking the type again in the fast path.
772a0201ab
Given that we called `rb_enc_str_asciionly_p`, if the string encoding
isn't valid UTF-8, we can't know it very cheaply by checking the
encoding and coderange that was just computed by Ruby, rather than
to do it ourselves.
Also Ruby might have already computed that earlier.
4b04c469d5
If an exception is raised the FBuffer is leaked.
For example, the following script leaks memory:
o = Object.new
def o.to_json(a) = raise
10.times do
100_000.times do
begin
JSON(o)
rescue
end
end
puts `ps -o rss= -p #{$$}`
end
Before:
31824
35696
40240
44304
47424
50944
54000
58384
62416
65296
After:
24416
24640
24640
24736
24736
24736
24736
24736
24736
24736
44df509dc2
This avoids pinning an id to the symbol used if a dynamic symbol is
passed in as a hash key.
rb_sym2str is available in Ruby 2.2+ and json depends on >= 2.3.
5cbafb8dbe
> https://github.com/flori/json/pull/525
> Rename escape_slash in script_safe and also escape E+2028 and E+2029
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
> https://github.com/flori/json/pull/454
> Remove unnecessary initialization of create_id in JSON.parse()
Co-authored-by: Watson <watson1978@gmail.com>
It is rather common to directly interpolate JSON string inside
<script> tags in HTML as to provide configuration or parameters to a
script.
However this may lead to XSS vulnerabilities, to prevent that 3
characters need to be escaped:
- `/` (forward slash)
- `U+2028` (LINE SEPARATOR)
- `U+2029` (PARAGRAPH SEPARATOR)
The forward slash need to be escaped to prevent closing the script
tag early, and the other two are valid JSON but invalid Javascript
and can be used to break JS parsing.
Given that the intent of escaping forward slash is the same than escaping
U+2028 and U+2029, I chos to rename and repurpose the existing `escape_slash`
option.
The C extension defines an `included` hook for the
`JSON::Ext::Generator::GeneratorMethods::String` module but neglects to
call `super` in the hook. This can break the functionality of various
other code that rely on the fact that `included` on `Module` will always
be called.
cd8bbe56a3
It makes testing for JSON errors very tedious. You either have
to use a Regexp or to regularly update all your assertions
when JSON is upgraded.
de9eb1d28e
This value should either be pinned, or looked up when needed at runtime.
Without pinning, the GC may move the encoding object, and that could
cause a crash.
In this case it is easier to find the value at runtime, and there is no
performance penalty (as Ruby caches encoding indexes). We can shorten
the code, be compaction friendly, and incur no performance penalty.
In where to convert Hash key to String for json, this patch will add shortcut for String/Symbol in Hash key.
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
json 65.000 i/100ms
Calculating -------------------------------------
json 659.576 (± 1.5%) i/s - 3.315k in 5.027127s
```
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
json 78.000 i/100ms
Calculating -------------------------------------
json 789.781 (± 2.7%) i/s - 3.978k in 5.041043s
```
```
require 'json'
require 'benchmark/ips'
obj = []
1000.times do |i|
obj << {
"id" => i,
:age => 42,
}
end
Benchmark.ips do |x|
x.report "json" do |iter|
count = 0
while count < iter
JSON.generate(obj)
count += 1
end
end
end
```
38c0f6dbe4
To convert Hash convert, this part was using following pseudo code
```
obj.keys.each do |key|
value = obj[key]
...
end
```
and `rb_funcall()` was called for `obj.keys`.
It might be slightly heavy to call the Ruby method.
This patch will iterate to convert Hash object about key/value using `rb_hash_foreach()` Ruby API instead of `rb_funcall()`.
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
json 55.000 i/100ms
Calculating -------------------------------------
json 558.501 (± 1.1%) i/s - 2.805k in 5.022986s
```
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
json 65.000 i/100ms
Calculating -------------------------------------
json 659.576 (± 1.5%) i/s - 3.315k in 5.027127s
```
```
require 'json'
require 'benchmark/ips'
obj = []
1000.times do |i|
obj << {
"id" => i,
:age => 42,
}
end
Benchmark.ips do |x|
x.report "json" do |iter|
count = 0
while count < iter
JSON.generate(obj)
count += 1
end
end
end
```
a73323dc5e
`rb_funcall` might be slightly heavy to call the Ruby method.
This patch will convert String encoding using `rb_str_encode()` instead of `rb_funcall()`.
## Before
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
json 78.000 i/100ms
Calculating -------------------------------------
json 789.781 (± 2.7%) i/s - 3.978k in 5.041043s
```
## After
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
json 129.000 i/100ms
Calculating -------------------------------------
json 1.300k (± 2.3%) i/s - 6.579k in 5.064656s
```
## Code
```
require 'json'
require 'benchmark/ips'
obj = []
1000.times do |i|
obj << {
"id" => i,
:age => 42,
}
end
Benchmark.ips do |x|
x.report "json" do |iter|
count = 0
while count < iter
JSON.generate(obj)
count += 1
end
end
end
```
9ae6d2969c
For some reason symbols (or classes) are being overridden in trunk
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67598 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit adds the new method `GC.compact` and compacting GC support.
Please see this issue for caveats:
https://bugs.ruby-lang.org/issues/15626
[Feature #15626]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67576 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Because hard to specify commits related to r67479 only.
So please commit again.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67499 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
JSON gem is referencing constants defined in Ruby then keeping a
reference as a global. We need to register these globals so they stay
pinned.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67483 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* include/ruby/ruby.h (RUBY_INTEGER_UNIFICATION): macro to tell if
Integer is integrated. [ruby-core:75718][Bug #12427]
* include/ruby/backward.h, internal.h (rb_cFixnum, rb_cBignum):
fallback to rb_cInteger.
* bignum.c, numeric.c, ext/json/generator/generator.{c,h}: use the
macro.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55394 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* [Feature #12005] Unify Fixnum and Bignum into Integer
* include/ruby/ruby.h (rb_class_of): Return rb_cInteger for fixnums.
* insns.def (INTEGER_REDEFINED_OP_FLAG): Unified from
FIXNUM_REDEFINED_OP_FLAG and BIGNUM_REDEFINED_OP_FLAG.
* vm_core.h: Ditto.
* vm_insnhelper.c (opt_eq_func): Use INTEGER_REDEFINED_OP_FLAG instead
of FIXNUM_REDEFINED_OP_FLAG.
* vm.c (vm_redefinition_check_flag): Use rb_cInteger instead of
rb_cFixnum and rb_cBignum.
(C): Use Integer instead of Fixnum and Bignum.
* numeric.c (fix_succ): Removed.
(Init_Numeric): Define Fixnum as Integer.
* bignum.c (bignew): Use rb_cInteger instead of Rb_cBignum.
(rb_int_coerce): replaced from rb_big_coerce and return fixnums
as-is.
(Init_Bignum): Define Bignum as Integer.
Don't define ===.
* error.c (builtin_class_name): Return "Integer" for fixnums.
* sprintf.c (ruby__sfvextra): Use rb_cInteger instead of rb_cFixnum.
* ext/-test-/testutil: New directory to test.
Currently it provides utilities for fixnum and bignum.
* ext/json/generator/generator.c: Define mInteger_to_json.
* lib/mathn.rb (Fixnum#/): Redefinition removed.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55024 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* ext/json/generator/generator.c (cState_s_allocate): allocate
structs with making new wrapper objects and get rid of potential
memory leak.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50661 b2dd03c8-39d4-4d8f-98ff-823fe69b080e