Commit graph

90 commits

Author SHA1 Message Date
Yusuke Endoh
64c24f6971 [ruby/json] Stop prebuilding object_delim2
Also, remove static functions that are no longer used.

This speeds up `JSON.generate` by about 5% in a benchmark.

4c984b2017
2024-10-17 08:54:47 +00:00
Yusuke Endoh
186e77209e [ruby/json] Stop prebuilding object_delim
This speeds up `JSON.generate` by about 4% in a benchmark

ed47a10e4f
2024-10-17 08:54:47 +00:00
Yusuke Endoh
88719fb300 [ruby/json] Stop prebuilding array_delim
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
2024-10-17 08:54:46 +00:00
Yusuke Endoh
fb84aa5501 [ruby/json] Directly use generate_json_string for object keys
... 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
2024-10-17 08:54:46 +00:00
Yusuke Endoh
3911189fba [ruby/json] Use efficient object-type dispatching
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
2024-10-17 08:54:45 +00:00
Yusuke Endoh
7962b4c342 [ruby/json] Use RARRAY_AREF instead of rb_ary_entry to improve performance
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
2024-10-17 08:54:45 +00:00
Jean Boussier
613694734e [ruby/json] generator.c: better fix for comparison of integers of different signs
c372dc9268
2024-10-08 12:22:42 +00:00
Jean Boussier
ea9d34082f [ruby/json] Fix compilation warning
```
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
2024-10-08 14:10:05 +09:00
Luke T. Shumaker
934d67b415 [ruby/json] generator.c: Optimize by combining calls to fbuffer_append
62301c0bc3
2024-10-08 14:10:05 +09:00
Luke T. Shumaker
74d459fd52 [ruby/json] Adjust to the CVTUTF code being gone
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
2024-10-08 14:10:05 +09:00
Luke T. Shumaker
6e47968929 [ruby/json] Delete code that is based on CVTUTF
I did this based on manual inspection, comparing the code to my re-created
history of CVTUTF at https://git.lukeshu.com/2git/cvtutf/ (created by the
scripts at https://git.lukeshu.com/2git/cvtutf-make/)

0819553144
2024-10-08 14:10:05 +09:00
Jean Boussier
4cd893b048 [flori/json] Optimize key type check in json_object_i
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
2024-10-03 14:20:34 +09:00
Jean Boussier
630c681321 [flori/json] JSON.dump: avoid redundant UTF-8 validation
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
2024-10-03 14:20:34 +09:00
Jean Boussier
d612f9fd34 [flori/json] Remove outdated ifdef checks
`json` requires Ruby 2.3, so `HAVE_RUBY_ENCODING_H` and `HAVE_RB_ENC_RAISE`
are always true.

5c8dc6b70a
2024-09-03 11:51:51 +09:00
Jean Boussier
c5ae432ec8
[flori/json] Cleanup useless ifdef
The json gem now requires Ruby 2.3, so there is no point keeping
compatibility code for older releases that don't have the
TypedData API.

45c86e153f
2024-06-04 12:23:48 +09:00
Peter Zhu
6e34386794
[flori/json] Fix memory leak when exception is raised during JSON generation
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
2024-03-27 08:24:28 +09:00
John Hawthorn
ea5776e7e4 [flori/json] Use rb_sym2str instead of SYM2ID
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
2023-12-25 21:12:49 +09:00
Hiroshi SHIBATA
86045fca24
Manually merged from flori/json
> 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>
2023-12-01 16:47:06 +09:00
Jean Boussier
0dfeb17296
Rename escape_slash in script_safe and also escape E+2028 and E+2029
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.
2023-12-01 16:47:06 +09:00
Ufuk Kayserilioglu
12dfd9d1c9
[flori/json] Call super in included hook
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
2023-05-24 09:37:30 +09:00
Jean Boussier
66b52f046f [flori/json] Stop including the parser source __LINE__ in exceptions
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
2022-07-29 19:10:10 +09:00
Aaron Patterson
8ef30bcc04
Fix GC compatibility: Don't stash encodings in global constants
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.
2021-02-01 12:20:34 -08:00
Kenta Murata
14d7d1df25
[json] Make json Ractor safe 2020-12-21 22:10:43 +09:00
Kenta Murata
98cc15ed1e
[json] Stop using prototype objects 2020-12-21 22:10:33 +09:00
Jean Boussier
e1659af372 Add an option to escape forward slash character
Squashed commit of the following:

commit 26d181059989279a79c433cedcd893b4f52e42ee
Author: Francois Chagnon <francois.chagnon@jadedpixel.com>
Date:   Tue Sep 15 21:17:34 2015 +0000

    add config options for escape_slash

commit fa282334051b16df91ca097dd7304b46f3bc7719
Author: Francois Chagnon <francois.chagnon@jadedpixel.com>
Date:   Mon Feb 9 21:09:33 2015 +0000

    add forward slash to escape character
2020-09-25 17:28:42 +09:00
Marc-Andre Lafortune
26041da2fb
[flori/json] Typo fix
26c1769969
2020-07-01 18:47:51 +09:00
zverok
7f1e3a7b7c [flori/json] Add :nodoc: for GeneratorMethods
2f3f44c180
2020-01-06 15:13:50 +09:00
Aaron Patterson
1d564acedc
Remove unused constant.
This constant isn't used, so lets remove it.
2019-10-17 13:35:26 -07:00
Aaron Patterson
9026e12f93
Look up constant instead of caching in a global
The global can go bad if the compactor runs, so we need to look up the
constant instead of caching it in a global.
2019-10-17 13:30:09 -07:00
Sho Hashimoto
308bbb4e10
[flori/json] Add ascii_only option to JSON::Ext::Generator::State.new.
0e99a9aac5
2019-10-14 19:54:49 +09:00
Watson
98a9445db9
[flori/json] Add shortcut converting to String
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
2019-10-14 19:54:49 +09:00
Watson
a2f9c38a71
[flori/json] Convert Hash object using rb_hash_foreach()
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
2019-10-14 19:54:49 +09:00
Nobuyoshi Nakada
2003755a2c
[flori/json] Fixed unexpected illegal/malformed utf-8 error
flori/json@c34d01ff6a does not
consider US-ASCII compatible but non-UTF-8 encodings, and causes
an error in RDoc tests.

4f471bf590
2019-10-14 19:54:48 +09:00
Watson
d7fa7e2c86
[flori/json] Convert string encoding to UTF-8 only when needed
## Before
```
$ 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
```

## After
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
                json   189.000  i/100ms
Calculating -------------------------------------
                json      1.964k (± 3.3%) i/s -      9.828k in   5.011237s
```

