Subject: bug#20154: 25.0.50; json-encode-string is too slow for large strings

From: Dmitry Gutov <[email-masked]>
Date: Sat, 21 Mar 2015 23:09:57 +0200

On 03/21/2015 10:07 AM, Eli Zaretskii wrote:

> Do you really need 'format' here?  Why not
>
>      (concat "\\" (char-to-string (char-after (match-beginning 0))))
>
> instead?
>
> Or even simply insert these two parts at the match point, after
> deleting the match.

Yes, thanks. It gave a small improvement, by 5ms or so. Here's the 
updated definition (by the way, `json-special-chars' is still needed, to 
convert ?\n to ?n, and so on, and the performance hit is negligible).

(defun json-encode-string-1 (string)
   "Return a JSON representation of STRING."
   (with-temp-buffer
     (insert string)
     (goto-char (point-min))
     ;; Skip over ASCIIish printable characters.
     (while (re-search-forward "\\([\"\\/\b\f\n\r\t]\\)\\|[^ -~]" nil t)
       (let ((c (char-before)))
         (delete-region (1- (point)) (point))
         (if (match-beginning 1)
             ;; Special JSON character (\n, \r, etc.).
             (insert "\\" (car (rassoc c json-special-chars)))
           ;; Fallback: UCS code point in \uNNNN form.
           (insert (format "\\u%04x" c)))))
     (concat "\"" (buffer-string) "\"")))

Futher, it seems I didn't measure its performance well enough to begin 
with. The average time out of 10 runs comes down to 85ms.

Compare it to 150ms, from this implementation:

(defun json-encode-string-2 (string)
   "Return a JSON representation of STRING."
   (concat "\""
           (replace-regexp-in-string
            "\\([\"\\/\b\f\n\r\t]\\)\\|[^ -~]"
            (lambda (s)
              (if (match-beginning 1)
                  (format "\\%c" (car (rassoc (string-to-char s)
                                              json-special-chars)))
                (format "\\u%04x" (string-to-char s))))
            string t t)
           "\""))