changelog shortlog graph tags branches changeset file revisions annotate raw help

Mercurial > core / lisp/lib/io/fast.lisp

revision 690: 90417ae14b21
child 695: 2bad47888dbf
     1.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2+++ b/lisp/lib/io/fast.lisp	Tue Oct 01 23:34:01 2024 -0400
     1.3@@ -0,0 +1,468 @@
     1.4+;;; fast.lisp --- Fast Octet Streams
     1.5+
     1.6+;; based on https://github.com/rpav/fast-io/tree/master
     1.7+
     1.8+;;; Code:
     1.9+(in-package :io/fast)
    1.10+
    1.11+ ;; Vector buffer
    1.12+
    1.13+(defvar *default-output-buffer-size* 16)
    1.14+
    1.15+(declaim (ftype (function (array-index) octet-vector) make-octet-vector)
    1.16+         (inline make-octet-vector))
    1.17+(defun make-octet-vector (len)
    1.18+  (make-array (the array-index len) :element-type 'octet))
    1.19+
    1.20+(declaim (inline output-buffer-vector output-buffer-fill output-buffer-len))
    1.21+(defstruct output-buffer
    1.22+  (vector (make-octet-vector *default-output-buffer-size*)
    1.23+   :type octet-vector)
    1.24+  (fill 0 :type array-index)
    1.25+  (len 0 :type array-index)
    1.26+  (queue nil :type list)
    1.27+  (last nil :type list)
    1.28+  (output nil))
    1.29+
    1.30+(defstruct input-buffer
    1.31+  (vector nil :type (or null octet-vector))
    1.32+  (pos 0 :type array-index)
    1.33+  (stream nil))
    1.34+
    1.35+(defun buffer-position (buffer)
    1.36+  "Return the number of bytes read (for an INPUT-BUFFER) or written
    1.37+   (for an OUTPUT-BUFFER)"
    1.38+  (etypecase buffer
    1.39+    (input-buffer (input-buffer-pos buffer))
    1.40+    (output-buffer (output-buffer-len buffer))))
    1.41+
    1.42+;; Sometimes it is usefull just to skip the buffer instead of reading from it.
    1.43+(defun (setf buffer-position) (new-pos buffer)
    1.44+  "Set the buffer position for input-buffer"
    1.45+  (check-type buffer input-buffer)
    1.46+  (let* ((pos (input-buffer-pos buffer))
    1.47+         (vec (input-buffer-vector buffer))
    1.48+         (vec-len (length vec)))
    1.49+    (declare (optimize (speed 3) (safety 1))
    1.50+             (type octet-vector vec)
    1.51+             (type non-negative-fixnum pos vec-len new-pos))
    1.52+    ;; Only need to update if pos or new-pos is in stream range.
    1.53+    (when-let ((stream-update-needed? (or (> pos vec-len)
    1.54+                                          (> new-pos vec-len)))
    1.55+               (stream (input-buffer-stream buffer)))
    1.56+      (let* ((stream-file-pos (file-position stream))
    1.57+             (pos-diff (- new-pos pos))
    1.58+             (stream-diff (cond ((and (> pos vec-len)
    1.59+                                      (< new-pos vec-len))
    1.60+                                 ;; branch for pos in stream and new-pos
    1.61+                                 ;; is in vector.
    1.62+                                 (- vec-len pos))
    1.63+                                ((and (< pos vec-len)
    1.64+                                      (> new-pos vec-len))
    1.65+                                 ;; branch for pos in vector. and new-pos
    1.66+                                 ;; is in stream.
    1.67+                                 (- pos-diff (- vec-len pos)))
    1.68+                                ;; otherwise stream-diff = pos-diff.
    1.69+                                (t pos-diff)))
    1.70+             (new-stream-pos (+ stream-file-pos stream-diff)))
    1.71+        (declare (type non-negative-fixnum stream-file-pos new-stream-pos)
    1.72+                 (type fixnum pos-diff stream-diff))
    1.73+        (file-position stream new-stream-pos))))
    1.74+  (setf (slot-value buffer 'pos) new-pos))
    1.75+
    1.76+(defun octets-from (sequence)
    1.77+  (let ((vec (make-octet-vector (length sequence))))
    1.78+    (replace vec sequence)
    1.79+    vec))
    1.80+
    1.81+(defun concat-buffer (buffer)
    1.82+  (let* ((len (output-buffer-len buffer))
    1.83+         (array
    1.84+           #+fast-io-sv
    1.85+           (if (eq :static (output-buffer-output buffer))
    1.86+               (static-vectors:make-static-vector (the array-index len))
    1.87+               (make-octet-vector len))
    1.88+           #-fast-io-sv
    1.89+           (make-octet-vector len)))
    1.90+    (loop as i = 0 then (+ i (length a))
    1.91+          for a in (output-buffer-queue buffer) do
    1.92+            (replace (the octet-vector array)
    1.93+                     (the octet-vector a) :start1 i)
    1.94+          finally
    1.95+             (replace (the octet-vector array)
    1.96+                      (output-buffer-vector buffer)
    1.97+                      :start1 i
    1.98+                      :end2 (output-buffer-fill buffer)))
    1.99+    array))
   1.100+
   1.101+(defun flush (output-buffer)
   1.102+  (when (> (output-buffer-fill output-buffer) 0)
   1.103+    (write-sequence (output-buffer-vector output-buffer)
   1.104+                    (output-buffer-output output-buffer)
   1.105+                    :start 0 :end (output-buffer-fill output-buffer))
   1.106+    (prog1 (output-buffer-fill output-buffer)
   1.107+      (setf (output-buffer-fill output-buffer) 0))))
   1.108+
   1.109+(defun extend (buffer &optional (min 1))
   1.110+  (let ((vector (output-buffer-vector buffer)))
   1.111+    (setf (output-buffer-last buffer)
   1.112+          (nconc (output-buffer-last buffer)
   1.113+                 (cons vector nil))
   1.114+          (output-buffer-vector buffer)
   1.115+          (make-octet-vector (max min (1+ (* 2 (length vector)))))
   1.116+          (output-buffer-fill buffer) 0)
   1.117+    (unless (output-buffer-queue buffer)
   1.118+      (setf (output-buffer-queue buffer)
   1.119+            (output-buffer-last buffer)))))
   1.120+
   1.121+(defun fast-write-byte (byte output-buffer)
   1.122+  (declare (type octet byte)
   1.123+           (type output-buffer output-buffer)
   1.124+           (optimize (speed 3) (safety 1)))
   1.125+  (when (= (output-buffer-fill output-buffer)
   1.126+           (array-dimension (output-buffer-vector output-buffer) 0))
   1.127+    (if (streamp (output-buffer-output output-buffer))
   1.128+        (flush output-buffer)
   1.129+        (extend output-buffer)))
   1.130+  (prog1
   1.131+      (setf (aref (output-buffer-vector output-buffer)
   1.132+                  (output-buffer-fill output-buffer))
   1.133+            byte)
   1.134+    (incf (output-buffer-fill output-buffer))
   1.135+    (incf (output-buffer-len output-buffer))))
   1.136+
   1.137+(defun fast-read-byte (input-buffer &optional (eof-error-p t) eof-value)
   1.138+  (declare (type input-buffer input-buffer))
   1.139+  (when-let ((vec (input-buffer-vector input-buffer))
   1.140+             (pos (input-buffer-pos input-buffer)))
   1.141+    (when (< pos (length vec))
   1.142+      (incf (input-buffer-pos input-buffer))
   1.143+      (return-from fast-read-byte (aref vec pos))))
   1.144+  (when-let ((stream (input-buffer-stream input-buffer)))
   1.145+    (let ((byte (read-byte stream eof-error-p eof-value)))
   1.146+      (unless (equal byte eof-value)
   1.147+        (incf (input-buffer-pos input-buffer)))
   1.148+      (return-from fast-read-byte byte)))
   1.149+  (if eof-error-p
   1.150+      (error 'end-of-file :stream input-buffer)
   1.151+      eof-value))
   1.152+
   1.153+(defun fast-peek-byte (input-buffer &optional peek-type (eof-error-p t) eof-value)
   1.154+  "This is like `peek-byte' only for fast-io input-buffers."
   1.155+  (declare (type input-buffer input-buffer))
   1.156+  (loop :for octet = (fast-read-byte input-buffer eof-error-p :eof)
   1.157+     :for new-pos :from (input-buffer-pos input-buffer)
   1.158+     :until (cond ((eq octet :eof)
   1.159+                   (return eof-value))
   1.160+                  ((null peek-type))
   1.161+                  ((eq peek-type 't)
   1.162+                   (plusp octet))
   1.163+                  ((= octet peek-type)))
   1.164+     :finally (setf (buffer-position input-buffer) new-pos)
   1.165+       (return octet)))
   1.166+
   1.167+(defun fast-write-sequence (sequence output-buffer &optional (start 0) end)
   1.168+  (if (streamp (output-buffer-output output-buffer))
   1.169+      (progn
   1.170+        (flush output-buffer)
   1.171+        (write-sequence sequence (output-buffer-output output-buffer) :start start :end end))
   1.172+      (progn
   1.173+        (let* ((start2 start)
   1.174+               (len (if end
   1.175+                        (- end start)
   1.176+                        (- (length sequence) start)))
   1.177+               (buffer-remaining
   1.178+                 (- (length (output-buffer-vector output-buffer))
   1.179+                    (output-buffer-fill output-buffer))))
   1.180+          (when (> buffer-remaining 0)
   1.181+            (replace (output-buffer-vector output-buffer)
   1.182+                     (the octet-vector sequence)
   1.183+                     :start1 (output-buffer-fill output-buffer)
   1.184+                     :start2 start2
   1.185+                     :end2 end)
   1.186+            (incf start2 buffer-remaining)
   1.187+            (incf (output-buffer-fill output-buffer)
   1.188+                  (min buffer-remaining len)))
   1.189+          (let ((sequence-remaining (- (or end (length sequence)) start2)))
   1.190+            (when (> sequence-remaining 0)
   1.191+              (extend output-buffer sequence-remaining)
   1.192+              (replace (output-buffer-vector output-buffer)
   1.193+                       (the octet-vector sequence)
   1.194+                       :start2 start2
   1.195+                       :end2 end)
   1.196+              (incf (output-buffer-fill output-buffer) sequence-remaining)))
   1.197+          (incf (output-buffer-len output-buffer) len)
   1.198+          len))))
   1.199+
   1.200+(defun fast-read-sequence (sequence input-buffer &optional (start 0) end)
   1.201+  (declare (type octet-vector sequence)
   1.202+           (type input-buffer input-buffer))
   1.203+  (let ((start1 start)
   1.204+        (total-len (if end
   1.205+                       (- end start)
   1.206+                       (- (length sequence) start))))
   1.207+    (when-let ((vec (input-buffer-vector input-buffer))
   1.208+               (pos (input-buffer-pos input-buffer)))
   1.209+      (when (< pos (length vec))
   1.210+        (let ((len (min total-len (- (length vec) pos))))
   1.211+          (replace sequence vec
   1.212+                   :start1 start1
   1.213+                   :start2 pos
   1.214+                   :end2 (+ pos len))
   1.215+          (incf (input-buffer-pos input-buffer) len)
   1.216+          (incf start1 len))))
   1.217+    (when (< start1 total-len)
   1.218+      (when-let ((stream (input-buffer-stream input-buffer)))
   1.219+        (let ((bytes-read (read-sequence sequence stream
   1.220+                                         :start start1
   1.221+                                         :end (+ total-len start1))))
   1.222+          (incf (input-buffer-pos input-buffer) bytes-read)
   1.223+          (return-from fast-read-sequence bytes-read))))
   1.224+    start1))
   1.225+
   1.226+(defun finish-output-buffer (output-buffer)
   1.227+  "Finish an output buffer. If it is backed by a vector (static or otherwise)
   1.228+it returns the final octet vector. If it is backed by a stream it ensures that
   1.229+all data has been flushed to the stream."
   1.230+  (if (streamp (output-buffer-output output-buffer))
   1.231+      (flush output-buffer)
   1.232+      (concat-buffer output-buffer)))
   1.233+
   1.234+(defmacro with-fast-output ((buffer &optional output) &body body)
   1.235+  "Create `BUFFER`, optionally outputting to `OUTPUT`."
   1.236+  `(let ((,buffer (make-output-buffer :output ,output)))
   1.237+     ,@body
   1.238+     (if (streamp (output-buffer-output ,buffer))
   1.239+         (flush ,buffer)
   1.240+         (finish-output-buffer ,buffer))))
   1.241+
   1.242+(defmacro with-fast-input ((buffer vector &optional stream (offset 0)) &body body)
   1.243+  `(let ((,buffer (make-input-buffer :vector ,vector :stream ,stream :pos ,offset)))
   1.244+     ,@body))
   1.245+
   1.246+ ;; READx and WRITEx
   1.247+;;; WRITE-UNSIGNED-BE, READ-UNSIGNED-BE, etc taken from PACK, which is
   1.248+;;; in the public domain.
   1.249+
   1.250+(defmacro write-unsigned-be (value size buffer)
   1.251+  (once-only (value buffer)
   1.252+    `(progn
   1.253+       ,@(loop for i from (* (1- size) 8) downto 0 by 8
   1.254+               collect `(fast-write-byte (ldb (byte 8 ,i) ,value) ,buffer)))))
   1.255+
   1.256+(defmacro read-unsigned-be (size buffer)
   1.257+  (with-gensyms (value)
   1.258+    (once-only (buffer)
   1.259+      `(let ((,value 0))
   1.260+         ,@(loop for i from (* (1- size) 8) downto 0 by 8
   1.261+                 collect `(setf (ldb (byte 8 ,i) ,value) (fast-read-byte ,buffer)))
   1.262+         ,value))))
   1.263+
   1.264+(defmacro write-unsigned-le (value size buffer)
   1.265+  (once-only (value buffer)
   1.266+    `(progn
   1.267+       ,@(loop for i from 0 below (* 8 size) by 8
   1.268+               collect `(fast-write-byte (ldb (byte 8 ,i) ,value) ,buffer)))))
   1.269+
   1.270+(defmacro read-unsigned-le (size buffer)
   1.271+  (with-gensyms (value)
   1.272+    (once-only (buffer)
   1.273+      `(let ((,value 0))
   1.274+         ,@(loop for i from 0 below (* 8 size) by 8
   1.275+                 collect `(setf (ldb (byte 8 ,i) ,value) (fast-read-byte ,buffer)))
   1.276+         ,value))))
   1.277+
   1.278+(declaim (inline unsigned-to-signed))
   1.279+(defun unsigned-to-signed (value size)
   1.280+  (let ((max-signed (expt 2 (1- (* 8 size))))
   1.281+        (to-subtract (expt 2 (* 8 size))))
   1.282+    (if (>= value max-signed)
   1.283+        (- value to-subtract)
   1.284+        value)))
   1.285+
   1.286+(declaim (inline signed-to-unsigned))
   1.287+(defun signed-to-unsigned (value size)
   1.288+  (if (minusp value)
   1.289+      (+ value (expt 2 (* 8 size)))
   1.290+      value))
   1.291+
   1.292+(defmacro make-readers (&rest bitlens)
   1.293+  (let ((names (mapcar (lambda (n)
   1.294+                         (mapcar (lambda (m) (symbolicate (format nil m n)))
   1.295+                                 '("READ~A-BE" "READU~A-BE"
   1.296+                                   "READ~A-LE" "READU~A-LE")))
   1.297+                       bitlens)))
   1.298+    `(eval-when (:compile-toplevel :load-toplevel :execute)
   1.299+       (declaim (inline ,@(flatten names)))
   1.300+       ,@(loop for fun in names
   1.301+               for bits in bitlens
   1.302+               as bytes = (truncate bits 8)
   1.303+               collect
   1.304+               `(progn
   1.305+                  (defun ,(first fun) (buffer)
   1.306+                    (unsigned-to-signed (read-unsigned-be ,bytes buffer) ,bytes))
   1.307+                  (defun ,(second fun) (buffer)
   1.308+                    (read-unsigned-be ,bytes buffer))
   1.309+                  (defun ,(third fun) (buffer)
   1.310+                    (unsigned-to-signed (read-unsigned-le ,bytes buffer) ,bytes))
   1.311+                  (defun ,(fourth fun) (buffer)
   1.312+                    (read-unsigned-le ,bytes buffer)))))))
   1.313+
   1.314+(defmacro make-writers (&rest bitlens)
   1.315+  (let ((names (mapcar (lambda (n)
   1.316+                         (mapcar (lambda (m) (symbolicate (format nil m n)))
   1.317+                                 '("WRITE~A-BE" "WRITEU~A-BE"
   1.318+                                   "WRITE~A-LE" "WRITEU~A-LE")))
   1.319+                       bitlens)))
   1.320+    `(eval-when (:compile-toplevel :load-toplevel :execute)
   1.321+       (declaim (notinline ,@(flatten names)))
   1.322+       ,@(loop for fun in names
   1.323+               for bits in bitlens
   1.324+               as bytes = (truncate bits 8)
   1.325+               collect
   1.326+               `(progn
   1.327+                  (defun ,(first fun) (value buffer)
   1.328+                    (declare (type (signed-byte ,bits) value))
   1.329+                    (write-unsigned-be (the (unsigned-byte ,bits)
   1.330+                                            (signed-to-unsigned value ,bytes)) ,bytes buffer))
   1.331+                  (defun ,(second fun) (value buffer)
   1.332+                    (declare (type (unsigned-byte ,bits) value))
   1.333+                    (write-unsigned-be (the (unsigned-byte ,bits) value)
   1.334+                                       ,bytes buffer))
   1.335+                  (defun ,(third fun) (value buffer)
   1.336+                    (declare (type (signed-byte ,bits) value))
   1.337+                    (write-unsigned-le (the (unsigned-byte ,bits)
   1.338+                                            (signed-to-unsigned value ,bytes)) ,bytes buffer))
   1.339+                  (defun ,(fourth fun) (value buffer)
   1.340+                    (declare (type (unsigned-byte ,bits) value))
   1.341+                    (write-unsigned-le (the (unsigned-byte ,bits) value)
   1.342+                                       ,bytes buffer)))))))
   1.343+
   1.344+(make-writers 16 24 32 64 128)
   1.345+(make-readers 16 24 32 64 128)
   1.346+
   1.347+(declaim (inline write8 writeu8 read8 readu8))
   1.348+(defun write8 (value buffer)
   1.349+  (declare (type (signed-byte 8) value))
   1.350+  (fast-write-byte (signed-to-unsigned value 1) buffer))
   1.351+
   1.352+(defun writeu8 (value buffer)
   1.353+  (declare (type (unsigned-byte 8) value))
   1.354+  (fast-write-byte value buffer))
   1.355+
   1.356+
   1.357+(defun read8 (buffer)
   1.358+  (unsigned-to-signed (fast-read-byte buffer) 1))
   1.359+
   1.360+(defun readu8 (buffer)
   1.361+  (fast-read-byte buffer))
   1.362+
   1.363+(setf (symbol-function 'write8-le) #'write8)
   1.364+(setf (symbol-function 'write8-be) #'write8)
   1.365+(setf (symbol-function 'writeu8-le) #'writeu8)
   1.366+(setf (symbol-function 'writeu8-be) #'writeu8)
   1.367+
   1.368+(setf (symbol-function 'read8-le) #'read8)
   1.369+(setf (symbol-function 'read8-be) #'read8)
   1.370+(setf (symbol-function 'readu8-le) #'readu8)
   1.371+(setf (symbol-function 'readu8-be) #'readu8)
   1.372+
   1.373+;; fast-stream
   1.374+
   1.375+(defclass fast-io-stream (fundamental-stream)
   1.376+  ((openp :type boolean :initform t)))
   1.377+
   1.378+(defmethod stream-file-position ((stream fast-io-stream))
   1.379+  (with-slots (buffer) stream
   1.380+    (buffer-position buffer)))
   1.381+
   1.382+(defmethod open-stream-p ((stream fast-io-stream))
   1.383+  (slot-value stream 'openep))
   1.384+
   1.385+ ;; fast-output-stream
   1.386+
   1.387+(defclass fast-output-stream (fast-io-stream fundamental-output-stream)
   1.388+  ((buffer :type output-buffer)))
   1.389+
   1.390+(defmethod initialize-instance ((self fast-output-stream) &key stream
   1.391+                                buffer-size &allow-other-keys)
   1.392+  (call-next-method)
   1.393+  (let ((*default-output-buffer-size* (or buffer-size *default-output-buffer-size*)))
   1.394+    (with-slots (buffer) self
   1.395+      (setf buffer (make-output-buffer :output stream)))))
   1.396+
   1.397+(defmethod output-stream-p ((stream fast-output-stream))
   1.398+  (with-slots (buffer) stream
   1.399+    (and (typep buffer 'output-buffer))))
   1.400+
   1.401+(defmethod stream-element-type ((stream fast-output-stream))
   1.402+  "Return the underlying array element-type.
   1.403+   Should always return '(unsigned-byte 8)."
   1.404+  (with-slots (buffer) stream
   1.405+    (array-element-type (output-buffer-vector buffer))))
   1.406+
   1.407+(defmethod stream-write-byte ((stream fast-output-stream) byte)
   1.408+  (with-slots (buffer) stream
   1.409+    (fast-write-byte byte buffer)))
   1.410+
   1.411+(defmethod stream-write-sequence ((stream fast-output-stream) sequence start end
   1.412+                                  &key &allow-other-keys)
   1.413+  (with-slots (buffer) stream
   1.414+    (fast-write-sequence sequence buffer start end))
   1.415+  sequence)
   1.416+
   1.417+(defun finish-output-stream (stream)
   1.418+  (with-slots (buffer) stream
   1.419+    (if (streamp (output-buffer-output buffer))
   1.420+        (flush buffer)
   1.421+        (finish-output-buffer buffer))))
   1.422+
   1.423+(defmethod close ((stream fast-output-stream) &key abort)
   1.424+  (declare (ignore abort))
   1.425+  (finish-output-stream stream)
   1.426+  (setf (slot-value stream 'openp) nil))
   1.427+
   1.428+ ;; fast-input-stream
   1.429+
   1.430+(defclass fast-input-stream (fast-io-stream fundamental-input-stream)
   1.431+  ((buffer :type input-buffer)))
   1.432+
   1.433+(defmethod initialize-instance ((self fast-input-stream) &key stream
   1.434+                                vector &allow-other-keys)
   1.435+  (call-next-method)
   1.436+  (with-slots (buffer) self
   1.437+    (setf buffer (make-input-buffer :vector vector :stream stream))))
   1.438+
   1.439+(defmethod input-stream-p ((stream fast-input-stream))
   1.440+  (with-slots (buffer) stream
   1.441+    (and (typep buffer 'input-buffer))))
   1.442+
   1.443+(defmethod stream-element-type ((stream fast-input-stream))
   1.444+  "Return element-type of the underlying vector or stream.
   1.445+   Return NIL if none are present."
   1.446+  (with-slots (buffer) stream
   1.447+    (if-let ((vec (input-buffer-vector buffer)))
   1.448+      (array-element-type vec)
   1.449+      (when-let ((stream (input-buffer-stream buffer)))
   1.450+        (stream-element-type stream)))))
   1.451+
   1.452+(defmethod (setf stream-file-position) (new-pos (stream fast-input-stream))
   1.453+  (with-slots (buffer) stream
   1.454+    (setf (buffer-position buffer) new-pos)))
   1.455+
   1.456+(defmethod peek-byte ((stream fast-input-stream) &optional peek-type (eof-error-p t) eof-value)
   1.457+  (with-slots (buffer) stream
   1.458+    (fast-peek-byte buffer peek-type eof-error-p eof-value)))
   1.459+
   1.460+(defmethod stream-read-byte ((stream fast-input-stream))
   1.461+  (with-slots (buffer) stream
   1.462+    (fast-read-byte buffer)))
   1.463+
   1.464+(defmethod stream-read-sequence ((stream fast-input-stream) sequence start end
   1.465+                                 &key &allow-other-keys)
   1.466+  (with-slots (buffer) stream
   1.467+    (fast-read-sequence sequence buffer start end)))
   1.468+
   1.469+(defmethod close ((stream fast-input-stream) &key abort)
   1.470+  (declare (ignore abort))
   1.471+  (setf (slot-value stream 'openp) nil))