Mercurial > core / lisp/lib/io/static-vector.lisp
changeset 695: |
2bad47888dbf |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 16:14:44 -0400 |
permissions: |
-rw-r--r-- |
description: |
add static-vector |
1 ;;; static-vector.lisp --- Static Vectors 3 ;; Vectors allocated in static memory. Useful for things like IO buffers 4 ;; created from Lisp and shared with C code. 8 ;; The source here is pulled directly from the STATIC-VECTORS package on 9 ;; Quicklisp: https://github.com/sionescu/static-vectors 12 (in-package :io/static-vector) 13 ;;; --- Checking for compile-time constants and evaluating such forms 18 (eql 'quote (car form)))) 20 (defun constantp (form &optional env) 21 (let ((form (if (symbolp form) 22 (macroexpand form env) 25 (cl:constantp form)))) 27 (defun eval-constant (form &optional env) 28 (declare (ignorable env)) 34 (ccl::eval-constant form) 36 (sb-int:constant-form-value form env) 40 (defun canonicalize-args (env element-type length) 41 (let* ((eltype-spec (or (and (constantp element-type) 43 (upgraded-array-element-type 44 (eval-constant element-type)))) 46 (length-spec (if (constantp length env) 47 `,(eval-constant length env) 49 (type-decl (if (eql '* eltype-spec) 51 `(simple-array ,eltype-spec (,length-spec))))) 52 (values (if (eql '* eltype-spec) 54 `(quote ,eltype-spec)) 55 (if (eql '* length-spec) 60 ;;; --- SBCL implementation 61 (declaim (inline fill-foreign-memory)) 62 (defun fill-foreign-memory (pointer length value) 63 "Fill LENGTH octets in foreign memory area POINTER with VALUE." 64 (std/alien:memset pointer value length) 67 (declaim (inline replace-foreign-memory)) 68 (defun replace-foreign-memory (dst-ptr src-ptr length) 69 "Copy LENGTH octets from foreign memory area SRC-PTR to DST-PTR." 70 (std/alien:memcpy dst-ptr src-ptr length) 73 ;;; We have to handle all the low-level bits including setting the array header 74 ;;; and keeping around the info about the original pointer returned by the 75 ;;; foreign allocator. 77 ;;; It goes like this: 79 ;;; 1. Compute the data size for the Lisp-visible memory (that means an extra #\Nul 80 ;;; at the end for strings) 81 ;;; 2. Sum the data size, the SBCL header size, and our extra header size to get 82 ;;; the total foreign size required 83 ;;; 3. Adjust the total foreign size to the required alignment, compute the header offset 84 ;;; and write the headers. 88 ;;; +-------------------+ 89 ;;; | Allocated address | <-- Original pointer 90 ;;; +-------------------+ 91 ;;; | Start gap ... | <-- For large alignments, there's a gap between 92 ;;; | | the data block and the headers. 93 ;;; +-------------------+ 94 ;;; | SV header | <-- The offset from the original pointer (DWORD) 95 ;;; +-------------------+ 96 ;;; | Lisp array header | <-- Array element-type and size (DWORD) 97 ;;; +-------------------+ 98 ;;; | Lisp array data | <-- Lisp-visible data 99 ;;; +-------------------+ 101 ;;; There's no end gap because when a alignment is requested, 102 ;;; the requested size must also be a multiple of the alignment. 104 (defconstant +array-header-size+ 105 (* sb-vm:vector-data-offset sb-vm:n-word-bytes)) 107 (declaim (inline vector-widetag-and-n-bytes)) 108 (defun vector-widetag-and-n-bytes (type) 109 "Returns the widetag and octet size of the upgraded array element type 110 for a given type specifier." 111 (let ((upgraded-type (upgraded-array-element-type type))) 113 ((nil t) (error "~A is not a specializable array element type" type)) 115 #+#.(cl:if (cl:find-symbol "%VECTOR-WIDETAG-AND-N-BITS" "SB-IMPL") 117 (sb-impl::%vector-widetag-and-n-bits type) 118 #+#.(cl:if (cl:find-symbol "%VECTOR-WIDETAG-AND-N-BITS-SHIFT" "SB-IMPL") 120 (multiple-value-bind (widetag shift) 121 (sb-impl::%vector-widetag-and-n-bits-shift type) 122 (values widetag (expt 2 (- shift 3)))))))) 124 (declaim (inline align)) 125 (defun align (size boundary) 127 (ceiling size boundary))) 129 (declaim (inline %memalign)) 130 (defun %memalign (size alignment) 131 (with-alien ((box (* t))) 132 (let ((errno (std/alien:posix-memalign box alignment size))) 133 (when (not (zerop errno)) 134 (error "posix_memalign() returned error ~A" errno)) 135 ;; (mem-ref box :pointer) 138 (defun %allocate-static-vector (length element-type alignment) 139 (declare (type (unsigned-byte 16) alignment)) 140 (flet ((allocation-sizes (length widetag n-bytes) 142 ;; We're allocating two headers: one for SBCL and 143 ;; the other one for our bookkeeping. 144 (align (* 2 +array-header-size+) alignment) 147 (* (if (= widetag sb-vm:simple-character-string-widetag) 148 (1+ length) ; for the final #\Nul 152 (multiple-value-bind (widetag n-bytes) 153 (vector-widetag-and-n-bytes element-type) 154 (multiple-value-bind (header-size data-size) 155 (allocation-sizes length widetag n-bytes) 156 (let* ((total-size (+ header-size data-size)) 157 (foreign-block (%memalign total-size alignment)) 158 (data-offset header-size ) 160 (- data-offset +array-header-size+)) 162 (sb-sys:sap+ foreign-block lisp-header-offset)) 164 (- data-offset (* 2 +array-header-size+))) 165 (extra-header-pointer 166 (sb-sys:sap+ foreign-block extra-header-offset))) 167 ;; Write Lisp header: tag and length 168 (setf (sb-sys:sap-ref-word lisp-header-pointer 0) widetag) 169 (setf (sb-sys:sap-ref-word lisp-header-pointer sb-vm:n-word-bytes) 170 (sb-vm:fixnumize length)) 171 ;; Save the relative position from the start of the foreign block 172 (setf (sb-sys:sap-ref-word extra-header-pointer 0) 173 (- data-offset (* 2 +array-header-size+))) 174 ;; Instantiate Lisp object 175 (sb-kernel:%make-lisp-obj (logior (sb-sys:sap-int lisp-header-pointer) 176 sb-vm:other-pointer-lowtag))))))) 178 (declaim (inline static-vector-address)) 179 (defun static-vector-address (vector) 180 "Return a foreign pointer to start of the Lisp VECTOR(including its header). 181 VECTOR must be a vector created by MAKE-STATIC-VECTOR." 182 (logandc2 (sb-kernel:get-lisp-obj-address vector) 185 (declaim (inline static-vector-pointer)) 186 (defun static-vector-pointer (vector &key (offset 0)) 187 "Return a foreign pointer to the beginning of VECTOR + OFFSET octets. 188 VECTOR must be a vector created by MAKE-STATIC-VECTOR." 189 (check-type offset unsigned-byte) 190 (sb-sys:int-sap (+ (static-vector-address vector) 194 (declaim (inline free-static-vector)) 195 (defun free-static-vector (vector) 196 "Free VECTOR, which must be a vector created by MAKE-STATIC-VECTOR." 197 (declare (sb-ext:muffle-conditions sb-ext:compiler-note)) 198 (let* ((extra-header-pointer 199 (sb-sys:int-sap (- (static-vector-address vector) +array-header-size+))) 201 (sb-sys:sap-ref-word extra-header-pointer 0))) 202 (free-alien (sap-alien (sb-sys:sap+ extra-header-pointer (- start-offset)) (* (unsigned 8))))) 205 (defmacro with-static-vector ((var length &rest args 206 &key (element-type ''(unsigned-byte 8)) 207 initial-contents initial-element) 208 &body body &environment env) 209 "Bind PTR-VAR to a static vector of length LENGTH and execute BODY 210 within its dynamic extent. The vector is freed upon exit." 211 (declare (ignorable element-type initial-contents initial-element)) 212 (multiple-value-bind (real-element-type length type-spec) 213 (canonicalize-args env element-type length) 214 (let ((args (copy-list args))) 215 (remf args :element-type) 216 `(sb-sys:without-interrupts 217 (let ((,var (make-static-vector ,length ,@args 218 :element-type ,real-element-type))) 219 (declare (type ,type-spec ,var)) 221 (sb-sys:with-local-interrupts ,@body) 222 (when ,var (free-static-vector ,var)))))))) 225 ;;; --- MAKE-STATIC-VECTOR 226 (declaim (inline check-initial-element)) 227 (defun check-initial-element (element-type initial-element) 228 (when (not (typep initial-element element-type)) 229 ;; FIXME: signal SUBTYPE-ERROR 230 (error "MAKE-STATIC-VECTOR: The type of :INITIAL-ELEMENT ~S is not a subtype ~ 231 of the array's :ELEMENT-TYPE ~S" 232 initial-element element-type))) 234 (declaim (inline check-initial-contents)) 235 (defun check-initial-contents (length initial-contents) 236 (let ((initial-contents-length (length initial-contents))) 237 (when (/= length initial-contents-length) 238 ;; FIXME: signal TYPE-ERROR 239 (error "MAKE-STATIC-VECTOR: There are ~A elements in the :INITIAL-CONTENTS, ~ 240 but requested vector length is ~A." 241 initial-contents-length length)))) 243 (declaim (inline check-initialization-arguments)) 244 (defun check-initialization-arguments (initial-element-p initial-contents-p) 245 (when (and initial-element-p initial-contents-p) 246 ;; FIXME: signal ARGUMENT-LIST-ERROR 247 (error "MAKE-STATIC-VECTOR: You must not specify both ~ 248 :INITIAL-ELEMENT and :INITIAL-CONTENTS"))) 250 (defun check-arguments (length element-type 251 initial-element initial-element-p 252 initial-contents initial-contents-p) 253 (check-initialization-arguments initial-element-p initial-contents-p) 254 (check-type length non-negative-fixnum) 255 (when initial-element-p 256 (check-initial-element element-type initial-element)) 257 (when initial-contents-p 258 (check-initial-contents length initial-contents))) 260 (defconstant +default-alignment+ 16) 261 (defconstant +max-alignment+ 4096) 263 (declaim (inline make-static-vector)) 264 (defun make-static-vector (length &key (element-type '(unsigned-byte 8)) 265 (initial-element nil initial-element-p) 266 (initial-contents nil initial-contents-p) 267 (alignment nil alignp)) 268 "Create a simple vector of length LENGTH and type ELEMENT-TYPE which will 269 not be moved by the garbage collector. The vector might be allocated in 270 foreign memory so you must always call FREE-STATIC-VECTOR to free it." 271 (declare #+sbcl (sb-ext:muffle-conditions sb-ext:compiler-note) 273 (check-arguments length element-type initial-element initial-element-p 274 initial-contents initial-contents-p) 276 ;; Check that the alignment is a power of 2 beteeen 16 and 4096. 278 (assert (and (<= +default-alignment+ alignment +max-alignment+) 279 (= 1 (logcount alignment)))) 281 (error "Allocation alignment not supported on this implementation.")) 282 ;; TODO: fix %allocate-static-vector for all implementations 284 (%allocate-static-vector length element-type 286 (or alignment +default-alignment+)))) 287 (if initial-element-p 288 (fill vector initial-element) 289 (replace vector initial-contents)))) 291 (defmacro with-static-vectors (((var length &rest args) &rest more-clauses) 293 "Allocate multiple static vectors at once." 294 `(with-static-vector (,var ,length ,@args) 296 `((with-static-vectors ,more-clauses