New JSON encoder (bug#70007)

It is in general at least 2x faster than the old encoder and does not
depend on any external library.  Using our own code also gives us
control over translation details: for example, we now have full
bignum support and tighter float formatting.

* src/json.c (json_delete, json_initialized, init_json_functions)
(json_malloc, json_free, init_json, json_out_of_memory)
(json_releae_object, check_string_without_embedded_nulls, json_check)
(json_check_utf8, lisp_to_json_nonscalar_1, lisp_to_json_nonscalar)
(lisp_to_json, json_available_p, ensure_json_available, json_insert)
(json_handle_nonlocal_exit, json_insert_callback):
Remove.  Remaining uses updated.
* src/json.c (json_out_t, symset_t, struct symset_tbl)
(symset_size, make_symset_table, push_symset, pop_symset)
(cleanup_symset_tables, symset_hash, symset_expand, symset_add)
(json_out_grow_buf, cleanup_json_out, json_make_room, JSON_OUT_STR)
(json_out_str, json_out_byte, json_out_fixnum, string_not_unicode)
(json_plain_char, json_out_string, json_out_nest, json_out_unnest)
(json_out_object_cons, json_out_object_hash), json_out_array)
(json_out_float, json_out_bignum, json_out_something)
(json_out_to_string, json_serialize): New.
(Fjson_serialize, Fjson_insert):
New JSON encoder implementation.
* test/src/json-tests.el (json-serialize/object-with-duplicate-keys)
(json-serialize/string): Update tests.
This commit is contained in:
Mattias Engdegård 2024-03-26 16:44:09 +01:00
parent ab016657e7
commit 890edfd2bb
5 changed files with 589 additions and 545 deletions

View file

@ -2013,10 +2013,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
init_random ();
init_xfaces ();
#if defined HAVE_JSON && !defined WINDOWSNT
init_json ();
#endif
if (!initialized)
syms_of_comp ();

1087
src/json.c

File diff suppressed because it is too large Load diff

View file

@ -4327,7 +4327,6 @@ extern void syms_of_image (void);
#ifdef HAVE_JSON
/* Defined in json.c. */
extern void init_json (void);
extern void syms_of_json (void);
#endif

View file

@ -2859,6 +2859,7 @@ decimal point. 0 is not allowed with `e' or `g'.
A value of nil means to use the shortest notation
that represents the number without losing information. */);
Vfloat_output_format = Qnil;
DEFSYM (Qfloat_output_format, "float-output-format");
DEFVAR_BOOL ("print-integers-as-characters", print_integers_as_characters,
doc: /* Non-nil means integers are printed using characters syntax.

View file

@ -126,11 +126,38 @@
(ert-deftest json-serialize/object-with-duplicate-keys ()
(skip-unless (fboundp 'json-serialize))
(let ((table (make-hash-table :test #'eq)))
(puthash (copy-sequence "abc") [1 2 t] table)
(puthash (copy-sequence "abc") :null table)
(should (equal (hash-table-count table) 2))
(should-error (json-serialize table) :type 'wrong-type-argument)))
(dolist (n '(1 5 20 100))
(let ((symbols (mapcar (lambda (i) (make-symbol (format "s%d" i)))
(number-sequence 1 n)))
(expected (concat "{"
(mapconcat (lambda (i) (format "\"s%d\":%d" i i))
(number-sequence 1 n) ",")
"}")))
;; alist
(should (equal (json-serialize
(append
(cl-mapcar #'cons
symbols (number-sequence 1 n))
(cl-mapcar #'cons
symbols (number-sequence 1001 (+ 1000 n)))))
expected))
;; plist
(should (equal (json-serialize
(append
(cl-mapcan #'list
symbols (number-sequence 1 n))
(cl-mapcan #'list
symbols (number-sequence 1001 (+ 1000 n)))))
expected))))
;; We don't check for duplicated keys in hash tables.
;; (let ((table (make-hash-table :test #'eq)))
;; (puthash (copy-sequence "abc") [1 2 t] table)
;; (puthash (copy-sequence "abc") :null table)
;; (should (equal (hash-table-count table) 2))
;; (should-error (json-serialize table) :type 'wrong-type-argument))
)
(ert-deftest json-parse-string/object ()
(skip-unless (fboundp 'json-parse-string))
@ -173,8 +200,8 @@
(should (equal (json-serialize ["\nasdфыв\u001f\u007ffgh\t"])
"[\"\\nasdфыв\\u001F\u007ffgh\\t\"]"))
(should (equal (json-serialize ["a\0b"]) "[\"a\\u0000b\"]"))
;; FIXME: Is this the right behavior?
(should (equal (json-serialize ["\u00C4\xC3\x84"]) "[\"\u00C4\u00C4\"]")))
(should-error (json-serialize ["\xC3\x84"]))
(should-error (json-serialize ["\u00C4\xC3\x84"])))
(ert-deftest json-serialize/invalid-unicode ()
(skip-unless (fboundp 'json-serialize))