## 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
```

c34d01ff6a
2019-10-14 19:54:48 +09:00
Watson
40724d7d10
[flori/json] Convert String encoding using rb_str_encode()
`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
2019-10-14 19:54:48 +09:00
Watson
641136c4af
[flori/json] Does not check whether illegal utf-8 if string has ascii only.
## Before
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
                json    25.000  i/100ms
Calculating -------------------------------------
                json    250.478  (± 4.8%) i/s -      1.250k in   5.002238s
```

## After
```
$ ruby bench_json_generate.rb
Warming up --------------------------------------
                json    32.000  i/100ms
Calculating -------------------------------------
                json    360.652  (± 3.6%) i/s -      1.824k in   5.064511s
```

## Test code
```
require 'json'
require 'benchmark/ips'

obj = []

1000.times do |i|
  obj << {
    :string => "x" * 100,
    :utf8 => "あ" * 100
  }
end

Benchmark.ips do |x|
  x.report "json" do |iter|
    count = 0
    while count < iter
      JSON.generate(obj)
      count += 1
    end
  end
end
```

91a24ecac3
2019-10-14 19:54:48 +09:00
Nobuyoshi Nakada
2a8be8ec33
Suppress uninitialized instance variable warnings 2019-07-09 08:31:27 +09:00
tenderlove
91793b8967 Add GC.compact again.
🙏

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67620 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2019-04-20 01:19:47 +00:00
tenderlove
744e5df715 Reverting compaction for now
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
2019-04-17 09:41:41 +00:00
tenderlove
3c55b643ae Adding GC.compact and compacting GC support.
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
2019-04-17 03:17:25 +00:00
kazu
25c1fd3b90 Reverting all commits from r67479 to r67496 because of CI failures
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
2019-04-10 09:15:21 +00:00
tenderlove
e385c159da Add error globals to mark list so they don't move
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
2019-04-09 21:13:32 +00:00
hsbt
6604e1b7cd Merge json-2.2.0 from flori/json.
https://github.com/flori/json/releases/tag/v2.2.0

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67127 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2019-02-23 03:43:58 +00:00
nobu
c8094ff154 no ID cache in Init functions
Init functions are called only once, cache is useless.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62429 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-16 08:39:48 +00:00
hsbt
83735ba29a Merge json-2.0.4.
* https://github.com/flori/json/releases/tag/v2.0.4
  * 09fabeb03e/CHANGES.md (2017-03-23-204)

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58323 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-04-12 00:21:18 +00:00
hsbt
11a94f2a36 * ext/json/*, test/json/json_parser_test.rb: Update json-2.0.2.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55784 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-08-01 03:16:30 +00:00
hsbt
1130658738 * ext/json/*, test/json/*: Update json-2.0.1.
Changes of 2.0.0: f679ebd0c6/CHANGES.md (2015-09-11-200)
  Changes of 2.0.1: f679ebd0c6/CHANGES.md (2016-07-01-201)
  [Feature #12542][ruby-dev:49706][fix GH-1395]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55576 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-07-05 11:49:39 +00:00
nobu
c071c05229 Integer unification macro
* 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
2016-06-13 11:34:56 +00:00
akr
f9727c12cc [Feature #12005] Unify Fixnum and Bignum into Integer
* [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
2016-05-17 06:53:48 +00:00
nobu
4d059bf9f5 generator.c: allocate structs with wrapper
* 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
2015-05-28 07:17:55 +00:00