Fix :bug-309448 test for faster CPUs.
[sbcl.git] / contrib / sb-grovel / foreign-glue.lisp
index c15509f..dcbda61 100644 (file)
 (defparameter lisp-type-table (make-hash-table :test 'eql))
 
 (macrolet ((define-alien-types ((type size) &rest defns)
-              `(progn
-                 ,@(loop for defn in defns
-                         collect (destructuring-bind (expected-type c-type lisp-type) defn
-                                   `(progn
-                                      (setf (gethash ',expected-type alien-type-table)
-                                            (lambda (,type ,size)
-                                              (declare (ignorable type size))
-                                              ,c-type))
-                                      (setf (gethash ',expected-type lisp-type-table)
-                                            (lambda (,type ,size)
-                                              (declare (ignorable type size))
-                                              ,lisp-type))))))))
+               `(progn
+                  ,@(loop for defn in defns
+                          collect (destructuring-bind (expected-type c-type lisp-type) defn
+                                    `(progn
+                                       (setf (gethash ',expected-type alien-type-table)
+                                             (lambda (,type ,size)
+                                               (declare (ignorable type size))
+                                               ,c-type))
+                                       (setf (gethash ',expected-type lisp-type-table)
+                                             (lambda (,type ,size)
+                                               (declare (ignorable type size))
+                                               ,lisp-type))))))))
   (define-alien-types (type size)
       (integer (or (gethash size (symbol-value (intern "*INTEGER-SIZES*")))
-                  `(integer ,(* 8 size)))
-              `(unsigned-byte ,(* 8 size)))
+                   `(integer ,(* 8 size)))
+               `(unsigned-byte ,(* 8 size)))
       (unsigned `(unsigned ,(* 8 size))
-               `(unsigned-byte ,(* 8 size)))
+                `(unsigned-byte ,(* 8 size)))
       (signed `(signed ,(* 8 size))
-             `(signed-byte ,(* 8 size)))
+              `(signed-byte ,(* 8 size)))
       (c-string `(array char ,size) 'cl:simple-string)
       (c-string-pointer 'c-string 'cl:simple-string)
       ;; TODO: multi-dimensional arrays, if they are ever needed.
       (array (destructuring-bind (array-tag elt-type &optional array-size) type
-              (declare (ignore array-tag))
-              ;; XXX: use of EVAL.  alien-size is a macro,
-              ;; unfortunately; and it will only accept unquoted type
-              ;; forms.
-              `(sb-alien:array ,elt-type ,(or array-size
-                                 (/ size (eval `(sb-alien:alien-size ,elt-type :bytes))))))
-            `(vector t))))
+               (declare (ignore array-tag))
+               ;; XXX: use of EVAL.  alien-size is a macro,
+               ;; unfortunately; and it will only accept unquoted type
+               ;; forms.
+               `(sb-alien:array ,elt-type ,(or array-size
+                                               (/ size (eval `(sb-alien:alien-size ,elt-type :bytes))))))
+             t)))
 
 (defun retrieve-type-for (type size table)
   (multiple-value-bind (type-fn found)
       (gethash (reintern (typecase type
-                          (list (first type))
-                          (t    type))
-                        (find-package '#:sb-grovel))
-              table)
+                           (list (first type))
+                           (t    type))
+                         (find-package '#:sb-grovel))
+               table)
     (values
      (if found
-        (funcall (the function type-fn) type size)
-        type)
+         (funcall (the function type-fn) type size)
+         type)
      found)))
 
 (defun alien-type-for (type size)
   (multiple-value-bind (val found)
       (retrieve-type-for type size lisp-type-table)
     (if found
-       val
-       t)))
+        val
+        t)))
 
 
 (defun mk-padding (len offset)
   (make-instance 'padding
-                :type `(array char ,len)
-                :offset offset
-                :size len
-                :name (gensym "PADDING")))
+                 :type `(array char ,len)
+                 :offset offset
+                 :size len
+                 :name (intern (format nil "PADDING-~D-~D" len offset))))
 (defun mk-struct (offset &rest children)
-  (make-instance 'struct :name (gensym "STRUCT")
-                :children (remove nil children)
-                :offset offset))
+  (make-instance 'struct :name (gentemp "STRUCT")
+                 :children (remove nil children)
+                 :offset offset))
 (defun mk-union (offset &rest children)
-  (make-instance 'union :name (gensym "UNION")
-                :children (remove nil children)
-                :offset offset))
+  (make-instance 'union :name (gentemp "UNION")
+                 :children (remove nil children)
+                 :offset offset))
 (defun mk-val (name type h-type offset size)
   (declare (ignore h-type))
   (make-instance 'value-slot :name name
-                :size size
-                :offset offset
-                :type type))
+                 :size size
+                 :offset offset
+                 :type type))
 
 ;;; struct tree classes
 
 (defmethod size ((slot structured-type))
   (let ((min-offset (offset slot)))
     (if (null (children slot))
-       0
-       (reduce #'max (mapcar (lambda (child)
-                               (+ (- (offset child) min-offset) (size child)))
-                             (children slot))
-               :initial-value 0))))
+        0
+        (reduce #'max (mapcar (lambda (child)
+                                (+ (- (offset child) min-offset) (size child)))
+                              (children slot))
+                :initial-value 0))))
 
 (defgeneric slot-end (slot))
 (defmethod slot-end ((slot slot))
 
 (defun overlap-p (elt1 elt2)
   (unless (or (zerop (size elt1))
-             (zerop (size elt2)))
+              (zerop (size elt2)))
     (or
      (and (<= (offset elt1)
-             (offset elt2))
-         (< (offset elt2)
-            (slot-end elt1)))
+              (offset elt2))
+          (< (offset elt2)
+             (slot-end elt1)))
      (and (<= (offset elt2)
-             (offset elt1))
-         (< (offset elt1)
-            (slot-end elt2))))))
+              (offset elt1))
+          (< (offset elt1)
+             (slot-end elt2))))))
 
 (defgeneric find-overlaps (root new-element))
 (defmethod find-overlaps ((root structured-type) new-element)
   (when (overlap-p root new-element)
     (let ((overlapping-elts (loop for child in (children root)
-                                 for overlap = (find-overlaps child new-element)
-                                 when overlap
-                                    return overlap)))
+                                  for overlap = (find-overlaps child new-element)
+                                  when overlap
+                                     return overlap)))
       (cons root overlapping-elts))))
 
 (defmethod find-overlaps ((root value-slot) new-element)
 
 (defgeneric pad-to-offset-of (to-pad parent))
   (macrolet ((skel (end-form)
-            `(let* ((end ,end-form)
-                    (len (abs (- (offset to-pad) end))))
-               (cond
-                 ((= end (offset to-pad)) ; we are at the right offset.
-                  nil)
-                 (t                    ; we have to pad between the
-                                       ; old slot's end and the new
-                                       ; slot's offset
-                  (mk-padding len end))))))
-  
+             `(let* ((end ,end-form)
+                     (len (abs (- (offset to-pad) end))))
+                (cond
+                  ((= end (offset to-pad)) ; we are at the right offset.
+                   nil)
+                  (t                    ; we have to pad between the
+                                        ; old slot's end and the new
+                                        ; slot's offset
+                   (mk-padding len end))))))
+
   (defmethod pad-to-offset-of (to-pad (parent struct))
     (skel (if (null (children parent))
-             0
-             (+ (size parent) (offset parent)))))
+              0
+              (+ (size parent) (offset parent)))))
   (defmethod pad-to-offset-of (to-pad (parent union))
     (skel (if (null (children parent))
-             (offset to-pad)
-             (offset parent)))))
+              (offset to-pad)
+              (offset parent)))))
 
 (defgeneric replace-by-union (in-st element new-element))
 (defmethod replace-by-union ((in-st struct) elt new-elt)
   (setf (children in-st) (remove elt (children in-st)))
   (let ((padding (pad-to-offset-of new-elt in-st)))
     (setf (children in-st)
-         (nconc (children in-st)
-                (list (mk-union (offset elt)
-                                elt
-                                (if padding
-                                    (mk-struct (offset elt)
-                                               padding
-                                               new-elt)
-                                    new-elt)))))))
+          (nconc (children in-st)
+                 (list (mk-union (offset elt)
+                                 elt
+                                 (if padding
+                                     (mk-struct (offset elt)
+                                                padding
+                                                new-elt)
+                                     new-elt)))))))
 
 (defmethod replace-by-union ((in-st union) elt new-elt)
   (let ((padding (pad-to-offset-of new-elt in-st)))
     (setf (children in-st)
-         (nconc (children in-st)
-                (list (if padding
-                          (mk-struct (offset in-st)
-                                     padding
-                                     new-elt)
-                          new-elt))))))
+          (nconc (children in-st)
+                 (list (if padding
+                           (mk-struct (offset in-st)
+                                      padding
+                                      new-elt)
+                           new-elt))))))
 
 (defgeneric insert-element (root new-elt))
 (defmethod insert-element ((root struct) (new-elt slot))
   (let ((overlaps (find-overlaps root new-elt)))
     (cond
       (overlaps (let ((last-structure (first (last overlaps 2)))
-                     (last-val (first (last overlaps))))
-                 (replace-by-union last-structure last-val new-elt)
-                 root))
+                      (last-val (first (last overlaps))))
+                  (replace-by-union last-structure last-val new-elt)
+                  root))
       (t
        (let ((padding (pad-to-offset-of new-elt root)))
-        (setf (children root)
-              (nconc (children root)
-                     (when padding (list padding))
-                     (list new-elt)))))))
+         (setf (children root)
+               (nconc (children root)
+                      (when padding (list padding))
+                      (list new-elt)))))))
   root)
 
 (defun sane-slot (alien-var &rest slots)
   "Emulates the SB-ALIEN:SLOT interface, with useful argument order for
 deeply nested structures."
   (labels ((rewriter (slots)
-            (if (null slots)
-                alien-var
-                `(sb-alien:slot ,(rewriter (rest slots))
-                                ',(first slots)))))
+             (if (null slots)
+                 alien-var
+                 `(sb-alien:slot ,(rewriter (rest slots))
+                                 ',(first slots)))))
     (rewriter slots)))
 
 (defgeneric accessor-modifier-for (element-type accessor-type))
 
-(defun identity-1 (thing &rest ignored)
-  (declare (ignore ignored))
-  thing)
-(defun (setf identity-1) (new-thing place &rest ignored)
-  (declare (ignore ignored))
-  (setf place new-thing))
-
 (defmethod accessor-modifier-for (element-type (accessor-type (eql :getter)))
-  'identity-1)
-(defmethod accessor-modifier-for ((element-type (eql 'C-STRING))
-                                 (accessor-type (eql :getter)))
-  'c-string->lisp-string)
+  nil)
 (defmethod accessor-modifier-for (element-type (accessor-type (eql :setter)))
   nil)
+
 (defmethod accessor-modifier-for ((element-type (eql 'C-STRING))
-                                 (accessor-type (eql :setter)))
-  'c-string->lisp-string)
+                                  (accessor-type (eql :getter)))
+  'c-string-reader[1])
+
 (defmethod accessor-modifier-for ((element-type (eql 'C-STRING))
-                                 (accessor-type (eql :getter)))
-  'c-string->lisp-string)
+                                  (accessor-type (eql :setter)))
+  'c-string-writer)
 
-(defun c-string->lisp-string (string &optional limit)
+;; The "[1]" in the name c-string-reader[1] refers to the CLHS
+;; glossary entry definitions for "reader".
+(defun c-string-reader[1] (place &optional limit)
   (declare (ignore limit))
-  (cast string c-string))
-
-(defun (setf c-string->lisp-string) (new-string alien &optional limit)
-  (declare (string new-string))
-  (let* ((upper-bound (or limit (1+ (length new-string))))
-        (last-elt (min (1- upper-bound) (length new-string))))
-    (loop for i upfrom 0 below last-elt
-         for char across new-string
-         do (setf (deref alien i) (char-code char)))
-    (setf (deref alien last-elt) 0)
-    (subseq new-string 0 last-elt)))
+  `(cast ,place c-string))
+
+(defun c-string-writer (string alien &optional limit)
+  (sb-int:with-unique-names
+      (stringvar upper-bound last-elt octets alien-ptr index)
+    `(let* ((,stringvar ,string)
+            (,upper-bound (or ,limit (1+ (length ,stringvar))))
+            (,last-elt (min (1- ,upper-bound) (length ,stringvar)))
+            (,octets (sb-ext:string-to-octets ,stringvar :end ,last-elt
+                                              :null-terminate t))
+            (,alien-ptr (cast ,alien (* unsigned-char))))
+       (declare (cl:type (simple-array (unsigned-byte 8) (*)) ,octets))
+       (declare (cl:type sb-int:index ,last-elt))
+       (dotimes (,index ,last-elt)
+         (setf (deref ,alien-ptr ,index) (aref ,octets ,index)))
+       (subseq ,stringvar 0 ,last-elt))))
 
 (defgeneric accessors-for (struct-name element path))
 (defmethod accessors-for (struct-name (root structured-type) path)
@@ -296,32 +295,42 @@ deeply nested structures."
 
 
 (defmethod accessors-for (struct-name (root value-slot) path)
-  (let ((rpath (reverse path))
-       (accessor-name (format nil "~A-~A"
-                              (symbol-name struct-name)
-                              (symbol-name (name root)))))
-    (labels ((accessor (root rpath)
-              (apply #'sane-slot 'struct (mapcar 'name (append (rest rpath) (list root))))))
-      `((defun ,(intern accessor-name) (struct)
-         (declare (type (alien ,struct-name) struct)
-                  (optimize (speed 3)))
-         (,(accessor-modifier-for (reintern (type root) (find-package :sb-grovel))
-                                  :getter)
-           ,(accessor root rpath) ,(size root)))
-       (defun (setf ,(intern accessor-name)) (new-val struct)
-         (declare (type (alien ,struct-name) struct)
-                  (type ,(lisp-type-for (type root) (size root)) new-val)
-                  (optimize (speed 3)))
-         ,(let* ((accessor-modifier (accessor-modifier-for (reintern (type root)
-                                                                     (find-package :sb-grovel))
-                                                           :setter))
-                 (modified-accessor (if accessor-modifier
-                                        `(,accessor-modifier ,(accessor root rpath) ,(size root))
-                                        (accessor root rpath))))
-            
-            `(setf ,modified-accessor new-val)))
-       (defconstant ,(intern (format nil "OFFSET-OF-~A" accessor-name))
-         ,(offset root))))))
+  (let* ((rpath (reverse path))
+         (accessor-name (intern
+                         (format nil "~A-~A"
+                                 (symbol-name struct-name)
+                                 (symbol-name (name root)))))
+         (offset-constant-name (intern
+                                (format nil "OFFSET-OF-~A" accessor-name)))
+         (var (gensym "VAR-"))
+         (place (apply #'sane-slot 'struct
+                       (mapcar 'name (append (rest rpath) (list root)))))
+         (reader (let ((reader (accessor-modifier-for
+                                (reintern (type root)
+                                          (find-package :sb-grovel))
+                                :getter)))
+                   (if reader
+                       (funcall reader place (size root))
+                       place)))
+         (writer (let ((writer (accessor-modifier-for
+                                (reintern (type root)
+                                          (find-package :sb-grovel))
+                                :setter)))
+                   (if writer
+                       (funcall writer var place (size root))
+                       `(setf ,place ,var)))))
+    `((declaim (inline ,accessor-name (setf ,accessor-name)))
+      (defun ,accessor-name (struct)
+        (declare (cl:type (alien (* ,struct-name)) struct)
+                 (optimize (speed 3)))
+        ,reader)
+      (defun (setf ,accessor-name) (,var struct)
+        (declare (cl:type (alien (* ,struct-name)) struct)
+                 (cl:type ,(lisp-type-for (type root) (size root)) ,var)
+                 (optimize (speed 3)))
+        ,writer)
+      (defconstant ,offset-constant-name
+                   ,(offset root)))))
 
 
 
@@ -331,66 +340,77 @@ deeply nested structures."
 (defgeneric generate-struct-definition (struct-name root path))
 (defmethod generate-struct-definition (struct-name (root structured-type) path)
   (let ((naccessors (accessors-for struct-name root path))
-       (nslots nil))
+        (nslots nil))
     (dolist (child (children root))
       (multiple-value-bind (slots accessors)
-         (generate-struct-definition struct-name child (cons root path))
-       (setf nslots (nconc nslots slots))
-       (setf naccessors (nconc naccessors accessors))))
+          (generate-struct-definition struct-name child (cons root path))
+        (setf nslots (nconc nslots slots))
+        (setf naccessors (nconc naccessors accessors))))
     (values `((,(name root) (,(type-of root) ,(name root) ,@nslots)))
-           naccessors)))
+            naccessors)))
 
 (defmethod generate-struct-definition (struct-name (root value-slot) path)
   (values `((,(name root) ,(alien-type-for (type root) (size root))))
-         (accessors-for struct-name root path)))
+          (accessors-for struct-name root path)))
 
 (defmacro define-c-struct (name size &rest elements)
   (multiple-value-bind (struct-elements accessors)
       (let* ((root (make-instance 'struct :name name :children nil :offset 0)))
-       (loop for e in (sort elements #'< :key #'fourth)
-             do (insert-element root (apply 'mk-val e))
-             finally (return root))
-       (setf (children root)
-             (nconc (children root)
-                    (list
-                     (mk-padding (max 0 (- size
-                                           (size root)))
-                                 (size root)))))
-       (generate-struct-definition name root nil))
-    `(progn
-       (eval-when (:compile-toplevel :load-toplevel :execute)
-        (sb-alien:define-alien-type ,@(first struct-elements)))
-       ,@accessors
-       (defmacro ,(intern (format nil "WITH-~A" name)) (var (&rest field-values) &body body)
-        (labels ((field-name (x)
-                   (intern (concatenate 'string
-                                        (symbol-name ',name) "-"
-                                        (symbol-name x))
-                           ,(symbol-package name))))
-          `(let ((,var ,'(,(intern (format nil "ALLOCATE-~A" name)))))
-             (unwind-protect
-                 (progn
-                   (progn ,@(mapcar (lambda (pair)
-                                      `(setf (,(field-name (first pair)) ,var) ,(second pair)))
-                                    field-values))
-                   ,@body)
-               (funcall ',',(intern (format nil "FREE-~A" name)) ,var)))))
-       (defconstant ,(intern (format nil "SIZE-OF-~A" name)) ,size)
-       (defun ,(intern (format nil "ALLOCATE-~A" name)) ()
-        (let* ((o (sb-alien:make-alien ,name))
-               (c-o (cast o (* (unsigned 8)))))
-          ;; we have to initialize the object to all-0 before we can
-          ;; expect to make sensible use of it - the object returned
-          ;; by make-alien is initialized to all-D0 bytes.
-          
-          ;; FIXME: This should be fixed in sb-alien, where better
-          ;; optimizations might be possible.
-          (loop for i from 0 below ,size 
-                do (setf (deref c-o i) 0))
-          o))
-       (defun ,(intern (format nil "FREE-~A" name)) (o)
-        (sb-alien:free-alien o)))))
-
+        (loop for e in (sort elements #'< :key #'fourth)
+              do (insert-element root (apply 'mk-val e))
+              finally (return root))
+        (setf (children root)
+              (nconc (children root)
+                     (list
+                      (mk-padding (max 0 (- size
+                                            (size root)))
+                                  (size root)))))
+        (generate-struct-definition name root nil))
+    (sb-int:with-unique-names (var field-values body field-name pair
+                                   object c-object index)
+      (let ((with (intern (format nil "WITH-~A" name)))
+            (allocate (intern (format nil "ALLOCATE-~A" name)))
+            (free (intern (format nil "FREE-~A" name)))
+            (size-of (intern (format nil "SIZE-OF-~A" name))))
+        `(progn
+           (sb-alien:define-alien-type ,@(first struct-elements))
+           ,@accessors
+           (defmacro ,with (,var (&rest ,field-values) &body ,body)
+             (labels ((,field-name (,var)
+                        (intern
+                         (format nil ,(format nil "~A-~~A" (symbol-name name))
+                                 (symbol-name ,var))
+                         ,(symbol-package name))))
+               `(sb-alien:with-alien ((,,var (* ,',name) ,'(,allocate)))
+                  (unwind-protect
+                      (progn
+                        (setf ,@(mapcan
+                                 (lambda (,pair)
+                                   `((,(,field-name (first ,pair)) ,,var)
+                                     ,(second ,pair)))
+                                 ,field-values))
+                        ,@,body)
+                    (,',free ,,var)))))
+           (defconstant ,size-of ,size)
+           (defun ,allocate ()
+             (let* ((,object (sb-alien:make-alien ,name))
+                    (,c-object (cast ,object (* (unsigned 8)))))
+               ;; we have to initialize the object to all-0 before we can
+               ;; expect to make sensible use of it - the object returned
+               ;; by make-alien is initialized to all-D0 bytes.
+
+               ;; FIXME: This should be fixed in sb-alien, where better
+               ;; optimizations might be possible.
+               (dotimes (,index ,size)
+                 (setf (deref ,c-object ,index) 0))
+               ,object))
+           (defun ,free (,object)
+             (sb-alien:free-alien ,object)))))))
+
+;; FIXME: Nothing in SBCL uses this, but kept it around in case there
+;; are third-party sb-grovel clients.  It should go away eventually,
+;; on the principle that sb-grovel should only have to be loaded in
+;; order to do an actual groveling run.
 (defun foreign-nullp (c)
-  "C is a pointer to 0?"
-  (null-alien c))
\ No newline at end of file
+  "Deprecated.  Use SB-ALIEN:NULL-ALIEN instead."
+  (null-alien c))