1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; cffi-scl.lisp --- CFFI-SYS implementation for the Scieneer Common Lisp.
5 ;;; Copyright (C) 2005-2006, James Bielman <jamesjb@jamesjb.com>
6 ;;; Copyright (C) 2006-2007, Scieneer Pty Ltd.
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.
31 (defpackage #:cffi-sys
32 (:use
#:common-lisp
#:alien
#:c-call
#:cffi-utils
#:alexandria
)
34 #:canonicalize-symbol-name-case
45 #:with-foreign-pointer
47 #:%foreign-funcall-pointer
48 #:%foreign-type-alignment
50 #:%load-foreign-library
51 #:%close-foreign-library
55 #:make-shareable-byte-vector
56 #:with-pointer-to-vector-data
57 #:%foreign-symbol-pointer
61 (in-package #:cffi-sys
)
65 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
66 (mapc (lambda (feature) (pushnew feature
*features
*))
67 '(cffi-features:flat-namespace
)))
71 (defun canonicalize-symbol-name-case (name)
72 (declare (string name
))
73 (if (eq ext
:*case-mode
* :upper
)
75 (string-downcase name
)))
77 ;;;# Basic Pointer Operations
79 (deftype foreign-pointer
()
80 'sys
:system-area-pointer
)
82 (declaim (inline pointerp
))
84 "Return true if 'ptr is a foreign pointer."
85 (sys:system-area-pointer-p ptr
))
87 (declaim (inline pointer-eq
))
88 (defun pointer-eq (ptr1 ptr2
)
89 "Return true if 'ptr1 and 'ptr2 point to the same address."
92 (declaim (inline null-pointer
))
93 (defun null-pointer ()
94 "Construct and return a null pointer."
97 (declaim (inline null-pointer-p
))
98 (defun null-pointer-p (ptr)
99 "Return true if 'ptr is a null pointer."
100 (zerop (sys:sap-int ptr
)))
102 (declaim (inline inc-pointer
))
103 (defun inc-pointer (ptr offset
)
104 "Return a pointer pointing 'offset bytes past 'ptr."
105 (sys:sap
+ ptr offset
))
107 (declaim (inline make-pointer
))
108 (defun make-pointer (address)
109 "Return a pointer pointing to 'address."
110 (sys:int-sap address
))
112 (declaim (inline pointer-address
))
113 (defun pointer-address (ptr)
114 "Return the address pointed to by 'ptr."
117 (defmacro with-foreign-pointer
((var size
&optional size-var
) &body body
)
118 "Bind 'var to 'size bytes of foreign memory during 'body. The
119 pointer in 'var is invalid beyond the dynamic extent of 'body, and
120 may be stack-allocated if supported by the implementation. If
121 'size-var is supplied, it will be bound to 'size during 'body."
123 (setf size-var
(gensym (symbol-name '#:size
))))
124 ;; If the size is constant we can stack-allocate.
125 (cond ((constantp size
)
126 (let ((alien-var (gensym (symbol-name '#:alien
))))
127 `(with-alien ((,alien-var
(array (unsigned 8) ,(eval size
))))
128 (let ((,size-var
,size
)
129 (,var
(alien-sap ,alien-var
)))
130 (declare (ignorable ,size-var
))
133 `(let ((,size-var
,size
))
134 (alien:with-bytes
(,var
,size-var
)
139 ;;; Functions and macros for allocating foreign memory on the stack and on the
140 ;;; heap. The main CFFI package defines macros that wrap 'foreign-alloc and
141 ;;; 'foreign-free in 'unwind-protect for the common usage when the memory has
144 (defun %foreign-alloc
(size)
145 "Allocate 'size bytes on the heap and return a pointer."
146 (declare (type (unsigned-byte #-
64bit
32 #+64bit
64) size
))
147 (alien-funcall (extern-alien "malloc"
148 (function system-area-pointer unsigned
))
151 (defun foreign-free (ptr)
152 "Free a 'ptr allocated by 'foreign-alloc."
153 (declare (type system-area-pointer ptr
))
154 (alien-funcall (extern-alien "free"
155 (function (values) system-area-pointer
))
158 ;;;# Shareable Vectors
160 (defun make-shareable-byte-vector (size)
161 "Create a Lisp vector of 'size bytes that can passed to
162 'with-pointer-to-vector-data."
163 (make-array size
:element-type
'(unsigned-byte 8)))
165 (defmacro with-pointer-to-vector-data
((ptr-var vector
) &body body
)
166 "Bind 'ptr-var to a foreign pointer to the data in 'vector."
167 (let ((vector-var (gensym (symbol-name '#:vector
))))
168 `(let ((,vector-var
,vector
))
169 (ext:with-pinned-object
(,vector-var
)
170 (let ((,ptr-var
(sys:vector-sap
,vector-var
)))
175 ;;; Define the %MEM-REF and %MEM-SET functions, as well as compiler
176 ;;; macros that optimize the case where the type keyword is constant
178 (defmacro define-mem-accessors
(&body pairs
)
180 (defun %mem-ref
(ptr type
&optional
(offset 0))
182 ,@(loop for
(keyword fn
) in pairs
183 collect
`(,keyword
(,fn ptr offset
)))))
184 (defun %mem-set
(value ptr type
&optional
(offset 0))
186 ,@(loop for
(keyword fn
) in pairs
187 collect
`(,keyword
(setf (,fn ptr offset
) value
)))))
188 (define-compiler-macro %mem-ref
189 (&whole form ptr type
&optional
(offset 0))
192 ,@(loop for
(keyword fn
) in pairs
193 collect
`(,keyword
`(,',fn
,ptr
,offset
))))
195 (define-compiler-macro %mem-set
196 (&whole form value ptr type
&optional
(offset 0))
200 ,@(loop for
(keyword fn
) in pairs
201 collect
`(,keyword
`(setf (,',fn
,ptr
,offset
)
205 (define-mem-accessors
206 (:char sys
:signed-sap-ref-8
)
207 (:unsigned-char sys
:sap-ref-8
)
208 (:short sys
:signed-sap-ref-16
)
209 (:unsigned-short sys
:sap-ref-16
)
210 (:int sys
:signed-sap-ref-32
)
211 (:unsigned-int sys
:sap-ref-32
)
212 (:long
#-
64bit sys
:signed-sap-ref-32
#+64bit sys
:signed-sap-ref-64
)
213 (:unsigned-long
#-
64bit sys
:sap-ref-32
#+64bit sys
:sap-ref-64
)
214 (:long-long sys
:signed-sap-ref-64
)
215 (:unsigned-long-long sys
:sap-ref-64
)
216 (:float sys
:sap-ref-single
)
217 (:double sys
:sap-ref-double
)
218 #+long-float
(:long-double sys
:sap-ref-long
)
219 (:pointer sys
:sap-ref-sap
))
221 ;;;# Calling Foreign Functions
223 (defun convert-foreign-type (type-keyword)
224 "Convert a CFFI type keyword to an ALIEN type."
227 (:unsigned-char
'unsigned-char
)
229 (:unsigned-short
'unsigned-short
)
231 (:unsigned-int
'unsigned-int
)
233 (:unsigned-long
'unsigned-long
)
234 (:long-long
'(signed 64))
235 (:unsigned-long-long
'(unsigned 64))
236 (:float
'single-float
)
237 (:double
'double-float
)
239 (:long-double
'long-float
)
240 (:pointer
'system-area-pointer
)
243 (defun %foreign-type-size
(type-keyword)
244 "Return the size in bytes of a foreign type."
245 (values (truncate (alien-internals:alien-type-bits
246 (alien-internals:parse-alien-type
247 (convert-foreign-type type-keyword
)))
250 (defun %foreign-type-alignment
(type-keyword)
251 "Return the alignment in bytes of a foreign type."
252 (values (truncate (alien-internals:alien-type-alignment
253 (alien-internals:parse-alien-type
254 (convert-foreign-type type-keyword
)))
257 (defun foreign-funcall-type-and-args (args)
258 "Return an 'alien function type for 'args."
259 (let ((return-type nil
))
260 (loop for
(type arg
) on args by
#'cddr
261 if arg collect
(convert-foreign-type type
) into types
262 and collect arg into fargs
263 else do
(setf return-type
(convert-foreign-type type
))
264 finally
(return (values types fargs return-type
)))))
266 (defmacro %%foreign-funcall
(name types fargs rettype
)
267 "Internal guts of '%foreign-funcall."
268 `(alien-funcall (extern-alien ,name
(function ,rettype
,@types
))
271 (defmacro %foreign-funcall
(name args
&key library calling-convention
)
272 "Perform a foreign function call, document it more later."
273 (declare (ignore library calling-convention
))
274 (multiple-value-bind (types fargs rettype
)
275 (foreign-funcall-type-and-args args
)
276 `(%%foreign-funcall
,name
,types
,fargs
,rettype
)))
278 (defmacro %foreign-funcall-pointer
(ptr args
&key calling-convention
)
279 "Funcall a pointer to a foreign function."
280 (declare (ignore calling-convention
))
281 (multiple-value-bind (types fargs rettype
)
282 (foreign-funcall-type-and-args args
)
283 (with-unique-names (function)
284 `(with-alien ((,function
(* (function ,rettype
,@types
)) ,ptr
))
285 (alien-funcall ,function
,@fargs
)))))
289 (defmacro %defcallback
(name rettype arg-names arg-types body
290 &key calling-convention
)
291 `(alien:defcallback
,name
292 (,(convert-foreign-type rettype
)
293 ,@(mapcar (lambda (sym type
)
294 (list sym
(convert-foreign-type type
)))
295 arg-names arg-types
))
298 (declaim (inline %callback
))
299 (defun %callback
(name)
300 (alien:callback-sap name
))
302 ;;;# Loading and Closing Foreign Libraries
304 (defun %load-foreign-library
(name path
)
305 "Load the foreign library 'name."
306 (declare (ignore name
))
307 (ext:load-dynamic-object path
))
309 (defun %close-foreign-library
(name)
310 "Closes the foreign library 'name."
311 (ext:close-dynamic-object name
))
313 (defun native-namestring (pathname)
314 (ext:unix-namestring pathname
))
318 (defun %foreign-symbol-pointer
(name library
)
319 "Returns a pointer to a foreign symbol 'name."
320 (declare (ignore library
))
321 (let ((sap (sys:foreign-symbol-address name
)))
322 (if (zerop (sys:sap-int sap
)) nil sap
)))