From 428339e2316a552713b265193d6648125042cc98 Mon Sep 17 00:00:00 2001
From: "Basil L. Contovounesios" <contovob@tcd.ie>
Date: Sun, 21 Feb 2021 20:10:08 +0000
Subject: Speed up json.el encoding

This replaces most json-encode-* functions with similar
json--print-* counterparts that insert into the current buffer
instead of returning a string (bug#46761).

Some unused but useful json-encode-* functions are kept for backward
compatibility and as a public API, and the rest are deprecated.

* etc/NEWS: Announce obsoletions.

* lisp/json.el: Document organization of library.  Make subsection
headings more consistent.
(json--encoding-current-indentation): Rename...
(json--print-indentation-prefix): ...to this, to reflect new use.
(json--encode-stringlike, json--encode-alist): Rename...
(json--print-stringlike, json--print-alist): ...to these,
respectively, and encode argument into current buffer instead.  All
callers changed.

(json--print-string, json--print-unordered-map, json--print-array)
(json--print): New functions.
(json-encode-string, json-encode-plist, json-encode-array)
(json-encode): Use them, respectively.

(json-encode-number, json-encode-hash-table): Mark as obsolete
aliases of json-encode.
(json-encode-key, json-encode-list): Mark as obsolete in preference
for json-encode.

(json--print-indentation-depth, json--print-keyval-separator): New
variables.
(json--with-output-to-string): New macro.
(json--print-indentation, json--print-keyword, json--print-key)
(json--print-pair, json--print-map, json--print-list): New
functions.

(json--with-indentation): Use json--print-indentation-depth to avoid
unnecessary string allocation.
(json-encoding-default-indentation, json-pretty-print-max-secs):
Clarify docstrings.
(json--escape, json--long-string-threshold, json--string-buffer):
Remove; no longer used.

* lisp/progmodes/js.el (js--js-encode-value): Replace
json-encode-string and json-encode-number with json-encode.
(js-eval-defun): Use json--print-list to avoid
json-encode-list->insert roundtrip.

* test/lisp/json-tests.el (test-json-encode-number)
(test-json-encode-hash-table, test-json-encode-hash-table-pretty)
(test-json-encode-hash-table-lisp-style)
(test-json-encode-hash-table-sort,  test-json-encode-list):  Replace
uses of obsolete functions with the equivalent use of json-encode.
(test-json-encode-key): Suppress obsoletion warnings.
(test-json-encode-string): Check that text properties are stripped.
---
 test/lisp/json-tests.el | 194 ++++++++++++++++++++++++------------------------
 1 file changed, 96 insertions(+), 98 deletions(-)

(limited to 'test/lisp/json-tests.el')

diff --git a/test/lisp/json-tests.el b/test/lisp/json-tests.el
index 9886dc0d457..f400fb064a6 100644
--- a/test/lisp/json-tests.el
+++ b/test/lisp/json-tests.el
@@ -329,13 +329,13 @@ Point is moved to beginning of the buffer."
           (should (equal (read str) res)))))))
 
 (ert-deftest test-json-encode-number ()
-  (should (equal (json-encode-number 0) "0"))
-  (should (equal (json-encode-number -0) "0"))
-  (should (equal (json-encode-number 3) "3"))
-  (should (equal (json-encode-number -5) "-5"))
-  (should (equal (json-encode-number 123.456) "123.456"))
+  (should (equal (json-encode 0) "0"))
+  (should (equal (json-encode -0) "0"))
+  (should (equal (json-encode 3) "3"))
+  (should (equal (json-encode -5) "-5"))
+  (should (equal (json-encode 123.456) "123.456"))
   (let ((bignum (1+ most-positive-fixnum)))
-    (should (equal (json-encode-number bignum)
+    (should (equal (json-encode bignum)
                    (number-to-string bignum)))))
 
 ;;; Strings
@@ -404,6 +404,8 @@ Point is moved to beginning of the buffer."
     (should (equal (json-read-string) "abcαβγ")))
   (json-tests--with-temp-buffer "\"\\nasd\\u0444\\u044b\\u0432fgh\\t\""
     (should (equal (json-read-string) "\nasdфывfgh\t")))
+  (json-tests--with-temp-buffer "\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\""
+    (should (equal (json-read-string) "abc\uFFFFαβγ𝔸𝐁𝖢\"\\")))
   ;; Bug#24784
   (json-tests--with-temp-buffer "\"\\uD834\\uDD1E\""
     (should (equal (json-read-string) "\U0001D11E")))
@@ -418,30 +420,37 @@ Point is moved to beginning of the buffer."
   (should (equal (json-encode-string "foo") "\"foo\""))
   (should (equal (json-encode-string "a\n\fb") "\"a\\n\\fb\""))
   (should (equal (json-encode-string "\nasdфыв\u001f\u007ffgh\t")
-                 "\"\\nasdфыв\\u001f\u007ffgh\\t\"")))
+                 "\"\\nasdфыв\\u001f\u007ffgh\\t\""))
+  ;; Bug#43549.
+  (should (equal (json-encode-string (propertize "foo" 'read-only t))
+                 "\"foo\""))
+  (should (equal (json-encode-string "a\0b") "\"a\\u0000b\""))
+  (should (equal (json-encode-string "abc\uFFFFαβγ𝔸𝐁𝖢\"\\")
+                 "\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\"")))
 
 (ert-deftest test-json-encode-key ()
-  (should (equal (json-encode-key '##) "\"\""))
-  (should (equal (json-encode-key :) "\"\""))
-  (should (equal (json-encode-key "") "\"\""))
-  (should (equal (json-encode-key 'a) "\"a\""))
-  (should (equal (json-encode-key :a) "\"a\""))
-  (should (equal (json-encode-key "a") "\"a\""))
-  (should (equal (json-encode-key t) "\"t\""))
-  (should (equal (json-encode-key :t) "\"t\""))
-  (should (equal (json-encode-key "t") "\"t\""))
-  (should (equal (json-encode-key nil) "\"nil\""))
-  (should (equal (json-encode-key :nil) "\"nil\""))
-  (should (equal (json-encode-key "nil") "\"nil\""))
-  (should (equal (json-encode-key ":a") "\":a\""))
-  (should (equal (json-encode-key ":t") "\":t\""))
-  (should (equal (json-encode-key ":nil") "\":nil\""))
-  (should (equal (should-error (json-encode-key 5))
-                 '(json-key-format 5)))
-  (should (equal (should-error (json-encode-key ["foo"]))
-                 '(json-key-format ["foo"])))
-  (should (equal (should-error (json-encode-key '("foo")))
-                 '(json-key-format ("foo")))))
+  (with-suppressed-warnings ((obsolete json-encode-key))
+    (should (equal (json-encode-key '##) "\"\""))
+    (should (equal (json-encode-key :) "\"\""))
+    (should (equal (json-encode-key "") "\"\""))
+    (should (equal (json-encode-key 'a) "\"a\""))
+    (should (equal (json-encode-key :a) "\"a\""))
+    (should (equal (json-encode-key "a") "\"a\""))
+    (should (equal (json-encode-key t) "\"t\""))
+    (should (equal (json-encode-key :t) "\"t\""))
+    (should (equal (json-encode-key "t") "\"t\""))
+    (should (equal (json-encode-key nil) "\"nil\""))
+    (should (equal (json-encode-key :nil) "\"nil\""))
+    (should (equal (json-encode-key "nil") "\"nil\""))
+    (should (equal (json-encode-key ":a") "\":a\""))
+    (should (equal (json-encode-key ":t") "\":t\""))
+    (should (equal (json-encode-key ":nil") "\":nil\""))
+    (should (equal (should-error (json-encode-key 5))
+                   '(json-key-format 5)))
+    (should (equal (should-error (json-encode-key ["foo"]))
+                   '(json-key-format ["foo"])))
+    (should (equal (should-error (json-encode-key '("foo")))
+                   '(json-key-format ("foo"))))))
 
 ;;; Objects
 
@@ -578,45 +587,32 @@ Point is moved to beginning of the buffer."
 (ert-deftest test-json-encode-hash-table ()
   (let ((json-encoding-object-sort-predicate nil)
         (json-encoding-pretty-print nil))
-    (should (equal (json-encode-hash-table #s(hash-table)) "{}"))
-    (should (equal (json-encode-hash-table #s(hash-table data (a 1)))
-                   "{\"a\":1}"))
-    (should (equal (json-encode-hash-table #s(hash-table data (t 1)))
-                   "{\"t\":1}"))
-    (should (equal (json-encode-hash-table #s(hash-table data (nil 1)))
-                   "{\"nil\":1}"))
-    (should (equal (json-encode-hash-table #s(hash-table data (:a 1)))
-                   "{\"a\":1}"))
-    (should (equal (json-encode-hash-table #s(hash-table data (:t 1)))
-                   "{\"t\":1}"))
-    (should (equal (json-encode-hash-table #s(hash-table data (:nil 1)))
-                   "{\"nil\":1}"))
-    (should (equal (json-encode-hash-table
-                    #s(hash-table test equal data ("a" 1)))
+    (should (equal (json-encode #s(hash-table)) "{}"))
+    (should (equal (json-encode #s(hash-table data (a 1))) "{\"a\":1}"))
+    (should (equal (json-encode #s(hash-table data (t 1))) "{\"t\":1}"))
+    (should (equal (json-encode #s(hash-table data (nil 1))) "{\"nil\":1}"))
+    (should (equal (json-encode #s(hash-table data (:a 1))) "{\"a\":1}"))
+    (should (equal (json-encode #s(hash-table data (:t 1))) "{\"t\":1}"))
+    (should (equal (json-encode #s(hash-table data (:nil 1))) "{\"nil\":1}"))
+    (should (equal (json-encode #s(hash-table test equal data ("a" 1)))
                    "{\"a\":1}"))
-    (should (equal (json-encode-hash-table
-                    #s(hash-table test equal data ("t" 1)))
+    (should (equal (json-encode #s(hash-table test equal data ("t" 1)))
                    "{\"t\":1}"))
-    (should (equal (json-encode-hash-table
-                    #s(hash-table test equal data ("nil" 1)))
+    (should (equal (json-encode #s(hash-table test equal data ("nil" 1)))
                    "{\"nil\":1}"))
-    (should (equal (json-encode-hash-table
-                    #s(hash-table test equal data (":a" 1)))
+    (should (equal (json-encode #s(hash-table test equal data (":a" 1)))
                    "{\":a\":1}"))
-    (should (equal (json-encode-hash-table
-                    #s(hash-table test equal data (":t" 1)))
+    (should (equal (json-encode #s(hash-table test equal data (":t" 1)))
                    "{\":t\":1}"))
-    (should (equal (json-encode-hash-table
-                    #s(hash-table test equal data (":nil" 1)))
+    (should (equal (json-encode #s(hash-table test equal data (":nil" 1)))
                    "{\":nil\":1}"))
-    (should (member (json-encode-hash-table #s(hash-table data (t 2 :nil 1)))
+    (should (member (json-encode #s(hash-table data (t 2 :nil 1)))
                     '("{\"nil\":1,\"t\":2}" "{\"t\":2,\"nil\":1}")))
-    (should (member (json-encode-hash-table
-                     #s(hash-table test equal data (:t 2 ":t" 1)))
+    (should (member (json-encode #s(hash-table test equal data (:t 2 ":t" 1)))
                     '("{\":t\":1,\"t\":2}" "{\"t\":2,\":t\":1}")))
-    (should (member (json-encode-hash-table #s(hash-table data (b 2 a 1)))
+    (should (member (json-encode #s(hash-table data (b 2 a 1)))
                     '("{\"a\":1,\"b\":2}" "{\"b\":2,\"a\":1}")))
-    (should (member (json-encode-hash-table #s(hash-table data (c 3 b 2 a 1)))
+    (should (member (json-encode #s(hash-table data (c 3 b 2 a 1)))
                     '("{\"a\":1,\"b\":2,\"c\":3}"
                       "{\"a\":1,\"c\":3,\"b\":2}"
                       "{\"b\":2,\"a\":1,\"c\":3}"
@@ -629,13 +625,12 @@ Point is moved to beginning of the buffer."
         (json-encoding-pretty-print t)
         (json-encoding-default-indentation " ")
         (json-encoding-lisp-style-closings nil))
-    (should (equal (json-encode-hash-table #s(hash-table)) "{}"))
-    (should (equal (json-encode-hash-table #s(hash-table data (a 1)))
-                   "{\n \"a\": 1\n}"))
-    (should (member (json-encode-hash-table #s(hash-table data (b 2 a 1)))
+    (should (equal (json-encode #s(hash-table)) "{}"))
+    (should (equal (json-encode #s(hash-table data (a 1))) "{\n \"a\": 1\n}"))
+    (should (member (json-encode #s(hash-table data (b 2 a 1)))
                     '("{\n \"a\": 1,\n \"b\": 2\n}"
                       "{\n \"b\": 2,\n \"a\": 1\n}")))
-    (should (member (json-encode-hash-table #s(hash-table data (c 3 b 2 a 1)))
+    (should (member (json-encode #s(hash-table data (c 3 b 2 a 1)))
                     '("{\n \"a\": 1,\n \"b\": 2,\n \"c\": 3\n}"
                       "{\n \"a\": 1,\n \"c\": 3,\n \"b\": 2\n}"
                       "{\n \"b\": 2,\n \"a\": 1,\n \"c\": 3\n}"
@@ -648,13 +643,12 @@ Point is moved to beginning of the buffer."
         (json-encoding-pretty-print t)
         (json-encoding-default-indentation " ")
         (json-encoding-lisp-style-closings t))
-    (should (equal (json-encode-hash-table #s(hash-table)) "{}"))
-    (should (equal (json-encode-hash-table #s(hash-table data (a 1)))
-                   "{\n \"a\": 1}"))
-    (should (member (json-encode-hash-table #s(hash-table data (b 2 a 1)))
+    (should (equal (json-encode #s(hash-table)) "{}"))
+    (should (equal (json-encode #s(hash-table data (a 1))) "{\n \"a\": 1}"))
+    (should (member (json-encode #s(hash-table data (b 2 a 1)))
                     '("{\n \"a\": 1,\n \"b\": 2}"
                       "{\n \"b\": 2,\n \"a\": 1}")))
-    (should (member (json-encode-hash-table #s(hash-table data (c 3 b 2 a 1)))
+    (should (member (json-encode #s(hash-table data (c 3 b 2 a 1)))
                     '("{\n \"a\": 1,\n \"b\": 2,\n \"c\": 3}"
                       "{\n \"a\": 1,\n \"c\": 3,\n \"b\": 2}"
                       "{\n \"b\": 2,\n \"a\": 1,\n \"c\": 3}"
@@ -672,7 +666,7 @@ Point is moved to beginning of the buffer."
                      (#s(hash-table data (c 3 b 2 a 1))
                         . "{\"a\":1,\"b\":2,\"c\":3}")))
       (let ((copy (map-pairs in)))
-        (should (equal (json-encode-hash-table in) out))
+        (should (equal (json-encode in) out))
         ;; Ensure sorting isn't destructive.
         (should (seq-set-equal-p (map-pairs in) copy))))))
 
@@ -785,38 +779,42 @@ Point is moved to beginning of the buffer."
         (should (equal in copy))))))
 
 (ert-deftest test-json-encode-list ()
+  "Test `json-encode-list' or its more moral equivalents."
   (let ((json-encoding-object-sort-predicate nil)
         (json-encoding-pretty-print nil))
-    (should (equal (json-encode-list ()) "{}"))
-    (should (equal (json-encode-list '(a)) "[\"a\"]"))
-    (should (equal (json-encode-list '(:a)) "[\"a\"]"))
-    (should (equal (json-encode-list '("a")) "[\"a\"]"))
-    (should (equal (json-encode-list '(a 1)) "[\"a\",1]"))
-    (should (equal (json-encode-list '("a" 1)) "[\"a\",1]"))
-    (should (equal (json-encode-list '(:a 1)) "{\"a\":1}"))
-    (should (equal (json-encode-list '((a . 1))) "{\"a\":1}"))
-    (should (equal (json-encode-list '((:a . 1))) "{\"a\":1}"))
-    (should (equal (json-encode-list '(:b 2 :a)) "[\"b\",2,\"a\"]"))
-    (should (equal (json-encode-list '(4 3 2 1)) "[4,3,2,1]"))
-    (should (equal (json-encode-list '(b 2 a 1)) "[\"b\",2,\"a\",1]"))
-    (should (equal (json-encode-list '(:b 2 :a 1)) "{\"b\":2,\"a\":1}"))
-    (should (equal (json-encode-list '((b . 2) (a . 1))) "{\"b\":2,\"a\":1}"))
-    (should (equal (json-encode-list '((:b . 2) (:a . 1)))
+    ;; Trick `json-encode' into using `json--print-list'.
+    (let ((json-null (list nil)))
+      (should (equal (json-encode ()) "{}")))
+    (should (equal (json-encode '(a)) "[\"a\"]"))
+    (should (equal (json-encode '(:a)) "[\"a\"]"))
+    (should (equal (json-encode '("a")) "[\"a\"]"))
+    (should (equal (json-encode '(a 1)) "[\"a\",1]"))
+    (should (equal (json-encode '("a" 1)) "[\"a\",1]"))
+    (should (equal (json-encode '(:a 1)) "{\"a\":1}"))
+    (should (equal (json-encode '((a . 1))) "{\"a\":1}"))
+    (should (equal (json-encode '((:a . 1))) "{\"a\":1}"))
+    (should (equal (json-encode '(:b 2 :a)) "[\"b\",2,\"a\"]"))
+    (should (equal (json-encode '(4 3 2 1)) "[4,3,2,1]"))
+    (should (equal (json-encode '(b 2 a 1)) "[\"b\",2,\"a\",1]"))
+    (should (equal (json-encode '(:b 2 :a 1)) "{\"b\":2,\"a\":1}"))
+    (should (equal (json-encode '((b . 2) (a . 1))) "{\"b\":2,\"a\":1}"))
+    (should (equal (json-encode '((:b . 2) (:a . 1)))
                    "{\"b\":2,\"a\":1}"))
-    (should (equal (json-encode-list '((a) 1)) "[[\"a\"],1]"))
-    (should (equal (json-encode-list '((:a) 1)) "[[\"a\"],1]"))
-    (should (equal (json-encode-list '(("a") 1)) "[[\"a\"],1]"))
-    (should (equal (json-encode-list '((a 1) 2)) "[[\"a\",1],2]"))
-    (should (equal (json-encode-list '((:a 1) 2)) "[{\"a\":1},2]"))
-    (should (equal (json-encode-list '(((a . 1)) 2)) "[{\"a\":1},2]"))
-    (should (equal (json-encode-list '(:a 1 :b (2))) "{\"a\":1,\"b\":[2]}"))
-    (should (equal (json-encode-list '((a . 1) (b 2))) "{\"a\":1,\"b\":[2]}"))
-    (should-error (json-encode-list '(a . 1)) :type 'wrong-type-argument)
-    (should-error (json-encode-list '((a . 1) 2)) :type 'wrong-type-argument)
-    (should (equal (should-error (json-encode-list []))
-                   '(json-error [])))
-    (should (equal (should-error (json-encode-list [a]))
-                   '(json-error [a])))))
+    (should (equal (json-encode '((a) 1)) "[[\"a\"],1]"))
+    (should (equal (json-encode '((:a) 1)) "[[\"a\"],1]"))
+    (should (equal (json-encode '(("a") 1)) "[[\"a\"],1]"))
+    (should (equal (json-encode '((a 1) 2)) "[[\"a\",1],2]"))
+    (should (equal (json-encode '((:a 1) 2)) "[{\"a\":1},2]"))
+    (should (equal (json-encode '(((a . 1)) 2)) "[{\"a\":1},2]"))
+    (should (equal (json-encode '(:a 1 :b (2))) "{\"a\":1,\"b\":[2]}"))
+    (should (equal (json-encode '((a . 1) (b 2))) "{\"a\":1,\"b\":[2]}"))
+    (should-error (json-encode '(a . 1)) :type 'wrong-type-argument)
+    (should-error (json-encode '((a . 1) 2)) :type 'wrong-type-argument)
+    (with-suppressed-warnings ((obsolete json-encode-list))
+      (should (equal (should-error (json-encode-list []))
+                     '(json-error [])))
+      (should (equal (should-error (json-encode-list [a]))
+                     '(json-error [a]))))))
 
 ;;; Arrays
 
-- 
cgit v1.2.3