| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| (in-package #:cffi) |
|
|
| |
| |
| |
| |
| |
| |
|
|
| (defvar *default-foreign-encoding* :utf-8 |
| "Default foreign encoding.") |
|
|
| |
| (defmacro bget (ptr off &optional (bytes 1) (endianness :ne)) |
| (let ((big-endian (member endianness |
| '(:be #+big-endian :ne #+little-endian :re)))) |
| (once-only (ptr off) |
| (ecase bytes |
| (1 `(mem-ref ,ptr :uint8 ,off)) |
| (2 (if big-endian |
| #+big-endian |
| `(mem-ref ,ptr :uint16 ,off) |
| #-big-endian |
| `(dpb (mem-ref ,ptr :uint8 ,off) (byte 8 8) |
| (mem-ref ,ptr :uint8 (1+ ,off))) |
| #+little-endian |
| `(mem-ref ,ptr :uint16 ,off) |
| #-little-endian |
| `(dpb (mem-ref ,ptr :uint8 (1+ ,off)) (byte 8 8) |
| (mem-ref ,ptr :uint8 ,off)))) |
| (4 (if big-endian |
| #+big-endian |
| `(mem-ref ,ptr :uint32 ,off) |
| #-big-endian |
| `(dpb (mem-ref ,ptr :uint8 ,off) (byte 8 24) |
| (dpb (mem-ref ,ptr :uint8 (1+ ,off)) (byte 8 16) |
| (dpb (mem-ref ,ptr :uint8 (+ ,off 2)) (byte 8 8) |
| (mem-ref ,ptr :uint8 (+ ,off 3))))) |
| #+little-endian |
| `(mem-ref ,ptr :uint32 ,off) |
| #-little-endian |
| `(dpb (mem-ref ,ptr :uint8 (+ ,off 3)) (byte 8 24) |
| (dpb (mem-ref ,ptr :uint8 (+ ,off 2)) (byte 8 16) |
| (dpb (mem-ref ,ptr :uint8 (1+ ,off)) (byte 8 8) |
| (mem-ref ,ptr :uint8 ,off)))))))))) |
|
|
| (defmacro bset (val ptr off &optional (bytes 1) (endianness :ne)) |
| (let ((big-endian (member endianness |
| '(:be #+big-endian :ne #+little-endian :re)))) |
| (ecase bytes |
| (1 `(setf (mem-ref ,ptr :uint8 ,off) ,val)) |
| (2 (if big-endian |
| #+big-endian |
| `(setf (mem-ref ,ptr :uint16 ,off) ,val) |
| #-big-endian |
| `(setf (mem-ref ,ptr :uint8 (1+ ,off)) (ldb (byte 8 0) ,val) |
| (mem-ref ,ptr :uint8 ,off) (ldb (byte 8 8) ,val)) |
| #+little-endian |
| `(setf (mem-ref ,ptr :uint16 ,off) ,val) |
| #-little-endian |
| `(setf (mem-ref ,ptr :uint8 ,off) (ldb (byte 8 0) ,val) |
| (mem-ref ,ptr :uint8 (1+ ,off)) (ldb (byte 8 8) ,val)))) |
| (4 (if big-endian |
| #+big-endian |
| `(setf (mem-ref ,ptr :uint32 ,off) ,val) |
| #-big-endian |
| `(setf (mem-ref ,ptr :uint8 (+ 3 ,off)) (ldb (byte 8 0) ,val) |
| (mem-ref ,ptr :uint8 (+ 2 ,off)) (ldb (byte 8 8) ,val) |
| (mem-ref ,ptr :uint8 (1+ ,off)) (ldb (byte 8 16) ,val) |
| (mem-ref ,ptr :uint8 ,off) (ldb (byte 8 24) ,val)) |
| #+little-endian |
| `(setf (mem-ref ,ptr :uint32 ,off) ,val) |
| #-little-endian |
| `(setf (mem-ref ,ptr :uint8 ,off) (ldb (byte 8 0) ,val) |
| (mem-ref ,ptr :uint8 (1+ ,off)) (ldb (byte 8 8) ,val) |
| (mem-ref ,ptr :uint8 (+ ,off 2)) (ldb (byte 8 16) ,val) |
| (mem-ref ,ptr :uint8 (+ ,off 3)) (ldb (byte 8 24) ,val))))))) |
|
|
| |
| (defparameter *foreign-string-mappings* |
| (instantiate-concrete-mappings |
| |
| :octet-seq-getter bget |
| :octet-seq-setter bset |
| :octet-seq-type foreign-pointer |
| :code-point-seq-getter babel::string-get |
| :code-point-seq-setter babel::string-set |
| :code-point-seq-type babel:simple-unicode-string)) |
|
|
| (defun null-terminator-len (encoding) |
| (length (enc-nul-encoding (get-character-encoding encoding)))) |
|
|
| (defun lisp-string-to-foreign (string buffer bufsize &key (start 0) end offset |
| (encoding *default-foreign-encoding*)) |
| (check-type string string) |
| (when offset |
| (setq buffer (inc-pointer buffer offset))) |
| (with-checked-simple-vector ((string (coerce string 'babel:unicode-string)) |
| (start start) (end end)) |
| (declare (type simple-string string)) |
| (let ((mapping (lookup-mapping *foreign-string-mappings* encoding)) |
| (nul-len (null-terminator-len encoding))) |
| (assert (plusp bufsize)) |
| (multiple-value-bind (size end) |
| (funcall (octet-counter mapping) string start end (- bufsize nul-len)) |
| (funcall (encoder mapping) string start end buffer 0) |
| (dotimes (i nul-len) |
| (setf (mem-ref buffer :char (+ size i)) 0)))) |
| buffer)) |
|
|
| |
| |
| |
| (defmacro %foreign-string-length (ptr offset type length) |
| (once-only (ptr offset) |
| `(do ((i 0 (+ i ,length))) |
| ((zerop (mem-ref ,ptr ,type (+ ,offset i))) i) |
| (declare (fixnum i))))) |
|
|
| |
| |
| |
| |
| (defun foreign-string-length (pointer &key (encoding *default-foreign-encoding*) |
| (offset 0)) |
| (ecase (null-terminator-len encoding) |
| (1 (%foreign-string-length pointer offset :uint8 1)) |
| (2 (%foreign-string-length pointer offset :uint16 2)) |
| (4 (%foreign-string-length pointer offset :uint32 4)))) |
|
|
| (defun foreign-string-to-lisp (pointer &key (offset 0) count |
| (max-chars (1- array-total-size-limit)) |
| (encoding *default-foreign-encoding*)) |
| "Copy at most COUNT bytes from POINTER plus OFFSET encoded in |
| ENCODING into a Lisp string and return it. If POINTER is a null |
| pointer, NIL is returned." |
| (unless (null-pointer-p pointer) |
| (let ((count (or count |
| (foreign-string-length |
| pointer :encoding encoding :offset offset))) |
| (mapping (lookup-mapping *foreign-string-mappings* encoding))) |
| (assert (plusp max-chars)) |
| (multiple-value-bind (size new-end) |
| (funcall (code-point-counter mapping) |
| pointer offset (+ offset count) max-chars) |
| (let ((string (make-string size :element-type 'babel:unicode-char))) |
| (funcall (decoder mapping) pointer offset new-end string 0) |
| (values string (- new-end offset))))))) |
|
|
| |
|
|
| (defun foreign-string-alloc (string &key (encoding *default-foreign-encoding*) |
| (null-terminated-p t) (start 0) end) |
| "Allocate a foreign string containing Lisp string STRING. |
| The string must be freed with FOREIGN-STRING-FREE." |
| (check-type string string) |
| (with-checked-simple-vector ((string (coerce string 'babel:unicode-string)) |
| (start start) (end end)) |
| (declare (type simple-string string)) |
| (let* ((mapping (lookup-mapping *foreign-string-mappings* encoding)) |
| (count (funcall (octet-counter mapping) string start end 0)) |
| (nul-length (if null-terminated-p |
| (null-terminator-len encoding) |
| 0)) |
| (length (+ count nul-length)) |
| (ptr (foreign-alloc :char :count length))) |
| (unwind-protect-case () |
| (funcall (encoder mapping) string start end ptr 0) |
| (:abort (foreign-free ptr))) |
| (dotimes (i nul-length) |
| (setf (mem-ref ptr :char (+ count i)) 0)) |
| (values ptr length)))) |
|
|
| (defun foreign-string-free (ptr) |
| "Free a foreign string allocated by FOREIGN-STRING-ALLOC." |
| (foreign-free ptr)) |
|
|
| (defmacro with-foreign-string ((var-or-vars lisp-string &rest args) &body body) |
| "VAR-OR-VARS is not evaluated and should be a list of the form |
| \(VAR &OPTIONAL BYTE-SIZE-VAR) or just a VAR symbol. VAR is |
| bound to a foreign string containing LISP-STRING in BODY. When |
| BYTE-SIZE-VAR is specified then bind the C buffer size |
| \(including the possible null terminator\(s)) to this variable." |
| (destructuring-bind (var &optional size-var) |
| (ensure-list var-or-vars) |
| `(multiple-value-bind (,var ,@(when size-var (list size-var))) |
| (foreign-string-alloc ,lisp-string ,@args) |
| (unwind-protect |
| (progn ,@body) |
| (foreign-string-free ,var))))) |
|
|
| (defmacro with-foreign-strings (bindings &body body) |
| "See WITH-FOREIGN-STRING's documentation." |
| (if bindings |
| `(with-foreign-string ,(first bindings) |
| (with-foreign-strings ,(rest bindings) |
| ,@body)) |
| `(progn ,@body))) |
|
|
| (defmacro with-foreign-pointer-as-string |
| ((var-or-vars size &rest args) &body body) |
| "VAR-OR-VARS is not evaluated and should be a list of the form |
| \(VAR &OPTIONAL SIZE-VAR) or just a VAR symbol. VAR is bound to |
| a foreign buffer of size SIZE within BODY. The return value is |
| constructed by calling FOREIGN-STRING-TO-LISP on the foreign |
| buffer along with ARGS." |
| (destructuring-bind (var &optional size-var) |
| (ensure-list var-or-vars) |
| `(with-foreign-pointer (,var ,size ,size-var) |
| (progn |
| ,@body |
| (values (foreign-string-to-lisp ,var ,@args)))))) |
|
|
| |
|
|
| (define-foreign-type foreign-string-type () |
| ( |
| (encoding :initform nil :initarg :encoding :reader encoding) |
| |
| (free-from-foreign :initarg :free-from-foreign |
| :reader fst-free-from-foreign-p |
| :initform nil :type boolean) |
| |
| (free-to-foreign :initarg :free-to-foreign |
| :reader fst-free-to-foreign-p |
| :initform t :type boolean)) |
| (:actual-type :pointer) |
| (:simple-parser :string)) |
|
|
| |
| (defun fst-encoding (type) |
| (or (encoding type) *default-foreign-encoding*)) |
|
|
| |
| (defmethod print-object ((type foreign-string-type) stream) |
| (print-unreadable-object (type stream :type t) |
| (format stream "~S" (fst-encoding type)))) |
|
|
| (defmethod translate-to-foreign ((s string) (type foreign-string-type)) |
| (values (foreign-string-alloc s :encoding (fst-encoding type)) |
| (fst-free-to-foreign-p type))) |
|
|
| (defmethod translate-to-foreign (obj (type foreign-string-type)) |
| (cond |
| ((pointerp obj) |
| (values obj nil)) |
| |
| |
| |
| (t (error "~A is not a Lisp string or pointer." obj)))) |
|
|
| (defmethod translate-from-foreign (ptr (type foreign-string-type)) |
| (unwind-protect |
| (values (foreign-string-to-lisp ptr :encoding (fst-encoding type))) |
| (when (fst-free-from-foreign-p type) |
| (foreign-free ptr)))) |
|
|
| (defmethod free-translated-object (ptr (type foreign-string-type) free-p) |
| (when free-p |
| (foreign-string-free ptr))) |
|
|
| (defmethod expand-to-foreign-dyn-indirect |
| (value var body (type foreign-string-type)) |
| (alexandria:with-gensyms (str) |
| (expand-to-foreign-dyn |
| value |
| str |
| (list |
| (expand-to-foreign-dyn-indirect str var body (parse-type :pointer))) |
| type))) |
|
|
| |
|
|
| (define-foreign-type foreign-string+ptr-type (foreign-string-type) |
| () |
| (:simple-parser :string+ptr)) |
|
|
| (defmethod translate-from-foreign (value (type foreign-string+ptr-type)) |
| (list (call-next-method) value)) |
|
|