695
|
1
|
;;; static-vector.lisp --- Static Vectors |
|
2
|
|
|
3
|
;; Vectors allocated in static memory. Useful for things like IO buffers |
|
4
|
;; created from Lisp and shared with C code. |
|
5
|
|
|
6
|
;;; Commentary: |
|
7
|
|
|
8
|
;; The source here is pulled directly from the STATIC-VECTORS package on |
|
9
|
;; Quicklisp: https://github.com/sionescu/static-vectors |
|
10
|
|
|
11
|
;;; Code: |
|
12
|
(in-package :io/static-vector) |
|
13
|
;;; --- Checking for compile-time constants and evaluating such forms |
|
14
|
|
|
15
|
(defun quotedp (form) |
|
16
|
(and (listp form) |
|
17
|
(= 2 (length form)) |
|
18
|
(eql 'quote (car form)))) |
|
19
|
|
|
20
|
(defun constantp (form &optional env) |
|
21
|
(let ((form (if (symbolp form) |
|
22
|
(macroexpand form env) |
|
23
|
form))) |
|
24
|
(or (quotedp form) |
|
25
|
(cl:constantp form)))) |
|
26
|
|
|
27
|
(defun eval-constant (form &optional env) |
|
28
|
(declare (ignorable env)) |
|
29
|
(cond |
|
30
|
((quotedp form) |
|
31
|
(second form)) |
|
32
|
(t |
|
33
|
#+clozure |
|
34
|
(ccl::eval-constant form) |
|
35
|
#+sbcl |
|
36
|
(sb-int:constant-form-value form env) |
|
37
|
#-(or clozure sbcl) |
|
38
|
(eval form)))) |
|
39
|
|
|
40
|
(defun canonicalize-args (env element-type length) |
|
41
|
(let* ((eltype-spec (or (and (constantp element-type) |
|
42
|
(ignore-errors |
|
43
|
(upgraded-array-element-type |
|
44
|
(eval-constant element-type)))) |
|
45
|
'*)) |
|
46
|
(length-spec (if (constantp length env) |
|
47
|
`,(eval-constant length env) |
|
48
|
'*)) |
|
49
|
(type-decl (if (eql '* eltype-spec) |
|
50
|
'simple-array |
|
51
|
`(simple-array ,eltype-spec (,length-spec))))) |
|
52
|
(values (if (eql '* eltype-spec) |
|
53
|
element-type |
|
54
|
`(quote ,eltype-spec)) |
|
55
|
(if (eql '* length-spec) |
|
56
|
length |
|
57
|
length-spec) |
|
58
|
type-decl))) |
|
59
|
|
|
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) |
|
65
|
pointer) |
|
66
|
|
|
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) |
|
71
|
dst-ptr) |
|
72
|
|
|
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. |
|
76
|
;;; |
|
77
|
;;; It goes like this: |
|
78
|
;;; |
|
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. |
|
85
|
;;; |
|
86
|
;;; Array layout: |
|
87
|
;;; |
|
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
|
;;; +-------------------+ |
|
100
|
;;; |
|
101
|
;;; There's no end gap because when a alignment is requested, |
|
102
|
;;; the requested size must also be a multiple of the alignment. |
|
103
|
|
|
104
|
(defconstant +array-header-size+ |
|
105
|
(* sb-vm:vector-data-offset sb-vm:n-word-bytes)) |
|
106
|
|
|
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))) |
|
112
|
(case upgraded-type |
|
113
|
((nil t) (error "~A is not a specializable array element type" type)) |
|
114
|
(t |
|
115
|
#+#.(cl:if (cl:find-symbol "%VECTOR-WIDETAG-AND-N-BITS" "SB-IMPL") |
|
116
|
'(and) '(or)) |
|
117
|
(sb-impl::%vector-widetag-and-n-bits type) |
|
118
|
#+#.(cl:if (cl:find-symbol "%VECTOR-WIDETAG-AND-N-BITS-SHIFT" "SB-IMPL") |
|
119
|
'(and) '(or)) |
|
120
|
(multiple-value-bind (widetag shift) |
|
121
|
(sb-impl::%vector-widetag-and-n-bits-shift type) |
|
122
|
(values widetag (expt 2 (- shift 3)))))))) |
|
123
|
|
|
124
|
(declaim (inline align)) |
|
125
|
(defun align (size boundary) |
|
126
|
(* boundary |
|
127
|
(ceiling size boundary))) |
|
128
|
|
|
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) |
|
136
|
(deref box)))) |
|
137
|
|
|
138
|
(defun %allocate-static-vector (length element-type alignment) |
|
139
|
(declare (type (unsigned-byte 16) alignment)) |
|
140
|
(flet ((allocation-sizes (length widetag n-bytes) |
|
141
|
(values |
|
142
|
;; We're allocating two headers: one for SBCL and |
|
143
|
;; the other one for our bookkeeping. |
|
144
|
(align (* 2 +array-header-size+) alignment) |
|
145
|
;; Align data size. |
|
146
|
(align |
|
147
|
(* (if (= widetag sb-vm:simple-character-string-widetag) |
|
148
|
(1+ length) ; for the final #\Nul |
|
149
|
length) |
|
150
|
n-bytes) |
|
151
|
alignment)))) |
|
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 ) |
|
159
|
(lisp-header-offset |
|
160
|
(- data-offset +array-header-size+)) |
|
161
|
(lisp-header-pointer |
|
162
|
(sb-sys:sap+ foreign-block lisp-header-offset)) |
|
163
|
(extra-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))))))) |
|
177
|
|
|
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) |
|
183
|
sb-vm:lowtag-mask)) |
|
184
|
|
|
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) |
|
191
|
+array-header-size+ |
|
192
|
offset))) |
|
193
|
|
|
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+))) |
|
200
|
(start-offset |
|
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))))) |
|
203
|
(values)) |
|
204
|
|
|
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)) |
|
220
|
(unwind-protect |
|
221
|
(sb-sys:with-local-interrupts ,@body) |
|
222
|
(when ,var (free-static-vector ,var)))))))) |
|
223
|
|
|
224
|
|
|
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))) |
|
233
|
|
|
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)))) |
|
242
|
|
|
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"))) |
|
249
|
|
|
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))) |
|
259
|
|
|
260
|
(defconstant +default-alignment+ 16) |
|
261
|
(defconstant +max-alignment+ 4096) |
|
262
|
|
|
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) |
|
272
|
(optimize speed)) |
|
273
|
(check-arguments length element-type initial-element initial-element-p |
|
274
|
initial-contents initial-contents-p) |
|
275
|
(when alignp |
|
276
|
;; Check that the alignment is a power of 2 beteeen 16 and 4096. |
|
277
|
#+(and sbcl unix) |
|
278
|
(assert (and (<= +default-alignment+ alignment +max-alignment+) |
|
279
|
(= 1 (logcount alignment)))) |
|
280
|
#-(and sbcl unix) |
|
281
|
(error "Allocation alignment not supported on this implementation.")) |
|
282
|
;; TODO: fix %allocate-static-vector for all implementations |
|
283
|
(let ((vector |
|
284
|
(%allocate-static-vector length element-type |
|
285
|
#+sbcl |
|
286
|
(or alignment +default-alignment+)))) |
|
287
|
(if initial-element-p |
|
288
|
(fill vector initial-element) |
|
289
|
(replace vector initial-contents)))) |
|
290
|
|
|
291
|
(defmacro with-static-vectors (((var length &rest args) &rest more-clauses) |
|
292
|
&body body) |
|
293
|
"Allocate multiple static vectors at once." |
|
294
|
`(with-static-vector (,var ,length ,@args) |
|
295
|
,@(if more-clauses |
|
296
|
`((with-static-vectors ,more-clauses |
|
297
|
,@body)) |
|
298
|
body))) |
|
299
|
|