1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; types.lisp --- User-defined CFFI types.
5 ;;; Copyright (C) 2005-2006, James Bielman <jamesjb@jamesjb.com>
6 ;;; Copyright (C) 2005-2007, Luis Oliveira <loliveira@common-lisp.net>
8 ;;; Permission is hereby granted, free of charge, to any person
9 ;;; obtaining a copy of this software and associated documentation
10 ;;; files (the "Software"), to deal in the Software without
11 ;;; restriction, including without limitation the rights to use, copy,
12 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
13 ;;; of the Software, and to permit persons to whom the Software is
14 ;;; furnished to do so, subject to the following conditions:
16 ;;; The above copyright notice and this permission notice shall be
17 ;;; included in all copies or substantial portions of the Software.
19 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 ;;; DEALINGS IN THE SOFTWARE.
33 ;; NOTE: In the C standard there's a "signed-char":
34 ;; https://stackoverflow.com/questions/436513/char-signed-char-char-unsigned-char
35 ;; and "char" may be either signed or unsigned, i.e. treating it as a small int
36 ;; is not wise. At the level of CFFI we can safely ignore this and assume that
37 ;; :char is mapped to "signed-char" by the CL implementation under us.
38 (define-built-in-foreign-type :char
)
39 (define-built-in-foreign-type :unsigned-char
)
40 (define-built-in-foreign-type :short
)
41 (define-built-in-foreign-type :unsigned-short
)
42 (define-built-in-foreign-type :int
)
43 (define-built-in-foreign-type :unsigned-int
)
44 (define-built-in-foreign-type :long
)
45 (define-built-in-foreign-type :unsigned-long
)
46 (define-built-in-foreign-type :float
)
47 (define-built-in-foreign-type :double
)
48 (define-built-in-foreign-type :void
)
50 #-cffi-sys
::no-long-long
52 (define-built-in-foreign-type :long-long
)
53 (define-built-in-foreign-type :unsigned-long-long
))
55 ;;; Define emulated LONG-LONG types. Needs checking whether we're
56 ;;; using the right sizes on various platforms.
58 ;;; A possibly better, certainly faster though more intrusive,
59 ;;; alternative is available here:
60 ;;; <http://article.gmane.org/gmane.lisp.cffi.devel/1091>
61 #+cffi-sys
::no-long-long
62 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
63 (defclass emulated-llong-type
(foreign-type) ())
64 (defmethod foreign-type-size ((tp emulated-llong-type
)) 8)
65 (defmethod foreign-type-alignment ((tp emulated-llong-type
))
66 ;; better than assuming that the alignment is 8
67 (foreign-type-alignment :long
))
68 (defmethod aggregatep ((tp emulated-llong-type
)) nil
)
70 (define-foreign-type emulated-llong
(emulated-llong-type)
72 (:simple-parser
:long-long
))
74 (define-foreign-type emulated-ullong
(emulated-llong-type)
76 (:simple-parser
:unsigned-long-long
))
78 (defmethod canonicalize ((tp emulated-llong
)) :long-long
)
79 (defmethod unparse-type ((tp emulated-llong
)) :long-long
)
80 (defmethod canonicalize ((tp emulated-ullong
)) :unsigned-long-long
)
81 (defmethod unparse-type ((tp emulated-ullong
)) :unsigned-long-long
)
83 (defun %emulated-mem-ref-64
(ptr type offset
)
84 (let ((value #+big-endian
85 (+ (ash (mem-ref ptr
:unsigned-long offset
) 32)
86 (mem-ref ptr
:unsigned-long
(+ offset
4)))
88 (+ (mem-ref ptr
:unsigned-long offset
)
89 (ash (mem-ref ptr
:unsigned-long
(+ offset
4)) 32))))
90 (if (and (eq type
:long-long
) (logbitp 63 value
))
91 (lognot (logxor value
#xFFFFFFFFFFFFFFFF
))
94 (defun %emulated-mem-set-64
(value ptr type offset
)
95 (when (and (eq type
:long-long
) (minusp value
))
96 (setq value
(lognot (logxor value
#xFFFFFFFFFFFFFFFF
))))
97 (%mem-set
(ldb (byte 32 0) value
) ptr
:unsigned-long
98 #+big-endian
(+ offset
4) #+little-endian offset
)
99 (%mem-set
(ldb (byte 32 32) value
) ptr
:unsigned-long
100 #+big-endian offset
#+little-endian
(+ offset
4))
103 ;;; When some lisp other than SCL supports :long-double we should
104 ;;; use #-cffi-sys::no-long-double here instead.
105 #+(and (or ecl scl
) long-float
) (define-built-in-foreign-type :long-double
)
107 (defparameter *possible-float-types
* '(:float
:double
:long-double
))
109 (defparameter *other-builtin-types
* '(:pointer
:void
)
110 "List of types other than integer or float built in to CFFI.")
112 (defparameter *built-in-integer-types
*
114 cffi
:*built-in-foreign-types
*
115 (append *possible-float-types
* *other-builtin-types
*))
116 "List of integer types supported by CFFI.")
118 (defparameter *built-in-float-types
*
120 cffi
:*built-in-foreign-types
*
121 (append *built-in-integer-types
* *other-builtin-types
*))
122 "List of real float types supported by CFFI.")
124 ;;;# Foreign Pointers
126 (define-compiler-macro inc-pointer
(&whole form pointer offset
)
127 (if (and (constantp offset
)
128 (eql 0 (eval offset
)))
132 (define-modify-macro incf-pointer
(&optional
(offset 1)) inc-pointer
)
134 (defun mem-ref (ptr type
&optional
(offset 0))
135 "Return the value of TYPE at OFFSET bytes from PTR. If TYPE is aggregate,
136 we don't return its 'value' but a pointer to it, which is PTR itself."
137 (let* ((parsed-type (parse-type type
))
138 (ctype (canonicalize parsed-type
)))
139 #+cffi-sys
::no-long-long
140 (when (member ctype
'(:long-long
:unsigned-long-long
))
142 (translate-from-foreign (%emulated-mem-ref-64 ptr ctype offset
)
145 (if (aggregatep parsed-type
)
146 (if (bare-struct-type-p parsed-type
)
147 (inc-pointer ptr offset
)
148 (translate-from-foreign (inc-pointer ptr offset
) parsed-type
))
149 (translate-from-foreign (%mem-ref ptr ctype offset
) parsed-type
))))
151 (define-compiler-macro mem-ref
(&whole form ptr type
&optional
(offset 0))
152 "Compiler macro to open-code MEM-REF when TYPE is constant."
154 (let* ((parsed-type (parse-type (eval type
)))
155 (ctype (canonicalize parsed-type
)))
156 ;; Bail out when using emulated long long types.
157 #+cffi-sys
::no-long-long
158 (when (member ctype
'(:long-long
:unsigned-long-long
))
159 (return-from mem-ref form
))
160 (if (aggregatep parsed-type
)
161 (if (bare-struct-type-p parsed-type
)
162 `(inc-pointer ,ptr
,offset
)
163 (expand-from-foreign `(inc-pointer ,ptr
,offset
) parsed-type
))
164 (expand-from-foreign `(%mem-ref
,ptr
,ctype
,offset
) parsed-type
)))
167 (defun mem-set (value ptr type
&optional
(offset 0))
168 "Set the value of TYPE at OFFSET bytes from PTR to VALUE."
169 (let* ((ptype (parse-type type
))
170 (ctype (canonicalize ptype
)))
171 #+cffi-sys
::no-long-long
172 (when (or (eq ctype
:long-long
) (eq ctype
:unsigned-long-long
))
174 (%emulated-mem-set-64
(translate-to-foreign value ptype
)
176 (if (aggregatep ptype
) ; XXX: backwards incompatible?
177 (translate-into-foreign-memory value ptype
(inc-pointer ptr offset
))
178 (%mem-set
(translate-to-foreign value ptype
) ptr ctype offset
))))
180 (define-setf-expander mem-ref
(ptr type
&optional
(offset 0) &environment env
)
181 "SETF expander for MEM-REF that doesn't rebind TYPE.
182 This is necessary for the compiler macro on MEM-SET to be able
183 to open-code (SETF MEM-REF) forms."
184 (multiple-value-bind (dummies vals newval setter getter
)
185 (get-setf-expansion ptr env
)
186 (declare (ignore setter newval
))
187 ;; if either TYPE or OFFSET are constant, we avoid rebinding them
188 ;; so that the compiler macros on MEM-SET and %MEM-SET work.
189 (with-unique-names (store type-tmp offset-tmp
)
191 (append (unless (constantp type
) (list type-tmp
))
192 (unless (constantp offset
) (list offset-tmp
))
194 (append (unless (constantp type
) (list type
))
195 (unless (constantp offset
) (list offset
))
199 (mem-set ,store
,getter
200 ,@(if (constantp type
) (list type
) (list type-tmp
))
201 ,@(if (constantp offset
) (list offset
) (list offset-tmp
)))
204 ,@(if (constantp type
) (list type
) (list type-tmp
))
205 ,@(if (constantp offset
) (list offset
) (list offset-tmp
)))))))
207 (define-compiler-macro mem-set
208 (&whole form value ptr type
&optional
(offset 0))
209 "Compiler macro to open-code (SETF MEM-REF) when type is constant."
211 (let* ((parsed-type (parse-type (eval type
)))
212 (ctype (canonicalize parsed-type
)))
213 ;; Bail out when using emulated long long types.
214 #+cffi-sys
::no-long-long
215 (when (member ctype
'(:long-long
:unsigned-long-long
))
216 (return-from mem-set form
))
217 (if (aggregatep parsed-type
)
218 (expand-into-foreign-memory
219 value parsed-type
`(inc-pointer ,ptr
,offset
))
220 `(%mem-set
,(expand-to-foreign value parsed-type
)
221 ,ptr
,ctype
,offset
)))
224 ;;;# Dereferencing Foreign Arrays
226 ;;; Maybe this should be named MEM-SVREF? [2007-02-28 LO]
227 (defun mem-aref (ptr type
&optional
(index 0))
228 "Like MEM-REF except for accessing 1d arrays."
229 (mem-ref ptr type
(* index
(foreign-type-size type
))))
231 (define-compiler-macro mem-aref
(&whole form ptr type
&optional
(index 0))
232 "Compiler macro to open-code MEM-AREF when TYPE (and eventually INDEX)."
234 (if (constantp index
)
236 ,(* (eval index
) (foreign-type-size (eval type
))))
237 `(mem-ref ,ptr
,type
(* ,index
,(foreign-type-size (eval type
)))))
240 (define-setf-expander mem-aref
(ptr type
&optional
(index 0) &environment env
)
241 "SETF expander for MEM-AREF."
242 (multiple-value-bind (dummies vals newval setter getter
)
243 (get-setf-expansion ptr env
)
244 (declare (ignore setter newval
))
245 ;; we avoid rebinding type and index, if possible (and if type is not
246 ;; constant, we don't bother about the index), so that the compiler macros
247 ;; on MEM-SET or %MEM-SET can work.
248 (with-unique-names (store type-tmp index-tmp
)
250 (append (unless (constantp type
)
252 (unless (and (constantp type
) (constantp index
))
255 (append (unless (constantp type
)
257 (unless (and (constantp type
) (constantp index
))
261 ;; Here we'll try to calculate the offset from the type and index,
262 ;; or if not possible at least get the type size early.
264 ,(if (constantp type
)
265 (if (constantp index
)
266 `(mem-set ,store
,getter
,type
267 ,(* (eval index
) (foreign-type-size (eval type
))))
268 `(mem-set ,store
,getter
,type
269 (* ,index-tmp
,(foreign-type-size (eval type
)))))
270 `(mem-set ,store
,getter
,type-tmp
271 (* ,index-tmp
(foreign-type-size ,type-tmp
))))
274 ,@(if (constantp type
)
277 ,@(if (and (constantp type
) (constantp index
))
279 (list index-tmp
)))))))
281 (defmethod translate-into-foreign-memory
282 (value (type foreign-pointer-type
) pointer
)
283 (setf (mem-aref pointer
:pointer
) value
))
285 (defmethod translate-into-foreign-memory
286 (value (type foreign-built-in-type
) pointer
)
287 (setf (mem-aref pointer
(unparse-type type
)) value
))
289 (defun mem-aptr (ptr type
&optional
(index 0))
290 "The pointer to the element."
291 (inc-pointer ptr
(* index
(foreign-type-size type
))))
293 (define-compiler-macro mem-aptr
(&whole form ptr type
&optional
(index 0))
294 "The pointer to the element."
295 (cond ((not (constantp type
))
297 ((not (constantp index
))
298 `(inc-pointer ,ptr
(* ,index
,(foreign-type-size (eval type
)))))
299 ((zerop (eval index
))
302 `(inc-pointer ,ptr
,(* (eval index
)
303 (foreign-type-size (eval type
)))))))
305 (define-foreign-type foreign-array-type
()
306 ((dimensions :reader dimensions
:initarg
:dimensions
)
307 (element-type :reader element-type
:initarg
:element-type
))
308 (:actual-type
:pointer
))
310 (defmethod aggregatep ((type foreign-array-type
))
313 (defmethod print-object ((type foreign-array-type
) stream
)
314 "Print a FOREIGN-ARRAY-TYPE instance to STREAM unreadably."
315 (print-unreadable-object (type stream
:type t
:identity nil
)
316 (format stream
"~S ~S" (element-type type
) (dimensions type
))))
318 (defun array-element-size (array-type)
319 (foreign-type-size (element-type array-type
)))
321 (defmethod foreign-type-size ((type foreign-array-type
))
322 (* (array-element-size type
) (reduce #'* (dimensions type
))))
324 (defmethod foreign-type-alignment ((type foreign-array-type
))
325 (foreign-type-alignment (element-type type
)))
327 (define-parse-method :array
(element-type &rest dimensions
)
328 (assert (plusp (length dimensions
)))
329 (make-instance 'foreign-array-type
330 :element-type element-type
331 :dimensions dimensions
))
333 (defun indexes-to-row-major-index (dimensions &rest subscripts
)
334 (apply #'+ (maplist (lambda (x y
)
335 (* (car x
) (apply #'* (cdr y
))))
339 (defun row-major-index-to-indexes (index dimensions
)
340 (loop with idx
= index
341 with rank
= (length dimensions
)
342 with indexes
= (make-list rank
)
343 for dim-index from
(- rank
1) downto
0 do
344 (setf (values idx
(nth dim-index indexes
))
345 (floor idx
(nth dim-index dimensions
)))
346 finally
(return indexes
)))
348 (defun foreign-alloc (type &key
(initial-element nil initial-element-p
)
349 (initial-contents nil initial-contents-p
)
350 (count 1 count-p
) null-terminated-p
)
351 "Allocate enough memory to hold COUNT objects of type TYPE. If
352 INITIAL-ELEMENT is supplied, each element of the newly allocated
353 memory is initialized with its value. If INITIAL-CONTENTS is supplied,
354 each of its elements will be used to initialize the contents of the
355 newly allocated memory."
356 (let (contents-length)
357 ;; Some error checking, etc...
358 (when (and null-terminated-p
359 (not (eq (canonicalize-foreign-type type
) :pointer
)))
360 (error "Cannot use :NULL-TERMINATED-P with non-pointer types."))
361 (when (and initial-element-p initial-contents-p
)
362 (error "Cannot specify both :INITIAL-ELEMENT and :INITIAL-CONTENTS"))
363 (when initial-contents-p
364 (setq contents-length
(length initial-contents
))
366 (assert (>= count contents-length
))
367 (setq count contents-length
)))
368 ;; Everything looks good.
369 (let ((ptr (%foreign-alloc
(* (foreign-type-size type
)
370 (if null-terminated-p
(1+ count
) count
)))))
371 (when initial-element-p
373 (setf (mem-aref ptr type i
) initial-element
)))
374 (when initial-contents-p
375 (dotimes (i contents-length
)
376 (setf (mem-aref ptr type i
) (elt initial-contents i
))))
377 (when null-terminated-p
378 (setf (mem-aref ptr
:pointer count
) (null-pointer)))
381 ;;; Simple compiler macro that kicks in when TYPE is constant and only
382 ;;; the COUNT argument is passed. (Note: hard-coding the type's size
383 ;;; into the fasl will likely break CLISP fasl cross-platform
385 (define-compiler-macro foreign-alloc
(&whole form type
&rest args
386 &key
(count 1 count-p
) &allow-other-keys
)
387 (if (or (and count-p
(<= (length args
) 2)) (null args
))
389 ((and (constantp type
) (constantp count
))
390 `(%foreign-alloc
,(* (eval count
) (foreign-type-size (eval type
)))))
392 `(%foreign-alloc
(* ,count
,(foreign-type-size (eval type
)))))
396 (defun lisp-array-to-foreign (array pointer array-type
)
397 "Copy elements from a Lisp array to POINTER. ARRAY-TYPE must be a CFFI array
399 (let* ((type (ensure-parsed-base-type array-type
))
400 (el-type (element-type type
))
401 (dimensions (dimensions type
)))
402 (loop with foreign-type-size
= (array-element-size type
)
403 with size
= (reduce #'* dimensions
)
404 for i from
0 below size
405 for offset
= (* i foreign-type-size
)
406 for element
= (apply #'aref array
407 (row-major-index-to-indexes i dimensions
))
408 do
(setf (mem-ref pointer el-type offset
) element
))))
410 (defun foreign-array-to-lisp (pointer array-type
&rest make-array-args
)
411 "Copy elements from pointer into a Lisp array. ARRAY-TYPE must be a CFFI array
412 type; the type of the resulting Lisp array can be defined in MAKE-ARRAY-ARGS
413 that are then passed to MAKE-ARRAY. If POINTER is a null pointer, returns NIL."
414 (unless (null-pointer-p pointer
)
415 (let* ((type (ensure-parsed-base-type array-type
))
416 (el-type (element-type type
))
417 (dimensions (dimensions type
))
418 (array (apply #'make-array dimensions make-array-args
)))
419 (loop with foreign-type-size
= (array-element-size type
)
420 with size
= (reduce #'* dimensions
)
421 for i from
0 below size
422 for offset
= (* i foreign-type-size
)
423 for element
= (mem-ref pointer el-type offset
)
424 do
(setf (apply #'aref array
425 (row-major-index-to-indexes i dimensions
))
429 (defun foreign-array-alloc (array array-type
)
430 "Allocate a foreign array containing the elements of lisp array.
431 The foreign array must be freed with foreign-array-free."
432 (check-type array array
)
433 (let* ((type (ensure-parsed-base-type array-type
))
434 (ptr (foreign-alloc (element-type type
)
435 :count
(reduce #'* (dimensions type
)))))
436 (lisp-array-to-foreign array ptr array-type
)
439 (defun foreign-array-free (ptr)
440 "Free a foreign array allocated by foreign-array-alloc."
443 (defmacro with-foreign-array
((var lisp-array array-type
) &body body
)
444 "Bind var to a foreign array containing lisp-array elements in body."
445 (with-unique-names (type)
446 `(let ((,type
(ensure-parsed-base-type ,array-type
)))
447 (with-foreign-pointer (,var
(* (reduce #'* (dimensions ,type
))
448 (array-element-size ,type
)))
449 (lisp-array-to-foreign ,lisp-array
,var
,array-type
)
452 (defun foreign-aref (ptr array-type
&rest indexes
)
453 (let* ((type (ensure-parsed-base-type array-type
))
454 (offset (* (array-element-size type
)
455 (apply #'indexes-to-row-major-index
456 (dimensions type
) indexes
))))
457 (mem-ref ptr
(element-type type
) offset
)))
459 (defun (setf foreign-aref
) (value ptr array-type
&rest indexes
)
460 (let* ((type (ensure-parsed-base-type array-type
))
461 (offset (* (array-element-size type
)
462 (apply #'indexes-to-row-major-index
463 (dimensions type
) indexes
))))
464 (setf (mem-ref ptr
(element-type type
) offset
) value
)))
466 ;;; Automatic translations for the :ARRAY type. Notice that these
467 ;;; translators will also invoke the appropriate translators for for
468 ;;; each of the array's elements since that's the normal behaviour of
469 ;;; the FOREIGN-ARRAY-* operators, but there's a FIXME: **it doesn't
472 ;;; This used to be in a separate type but let's experiment with just
473 ;;; one type for a while. [2008-12-30 LO]
475 ;;; FIXME: those ugly invocations of UNPARSE-TYPE suggest that these
476 ;;; foreign array operators should take the type and dimention
477 ;;; arguments "unboxed". [2008-12-31 LO]
479 (defmethod translate-to-foreign (array (type foreign-array-type
))
480 (foreign-array-alloc array
(unparse-type type
)))
482 (defmethod translate-aggregate-to-foreign (ptr value
(type foreign-array-type
))
483 (lisp-array-to-foreign value ptr
(unparse-type type
)))
485 (defmethod translate-from-foreign (pointer (type foreign-array-type
))
486 (foreign-array-to-lisp pointer
(unparse-type type
)))
488 (defmethod free-translated-object (pointer (type foreign-array-type
) param
)
489 (declare (ignore param
))
490 (foreign-array-free pointer
))
492 ;;;# Foreign Structures
494 ;;;## Foreign Structure Slots
496 (defgeneric foreign-struct-slot-pointer
(ptr slot
)
498 "Get the address of SLOT relative to PTR."))
500 (defgeneric foreign-struct-slot-pointer-form
(ptr slot
)
502 "Return a form to get the address of SLOT in PTR."))
504 (defgeneric foreign-struct-slot-value
(ptr slot
)
506 "Return the value of SLOT in structure PTR."))
508 (defgeneric (setf foreign-struct-slot-value
) (value ptr slot
)
510 "Set the value of a SLOT in structure PTR."))
512 (defgeneric foreign-struct-slot-value-form
(ptr slot
)
514 "Return a form to get the value of SLOT in struct PTR."))
516 (defgeneric foreign-struct-slot-set-form
(value ptr slot
)
518 "Return a form to set the value of SLOT in struct PTR."))
520 (defclass foreign-struct-slot
()
521 ((name :initarg
:name
:reader slot-name
)
522 (offset :initarg
:offset
:accessor slot-offset
)
523 ;; FIXME: the type should probably be parsed?
524 (type :initarg
:type
:accessor slot-type
))
525 (:documentation
"Base class for simple and aggregate slots."))
527 (defmethod foreign-struct-slot-pointer (ptr (slot foreign-struct-slot
))
528 "Return the address of SLOT relative to PTR."
529 (inc-pointer ptr
(slot-offset slot
)))
531 (defmethod foreign-struct-slot-pointer-form (ptr (slot foreign-struct-slot
))
532 "Return a form to get the address of SLOT relative to PTR."
533 (let ((offset (slot-offset slot
)))
536 `(inc-pointer ,ptr
,offset
))))
538 (defun foreign-slot-names (type)
539 "Returns a list of TYPE's slot names in no particular order."
540 (loop for value being the hash-values
541 in
(slots (ensure-parsed-base-type type
))
542 collect
(slot-name value
)))
546 (defclass simple-struct-slot
(foreign-struct-slot)
548 (:documentation
"Non-aggregate structure slots."))
550 (defmethod foreign-struct-slot-value (ptr (slot simple-struct-slot
))
551 "Return the value of a simple SLOT from a struct at PTR."
552 (mem-ref ptr
(slot-type slot
) (slot-offset slot
)))
554 (defmethod foreign-struct-slot-value-form (ptr (slot simple-struct-slot
))
555 "Return a form to get the value of a slot from PTR."
556 `(mem-ref ,ptr
',(slot-type slot
) ,(slot-offset slot
)))
558 (defmethod (setf foreign-struct-slot-value
) (value ptr
(slot simple-struct-slot
))
559 "Set the value of a simple SLOT to VALUE in PTR."
560 (setf (mem-ref ptr
(slot-type slot
) (slot-offset slot
)) value
))
562 (defmethod foreign-struct-slot-set-form (value ptr
(slot simple-struct-slot
))
563 "Return a form to set the value of a simple structure slot."
564 `(setf (mem-ref ,ptr
',(slot-type slot
) ,(slot-offset slot
)) ,value
))
566 ;;;### Aggregate Slots
568 (defclass aggregate-struct-slot
(foreign-struct-slot)
569 ((count :initarg
:count
:accessor slot-count
))
570 (:documentation
"Aggregate structure slots."))
572 ;;; Since MEM-REF returns a pointer for struct types we are able to
573 ;;; chain together slot names when accessing slot values in nested
575 (defmethod foreign-struct-slot-value (ptr (slot aggregate-struct-slot
))
576 "Return a pointer to SLOT relative to PTR."
577 (convert-from-foreign (inc-pointer ptr
(slot-offset slot
))
580 (defmethod foreign-struct-slot-value-form (ptr (slot aggregate-struct-slot
))
581 "Return a form to get the value of SLOT relative to PTR."
582 `(convert-from-foreign (inc-pointer ,ptr
,(slot-offset slot
))
585 (defmethod translate-aggregate-to-foreign (ptr value
(type foreign-struct-type
))
586 ;;; FIXME: use the block memory interface instead.
587 (loop for i below
(foreign-type-size type
)
588 do
(%mem-set
(%mem-ref value
:char i
) ptr
:char i
)))
590 (defmethod (setf foreign-struct-slot-value
)
591 (value ptr
(slot aggregate-struct-slot
))
592 "Set the value of an aggregate SLOT to VALUE in PTR."
593 (translate-aggregate-to-foreign (inc-pointer ptr
(slot-offset slot
))
595 (parse-type (slot-type slot
))))
597 (defmethod foreign-struct-slot-set-form (value ptr
(slot aggregate-struct-slot
))
598 "Return a form to get the value of an aggregate SLOT relative to PTR."
599 `(translate-aggregate-to-foreign (inc-pointer ,ptr
,(slot-offset slot
))
601 ,(parse-type (slot-type slot
))))
603 ;;;## Defining Foreign Structures
605 (defun make-struct-slot (name offset type count
)
606 "Make the appropriate type of structure slot."
607 ;; If TYPE is an aggregate type or COUNT is >1, create an
608 ;; AGGREGATE-STRUCT-SLOT, otherwise a SIMPLE-STRUCT-SLOT.
609 (if (or (> count
1) (aggregatep (parse-type type
)))
610 (make-instance 'aggregate-struct-slot
:offset offset
:type type
611 :name name
:count count
)
612 (make-instance 'simple-struct-slot
:offset offset
:type type
615 (defun parse-deprecated-struct-type (name struct-or-union
)
616 (check-type struct-or-union
(member :struct
:union
))
617 (let* ((struct-type-name `(,struct-or-union
,name
))
618 (struct-type (parse-type struct-type-name
)))
619 (simple-style-warning
620 "bare references to struct types are deprecated. ~
621 Please use ~S or ~S instead."
622 `(:pointer
,struct-type-name
) struct-type-name
)
623 (make-instance (class-of struct-type
)
624 :alignment
(alignment struct-type
)
625 :size
(size struct-type
)
626 :slots
(slots struct-type
)
627 :name
(name struct-type
)
630 ;;; Regarding structure alignment, the following ABIs were checked:
631 ;;; - System-V ABI: x86, x86-64, ppc, arm, mips and itanium. (more?)
632 ;;; - Mac OS X ABI Function Call Guide: ppc32, ppc64 and x86.
636 ;;; 1. "An entire structure or union object is aligned on the same
637 ;;; boundary as its most strictly aligned member."
639 ;;; 2. "Each member is assigned to the lowest available offset with
640 ;;; the appropriate alignment. This may require internal
641 ;;; padding, depending on the previous member."
643 ;;; 3. "A structure's size is increased, if necessary, to make it a
644 ;;; multiple of the alignment. This may require tail padding,
645 ;;; depending on the last member."
647 ;;; Special cases from darwin/ppc32's ABI:
648 ;;; http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/index.html
650 ;;; 4. "The embedding alignment of the first element in a data
651 ;;; structure is equal to the element's natural alignment."
653 ;;; 5. "For subsequent elements that have a natural alignment
654 ;;; greater than 4 bytes, the embedding alignment is 4, unless
655 ;;; the element is a vector." (note: this applies for
658 ;; FIXME: get a better name for this. --luis
659 (defun get-alignment (type alignment-type firstp
)
660 "Return alignment for TYPE according to ALIGNMENT-TYPE."
661 (declare (ignorable firstp
))
662 (ecase alignment-type
663 (:normal
#-
(and darwin ppc
)
664 (foreign-type-alignment type
)
667 (foreign-type-alignment type
)
668 (min 4 (foreign-type-alignment type
))))))
670 (defun adjust-for-alignment (type offset alignment-type firstp
)
671 "Return OFFSET aligned properly for TYPE according to ALIGNMENT-TYPE."
672 (let* ((align (get-alignment type alignment-type firstp
))
673 (rem (mod offset align
)))
676 (+ offset
(- align rem
)))))
678 (defmacro with-tentative-type-definition
((name value namespace
) &body body
)
679 (once-only (name namespace
)
680 `(unwind-protect-case ()
682 (notice-foreign-type ,name
,value
,namespace
)
684 (:abort
(undefine-foreign-type ,name
,namespace
)))))
686 (defun notice-foreign-struct-definition (name options slots
)
687 "Parse and install a foreign structure definition."
688 (destructuring-bind (&key size
(class 'foreign-struct-type
))
690 (let ((struct (make-instance class
:name name
))
695 (with-tentative-type-definition (name struct
:struct
)
697 (dolist (slotdef slots
)
698 (destructuring-bind (slotname type
&key
(count 1) offset
) slotdef
699 (when (eq (canonicalize-foreign-type type
) :void
)
700 (simple-foreign-type-error type
:struct
701 "In struct ~S: void type not allowed in field ~S"
705 (adjust-for-alignment type current-offset
:normal firstp
)))
706 (let* ((slot (make-struct-slot slotname current-offset type count
))
707 (align (get-alignment (slot-type slot
) :normal firstp
)))
708 (setf (gethash slotname
(slots struct
)) slot
)
709 (when (> align max-align
)
710 (setq max-align align
)))
711 (incf current-offset
(* count
(foreign-type-size type
)))
712 (setf max-offset
(max max-offset current-offset
)))
714 ;; calculate padding and alignment
715 (setf (alignment struct
) max-align
) ; See point 1 above.
716 (let ((tail-padding (- max-align
(rem max-offset max-align
))))
717 (unless (= tail-padding max-align
) ; See point 3 above.
718 (incf max-offset tail-padding
)))
719 (setf (size struct
) (or size max-offset
))))))
721 (defun generate-struct-accessors (name conc-name slot-names
)
722 (loop with pointer-arg
= (symbolicate '#:pointer-to- name
)
723 for slot in slot-names
724 for accessor
= (symbolicate conc-name slot
)
725 collect
`(defun ,accessor
(,pointer-arg
)
726 (foreign-slot-value ,pointer-arg
'(:struct
,name
) ',slot
))
727 collect
`(defun (setf ,accessor
) (value ,pointer-arg
)
728 (foreign-slot-set value
,pointer-arg
'(:struct
,name
) ',slot
))))
730 (define-parse-method :struct
(name)
731 (funcall (find-type-parser name
:struct
)))
733 (defvar *defcstruct-hook
* nil
)
735 (defmacro defcstruct
(name-and-options &body fields
)
736 "Define the layout of a foreign structure."
737 (discard-docstring fields
)
738 (destructuring-bind (name . options
)
739 (ensure-list name-and-options
)
740 (let ((conc-name (getf options
:conc-name
)))
741 (remf options
:conc-name
)
742 (unless (getf options
:class
) (setf (getf options
:class
) (symbolicate name
'-tclass
)))
743 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
744 ;; m-f-s-t could do with this with mop:ensure-class.
745 ,(when-let (class (getf options
:class
))
746 `(defclass ,class
(foreign-struct-type
747 translatable-foreign-type
)
749 (notice-foreign-struct-definition ',name
',options
',fields
)
751 (generate-struct-accessors name conc-name
752 (mapcar #'car fields
)))
753 ,@(when *defcstruct-hook
*
754 ;; If non-nil, *defcstruct-hook* should be a function
755 ;; of the arguments that returns NIL or a list of
756 ;; forms to include in the expansion.
757 (apply *defcstruct-hook
* name-and-options fields
))
758 (define-parse-method ,name
()
759 (parse-deprecated-struct-type ',name
:struct
))
762 ;;;## Accessing Foreign Structure Slots
764 (defun get-slot-info (type slot-name
)
765 "Return the slot info for SLOT-NAME or raise an error."
766 (let* ((struct (ensure-parsed-base-type type
))
767 (info (gethash slot-name
(slots struct
))))
769 (simple-foreign-type-error type
:struct
770 "Undefined slot ~A in foreign type ~A."
774 (defun foreign-slot-pointer (ptr type slot-name
)
775 "Return the address of SLOT-NAME in the structure at PTR."
776 (foreign-struct-slot-pointer ptr
(get-slot-info type slot-name
)))
778 (define-compiler-macro foreign-slot-pointer
(&whole whole ptr type slot-name
)
779 (if (and (constantp type
) (constantp slot-name
))
780 (foreign-struct-slot-pointer-form
781 ptr
(get-slot-info (eval type
) (eval slot-name
)))
784 (defun foreign-slot-type (type slot-name
)
785 "Return the type of SLOT in a struct TYPE."
786 (slot-type (get-slot-info type slot-name
)))
788 (defun foreign-slot-offset (type slot-name
)
789 "Return the offset of SLOT in a struct TYPE."
790 (slot-offset (get-slot-info type slot-name
)))
792 (defun foreign-slot-count (type slot-name
)
793 "Return the number of items in SLOT in a struct TYPE."
794 (slot-count (get-slot-info type slot-name
)))
796 (defun foreign-slot-value (ptr type slot-name
)
797 "Return the value of SLOT-NAME in the foreign structure at PTR."
798 (foreign-struct-slot-value ptr
(get-slot-info type slot-name
)))
800 (define-compiler-macro foreign-slot-value
(&whole form ptr type slot-name
)
801 "Optimizer for FOREIGN-SLOT-VALUE when TYPE is constant."
802 (if (and (constantp type
) (constantp slot-name
))
803 (foreign-struct-slot-value-form
804 ptr
(get-slot-info (eval type
) (eval slot-name
)))
807 (define-setf-expander foreign-slot-value
(ptr type slot-name
&environment env
)
808 "SETF expander for FOREIGN-SLOT-VALUE."
809 (multiple-value-bind (dummies vals newval setter getter
)
810 (get-setf-expansion ptr env
)
811 (declare (ignore setter newval
))
812 (if (and (constantp type
) (constantp slot-name
))
813 ;; if TYPE and SLOT-NAME are constant we avoid rebinding them
814 ;; so that the compiler macro on FOREIGN-SLOT-SET works.
815 (with-unique-names (store)
821 (foreign-slot-set ,store
,getter
,type
,slot-name
)
823 `(foreign-slot-value ,getter
,type
,slot-name
)))
825 (with-unique-names (store slot-name-tmp type-tmp
)
827 (list* type-tmp slot-name-tmp dummies
)
828 (list* type slot-name vals
)
831 (foreign-slot-set ,store
,getter
,type-tmp
,slot-name-tmp
)
833 `(foreign-slot-value ,getter
,type-tmp
,slot-name-tmp
))))))
835 (defun foreign-slot-set (value ptr type slot-name
)
836 "Set the value of SLOT-NAME in a foreign structure."
837 (setf (foreign-struct-slot-value ptr
(get-slot-info type slot-name
)) value
))
839 (define-compiler-macro foreign-slot-set
840 (&whole form value ptr type slot-name
)
841 "Optimizer when TYPE and SLOT-NAME are constant."
842 (if (and (constantp type
) (constantp slot-name
))
843 (foreign-struct-slot-set-form
844 value ptr
(get-slot-info (eval type
) (eval slot-name
)))
847 (defmacro with-foreign-slots
((vars ptr type
) &body body
)
848 "Create local symbol macros for each var in VARS to reference
849 foreign slots in PTR of TYPE. Similar to WITH-SLOTS.
850 Each var can be of the form:
851 name name bound to slot of same name
852 (:pointer name) name bound to pointer to slot of same name
853 (name slot-name) name bound to slot-name
854 (name :pointer slot-name) name bound to pointer to slot-name"
855 (let ((ptr-var (gensym "PTR")))
856 `(let ((,ptr-var
,ptr
))
858 ,(loop :for var
:in vars
861 (let ((p1 (first var
)) (p2 (second var
)) (p3 (third var
)))
863 `(,p2
(foreign-slot-pointer ,ptr-var
',type
',p2
))
865 `(,p1
(foreign-slot-pointer ,ptr-var
',type
',p3
))
866 `(,p1
(foreign-slot-value ,ptr-var
',type
',p2
)))))
867 `(,var
(foreign-slot-value ,ptr-var
',type
',var
))))
870 ;;; We could add an option to define a struct instead of a class, in
871 ;;; the unlikely event someone needs something like that.
872 (defmacro define-c-struct-wrapper
(class-and-type supers
&optional slots
)
873 "Define a new class with CLOS slots matching those of a foreign
874 struct type. An INITIALIZE-INSTANCE method is defined which
875 takes a :POINTER initarg that is used to store the slots of a
876 foreign object. This pointer is only used for initialization and
879 CLASS-AND-TYPE is either a list of the form (class-name
880 struct-type) or a single symbol naming both. The class will
881 inherit SUPERS. If a list of SLOTS is specified, only those
882 slots will be defined and stored."
883 (destructuring-bind (class-name &optional
(struct-type (list :struct class-name
)))
884 (ensure-list class-and-type
)
885 (let ((slots (or slots
(foreign-slot-names struct-type
))))
887 (defclass ,class-name
,supers
888 ,(loop for slot in slots collect
889 `(,slot
:reader
,(format-symbol t
"~A-~A" class-name slot
))))
890 ;; This could be done in a parent class by using
891 ;; FOREIGN-SLOT-NAMES when instantiating but then the compiler
892 ;; macros wouldn't kick in.
893 (defmethod initialize-instance :after
((inst ,class-name
) &key pointer
)
894 (with-foreign-slots (,slots pointer
,struct-type
)
895 ,@(loop for slot in slots collect
896 `(setf (slot-value inst
',slot
) ,slot
))))
901 ;;; A union is a subclass of FOREIGN-STRUCT-TYPE in which all slots
902 ;;; have an offset of zero.
904 ;;; See also the notes regarding ABI requirements in
905 ;;; NOTICE-FOREIGN-STRUCT-DEFINITION
906 (defun notice-foreign-union-definition (name-and-options slots
)
907 "Parse and install a foreign union definition."
908 (destructuring-bind (name &key size
)
909 (ensure-list name-and-options
)
910 (let ((union (make-instance 'foreign-union-type
:name name
))
913 (with-tentative-type-definition (name union
:union
)
914 (dolist (slotdef slots
)
915 (destructuring-bind (slotname type
&key
(count 1)) slotdef
916 (when (eq (canonicalize-foreign-type type
) :void
)
917 (simple-foreign-type-error name
:struct
918 "In union ~S: void type not allowed in field ~S"
920 (let* ((slot (make-struct-slot slotname
0 type count
))
921 (size (* count
(foreign-type-size type
)))
922 (align (foreign-type-alignment (slot-type slot
))))
923 (setf (gethash slotname
(slots union
)) slot
)
924 (when (> size max-size
)
925 (setf max-size size
))
926 (when (> align max-align
)
927 (setf max-align align
)))))
928 (setf (size union
) (or size max-size
))
929 (setf (alignment union
) max-align
)))))
931 (define-parse-method :union
(name)
932 (funcall (find-type-parser name
:union
)))
934 (defmacro defcunion
(name-and-options &body fields
)
935 "Define the layout of a foreign union."
936 (discard-docstring fields
)
937 (destructuring-bind (name &key size
)
938 (ensure-list name-and-options
)
939 (declare (ignore size
))
940 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
941 (notice-foreign-union-definition ',name-and-options
',fields
)
942 (define-parse-method ,name
()
943 (parse-deprecated-struct-type ',name
:union
))
946 ;;;# Operations on Types
948 (defmethod foreign-type-alignment (type)
949 "Return the alignment in bytes of a foreign type."
950 (foreign-type-alignment (parse-type type
)))
952 (defmacro with-foreign-object
((var type
&optional
(count 1)) &body body
)
953 "Bind VAR to a pointer to COUNT objects of TYPE during BODY.
954 The buffer has dynamic extent and may be stack allocated."
955 `(with-foreign-pointer
956 (,var
,(if (constantp type
)
957 ;; with-foreign-pointer may benefit from constant folding:
958 (if (constantp count
)
959 (* (eval count
) (foreign-type-size (eval type
)))
960 `(* ,count
,(foreign-type-size (eval type
))))
961 `(* ,count
(foreign-type-size ,type
))))
964 (defmacro with-foreign-objects
(bindings &body body
)
966 `(with-foreign-object ,(car bindings
)
967 (with-foreign-objects ,(cdr bindings
)
971 ;;;## Anonymous Type Translators
973 ;;; (:wrapper :to-c some-function :from-c another-function)
975 ;;; TODO: We will need to add a FREE function to this as well I think.
978 (define-foreign-type foreign-type-wrapper
()
979 ((to-c :initarg
:to-c
:reader wrapper-to-c
)
980 (from-c :initarg
:from-c
:reader wrapper-from-c
))
981 (:documentation
"Wrapper type."))
983 (define-parse-method :wrapper
(base-type &key to-c from-c
)
984 (make-instance 'foreign-type-wrapper
985 :actual-type
(parse-type base-type
)
986 :to-c
(or to-c
'identity
)
987 :from-c
(or from-c
'identity
)))
989 (defmethod translate-to-foreign (value (type foreign-type-wrapper
))
990 (translate-to-foreign
991 (funcall (slot-value type
'to-c
) value
) (actual-type type
)))
993 (defmethod translate-from-foreign (value (type foreign-type-wrapper
))
994 (funcall (slot-value type
'from-c
)
995 (translate-from-foreign value
(actual-type type
))))
999 ;;; Boolean type. Maps to an :int by default. Only accepts integer types.
1000 (define-foreign-type foreign-boolean-type
()
1003 (define-parse-method :boolean
(&optional
(base-type :int
))
1005 'foreign-boolean-type
:actual-type
1006 (ecase (canonicalize-foreign-type base-type
)
1007 ((:char
:unsigned-char
:int
:unsigned-int
:long
:unsigned-long
1008 #-cffi-sys
::no-long-long
:long-long
1009 #-cffi-sys
::no-long-long
:unsigned-long-long
) base-type
))))
1011 (defmethod translate-to-foreign (value (type foreign-boolean-type
))
1014 (defmethod translate-from-foreign (value (type foreign-boolean-type
))
1015 (not (zerop value
)))
1017 (defmethod expand-to-foreign (value (type foreign-boolean-type
))
1018 "Optimization for the :boolean type."
1019 (if (constantp value
)
1020 (if (eval value
) 1 0)
1023 (defmethod expand-from-foreign (value (type foreign-boolean-type
))
1024 "Optimization for the :boolean type."
1025 (if (constantp value
) ; very unlikely, heh
1026 (not (zerop (eval value
)))
1027 `(not (zerop ,value
))))
1029 ;;; Boolean type that represents C99 _Bool
1030 (defctype :bool
(:boolean
:char
))
1032 ;;;# Typedefs for built-in types.
1034 (defctype :uchar
:unsigned-char
)
1035 (defctype :ushort
:unsigned-short
)
1036 (defctype :uint
:unsigned-int
)
1037 (defctype :ulong
:unsigned-long
)
1038 (defctype :llong
:long-long
)
1039 (defctype :ullong
:unsigned-long-long
)
1041 (defmacro defctype-matching
(name size-or-type base-types
&key
(match-by '=))
1042 (let* ((target-size (typecase size-or-type
1043 (integer size-or-type
)
1044 (t (foreign-type-size size-or-type
))))
1045 (matching-type (loop for type in base-types
1046 for size
= (foreign-type-size type
)
1047 when
(funcall match-by target-size size
)
1050 `(defctype ,name
,matching-type
)
1051 `(warn "Found no matching type of size ~d in~% ~a"
1052 ,target-size
',base-types
))))
1054 ;;; We try to define the :[u]int{8,16,32,64} types by looking at
1055 ;;; the sizes of the built-in integer types and defining typedefs.
1056 (macrolet ((match-types (sized-types base-types
)
1057 `(progn ,@(loop for
(name size-or-type
) in sized-types
1058 collect
`(defctype-matching ,name
,size-or-type
,base-types
)))))
1060 (match-types ((:int8
1) (:int16
2) (:int32
4) (:int64
8)
1062 (:char
:short
:int
:long
:long-long
))
1064 (match-types ((:uint8
1) (:uint16
2) (:uint32
4) (:uint64
8)
1065 (:uintptr
:pointer
))
1066 (:unsigned-char
:unsigned-short
:unsigned-int
:unsigned-long
1067 :unsigned-long-long
)))
1069 ;;; Pretty safe bets.
1070 (defctype :size
#+64-bit
:uint64
#+32-bit
:uint32
)
1071 (defctype :ssize
#+64-bit
:int64
#+32-bit
:int32
)
1072 (defctype :ptrdiff
:ssize
)
1073 (defctype :offset
#+(or 64-bit bsd
) :int64
#-
(or 64-bit bsd
) :int32
)