1 (in-package :sb-grovel
)
3 ;;;; The macros defined here are called from #:Gconstants.lisp, which was
4 ;;;; generated from constants.lisp by the C compiler as driven by that
5 ;;;; wacky def-to-lisp thing.
7 ;;; (def-foreign-routine ("stat" STAT ) (INTEGER 32) (FILE-NAME
8 ;;; C-CALL:C-STRING) (BUF (* T)) )
10 ;;; I can't help thinking this was originally going to do something a
12 (defmacro define-foreign-routine
13 (&whole it
(c-name lisp-name
) return-type
&rest args
)
14 (declare (ignorable c-name lisp-name return-type args
))
15 `(define-alien-routine ,@(cdr it
)))
21 #| C structs need
: the with-... interface.
26 XXX
: :distrust-length t fields are dangerous. they should only be at
27 the end of the structure
(they mess up offset
/size calculations
)
30 (defun reintern (symbol &optional
(package *package
*))
32 (intern (symbol-name symbol
) package
)
35 (defparameter alien-type-table
(make-hash-table :test
'eql
))
36 (defparameter lisp-type-table
(make-hash-table :test
'eql
))
38 (macrolet ((define-alien-types ((type size
) &rest defns
)
40 ,@(loop for defn in defns
41 collect
(destructuring-bind (expected-type c-type lisp-type
) defn
43 (setf (gethash ',expected-type alien-type-table
)
45 (declare (ignorable type size
))
47 (setf (gethash ',expected-type lisp-type-table
)
49 (declare (ignorable type size
))
51 (define-alien-types (type size
)
52 (integer (or (gethash size
(symbol-value (intern "*INTEGER-SIZES*")))
53 `(integer ,(* 8 size
)))
54 `(unsigned-byte ,(* 8 size
)))
55 (unsigned `(unsigned ,(* 8 size
))
56 `(unsigned-byte ,(* 8 size
)))
57 (signed `(signed ,(* 8 size
))
58 `(signed-byte ,(* 8 size
)))
59 (c-string `(array char
,size
) 'cl
:simple-string
)
60 (c-string-pointer 'c-string
'cl
:simple-string
)
61 ;; TODO: multi-dimensional arrays, if they are ever needed.
62 (array (destructuring-bind (array-tag elt-type
&optional array-size
) type
63 (declare (ignore array-tag
))
64 ;; XXX: use of EVAL. alien-size is a macro,
65 ;; unfortunately; and it will only accept unquoted type
67 `(sb-alien:array
,elt-type
,(or array-size
68 (/ size
(eval `(sb-alien:alien-size
,elt-type
:bytes
))))))
71 (defun retrieve-type-for (type size table
)
72 (multiple-value-bind (type-fn found
)
73 (gethash (reintern (typecase type
76 (find-package '#:sb-grovel
))
80 (funcall (the function type-fn
) type size
)
84 (defun alien-type-for (type size
)
85 (reintern (retrieve-type-for type size alien-type-table
)))
87 (defun lisp-type-for (type size
)
88 (multiple-value-bind (val found
)
89 (retrieve-type-for type size lisp-type-table
)
95 (defun mk-padding (len offset
)
96 (make-instance 'padding
97 :type
`(array char
,len
)
100 :name
(gentemp "PADDING")))
101 (defun mk-struct (offset &rest children
)
102 (make-instance 'struct
:name
(gentemp "STRUCT")
103 :children
(remove nil children
)
105 (defun mk-union (offset &rest children
)
106 (make-instance 'union
:name
(gentemp "UNION")
107 :children
(remove nil children
)
109 (defun mk-val (name type h-type offset size
)
110 (declare (ignore h-type
))
111 (make-instance 'value-slot
:name name
116 ;;; struct tree classes
119 ((offset :initarg
:offset
:reader offset
)
120 (name :initarg
:name
:reader name
)))
122 (defclass structured-type
(slot)
123 ((children :initarg
:children
:accessor children
)))
125 (defclass union
(structured-type)
128 (defclass struct
(structured-type)
131 (defclass value-slot
(slot)
132 ((size :initarg
:size
:reader size
)
133 (type :initarg
:type
:reader type
)))
135 (defclass padding
(value-slot)
138 (defmethod print-object ((o value-slot
) s
)
139 (print-unreadable-object (o s
:type t
)
140 (format s
"~S ~A+~A=~A" (name o
) (offset o
) (size o
) (slot-end o
))))
142 (defmethod print-object ((o structured-type
) s
)
143 (print-unreadable-object (o s
:type t
)
144 (format s
"~S ~A" (name o
) (children o
))))
146 (defmethod size ((slot structured-type
))
147 (let ((min-offset (offset slot
)))
148 (if (null (children slot
))
150 (reduce #'max
(mapcar (lambda (child)
151 (+ (- (offset child
) min-offset
) (size child
)))
155 (defgeneric slot-end
(slot))
156 (defmethod slot-end ((slot slot
))
157 (+ (offset slot
) (size slot
)))
159 (defun overlap-p (elt1 elt2
)
160 (unless (or (zerop (size elt1
))
163 (and (<= (offset elt1
)
167 (and (<= (offset elt2
)
172 (defgeneric find-overlaps
(root new-element
))
173 (defmethod find-overlaps ((root structured-type
) new-element
)
174 (when (overlap-p root new-element
)
175 (let ((overlapping-elts (loop for child in
(children root
)
176 for overlap
= (find-overlaps child new-element
)
179 (cons root overlapping-elts
))))
181 (defmethod find-overlaps ((root value-slot
) new-element
)
182 (when (overlap-p root new-element
)
185 (defgeneric pad-to-offset-of
(to-pad parent
))
186 (macrolet ((skel (end-form)
187 `(let* ((end ,end-form
)
188 (len (abs (- (offset to-pad
) end
))))
190 ((= end
(offset to-pad
)) ; we are at the right offset.
192 (t ; we have to pad between the
193 ; old slot's end and the new
195 (mk-padding len end
))))))
197 (defmethod pad-to-offset-of (to-pad (parent struct
))
198 (skel (if (null (children parent
))
200 (+ (size parent
) (offset parent
)))))
201 (defmethod pad-to-offset-of (to-pad (parent union
))
202 (skel (if (null (children parent
))
206 (defgeneric replace-by-union
(in-st element new-element
))
207 (defmethod replace-by-union ((in-st struct
) elt new-elt
)
208 (setf (children in-st
) (remove elt
(children in-st
)))
209 (let ((padding (pad-to-offset-of new-elt in-st
)))
210 (setf (children in-st
)
211 (nconc (children in-st
)
212 (list (mk-union (offset elt
)
215 (mk-struct (offset elt
)
220 (defmethod replace-by-union ((in-st union
) elt new-elt
)
221 (let ((padding (pad-to-offset-of new-elt in-st
)))
222 (setf (children in-st
)
223 (nconc (children in-st
)
225 (mk-struct (offset in-st
)
230 (defgeneric insert-element
(root new-elt
))
231 (defmethod insert-element ((root struct
) (new-elt slot
))
232 (let ((overlaps (find-overlaps root new-elt
)))
234 (overlaps (let ((last-structure (first (last overlaps
2)))
235 (last-val (first (last overlaps
))))
236 (replace-by-union last-structure last-val new-elt
)
239 (let ((padding (pad-to-offset-of new-elt root
)))
240 (setf (children root
)
241 (nconc (children root
)
242 (when padding
(list padding
))
246 (defun sane-slot (alien-var &rest slots
)
247 "Emulates the SB-ALIEN:SLOT interface, with useful argument order for
248 deeply nested structures."
249 (labels ((rewriter (slots)
252 `(sb-alien:slot
,(rewriter (rest slots
))
256 (defgeneric accessor-modifier-for
(element-type accessor-type
))
258 (defmacro identity-1
(thing &rest ignored
)
259 (declare (ignore ignored
))
261 (defun (setf identity-1
) (new-thing place
&rest ignored
)
262 (declare (ignore ignored
))
263 (setf place new-thing
))
265 (defmethod accessor-modifier-for (element-type (accessor-type (eql :getter
)))
267 (defmethod accessor-modifier-for ((element-type (eql 'C-STRING
))
268 (accessor-type (eql :getter
)))
269 'c-string-
>lisp-string
)
270 (defmethod accessor-modifier-for (element-type (accessor-type (eql :setter
)))
272 (defmethod accessor-modifier-for ((element-type (eql 'C-STRING
))
273 (accessor-type (eql :setter
)))
274 'c-string-
>lisp-string
)
276 (defun c-string->lisp-string
(string &optional limit
)
277 (declare (ignore limit
))
278 (cast string c-string
))
280 (defun (setf c-string-
>lisp-string
) (new-string alien
&optional limit
)
281 (declare (string new-string
))
282 (let* ((upper-bound (or limit
(1+ (length new-string
))))
283 (last-elt (min (1- upper-bound
) (length new-string
)))
284 (octets (sb-ext:string-to-octets new-string
:end last-elt
286 (alien-pointer (cast alien
(* unsigned-char
))))
287 (declare (cl:type
(simple-array (unsigned-byte 8) (*)) octets
))
288 (declare (cl:type sb-int
:index last-elt
))
289 (loop for i from
0 to last-elt
290 do
(setf (deref alien-pointer i
) (aref octets i
)))
291 (subseq new-string
0 last-elt
)))
293 (defgeneric accessors-for
(struct-name element path
))
294 (defmethod accessors-for (struct-name (root structured-type
) path
)
298 (defmethod accessors-for (struct-name (root value-slot
) path
)
299 (let ((rpath (reverse path
))
300 (accessor-name (format nil
"~A-~A"
301 (symbol-name struct-name
)
302 (symbol-name (name root
)))))
303 (labels ((accessor (root rpath
)
304 (apply #'sane-slot
'struct
(mapcar 'name
(append (rest rpath
) (list root
))))))
305 `((declaim (inline ,(intern accessor-name
)
306 (setf ,(intern accessor-name
))))
307 (defun ,(intern accessor-name
) (struct)
308 (declare (cl:type
(alien (* ,struct-name
)) struct
)
309 (optimize (speed 3)))
310 (,(accessor-modifier-for (reintern (type root
) (find-package :sb-grovel
))
312 ,(accessor root rpath
) ,(size root
)))
313 (defun (setf ,(intern accessor-name
)) (new-val struct
)
314 (declare (cl:type
(alien (* ,struct-name
)) struct
)
315 (cl:type
,(lisp-type-for (type root
) (size root
)) new-val
)
316 (optimize (speed 3)))
317 ,(let* ((accessor-modifier (accessor-modifier-for (reintern (type root
)
318 (find-package :sb-grovel
))
320 (modified-accessor (if accessor-modifier
321 `(,accessor-modifier
,(accessor root rpath
) ,(size root
))
322 (accessor root rpath
))))
324 `(setf ,modified-accessor new-val
)))
325 (defconstant ,(intern (format nil
"OFFSET-OF-~A" accessor-name
))
330 (defmethod accessors-for (struct (root padding
) path
)
333 (defgeneric generate-struct-definition
(struct-name root path
))
334 (defmethod generate-struct-definition (struct-name (root structured-type
) path
)
335 (let ((naccessors (accessors-for struct-name root path
))
337 (dolist (child (children root
))
338 (multiple-value-bind (slots accessors
)
339 (generate-struct-definition struct-name child
(cons root path
))
340 (setf nslots
(nconc nslots slots
))
341 (setf naccessors
(nconc naccessors accessors
))))
342 (values `((,(name root
) (,(type-of root
) ,(name root
) ,@nslots
)))
345 (defmethod generate-struct-definition (struct-name (root value-slot
) path
)
346 (values `((,(name root
) ,(alien-type-for (type root
) (size root
))))
347 (accessors-for struct-name root path
)))
349 (defmacro define-c-struct
(name size
&rest elements
)
350 (multiple-value-bind (struct-elements accessors
)
351 (let* ((root (make-instance 'struct
:name name
:children nil
:offset
0)))
352 (loop for e in
(sort elements
#'< :key
#'fourth
)
353 do
(insert-element root
(apply 'mk-val e
))
354 finally
(return root
))
355 (setf (children root
)
356 (nconc (children root
)
358 (mk-padding (max 0 (- size
361 (generate-struct-definition name root nil
))
363 (sb-alien:define-alien-type
,@(first struct-elements
))
365 (defmacro ,(intern (format nil
"WITH-~A" name
)) (var (&rest field-values
) &body body
)
366 (labels ((field-name (x)
367 (intern (concatenate 'string
368 (symbol-name ',name
) "-"
370 ,(symbol-package name
))))
371 `(sb-alien:with-alien
((,var
(* ,',name
) ,'(,(intern (format nil
"ALLOCATE-~A" name
)))))
374 (progn ,@(mapcar (lambda (pair)
375 `(setf (,(field-name (first pair
)) ,var
) ,(second pair
)))
378 (funcall ',',(intern (format nil
"FREE-~A" name
)) ,var
)))))
379 (defconstant ,(intern (format nil
"SIZE-OF-~A" name
)) ,size
)
380 (defun ,(intern (format nil
"ALLOCATE-~A" name
)) ()
381 (let* ((o (sb-alien:make-alien
,name
))
382 (c-o (cast o
(* (unsigned 8)))))
383 ;; we have to initialize the object to all-0 before we can
384 ;; expect to make sensible use of it - the object returned
385 ;; by make-alien is initialized to all-D0 bytes.
387 ;; FIXME: This should be fixed in sb-alien, where better
388 ;; optimizations might be possible.
389 (loop for i from
0 below
,size
390 do
(setf (deref c-o i
) 0))
392 (defun ,(intern (format nil
"FREE-~A" name
)) (o)
393 (sb-alien:free-alien o
)))))
395 (defun foreign-nullp (c)
396 "C is a pointer to 0?"