]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix json-insert unibyte buffer bug (bug#70007)
authorMattias Engdegård <mattiase@acm.org>
Tue, 2 Apr 2024 16:58:09 +0000 (18:58 +0200)
committerEshel Yaron <me@eshelyaron.com>
Wed, 3 Apr 2024 06:48:04 +0000 (08:48 +0200)
Previously, a unibyte target buffer could be put in an incorrect state
if json-insert was used to insert non-ASCII characters.

* src/json.c (Fjson_insert): Simplify.  Don't attempt to decode the data
being inserted: it is guaranteed to be correct UTF-8 and is correct for
both unibyte and multibyte buffers.
* test/src/json-tests.el (json-serialize/roundtrip)
(json-serialize/roundtrip-scalars): Extend tests.

(cherry picked from commit 617debf67392946b4b42fdf364c69da6f094a840)

src/json.c
test/src/json-tests.el

index 0b4414956ee1582d45ea9889e05dc8e7b3fec406..45dcdde98eaf087ea11a3560aff3244d21cd326d 100644 (file)
@@ -646,8 +646,6 @@ usage: (json-insert OBJECT &rest ARGS)  */)
   json_out_t jo;
   json_serialize (&jo, args[0], nargs - 1, args + 1);
 
-  /* FIXME: All the work below just to insert a string into a buffer?  */
-
   prepare_to_modify_buffer (PT, PT, NULL);
   move_gap_both (PT, PT_BYTE);
   if (GAP_SIZE < jo.size)
@@ -657,48 +655,22 @@ usage: (json-insert OBJECT &rest ARGS)  */)
   /* No need to keep allocation beyond this point.  */
   unbind_to (count, Qnil);
 
-  ptrdiff_t inserted = 0;
+  bool ub_buffer = NILP (BVAR (current_buffer, enable_multibyte_characters));
   ptrdiff_t inserted_bytes = jo.size;
+  ptrdiff_t inserted = ub_buffer ? jo.size : jo.size - jo.chars_delta;
+  eassert (inserted > 0);
 
-  /* If required, decode the stuff we've read into the gap.  */
-  struct coding_system coding;
-  /* JSON strings are UTF-8 encoded strings.  */
-  setup_coding_system (Qutf_8_unix, &coding);
-  coding.dst_multibyte = !NILP (BVAR (current_buffer,
-                                     enable_multibyte_characters));
-  if (CODING_MAY_REQUIRE_DECODING (&coding))
-    {
-      /* Now we have all the new bytes at the beginning of the gap,
-        but `decode_coding_gap` needs them at the end of the gap, so
-        we need to move them.  */
-      memmove (GAP_END_ADDR - inserted_bytes, GPT_ADDR, inserted_bytes);
-      decode_coding_gap (&coding, inserted_bytes);
-      inserted = coding.produced_char;
-    }
-  else
-    {
-      /* Make the inserted text part of the buffer, as unibyte text.  */
-      eassert (NILP (BVAR (current_buffer, enable_multibyte_characters)));
-      insert_from_gap_1 (inserted_bytes, inserted_bytes, false);
-
-      /* The target buffer is unibyte, so we don't need to decode.  */
-      invalidate_buffer_caches (current_buffer,
-                               PT, PT + inserted_bytes);
-      adjust_after_insert (PT, PT_BYTE,
-                          PT + inserted_bytes,
-                          PT_BYTE + inserted_bytes,
-                          inserted_bytes);
-      inserted = inserted_bytes;
-    }
+  insert_from_gap_1 (inserted, inserted_bytes, false);
+  invalidate_buffer_caches (current_buffer, PT, PT + inserted);
+  adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted_bytes,
+                      inserted);
 
   /* Call after-change hooks.  */
   signal_after_change (PT, 0, inserted);
-  if (inserted > 0)
-    {
-      update_compositions (PT, PT, CHECK_BORDER);
-      /* Move point to after the inserted text.  */
-      SET_PT_BOTH (PT + inserted, PT_BYTE + inserted_bytes);
-    }
+
+  update_compositions (PT, PT, CHECK_BORDER);
+  /* Move point to after the inserted text.  */
+  SET_PT_BOTH (PT + inserted, PT_BYTE + inserted_bytes);
 
   return Qnil;
 }
index 8b730ef8c909363acc1d6fe170d17ba9b123edee..ebac70fb1c7e19d55ed5f4aebb75fef90b6a8feb 100644 (file)
 (require 'map)
 (require 'subr-x)
 
-(declare-function json-serialize "json.c" (object &rest args))
-(declare-function json-insert "json.c" (object &rest args))
-(declare-function json-parse-string "json.c" (string &rest args))
-(declare-function json-parse-buffer "json.c" (&rest args))
-
 (define-error 'json-tests--error "JSON test error")
 
 (ert-deftest json-serialize/roundtrip ()
   ;; The noncharacter U+FFFF should be passed through,
   ;; cf. https://www.unicode.org/faq/private_use.html#noncharacters.
-  (let ((lisp [:null :false t 0 123 -456 3.75 "abc\uFFFFαβγ𝔸𝐁𝖢\"\\"])
-        (json "[null,false,true,0,123,-456,3.75,\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\"]"))
-    (should (equal (json-serialize lisp) json))
+  (let* ((lisp [:null :false t 0 123 -456 3.75 "abc\uFFFFαβγ𝔸𝐁𝖢\"\\"])
+         (json
+          "[null,false,true,0,123,-456,3.75,\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\"]")
+         (json-bytes (encode-coding-string json 'utf-8)))
+    (should (equal (json-serialize lisp) json))  ; or `json-bytes'?
     (with-temp-buffer
+      ;; multibyte buffer
       (json-insert lisp)
       (should (equal (buffer-string) json))
+      (should (equal (point) (1+ (length json))))
+      (should (eobp)))
+    (with-temp-buffer
+      ;; unibyte buffer
+      (set-buffer-multibyte nil)
+      (json-insert lisp)
+      (should (equal (buffer-string) json-bytes))
+      (should (equal (point) (1+ (length json-bytes))))
       (should (eobp)))
     (should (equal (json-parse-string json) lisp))
     (with-temp-buffer
+      ;; multibyte buffer
       (insert json)
       (goto-char 1)
       (should (equal (json-parse-buffer) lisp))
+      (should (equal (point) (1+ (length json))))
+      (should (eobp)))
+    (with-temp-buffer
+      ;; unibyte buffer
+      (set-buffer-multibyte nil)
+      (insert json-bytes)
+      (goto-char 1)
+      (should (equal (json-parse-buffer) lisp))
+      (should (equal (point) (1+ (length json-bytes))))
       (should (eobp)))))
 
 (ert-deftest json-serialize/roundtrip-scalars ()
           (json-insert lisp)
           (should (equal (buffer-string) json))
           (should (eobp)))
+        (with-temp-buffer
+          (set-buffer-multibyte nil)
+          (json-insert lisp)
+          (should (equal (buffer-string) (encode-coding-string json 'utf-8)))
+          (should (eobp)))
         (should (equal (json-parse-string json) lisp))
         (with-temp-buffer
           (insert json)
           (goto-char 1)
           (should (equal (json-parse-buffer) lisp))
+          (should (eobp)))
+        (with-temp-buffer
+          (set-buffer-multibyte nil)
+          (insert (encode-coding-string json 'utf-8))
+          (goto-char 1)
+          (should (equal (json-parse-buffer) lisp))
           (should (eobp)))))))
 
 (ert-deftest json-serialize/object ()