master: Micro-optimize READ-STRING

melisgl via Sbcl-commits <[email protected]> Wed, 29 Jul 2026 07:42:09 +0000
Newsgroups gmane.lisp.steel-bank.cvs
Message-ID <[email protected]>
The branch "master" has been updated in SBCL:
       via  980d22466feca1cf6a3f7570b3783ed6f34ae79c (commit)
      from  05b15081a2cf14dd8e0c3b4940695d63fbbb38bb (commit)

- Log -----------------------------------------------------------------
commit 980d22466feca1cf6a3f7570b3783ed6f34ae79c
Author: Gabor Melis <[email protected]>
Date:   Tue Jul 28 13:17:33 2026 +0200

    Micro-optimize READ-STRING
    
    No more calls to GENERIC-+.
---
 src/code/reader.lisp | 40 +++++++++++++++++++++-------------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/src/code/reader.lisp b/src/code/reader.lisp
index 8ad0027dc..7cd4b24eb 100644
--- a/src/code/reader.lisp
+++ b/src/code/reader.lisp
@@ -924,14 +924,15 @@ FROM-READTABLE defaults to the standard Lisp readtable when NIL."
                stream "More than one object follows . in list.")))))))
 
 (defun read-string (stream closech)
-  ;; This accumulates chars until it sees same char that invoked it.
-  ;; We avoid copying any given input character more than twice-
-  ;; once to a temp buffer and then to the result. In the worst case,
-  ;; we can waste space equal the unwasted space, if the final character
-  ;; causes allocation of a new buffer for just that character,
-  ;; because the buffer size is doubled each time it overflows.
-  ;; (Would be better to peek at the frc-buffer if the stream has one.)
-  ;; Scratch vectors are GC-able as soon as this function returns though.
+  ;; Accumulate chars until an unescaped CLOSECH. We avoid copying any
+  ;; given input character more than twice: once into a temp buffer
+  ;; and then to the result. In the worst case, the total size of the
+  ;; chained buffers is 3 times the length of the result string (if
+  ;; the final character causes allocation of a new buffer for just
+  ;; that character) because the buffer size is doubled each time it
+  ;; overflows. (Would be better to peek at the frc-buffer if the
+  ;; stream has one.) Scratch vectors are GC-able as soon as this
+  ;; function returns though.
   (declare (character closech))
   (macrolet ((scan (read-a-char eofp &optional finish)
                `(loop (let ((char ,read-a-char))
@@ -946,6 +947,7 @@ FROM-READTABLE defaults to the standard Lisp readtable when NIL."
                         (when (>= ptr lim)
                           (unless suppress
                             (push buf chain)
+                            (incf total-chain-length lim)
                             (setq lim (the index (ash lim 1))
                                   buf (make-array lim :element-type 'character)))
                           (setq ptr 0))
@@ -960,8 +962,10 @@ FROM-READTABLE defaults to the standard Lisp readtable when NIL."
            (lim (length buf))
            (ptr 0)
            (only-base-chars t)
+           (total-chain-length 0)
            (chain))
-      (declare (type (simple-array character (*)) buf))
+      (declare (type (simple-array character (*)) buf)
+               (type index ptr total-chain-length))
       (reset-read-buffer token-buf)
       (if (ansi-stream-p stream)
           (prepare-for-fast-read-char stream
@@ -970,20 +974,18 @@ FROM-READTABLE defaults to the standard Lisp readtable when NIL."
           (scan (read-char stream nil +EOF+) (eq char +EOF+)))
       (if suppress
           ""
-          (let* ((sum (loop for buf in chain sum (length buf)))
-                 (result
-                  (make-array (+ sum ptr)
-                              :element-type (if only-base-chars
-                                                (%readtable-string-preference rt)
-                                                'character))))
-            (setq ptr sum)
+          (let* ((element-type (if only-base-chars
+                                   (%readtable-string-preference rt)
+                                   'character))
+                 (result (make-array (+ total-chain-length ptr)
+                                     :element-type element-type)))
+            (setq ptr total-chain-length)
             ;; Now work backwards from the end
             (replace result buf :start1 ptr)
             (dolist (buf chain result)
               (declare (type (simple-array character (*)) buf))
-              (let ((len (length buf)))
-                (decf ptr len)
-                (replace result buf :start1 ptr))))))))
+              (decf ptr (length buf))
+              (replace result buf :start1 ptr)))))))
 
 (defun read-right-paren (stream ignore)
   (declare (ignore ignore))

-----------------------------------------------------------------------


hooks/post-receive
-- 
SBCL