X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=contrib%2Fsb-grovel%2Fforeign-glue.lisp;h=dcbda61d1de4d40031610d98ab374e683e5ac45a;hb=b27852e74e11ccc9808305a73d815a4a67d77963;hp=b2c1f00bbffa25c15d155d0d9a428d562067659b;hpb=1de341cf0652fb0eb8354f64d95acb0899811173;p=sbcl.git diff --git a/contrib/sb-grovel/foreign-glue.lisp b/contrib/sb-grovel/foreign-glue.lisp index b2c1f00..dcbda61 100644 --- a/contrib/sb-grovel/foreign-glue.lisp +++ b/contrib/sb-grovel/foreign-glue.lisp @@ -13,84 +13,404 @@ (&whole it (c-name lisp-name) return-type &rest args) (declare (ignorable c-name lisp-name return-type args)) `(define-alien-routine ,@(cdr it))) -#|| -(define-c-accessor FOO-PORT SOCKADDR-IN (ARRAY (UNSIGNED 8) 2) 2 2) -(define-c-accessor SOCKADDR-IN-FAMILY SOCKADDR-IN INTEGER 6 2) -||# -;;; define-c-accessor makes us a setter and a getter for changing -;;; memory at the appropriate offset - -;;; (define-c-accessor STAT-ATIME STAT (INTEGER 32) 56 4) - - -(defmacro define-c-accessor (el structure type offset length) - (declare (ignore structure)) - (let* ((ty (cond - ((eql type 'integer) `(,type ,(* 8 length))) - ((eql (car type) '*) `(unsigned ,(* 8 length))) - ((eql type 'c-string) `(unsigned ,(* 8 length))) - ((eql (car type) 'array) (cadr type)))) - (sap-ref-? (intern (format nil "~ASAP-REF-~A" - (if (member (car ty) '(INTEGER SIGNED)) - "SIGNED-" "") - (cadr ty)) - (find-package "SB-SYS")))) - (labels ((template (before after) - `(let* ((addr (+ 8 (logandc1 7 (sb-kernel:get-lisp-obj-address ptr)))) - (sap (sb-sys:int-sap (the (unsigned-byte 32) (+ addr ,offset))))) - (,before (,sap-ref-? sap index) ,after)))) - `(progn - ;;(declaim (inline ,el (setf ,el))) - (defun ,el (ptr &optional (index 0)) - (declare (optimize (speed 3))) - (sb-sys:without-gcing - ,(template 'prog1 nil))) - (defconstant ,(intern (format nil "OFFSET-OF-~A" el)) ,offset) - (defun (setf ,el) (newval ptr &optional (index 0)) - (declare (optimize (speed 3))) - (sb-sys:without-gcing - ,(template 'setf 'newval))))))) - - -;;; make memory allocator for appropriately-sized block of memory, and -;;; a constant to tell us how big it was anyway -(defmacro define-c-struct (name size) - (labels ((p (x) (intern (concatenate 'string x (symbol-name name)) - (symbol-package name)))) - `(progn - (defun ,(p "ALLOCATE-") () (make-array ,size :initial-element 0 - :element-type '(unsigned-byte 8))) - (defconstant ,(p "SIZE-OF-") ,size) - (defun ,(p "FREE-" ) (p) (declare (ignore p))) - (defmacro ,(p "WITH-") (var (&rest field-values) &body body) - (labels ((field-name (x) - (intern (concatenate 'string - (symbol-name ',name) "-" - (symbol-name x)) - ,(symbol-package name)))) - (append `(let ((,var ,'(,(p "ALLOCATE-"))))) - (mapcar (lambda (pair) - `(setf (,(field-name (car pair)) ,var) ,(cadr pair))) - field-values) - body)))))) + + +;;; strctures + +#| C structs need: the with-... interface. +|# + +;;; global XXXs: +#| + XXX: :distrust-length t fields are dangerous. they should only be at + the end of the structure (they mess up offset/size calculations) +|# + +(defun reintern (symbol &optional (package *package*)) + (if (symbolp symbol) + (intern (symbol-name symbol) package) + symbol)) + +(defparameter alien-type-table (make-hash-table :test 'eql)) +(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)))))))) + (define-alien-types (type size) + (integer (or (gethash size (symbol-value (intern "*INTEGER-SIZES*"))) + `(integer ,(* 8 size))) + `(unsigned-byte ,(* 8 size))) + (unsigned `(unsigned ,(* 8 size)) + `(unsigned-byte ,(* 8 size))) + (signed `(signed ,(* 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)))))) + 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) + (values + (if found + (funcall (the function type-fn) type size) + type) + found))) + +(defun alien-type-for (type size) + (reintern (retrieve-type-for type size alien-type-table))) + +(defun lisp-type-for (type size) + (multiple-value-bind (val found) + (retrieve-type-for type size lisp-type-table) + (if found + val + t))) + + +(defun mk-padding (len offset) + (make-instance '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 (gentemp "STRUCT") + :children (remove nil children) + :offset offset)) +(defun mk-union (offset &rest children) + (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)) + +;;; struct tree classes + +(defclass slot () + ((offset :initarg :offset :reader offset) + (name :initarg :name :reader name))) + +(defclass structured-type (slot) + ((children :initarg :children :accessor children))) + +(defclass union (structured-type) + ()) + +(defclass struct (structured-type) + ()) + +(defclass value-slot (slot) + ((size :initarg :size :reader size) + (type :initarg :type :reader type))) + +(defclass padding (value-slot) + ()) + +(defmethod print-object ((o value-slot) s) + (print-unreadable-object (o s :type t) + (format s "~S ~A+~A=~A" (name o) (offset o) (size o) (slot-end o)))) + +(defmethod print-object ((o structured-type) s) + (print-unreadable-object (o s :type t) + (format s "~S ~A" (name o) (children o)))) + +(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)))) + +(defgeneric slot-end (slot)) +(defmethod slot-end ((slot slot)) + (+ (offset slot) (size slot))) + +(defun overlap-p (elt1 elt2) + (unless (or (zerop (size elt1)) + (zerop (size elt2))) + (or + (and (<= (offset elt1) + (offset elt2)) + (< (offset elt2) + (slot-end elt1))) + (and (<= (offset 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))) + (cons root overlapping-elts)))) + +(defmethod find-overlaps ((root value-slot) new-element) + (when (overlap-p root new-element) + (list root))) + +(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)))))) + + (defmethod pad-to-offset-of (to-pad (parent struct)) + (skel (if (null (children 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))))) + +(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))))))) + +(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)))))) + +(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)) + (t + (let ((padding (pad-to-offset-of new-elt root))) + (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))))) + (rewriter slots))) + +(defgeneric accessor-modifier-for (element-type accessor-type)) + +(defmethod accessor-modifier-for (element-type (accessor-type (eql :getter))) + nil) +(defmethod accessor-modifier-for (element-type (accessor-type (eql :setter))) + nil) + +(defmethod accessor-modifier-for ((element-type (eql 'C-STRING)) + (accessor-type (eql :getter))) + 'c-string-reader[1]) + +(defmethod accessor-modifier-for ((element-type (eql 'C-STRING)) + (accessor-type (eql :setter))) + 'c-string-writer) + +;; 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 ,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) + nil) + + +(defmethod accessors-for (struct-name (root value-slot) path) + (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))))) + + + +(defmethod accessors-for (struct (root padding) path) + nil) + +(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)) + (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)))) + (values `((,(name root) (,(type-of root) ,(name root) ,@nslots))) + 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))) + +(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)) + (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?" - (= 0 (sb-sys:sap-int (sb-alien:alien-sap c)))) - -;;; this could be a lot faster if I cared enough to think about it -(defun foreign-vector (pointer size length) - "Compose a vector of the words found in foreign memory starting at -POINTER. Each word is SIZE bytes long; LENGTH gives the number of -elements of the returned vector. See also FOREIGN-VECTOR-UNTIL-ZERO" - (assert (= size 1)) - (let ((ptr - (typecase pointer - (sb-sys:system-area-pointer - (sap-alien pointer (* (sb-alien:unsigned 8)))) - (t - (sb-alien:cast pointer (* (sb-alien:unsigned 8)))))) - (result (make-array length :element-type '(unsigned-byte 8)))) - (loop for i from 0 to (1- length) by size - do (setf (aref result i) (sb-alien:deref ptr i))) - result)) + "Deprecated. Use SB-ALIEN:NULL-ALIEN instead." + (null-alien c))