;; License along with this library; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-;; $Id: glib.lisp,v 1.1 2000/08/14 16:44:31 espen Exp $
+;; $Id: glib.lisp,v 1.27 2005/02/14 00:44:26 espen Exp $
(in-package "GLIB")
+
(use-prefix "g")
;;;; Memory management
-(define-foreign ("g_malloc0" allocate-memory) () pointer
+(defbinding (allocate-memory "g_malloc0") () pointer
(size unsigned-long))
-(define-foreign ("g_realloc" reallocate-memory) () pointer
+(defbinding (reallocate-memory "g_realloc") () pointer
(address pointer)
(size unsigned-long))
-(define-foreign ("g_free" deallocate-memory) () nil
+(defbinding (deallocate-memory "g_free") () nil
(address pointer))
+;; (defun deallocate-memory (address)
+;; (declare (ignore address)))
(defun copy-memory (from length &optional (to (allocate-memory length)))
- (kernel:system-area-copy from 0 to 0 (* 8 length))
+ (;#+cmu kernel:system-area-copy
+ ;#+sbcl sb-impl::system-area-copy
+ system-area-copy from 0 to 0 (* 8 length))
to)
+;;;; User data mechanism
+
+(internal *user-data* *user-data-count*)
+
+(defvar *user-data* (make-hash-table))
+(defvar *user-data-count* 0)
+
+(defun register-user-data (object &optional destroy-function)
+ (check-type destroy-function (or null symbol function))
+ (incf *user-data-count*)
+ (setf
+ (gethash *user-data-count* *user-data*)
+ (cons object destroy-function))
+ *user-data-count*)
+
+(defun find-user-data (id)
+ (check-type id fixnum)
+ (multiple-value-bind (user-data p) (gethash id *user-data*)
+ (values (car user-data) p)))
+
+(defun user-data-exists-p (id)
+ (nth-value 1 (find-user-data id)))
-;;;; Linked list
+(defun update-user-data (id object)
+ (check-type id fixnum)
+ (multiple-value-bind (user-data exists-p) (gethash id *user-data*)
+ (cond
+ ((not exists-p) (error "User data id ~A does not exist" id))
+ (t
+ (when (cdr user-data)
+ (funcall (cdr user-data) (car user-data)))
+ (setf (car user-data) object)))))
-(deftype glist () 'pointer)
-(deftype double-list (type) `(or (null (cons ,type list))))
+(defun destroy-user-data (id)
+ (check-type id fixnum)
+ (let ((user-data (gethash id *user-data*)))
+ (when (cdr user-data)
+ (funcall (cdr user-data) (car user-data))))
+ (remhash id *user-data*))
-(define-foreign ("g_list_append" %glist-append) () glist
- (glist glist)
- (data unsigned))
+;;;; Quarks
-(defmacro glist-append (glist value type-spec)
- (ecase (first (mklist (translate-type-spec type-spec)))
- (unsigned `(%glist-append ,glist ,value))
-; (signed `(%glist-append ,glist (signed-to-unsigned ,value)))
- (system-area-pointer `(%glist-append ,glist (system:sap-int ,value)))))
+(deftype quark () 'unsigned)
+(defbinding %quark-from-string () quark
+ (string string))
-(defmacro glist-data (glist type-spec)
- (ecase (first (mklist (translate-type-spec type-spec)))
- (unsigned `(sap-ref-unsigned ,glist 0))
- (signed `(sap-ref-signed ,glist 0))
- (system-area-pointer `(sap-ref-sap ,glist 0))))
+(defun quark-intern (object)
+ (etypecase object
+ (quark object)
+ (string (%quark-from-string object))
+ (symbol (%quark-from-string (format nil "clg-~A:~A"
+ (package-name (symbol-package object))
+ object)))))
+(defbinding quark-to-string () (copy-of string)
+ (quark quark))
+
+
+;;;; Linked list (GList)
+
+(deftype glist (type)
+ `(or (null (cons ,type list))))
+
+(defbinding (%glist-append "g_list_append") () pointer
+ (glist pointer)
+ (nil null))
+
+(defun make-glist (type list)
+ (loop
+ with writer = (writer-function type)
+ for element in list
+ as glist = (%glist-append (or glist (make-pointer 0)))
+ do (funcall writer element glist)
+ finally (return glist)))
(defun glist-next (glist)
(unless (null-pointer-p glist)
- (sap-ref-sap glist +size-of-sap+)))
-
+ (sap-ref-sap glist +size-of-pointer+)))
-(define-foreign ("g_list_free" glist-free) () nil
+;; Also used for gslists
+(defun map-glist (seqtype function glist element-type)
+ (let ((reader (reader-function element-type)))
+ (case seqtype
+ ((nil)
+ (loop
+ as tmp = glist then (glist-next tmp)
+ until (null-pointer-p tmp)
+ do (funcall function (funcall reader tmp))))
+ (list
+ (loop
+ as tmp = glist then (glist-next tmp)
+ until (null-pointer-p tmp)
+ collect (funcall function (funcall reader tmp))))
+ (t
+ (coerce
+ (loop
+ as tmp = glist then (glist-next tmp)
+ until (null-pointer-p tmp)
+ collect (funcall function (funcall reader tmp)))
+ seqtype)))))
+
+(defbinding (glist-free "g_list_free") () nil
(glist pointer))
-
-(deftype-method translate-type-spec double-list (type-spec)
- (declare (ignore type-spec))
- 'system-area-pointer)
-
-(deftype-method translate-to-alien double-list (type-spec list &optional copy)
- (declare (ignore copy))
- (let* ((element-type-spec (second (type-expand-to 'double-list type-spec)))
- (to-alien (translate-to-alien element-type-spec 'element t)))
- `(let ((glist (make-pointer 0)))
- (dolist (element ,list glist)
- (setq glist (glist-append glist ,to-alien element-type-spec))))))
-
-(deftype-method
- translate-from-alien
- double-list (type-spec glist &optional (alloc :dynamic))
- (let ((element-type-spec (second (type-expand-to 'double-list type-spec))))
- `(let ((glist ,glist)
- (list nil))
- (do ((tmp glist (glist-next tmp)))
- ((null-pointer-p tmp))
- (push
- ,(translate-from-alien
- element-type-spec `(glist-data tmp ,element-type-spec) alloc)
- list))
- ,(when (eq alloc :dynamic)
- '(glist-free glist))
- (nreverse list))))
-
-(deftype-method cleanup-alien double-list (type-spec glist &optional copied)
- (declare (ignore copied))
- (let* ((element-type-spec (second (type-expand-to 'double-list type-spec)))
- (alien-type-spec (translate-type-spec element-type-spec)))
+(defun destroy-glist (glist element-type)
+ (loop
+ with destroy = (destroy-function element-type)
+ as tmp = glist then (glist-next tmp)
+ until (null-pointer-p tmp)
+ do (funcall destroy tmp 0))
+ (glist-free glist))
+
+(defmethod alien-type ((type (eql 'glist)) &rest args)
+ (declare (ignore type args))
+ (alien-type 'pointer))
+
+(defmethod size-of ((type (eql 'glist)) &rest args)
+ (declare (ignore type args))
+ (size-of 'pointer))
+
+(defmethod to-alien-form (list (type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ `(make-glist ',element-type ,list)))
+
+(defmethod to-alien-function ((type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (list)
+ (make-glist element-type list))))
+
+(defmethod from-alien-form (glist (type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
`(let ((glist ,glist))
- (unless (null-pointer-p glist)
- ,(when (eq alien-type-spec 'system-area-pointer)
- `(do ((tmp glist (glist-next tmp)))
- ((null-pointer-p tmp))
- ,(cleanup-alien element-type-spec '(glist-data tmp) t)))
- (glist-free glist)))))
-
-
-
-;;; Array
-#|
-(define-foreign ("g_array_new" %array-new) () garray
- (zero-terminated boolean)
- (clear boolean)
- (element-size unsigned-int))
-
-(defun array-new (&key zero-terminated clear (element-size 4) initial-contents)
- (let ((array (%array-new zero-terminated clear element-size)))
- (when initial-contents
- (dolist (element initial-contents)
- (array-append array element)))
- array))
-
-(define-foreign ("g_array_free" %array-free) () none
- (array garray)
- (free-segment boolean))
-
-(defun array-free (array &optional free-data (free-segment t))
- (when free-data
- (dotimes (i (array-get-size array))
- (free (array-get-pointer array i))))
- (%array-free array free-segment))
-
-(defmacro with-array (binding &body body)
- (let ((array (gensym)))
- (destructuring-bind (var &rest args
- &key (free-contents nil) (free-segment t)
- &allow-other-keys )
- binding
- (remf args :free-contents)
- (remf args :free-segment)
- `(let* ((,array (array-new ,@args))
- (,var (array-get-data ,array)))
- (unwind-protect
- ,@body
- (array-free ,array ,free-contents ,free-segment))))))
-
-;; cl-gtk.c
-(define-foreign ("g_array_insert_int" array-insert-int) () garray
- (array garray)
- (index unsigned-int)
- (value int))
-
-(defun array-insert-value (array index value)
- (etypecase value
- (null (array-insert-int array index 0))
- (integer (array-insert-int array index value))
- (string (array-insert-int array index (sap-int (gforeign::pointer-to-sap (%strdup value)))))
- (pointer (array-insert-int array index (sap-int (gforeign::pointer-to-sap value))))))
-
-(defun array-prepend (array value)
- (array-insert-value array 0 value))
-
-(defun array-append (array value)
- (array-insert-value array (array-get-size array) value))
-
-;; cl-gtk.c
-(define-foreign ("g_array_get_int" array-get-int) () int
- (array garray)
- (index unsigned-int))
-
-(defun array-get-pointer (array index)
- (gforeign::sap-to-pointer (int-sap (array-get-int array index))))
-
-;; cl-gtk.c
-(define-foreign ("g_array_get_data" array-get-data) () pointer
- (array garray))
-
-(define-foreign ("g_array_set_size" array-set-size) () garray
- (array garray)
- (size unsigned-int))
-
-;; cl-gtk.c
-(define-foreign ("g_array_get_size" array-get-size) () int
- (array garray))
-|#
\ No newline at end of file
+ (unwind-protect
+ (map-glist 'list #'identity glist ',element-type)
+ (destroy-glist glist ',element-type)))))
+
+(defmethod from-alien-function ((type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (glist)
+ (unwind-protect
+ (map-glist 'list #'identity glist element-type)
+ (destroy-glist glist element-type)))))
+
+(defmethod copy-from-alien-form (glist (type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ `(map-glist 'list #'identity ,glist ',element-type)))
+
+(defmethod copy-from-alien-function ((type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (glist)
+ (map-glist 'list #'identity glist element-type))))
+
+(defmethod cleanup-form (glist (type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ `(destroy-glist ,glist ',element-type)))
+
+(defmethod cleanup-function ((type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (glist)
+ (destroy-glist glist element-type))))
+
+(defmethod writer-function ((type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (list location &optional (offset 0))
+ (setf
+ (sap-ref-sap location offset)
+ (make-glist element-type list)))))
+
+(defmethod reader-function ((type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (location &optional (offset 0))
+ (unless (null-pointer-p (sap-ref-sap location offset))
+ (map-glist 'list #'identity (sap-ref-sap location offset) element-type)))))
+
+(defmethod destroy-function ((type (eql 'glist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (location &optional (offset 0))
+ (unless (null-pointer-p (sap-ref-sap location offset))
+ (destroy-glist (sap-ref-sap location offset) element-type)
+ (setf (sap-ref-sap location offset) (make-pointer 0))))))
+
+
+
+;;;; Single linked list (GSList)
+
+(deftype gslist (type) `(or (null (cons ,type list))))
+
+(defbinding (%gslist-prepend "g_slist_prepend") () pointer
+ (gslist pointer)
+ (nil null))
+
+(defun make-gslist (type list)
+ (loop
+ with writer = (writer-function type)
+ for element in (reverse list)
+ as gslist = (%gslist-prepend (or gslist (make-pointer 0)))
+ do (funcall writer element gslist)
+ finally (return gslist)))
+
+(defbinding (gslist-free "g_slist_free") () nil
+ (gslist pointer))
+
+(defun destroy-gslist (gslist element-type)
+ (loop
+ with destroy = (destroy-function element-type)
+ as tmp = gslist then (glist-next tmp)
+ until (null-pointer-p tmp)
+ do (funcall destroy tmp 0))
+ (gslist-free gslist))
+
+(defmethod alien-type ((type (eql 'gslist)) &rest args)
+ (declare (ignore type args))
+ (alien-type 'pointer))
+
+(defmethod size-of ((type (eql 'gslist)) &rest args)
+ (declare (ignore type args))
+ (size-of 'pointer))
+
+(defmethod to-alien-form (list (type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ `(make-sglist ',element-type ,list)))
+
+(defmethod to-alien-function ((type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (list)
+ (make-gslist element-type list))))
+
+(defmethod from-alien-form (gslist (type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ `(let ((gslist ,gslist))
+ (unwind-protect
+ (map-glist 'list #'identity gslist ',element-type)
+ (destroy-gslist gslist ',element-type)))))
+
+(defmethod from-alien-function ((type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (gslist)
+ (unwind-protect
+ (map-glist 'list #'identity gslist element-type)
+ (destroy-gslist gslist element-type)))))
+
+(defmethod copy-from-alien-form (gslist (type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ `(map-glist 'list #'identity ,gslist ',element-type)))
+
+(defmethod copy-from-alien-function ((type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (gslist)
+ (map-glist 'list #'identity gslist element-type))))
+
+(defmethod cleanup-form (gslist (type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ `(destroy-gslist ,gslist ',element-type)))
+
+(defmethod cleanup-function ((type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (gslist)
+ (destroy-gslist gslist element-type))))
+
+(defmethod writer-function ((type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (list location &optional (offset 0))
+ (setf
+ (sap-ref-sap location offset)
+ (make-gslist element-type list)))))
+
+(defmethod reader-function ((type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (location &optional (offset 0))
+ (unless (null-pointer-p (sap-ref-sap location offset))
+ (map-glist 'list #'identity (sap-ref-sap location offset) element-type)))))
+
+(defmethod destroy-function ((type (eql 'gslist)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ #'(lambda (location &optional (offset 0))
+ (unless (null-pointer-p (sap-ref-sap location offset))
+ (destroy-gslist (sap-ref-sap location offset) element-type)
+ (setf (sap-ref-sap location offset) (make-pointer 0))))))
+
+
+;;; Vector
+
+(defun make-c-vector (type length &optional content location)
+ (let* ((size-of-type (size-of type))
+ (location (or location (allocate-memory (* size-of-type length))))
+ (writer (writer-function type)))
+ (etypecase content
+ (vector
+ (loop
+ for element across content
+ for i from 0 below length
+ as offset = 0 then (+ offset size-of-type)
+ do (funcall writer element location offset)))
+ (list
+ (loop
+ for element in content
+ for i from 0 below length
+ as offset = 0 then (+ offset size-of-type)
+ do (funcall writer element location offset))))
+ location))
+
+
+(defun map-c-vector (seqtype function location element-type length)
+ (let ((reader (reader-function element-type))
+ (size-of-element (size-of element-type)))
+ (case seqtype
+ ((nil)
+ (loop
+ for i from 0 below length
+ as offset = 0 then (+ offset size-of-element)
+ do (funcall function (funcall reader location offset))))
+ (list
+ (loop
+ for i from 0 below length
+ as offset = 0 then (+ offset size-of-element)
+ collect (funcall function (funcall reader location offset))))
+ (t
+ (loop
+ with sequence = (make-sequence seqtype length)
+ for i from 0 below length
+ as offset = 0 then (+ offset size-of-element)
+ do (setf
+ (elt sequence i)
+ (funcall function (funcall reader location offset)))
+ finally (return sequence))))))
+
+
+(defun destroy-c-vector (location element-type length)
+ (loop
+ with destroy = (destroy-function element-type)
+ with element-size = (size-of element-type)
+ for i from 0 below length
+ as offset = 0 then (+ offset element-size)
+ do (funcall destroy location offset))
+ (deallocate-memory location))
+
+
+(defmethod alien-type ((type (eql 'vector)) &rest args)
+ (declare (ignore type args))
+ (alien-type 'pointer))
+
+(defmethod size-of ((type (eql 'vector)) &rest args)
+ (declare (ignore type args))
+ (size-of 'pointer))
+
+(defmethod to-alien-form (vector (type (eql 'vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type &optional (length '*)) args
+ (if (eq length '*)
+ `(let* ((vector ,vector)
+ (location (sap+
+ (allocate-memory (+ ,+size-of-int+
+ (* ,(size-of element-type)
+ (length vector))))
+ ,+size-of-int+)))
+ (make-c-vector ',element-type (length vector) vector location)
+ (setf (sap-ref-32 location ,(- +size-of-int+)) (length vector))
+ location)
+ `(make-c-vector ',element-type ,length ,vector))))
+
+(defmethod from-alien-form (c-vector (type (eql 'vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type &optional (length '*)) args
+ (if (eq length '*)
+ (error "Can't use vector of variable size as return type")
+ `(let ((c-vector ,c-vector))
+ (prog1
+ (map-c-vector 'vector #'identity c-vector ',element-type ,length)
+ (destroy-c-vector c-vector ',element-type ,length))))))
+
+(defmethod copy-from-alien-form (c-vector (type (eql 'vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type &optional (length '*)) args
+ (if (eq length '*)
+ (error "Can't use vector of variable size as return type")
+ `(map-c-vector 'vector #'identity ,c-vector ',element-type ',length))))
+
+(defmethod cleanup-form (location (type (eql 'vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type &optional (length '*)) args
+ `(let* ((location ,location)
+ (length ,(if (eq length '*)
+ `(sap-ref-32 location ,(- +size-of-int+))
+ length)))
+ (loop
+ with destroy = (destroy-function ',element-type)
+ for i from 0 below length
+ as offset = 0 then (+ offset ,(size-of element-type))
+ do (funcall destroy location offset))
+ (deallocate-memory ,(if (eq length '*)
+ `(sap+ location ,(- +size-of-int+))
+ 'location)))))
+
+(defmethod writer-function ((type (eql 'vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type &optional (length '*)) args
+ #'(lambda (vector location &optional (offset 0))
+ (setf
+ (sap-ref-sap location offset)
+ (make-c-vector element-type length vector)))))
+
+(defmethod reader-function ((type (eql 'vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type &optional (length '*)) args
+ (if (eq length '*)
+ (error "Can't create reader function for vector of variable size")
+ #'(lambda (location &optional (offset 0))
+ (unless (null-pointer-p (sap-ref-sap location offset))
+ (map-c-vector 'vector #'identity (sap-ref-sap location offset)
+ element-type length))))))
+
+(defmethod destroy-function ((type (eql 'vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type &optional (length '*)) args
+ (if (eq length '*)
+ (error "Can't create destroy function for vector of variable size")
+ #'(lambda (location &optional (offset 0))
+ (unless (null-pointer-p (sap-ref-sap location offset))
+ (destroy-c-vector
+ (sap-ref-sap location offset) element-type length)
+ (setf (sap-ref-sap location offset) (make-pointer 0)))))))
+
+
+;;;; Null terminated vector
+
+(defun make-0-vector (type content &optional location)
+ (let* ((size-of-type (size-of type))
+ (location (or
+ location
+ (allocate-memory (* size-of-type (1+ (length content))))))
+ (writer (writer-function type)))
+ (etypecase content
+ (vector
+ (loop
+ for element across content
+ as offset = 0 then (+ offset size-of-type)
+ do (funcall writer element location offset)
+ finally (setf (sap-ref-sap location offset) (make-pointer 0))))
+ (list
+ (loop
+ for element in content
+ as offset = 0 then (+ offset size-of-type)
+ do (funcall writer element location offset)
+ finally (setf (sap-ref-sap location (+ offset size-of-type)) (make-pointer 0)))))
+ location))
+
+
+(defun map-0-vector (seqtype function location element-type)
+ (let ((reader (reader-function element-type))
+ (size-of-element (size-of element-type)))
+ (case seqtype
+ ((nil)
+ (loop
+ as offset = 0 then (+ offset size-of-element)
+ until (null-pointer-p (sap-ref-sap location offset))
+ do (funcall function (funcall reader location offset))))
+ (list
+ (loop
+ as offset = 0 then (+ offset size-of-element)
+ until (null-pointer-p (sap-ref-sap location offset))
+ collect (funcall function (funcall reader location offset))))
+ (t
+ (coerce
+ (loop
+ as offset = 0 then (+ offset size-of-element)
+ until (null-pointer-p (sap-ref-sap location offset))
+ collect (funcall function (funcall reader location offset)))
+ seqtype)))))
+
+
+(defun destroy-0-vector (location element-type)
+ (loop
+ with destroy = (destroy-function element-type)
+ with element-size = (size-of element-type)
+ as offset = 0 then (+ offset element-size)
+ until (null-pointer-p (sap-ref-sap location offset))
+ do (funcall destroy location offset))
+ (deallocate-memory location))
+
+
+(defmethod alien-type ((type (eql 'null-terminated-vector)) &rest args)
+ (declare (ignore type args))
+ (alien-type 'pointer))
+
+(defmethod size-of ((type (eql 'null-terminated-vector)) &rest args)
+ (declare (ignore type args))
+ (alien-type 'pointer))
+
+(defmethod writer-function ((type (eql 'null-terminated-vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ (unless (eq (alien-type element-type) (alien-type 'pointer))
+ (error "Elements in null-terminated vectors need to be of pointer types"))
+ #'(lambda (vector location &optional (offset 0))
+ (setf
+ (sap-ref-sap location offset)
+ (make-0-vector element-type vector)))))
+
+(defmethod reader-function ((type (eql 'null-terminated-vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ (unless (eq (alien-type element-type) (alien-type 'pointer))
+ (error "Elements in null-terminated vectors need to be of pointer types"))
+ #'(lambda (location &optional (offset 0))
+ (unless (null-pointer-p (sap-ref-sap location offset))
+ (map-0-vector 'vector #'identity (sap-ref-sap location offset)
+ element-type)))))
+
+(defmethod destroy-function ((type (eql 'null-terminated-vector)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (element-type) args
+ (unless (eq (alien-type element-type) (alien-type 'pointer))
+ (error "Elements in null-terminated vectors need to be of pointer types"))
+ #'(lambda (location &optional (offset 0))
+ (unless (null-pointer-p (sap-ref-sap location offset))
+ (destroy-0-vector
+ (sap-ref-sap location offset) element-type)
+ (setf (sap-ref-sap location offset) (make-pointer 0))))))
+
+(defmethod unbound-value ((type (eql 'null-terminated-vector)) &rest args)
+ (declare (ignore type args))
+ (values t nil))