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 (define-built-in-foreign-type :char
)
34 (define-built-in-foreign-type :unsigned-char
)
35 (define-built-in-foreign-type :short
)
36 (define-built-in-foreign-type :unsigned-short
)
37 (define-built-in-foreign-type :int
)
38 (define-built-in-foreign-type :unsigned-int
)
39 (define-built-in-foreign-type :long
)
40 (define-built-in-foreign-type :unsigned-long
)
41 (define-built-in-foreign-type :float
)
42 (define-built-in-foreign-type :double
)
43 (define-built-in-foreign-type :void
)
45 #-cffi-features
:no-long-long
47 (define-built-in-foreign-type :long-long
)
48 (define-built-in-foreign-type :unsigned-long-long
))
50 ;;; When some lisp other than SCL supports :long-double we should
51 ;;; use #-cffi-features:no-long-double here instead.
52 #+(and scl long-float
) (define-built-in-foreign-type :long-double
)
56 (define-modify-macro incf-pointer
(&optional
(offset 1)) inc-pointer
)
58 (defun mem-ref (ptr type
&optional
(offset 0))
59 "Return the value of TYPE at OFFSET bytes from PTR. If TYPE is aggregate,
60 we don't return its 'value' but a pointer to it, which is PTR itself."
61 (let ((ptype (parse-type type
)))
62 (if (aggregatep ptype
)
63 (inc-pointer ptr offset
)
64 (let ((raw-value (%mem-ref ptr
(canonicalize ptype
) offset
)))
65 (translate-from-foreign raw-value ptype
)))))
67 (define-compiler-macro mem-ref
(&whole form ptr type
&optional
(offset 0))
68 "Compiler macro to open-code MEM-REF when TYPE is constant."
70 (let ((parsed-type (parse-type (eval type
))))
71 (if (aggregatep parsed-type
)
72 `(inc-pointer ,ptr
,offset
)
74 `(%mem-ref
,ptr
,(canonicalize parsed-type
) ,offset
)
78 (defun mem-set (value ptr type
&optional
(offset 0))
79 "Set the value of TYPE at OFFSET bytes from PTR to VALUE."
80 (let ((ptype (parse-type type
)))
81 (%mem-set
(translate-to-foreign value ptype
)
82 ptr
(canonicalize ptype
) offset
)))
84 (define-setf-expander mem-ref
(ptr type
&optional
(offset 0) &environment env
)
85 "SETF expander for MEM-REF that doesn't rebind TYPE.
86 This is necessary for the compiler macro on MEM-SET to be able
87 to open-code (SETF MEM-REF) forms."
88 (multiple-value-bind (dummies vals newval setter getter
)
89 (get-setf-expansion ptr env
)
90 (declare (ignore setter newval
))
91 ;; if either TYPE or OFFSET are constant, we avoid rebinding them
92 ;; so that the compiler macros on MEM-SET and %MEM-SET work.
93 (with-unique-names (store type-tmp offset-tmp
)
95 (append (unless (constantp type
) (list type-tmp
))
96 (unless (constantp offset
) (list offset-tmp
))
98 (append (unless (constantp type
) (list type
))
99 (unless (constantp offset
) (list offset
))
103 (mem-set ,store
,getter
104 ,@(if (constantp type
) (list type
) (list type-tmp
))
105 ,@(if (constantp offset
) (list offset
) (list offset-tmp
)))
108 ,@(if (constantp type
) (list type
) (list type-tmp
))
109 ,@(if (constantp offset
) (list offset
) (list offset-tmp
)))))))
111 (define-compiler-macro mem-set
112 (&whole form value ptr type
&optional
(offset 0))
113 "Compiler macro to open-code (SETF MEM-REF) when type is constant."
115 (let ((parsed-type (parse-type (eval type
))))
116 `(%mem-set
,(expand-to-foreign value parsed-type
) ,ptr
117 ,(canonicalize parsed-type
) ,offset
))
120 ;;;# Dereferencing Foreign Arrays
122 ;;; Maybe this should be named MEM-SVREF? [2007-02-28 LO]
123 (defun mem-aref (ptr type
&optional
(index 0))
124 "Like MEM-REF except for accessing 1d arrays."
125 (mem-ref ptr type
(* index
(foreign-type-size type
))))
127 (define-compiler-macro mem-aref
(&whole form ptr type
&optional
(index 0))
128 "Compiler macro to open-code MEM-AREF when TYPE (and eventually INDEX)."
130 (if (constantp index
)
132 ,(* (eval index
) (foreign-type-size (eval type
))))
133 `(mem-ref ,ptr
,type
(* ,index
,(foreign-type-size (eval type
)))))
136 (define-setf-expander mem-aref
(ptr type
&optional
(index 0) &environment env
)
137 "SETF expander for MEM-AREF."
138 (multiple-value-bind (dummies vals newval setter getter
)
139 (get-setf-expansion ptr env
)
140 (declare (ignore setter newval
))
141 ;; we avoid rebinding type and index, if possible (and if type is not
142 ;; constant, we don't bother about the index), so that the compiler macros
143 ;; on MEM-SET or %MEM-SET can work.
144 (with-unique-names (store type-tmp index-tmp
)
146 (append (unless (constantp type
)
148 (unless (and (constantp type
) (constantp index
))
151 (append (unless (constantp type
)
153 (unless (and (constantp type
) (constantp index
))
157 ;; Here we'll try to calculate the offset from the type and index,
158 ;; or if not possible at least get the type size early.
160 ,(if (constantp type
)
161 (if (constantp index
)
162 `(mem-set ,store
,getter
,type
163 ,(* (eval index
) (foreign-type-size (eval type
))))
164 `(mem-set ,store
,getter
,type
165 (* ,index-tmp
,(foreign-type-size (eval type
)))))
166 `(mem-set ,store
,getter
,type-tmp
167 (* ,index-tmp
(foreign-type-size ,type-tmp
))))
170 ,@(if (constantp type
)
173 ,@(if (and (constantp type
) (constantp index
))
175 (list index-tmp
)))))))
177 (define-foreign-type foreign-array-type
()
178 ((dimensions :reader dimensions
:initarg
:dimensions
)
179 (element-type :reader element-type
:initarg
:element-type
))
180 (:actual-type
:pointer
))
182 (defmethod print-object ((type foreign-array-type
) stream
)
183 "Print a FOREIGN-ARRAY-TYPE instance to STREAM unreadably."
184 (print-unreadable-object (type stream
:type t
:identity nil
)
185 (format stream
"~S ~S" (element-type type
) (dimensions type
))))
187 (define-parse-method :array
(element-type &rest dimensions
)
188 (make-instance 'foreign-array-type
189 :element-type element-type
190 :dimensions dimensions
))
192 (defun array-element-size (array-type)
193 (foreign-type-size (element-type array-type
)))
195 (defun indexes-to-row-major-index (dimensions &rest subscripts
)
196 (apply #'+ (maplist (lambda (x y
)
197 (* (car x
) (apply #'* (cdr y
))))
201 (defun row-major-index-to-indexes (index dimensions
)
202 (loop with idx
= index
203 with rank
= (length dimensions
)
204 with indexes
= (make-list rank
)
205 for dim-index from
(- rank
1) downto
0 do
206 (setf (values idx
(nth dim-index indexes
))
207 (floor idx
(nth dim-index dimensions
)))
208 finally
(return indexes
)))
210 (defun lisp-array-to-foreign (array pointer array-type
)
211 "Copy elements from a Lisp array to POINTER."
212 (let* ((type (follow-typedefs (parse-type array-type
)))
213 (el-type (element-type type
))
214 (dimensions (dimensions type
)))
215 (loop with foreign-type-size
= (array-element-size type
)
216 with size
= (reduce #'* dimensions
)
217 for i from
0 below size
218 for offset
= (* i foreign-type-size
)
219 for element
= (apply #'aref array
220 (row-major-index-to-indexes i dimensions
))
221 do
(setf (mem-ref pointer el-type offset
) element
))))
223 (defun foreign-array-to-lisp (pointer array-type
)
224 "Copy elements from ptr into a Lisp array. If POINTER is a null
225 pointer, returns NIL."
226 (unless (null-pointer-p pointer
)
227 (let* ((type (follow-typedefs (parse-type array-type
)))
228 (el-type (element-type type
))
229 (dimensions (dimensions type
))
230 (array (make-array dimensions
)))
231 (loop with foreign-type-size
= (array-element-size type
)
232 with size
= (reduce #'* dimensions
)
233 for i from
0 below size
234 for offset
= (* i foreign-type-size
)
235 for element
= (mem-ref pointer el-type offset
)
236 do
(setf (apply #'aref array
237 (row-major-index-to-indexes i dimensions
))
241 (defun foreign-array-alloc (array array-type
)
242 "Allocate a foreign array containing the elements of lisp array.
243 The foreign array must be freed with foreign-array-free."
244 (check-type array array
)
245 (let* ((type (follow-typedefs (parse-type array-type
)))
246 (ptr (foreign-alloc (element-type type
)
247 :count
(reduce #'* (dimensions type
)))))
248 (lisp-array-to-foreign array ptr array-type
)
251 (defun foreign-array-free (ptr)
252 "Free a foreign array allocated by foreign-array-alloc."
255 (defmacro with-foreign-array
((var lisp-array array-type
) &body body
)
256 "Bind var to a foreign array containing lisp-array elements in body."
257 (with-unique-names (type)
258 `(let ((,type
(follow-typedefs (parse-type ,array-type
))))
259 (with-foreign-pointer (,var
(* (reduce #'* (dimensions ,type
))
260 (array-element-size ,type
)))
261 (lisp-array-to-foreign ,lisp-array
,var
,array-type
)
264 (defun foreign-aref (ptr array-type
&rest indexes
)
265 (let* ((type (follow-typedefs (parse-type array-type
)))
266 (offset (* (array-element-size type
)
267 (apply #'indexes-to-row-major-index
268 (dimensions type
) indexes
))))
269 (mem-ref ptr
(element-type type
) offset
)))
271 (defun (setf foreign-aref
) (value ptr array-type
&rest indexes
)
272 (let* ((type (follow-typedefs (parse-type array-type
)))
273 (offset (* (array-element-size type
)
274 (apply #'indexes-to-row-major-index
275 (dimensions type
) indexes
))))
276 (setf (mem-ref ptr
(element-type type
) offset
) value
)))
278 ;;; This type has defined type translators to allocate and free the
279 ;;; array. It will also invoke type translators for each of the
280 ;;; array's element. **But it doesn't free them yet**
281 (define-foreign-type auto-array-type
(foreign-array-type)
284 (define-parse-method :auto-array
(element-type &rest dimensions
)
285 (assert (>= (length dimensions
) 1))
286 (make-instance 'auto-array-type
287 :element-type element-type
288 :dimensions dimensions
))
290 (defmethod translate-to-foreign (array (type auto-array-type
))
291 (foreign-array-alloc array
(unparse-type type
)))
293 (defmethod translate-from-foreign (pointer (type auto-array-type
))
294 (foreign-array-to-lisp pointer
(unparse-type type
)))
296 (defmethod free-translated-object (pointer (type auto-array-type
) param
)
297 (declare (ignore param
))
298 (foreign-array-free pointer
))
300 ;;;# Foreign Structures
302 ;;;## Foreign Structure Slots
304 (defgeneric foreign-struct-slot-pointer
(ptr slot
)
306 "Get the address of SLOT relative to PTR."))
308 (defgeneric foreign-struct-slot-pointer-form
(ptr slot
)
310 "Return a form to get the address of SLOT in PTR."))
312 (defgeneric foreign-struct-slot-value
(ptr slot
)
314 "Return the value of SLOT in structure PTR."))
316 (defgeneric (setf foreign-struct-slot-value
) (value ptr slot
)
318 "Set the value of a SLOT in structure PTR."))
320 (defgeneric foreign-struct-slot-value-form
(ptr slot
)
322 "Return a form to get the value of SLOT in struct PTR."))
324 (defgeneric foreign-struct-slot-set-form
(value ptr slot
)
326 "Return a form to set the value of SLOT in struct PTR."))
328 (defclass foreign-struct-slot
()
329 ((name :initarg
:name
:reader slot-name
)
330 (offset :initarg
:offset
:accessor slot-offset
)
331 (type :initarg
:type
:accessor slot-type
))
332 (:documentation
"Base class for simple and aggregate slots."))
334 (defmethod foreign-struct-slot-pointer (ptr (slot foreign-struct-slot
))
335 "Return the address of SLOT relative to PTR."
336 (inc-pointer ptr
(slot-offset slot
)))
338 (defmethod foreign-struct-slot-pointer-form (ptr (slot foreign-struct-slot
))
339 "Return a form to get the address of SLOT relative to PTR."
340 (let ((offset (slot-offset slot
)))
343 `(inc-pointer ,ptr
,offset
))))
345 (defun foreign-slot-names (type)
346 "Returns a list of TYPE's slot names in no particular order."
347 (loop for value being the hash-values
348 in
(slots (follow-typedefs (parse-type type
)))
349 collect
(slot-name value
)))
353 (defclass simple-struct-slot
(foreign-struct-slot)
355 (:documentation
"Non-aggregate structure slots."))
357 (defmethod foreign-struct-slot-value (ptr (slot simple-struct-slot
))
358 "Return the value of a simple SLOT from a struct at PTR."
359 (mem-ref ptr
(slot-type slot
) (slot-offset slot
)))
361 (defmethod foreign-struct-slot-value-form (ptr (slot simple-struct-slot
))
362 "Return a form to get the value of a slot from PTR."
363 `(mem-ref ,ptr
',(slot-type slot
) ,(slot-offset slot
)))
365 (defmethod (setf foreign-struct-slot-value
) (value ptr
(slot simple-struct-slot
))
366 "Set the value of a simple SLOT to VALUE in PTR."
367 (setf (mem-ref ptr
(slot-type slot
) (slot-offset slot
)) value
))
369 (defmethod foreign-struct-slot-set-form (value ptr
(slot simple-struct-slot
))
370 "Return a form to set the value of a simple structure slot."
371 `(setf (mem-ref ,ptr
',(slot-type slot
) ,(slot-offset slot
)) ,value
))
373 ;;;### Aggregate Slots
375 (defclass aggregate-struct-slot
(foreign-struct-slot)
376 ((count :initarg
:count
:accessor slot-count
))
377 (:documentation
"Aggregate structure slots."))
379 ;;; A case could be made for just returning an error here instead of
380 ;;; this rather DWIM-ish behavior to return the address. It would
381 ;;; complicate being able to chain together slot names when accessing
382 ;;; slot values in nested structures though.
383 (defmethod foreign-struct-slot-value (ptr (slot aggregate-struct-slot
))
384 "Return a pointer to SLOT relative to PTR."
385 (foreign-struct-slot-pointer ptr slot
))
387 (defmethod foreign-struct-slot-value-form (ptr (slot aggregate-struct-slot
))
388 "Return a form to get the value of SLOT relative to PTR."
389 (foreign-struct-slot-pointer-form ptr slot
))
391 ;;; This is definitely an error though. Eventually, we could define a
392 ;;; new type of type translator that can convert certain aggregate
393 ;;; types, notably C strings or arrays of integers. For now, just error.
394 (defmethod (setf foreign-struct-slot-value
) (value ptr
(slot aggregate-struct-slot
))
395 "Signal an error; setting aggregate slot values is forbidden."
396 (declare (ignore value ptr
))
397 (error "Cannot set value of aggregate slot ~A." slot
))
399 (defmethod foreign-struct-slot-set-form (value ptr
(slot aggregate-struct-slot
))
400 "Signal an error; setting aggregate slot values is forbidden."
401 (declare (ignore value ptr
))
402 (error "Cannot set value of aggregate slot ~A." slot
))
404 ;;;## Defining Foreign Structures
406 (defun make-struct-slot (name offset type count
)
407 "Make the appropriate type of structure slot."
408 ;; If TYPE is an aggregate type or COUNT is >1, create an
409 ;; AGGREGATE-STRUCT-SLOT, otherwise a SIMPLE-STRUCT-SLOT.
410 (if (or (> count
1) (aggregatep (parse-type type
)))
411 (make-instance 'aggregate-struct-slot
:offset offset
:type type
412 :name name
:count count
)
413 (make-instance 'simple-struct-slot
:offset offset
:type type
416 ;;; Regarding structure alignment, the following ABIs were checked:
417 ;;; - System-V ABI: x86, x86-64, ppc, arm, mips and itanium. (more?)
418 ;;; - Mac OS X ABI Function Call Guide: ppc32, ppc64 and x86.
422 ;;; 1. "An entire structure or union object is aligned on the same
423 ;;; boundary as its most strictly aligned member."
425 ;;; 2. "Each member is assigned to the lowest available offset with
426 ;;; the appropriate alignment. This may require internal
427 ;;; padding, depending on the previous member."
429 ;;; 3. "A structure's size is increased, if necessary, to make it a
430 ;;; multiple of the alignment. This may require tail padding,
431 ;;; depending on the last member."
433 ;;; Special cases from darwin/ppc32's ABI:
434 ;;; http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/index.html
436 ;;; 4. "The embedding alignment of the first element in a data
437 ;;; structure is equal to the element's natural alignment."
439 ;;; 5. "For subsequent elements that have a natural alignment
440 ;;; greater than 4 bytes, the embedding alignment is 4, unless
441 ;;; the element is a vector." (note: this applies for
444 ;; FIXME: get a better name for this. --luis
445 (defun get-alignment (type alignment-type firstp
)
446 "Return alignment for TYPE according to ALIGNMENT-TYPE."
447 (declare (ignorable firstp
))
448 (ecase alignment-type
449 (:normal
#-
(and cffi-features
:darwin cffi-features
:ppc32
)
450 (foreign-type-alignment type
)
451 #+(and cffi-features
:darwin cffi-features
:ppc32
)
453 (foreign-type-alignment type
)
454 (min 4 (foreign-type-alignment type
))))))
456 (defun adjust-for-alignment (type offset alignment-type firstp
)
457 "Return OFFSET aligned properly for TYPE according to ALIGNMENT-TYPE."
458 (let* ((align (get-alignment type alignment-type firstp
))
459 (rem (mod offset align
)))
462 (+ offset
(- align rem
)))))
464 (defun notice-foreign-struct-definition (name-and-options slots
)
465 "Parse and install a foreign structure definition."
466 (destructuring-bind (name &key size
(class 'foreign-struct-type
))
467 (ensure-list name-and-options
)
468 (let ((struct (make-instance class
:name name
))
473 (dolist (slotdef slots
)
474 (destructuring-bind (slotname type
&key
(count 1) offset
) slotdef
475 (when (eq (canonicalize-foreign-type type
) :void
)
476 (error "void type not allowed in structure definition: ~S" slotdef
))
479 (adjust-for-alignment type current-offset
:normal firstp
)))
480 (let* ((slot (make-struct-slot slotname current-offset type count
))
481 (align (get-alignment (slot-type slot
) :normal firstp
)))
482 (setf (gethash slotname
(slots struct
)) slot
)
483 (when (> align max-align
)
484 (setq max-align align
)))
485 (incf current-offset
(* count
(foreign-type-size type
))))
487 ;; calculate padding and alignment
488 (setf (alignment struct
) max-align
) ; See point 1 above.
489 (let ((tail-padding (- max-align
(rem current-offset max-align
))))
490 (unless (= tail-padding max-align
) ; See point 3 above.
491 (incf current-offset tail-padding
)))
492 (setf (size struct
) (or size current-offset
))
493 (notice-foreign-type name struct
))))
495 (defmacro defcstruct
(name-and-options &body fields
)
496 "Define the layout of a foreign structure."
497 (discard-docstring fields
)
498 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
499 ;; n-f-s-d could do with this with mop:ensure-class.
500 ,(let-when (class (getf (cdr (ensure-list name-and-options
)) :class
))
501 `(defclass ,class
(foreign-struct-type) ()))
502 (notice-foreign-struct-definition ',name-and-options
',fields
)))
504 ;;;## Accessing Foreign Structure Slots
506 (defun get-slot-info (type slot-name
)
507 "Return the slot info for SLOT-NAME or raise an error."
508 (let* ((struct (follow-typedefs (parse-type type
)))
509 (info (gethash slot-name
(slots struct
))))
511 (error "Undefined slot ~A in foreign type ~A." slot-name type
))
514 (defun foreign-slot-pointer (ptr type slot-name
)
515 "Return the address of SLOT-NAME in the structure at PTR."
516 (foreign-struct-slot-pointer ptr
(get-slot-info type slot-name
)))
518 (defun foreign-slot-offset (type slot-name
)
519 "Return the offset of SLOT in a struct TYPE."
520 (slot-offset (get-slot-info type slot-name
)))
522 (defun foreign-slot-value (ptr type slot-name
)
523 "Return the value of SLOT-NAME in the foreign structure at PTR."
524 (foreign-struct-slot-value ptr
(get-slot-info type slot-name
)))
526 (define-compiler-macro foreign-slot-value
(&whole form ptr type slot-name
)
527 "Optimizer for FOREIGN-SLOT-VALUE when TYPE is constant."
528 (if (and (constantp type
) (constantp slot-name
))
529 (foreign-struct-slot-value-form
530 ptr
(get-slot-info (eval type
) (eval slot-name
)))
533 (define-setf-expander foreign-slot-value
(ptr type slot-name
&environment env
)
534 "SETF expander for FOREIGN-SLOT-VALUE."
535 (multiple-value-bind (dummies vals newval setter getter
)
536 (get-setf-expansion ptr env
)
537 (declare (ignore setter newval
))
538 (if (and (constantp type
) (constantp slot-name
))
539 ;; if TYPE and SLOT-NAME are constant we avoid rebinding them
540 ;; so that the compiler macro on FOREIGN-SLOT-SET works.
541 (with-unique-names (store)
547 (foreign-slot-set ,store
,getter
,type
,slot-name
)
549 `(foreign-slot-value ,getter
,type
,slot-name
)))
551 (with-unique-names (store slot-name-tmp type-tmp
)
553 (list* type-tmp slot-name-tmp dummies
)
554 (list* type slot-name vals
)
557 (foreign-slot-set ,store
,getter
,type-tmp
,slot-name-tmp
)
559 `(foreign-slot-value ,getter
,type-tmp
,slot-name-tmp
))))))
561 (defun foreign-slot-set (value ptr type slot-name
)
562 "Set the value of SLOT-NAME in a foreign structure."
563 (setf (foreign-struct-slot-value ptr
(get-slot-info type slot-name
)) value
))
565 (define-compiler-macro foreign-slot-set
566 (&whole form value ptr type slot-name
)
567 "Optimizer when TYPE and SLOT-NAME are constant."
568 (if (and (constantp type
) (constantp slot-name
))
569 (foreign-struct-slot-set-form
570 value ptr
(get-slot-info (eval type
) (eval slot-name
)))
573 (defmacro with-foreign-slots
((vars ptr type
) &body body
)
574 "Create local symbol macros for each var in VARS to reference
575 foreign slots in PTR of TYPE. Similar to WITH-SLOTS."
576 (let ((ptr-var (gensym "PTR")))
577 `(let ((,ptr-var
,ptr
))
579 ,(loop for var in vars
580 collect
`(,var
(foreign-slot-value ,ptr-var
',type
',var
)))
585 ;;; A union is a FOREIGN-STRUCT-TYPE in which all slots have an offset
588 ;;; See also the notes regarding ABI requirements in
589 ;;; NOTICE-FOREIGN-STRUCT-DEFINITION
590 (defun notice-foreign-union-definition (name-and-options slots
)
591 "Parse and install a foreign union definition."
592 (destructuring-bind (name &key size
)
593 (ensure-list name-and-options
)
594 (let ((struct (make-instance 'foreign-struct-type
:name name
))
597 (dolist (slotdef slots
)
598 (destructuring-bind (slotname type
&key
(count 1)) slotdef
599 (when (eq (canonicalize-foreign-type type
) :void
)
600 (error "void type not allowed in union definition: ~S" slotdef
))
601 (let* ((slot (make-struct-slot slotname
0 type count
))
602 (size (* count
(foreign-type-size type
)))
603 (align (foreign-type-alignment (slot-type slot
))))
604 (setf (gethash slotname
(slots struct
)) slot
)
605 (when (> size max-size
)
606 (setf max-size size
))
607 (when (> align max-align
)
608 (setf max-align align
)))))
609 (setf (size struct
) (or size max-size
))
610 (setf (alignment struct
) max-align
)
611 (notice-foreign-type name struct
))))
613 (defmacro defcunion
(name &body fields
)
614 "Define the layout of a foreign union."
615 (discard-docstring fields
)
616 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
617 (notice-foreign-union-definition ',name
',fields
)))
619 ;;;# Operations on Types
621 (defmethod foreign-type-alignment (type)
622 "Return the alignment in bytes of a foreign type."
623 (foreign-type-alignment (parse-type type
)))
625 (defun foreign-alloc (type &key
(initial-element nil initial-element-p
)
626 (initial-contents nil initial-contents-p
)
627 (count 1 count-p
) null-terminated-p
)
628 "Allocate enough memory to hold COUNT objects of type TYPE. If
629 INITIAL-ELEMENT is supplied, each element of the newly allocated
630 memory is initialized with its value. If INITIAL-CONTENTS is supplied,
631 each of its elements will be used to initialize the contents of the
632 newly allocated memory."
633 (let (contents-length)
634 ;; Some error checking, etc...
635 (when (and null-terminated-p
636 (not (eq (canonicalize-foreign-type type
) :pointer
)))
637 (error "Cannot use :NULL-TERMINATED-P with non-pointer types."))
638 (when (and initial-element-p initial-contents-p
)
639 (error "Cannot specify both :INITIAL-ELEMENT and :INITIAL-CONTENTS"))
640 (when initial-contents-p
641 (setq contents-length
(length initial-contents
))
643 (assert (>= count contents-length
))
644 (setq count contents-length
)))
645 ;; Everything looks good.
646 (let ((ptr (%foreign-alloc
(* (foreign-type-size type
)
647 (if null-terminated-p
(1+ count
) count
)))))
648 (when initial-element-p
650 (setf (mem-aref ptr type i
) initial-element
)))
651 (when initial-contents-p
652 (dotimes (i contents-length
)
653 (setf (mem-aref ptr type i
) (elt initial-contents i
))))
654 (when null-terminated-p
655 (setf (mem-aref ptr
:pointer count
) (null-pointer)))
658 ;;; Simple compiler macro that kicks in when TYPE is constant and only
659 ;;; the COUNT argument is passed. (Note: hard-coding the type's size
660 ;;; into the fasl will likely break CLISP fasl cross-platform
662 (define-compiler-macro foreign-alloc
(&whole form type
&rest args
663 &key
(count 1 count-p
) &allow-other-keys
)
664 (if (or (and count-p
(<= (length args
) 2)) (null args
))
666 ((and (constantp type
) (constantp count
))
667 `(%foreign-alloc
,(* (eval count
) (foreign-type-size (eval type
)))))
669 `(%foreign-alloc
(* ,count
,(foreign-type-size (eval type
)))))
673 (defmacro with-foreign-object
((var type
&optional
(count 1)) &body body
)
674 "Bind VAR to a pointer to COUNT objects of TYPE during BODY.
675 The buffer has dynamic extent and may be stack allocated."
676 `(with-foreign-pointer
677 (,var
,(if (constantp type
)
678 ;; with-foreign-pointer may benefit from constant folding:
679 (if (constantp count
)
680 (* (eval count
) (foreign-type-size (eval type
)))
681 `(* ,count
,(foreign-type-size (eval type
))))
682 `(* ,count
(foreign-type-size ,type
))))
685 (defmacro with-foreign-objects
(bindings &body body
)
687 `(with-foreign-object ,(car bindings
)
688 (with-foreign-objects ,(cdr bindings
)
692 ;;;## Anonymous Type Translators
694 ;;; (:wrapper :to-c some-function :from-c another-function)
696 ;;; TODO: We will need to add a FREE function to this as well I think.
699 (define-foreign-type foreign-type-wrapper
()
700 ((to-c :initarg
:to-c
:reader wrapper-to-c
)
701 (from-c :initarg
:from-c
:reader wrapper-from-c
))
702 (:documentation
"Wrapper type."))
704 (define-parse-method :wrapper
(base-type &key to-c from-c
)
705 (make-instance 'foreign-type-wrapper
706 :actual-type
(parse-type base-type
)
707 :to-c
(or to-c
'identity
)
708 :from-c
(or from-c
'identity
)))
710 (defmethod translate-to-foreign (value (type foreign-type-wrapper
))
711 (translate-to-foreign
712 (funcall (slot-value type
'to-c
) value
) (actual-type type
)))
714 (defmethod translate-from-foreign (value (type foreign-type-wrapper
))
715 (funcall (slot-value type
'from-c
)
716 (translate-from-foreign value
(actual-type type
))))
720 ;;; Boolean type. Maps to an :int by default. Only accepts integer types.
721 (define-foreign-type foreign-boolean-type
()
724 (define-parse-method :boolean
(&optional
(base-type :int
))
726 'foreign-boolean-type
:actual-type
727 (ecase (canonicalize-foreign-type base-type
)
728 ((:char
:unsigned-char
:int
:unsigned-int
:long
:unsigned-long
729 #-cffi-features
:no-long-long
:long-long
730 #-cffi-features
:no-long-long
:unsigned-long-long
) base-type
))))
732 (defmethod translate-to-foreign (value (type foreign-boolean-type
))
735 (defmethod translate-from-foreign (value (type foreign-boolean-type
))
738 (defmethod expand-to-foreign (value (type foreign-boolean-type
))
739 "Optimization for the :boolean type."
740 (if (constantp value
)
741 (if (eval value
) 1 0)
744 (defmethod expand-from-foreign (value (type foreign-boolean-type
))
745 "Optimization for the :boolean type."
746 (if (constantp value
) ; very unlikely, heh
747 (not (zerop (eval value
)))
748 `(not (zerop ,value
))))
750 ;;;# Typedefs for built-in types.
752 (defctype :uchar
:unsigned-char
)
753 (defctype :ushort
:unsigned-short
)
754 (defctype :uint
:unsigned-int
)
755 (defctype :ulong
:unsigned-long
)
757 #-cffi-features
:no-long-long
759 (defctype :llong
:long-long
)
760 (defctype :ullong
:unsigned-long-long
))
762 ;;; We try to define the :[u]int{8,16,32,64} types by looking at
763 ;;; the sizes of the built-in integer types and defining typedefs.
764 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
766 ((match-types (sized-types mtypes
)
768 ,@(loop for
(type . size
) in sized-types
769 for m
= (car (member size mtypes
:key
#'foreign-type-size
))
770 when m collect
`(defctype ,type
,m
)))))
772 (match-types ((:int8 .
1) (:int16 .
2) (:int32 .
4) (:int64 .
8))
773 (:char
:short
:int
:long
774 #-cffi-features
:no-long-long
:long-long
))
776 (match-types ((:uint8 .
1) (:uint16 .
2) (:uint32 .
4) (:uint64 .
8))
777 (:unsigned-char
:unsigned-short
:unsigned-int
:unsigned-long
778 #-cffi-features
:no-long-long
:unsigned-long-long
))))