From: Nikodemus Siivola Date: Mon, 30 May 2011 11:59:18 +0000 (+0000) Subject: 1.0.48.34: MAKE-ALIEN-STRING X-Git-Url: http://repo.macrolet.net/gitweb/?a=commitdiff_plain;h=1100ef9f0598e4b72c1dacaae530ca6a93de706b;p=sbcl.git 1.0.48.34: MAKE-ALIEN-STRING An easy way to copy lisp strings to malloc'ed memory. I can't believe we didn't have this before. ...just seems insane. I keep expecting to find a nice way to do this squirreled away somewhere in SB-ALIEN. Here's hoping I won't embarrass myself by finding one. Also fix the OAOOM in MAKE-ALIEN and FREE-ALIEN documentation: make the docstrings the canonical versions, and include them in the manual. --- diff --git a/NEWS b/NEWS index f8eb769..aead0d4 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,8 @@ changes relative to sbcl-1.0.48: terminal even if one is available. ** --script can be an argument, causing the script to be loaded from standard input. + * enhancement: MAKE-ALIEN-STRING provides an easy way to transport lisp + strings to foreign memory. * optimization: using a &REST argument only in APPLY or VALUES-LIST calls allows the compiler to automatically elide rest-list allocation so long as the call sites are in functions that the compiler knows cannot escape. diff --git a/doc/manual/ffi.texinfo b/doc/manual/ffi.texinfo index d6b6d05..95390dd 100644 --- a/doc/manual/ffi.texinfo +++ b/doc/manual/ffi.texinfo @@ -477,60 +477,15 @@ record type. @subsection Foreign Dynamic Allocation Lisp code can call the C standard library functions @code{malloc} and -@code{free} to dynamically allocate and deallocate foreign -variables. The Lisp code shares the same allocator with foreign C -code, so it's OK for foreign code to call @code{free} on the result of -Lisp @code{sb-alien:make-alien}, or for Lisp code to call -@code{sb-alien:free-alien} on foreign objects allocated by C -code. - -@defmac @sbalien{make-alien} @var{type} @var{size} - -The @code{sb-alien:make-alien} macro -returns a dynamically allocated foreign value of the specified -@var{type} (which is not evaluated.) The allocated memory is not -initialized, and may contain arbitrary junk. If supplied, -@var{size} is an expression to evaluate to compute the size of the -allocated object. There are two major cases: - -@itemize -@item -When @var{type} is a foreign array type, an array of that type is -allocated and a pointer to it is returned. Note that you must use -@code{deref} to change the result to an array before you can use -@code{deref} to read or write elements: - -@lisp -(cl:in-package "CL-USER") ; which USEs package "SB-ALIEN" -(defvar *foo* (make-alien (array char 10))) -(type-of *foo*) @result{} (alien (* (array (signed 8) 10))) -(setf (deref (deref foo) 0) 10) @result{} 10 -@end lisp - -If supplied, @var{size} is used as the first dimension for the - array. - -@item -When @var{type} is any other foreign type, then an object for that -type is allocated, and a pointer to it is returned. So -@code{(make-alien int)} returns a @code{(* int)}. If @var{size} is -specified, then a block of that many objects is allocated, with the -result pointing to the first one. - -@end itemize - -@end defmac - -@defun @sbalien{free-alien} @var{foreign-value} - -The @code{sb-alien:free-alien} macro -frees the storage for @var{foreign-value}, -which must have been allocated with Lisp @code{make-alien} -or C @code{malloc}. - -See also the @code{sb-alien:with-alien} macro, which allocates foreign -values on the stack. -@end defun +@code{free} to dynamically allocate and deallocate foreign variables. +The Lisp code shares the same allocator with foreign C code, so it's +OK for foreign code to call @code{free} on the result of Lisp +@code{sb-alien:make-alien}, or for Lisp code to call +@code{sb-alien:free-alien} on foreign objects allocated by C code. + +@include macro-sb-alien-make-alien.texinfo +@include fun-sb-alien-make-alien-string.texinfo +@include fun-sb-alien-free-alien.texinfo @node Foreign Variables @comment node-name, next, previous, up diff --git a/package-data-list.lisp-expr b/package-data-list.lisp-expr index b77f7de..f821934 100644 --- a/package-data-list.lisp-expr +++ b/package-data-list.lisp-expr @@ -53,6 +53,7 @@ of SBCL which maintained the CMU-CL-style split into two packages.)" "INT" "LOAD-1-FOREIGN" "LOAD-FOREIGN" "LOAD-SHARED-OBJECT" "LONG" "LONG-LONG" "MAKE-ALIEN" + "MAKE-ALIEN-STRING" "NULL-ALIEN" "SAP-ALIEN" "SHORT" "SIGNED" "SLOT" "STRUCT" "UNDEFINED-ALIEN-ERROR" diff --git a/src/code/target-alieneval.lisp b/src/code/target-alieneval.lisp index 2113489..e95abe3 100644 --- a/src/code/target-alieneval.lisp +++ b/src/code/target-alieneval.lisp @@ -229,12 +229,38 @@ (defmacro make-alien (type &optional size &environment env) #!+sb-doc - "Allocate an alien of type TYPE and return an alien pointer to it. If SIZE -is supplied, how it is interpreted depends on TYPE. If TYPE is an array type, -SIZE is used as the first dimension for the allocated array. If TYPE is not an -array, then SIZE is the number of elements to allocate. The memory is -allocated using ``malloc'', so it can be passed to foreign functions which use -``free''." + "Allocate an alien of type TYPE in foreign heap, and return an alien +pointer to it. The allocated memory is not initialized, and may +contain garbage. The memory is allocated using malloc(3), so it can be +passed to foreign functions which use free(3), or released using +FREE-ALIEN. + +For alien stack allocation, see macro WITH-ALIEN. + +The TYPE argument is not evaluated. If SIZE is supplied, how it is +interpreted depends on TYPE: + + * When TYPE is a foreign array type, an array of that type is + allocated, and a pointer to it is returned. Note that you + must use DEREF to first access the arrey through the pointer. + + If supplied, SIZE is used as the first dimension for the array. + + * When TYPE is any other foreign type, then an object for that + type is allocated, and a pointer to it is returned. So + (make-alien int) returns a (* int). + + If SIZE is specified, then a block of that many objects is + allocated, with the result pointing to the first one. + +Examples: + + (defvar *foo* (make-alien (array char 10))) + (type-of *foo*) ; => (alien (* (array (signed 8) 10))) + (setf (deref (deref foo) 0) 10) ; => 10 + + (make-alien char 12) ; => (alien (* (signed 8))) +" (let ((alien-type (if (alien-type-p type) type (parse-alien-type type env)))) @@ -285,11 +311,51 @@ allocated using ``malloc'', so it can be passed to foreign functions which use #!-sb-fluid (declaim (inline free-alien)) (defun free-alien (alien) #!+sb-doc - "Dispose of the storage pointed to by ALIEN. ALIEN must have been allocated - by MAKE-ALIEN or malloc(3)." + "Dispose of the storage pointed to by ALIEN. The ALIEN must have been +allocated by MAKE-ALIEN, MAKE-ALIEN-STRING or malloc(3)." (alien-funcall (extern-alien "free" (function (values) system-area-pointer)) (alien-sap alien)) nil) + +(declaim (type (sfunction * system-area-pointer) %make-alien-string)) +(defun %make-alien-string (string &key (start 0) end + (external-format :default) + (null-terminate t)) + ;; FIXME: This is slow. We want a function to get the length of the + ;; encoded string so we can allocate the foreign memory first and + ;; encode directly there. + (let* ((octets (string-to-octets string + :start start :end end + :external-format external-format + :null-terminate null-terminate)) + (count (length octets)) + (buf (%make-alien (* 8 count)))) + (sb!kernel:copy-ub8-to-system-area octets 0 buf 0 count) + buf)) + +(defun make-alien-string (string &rest rest + &key (start 0) end + (external-format :default) + (null-terminate t)) + "Copy part of STRING delimited by START and END into freshly +allocated foreign memory, freeable using free(3) or FREE-ALIEN. +Returns the allocated string as a (* CHAR) alien, and the number of +bytes allocated as secondary value. + +The string is encoded using EXTERNAL-FORMAT. If NULL-TERMINATE is +true (the default), the alien string is terminated by an additional +null byte. +" + (declare (ignore start end external-format null-terminate)) + (multiple-value-bind (sap bytes) + (apply #'%make-alien-string string rest) + (values (%sap-alien sap (parse-alien-type '(* char) nil)) + bytes))) + +(define-compiler-macro make-alien-string (&rest args) + `(multiple-value-bind (sap bytes) (%make-alien-string ,@args) + (values (%sap-alien sap ',(parse-alien-type '(* char) nil)) + bytes))) ;;;; the SLOT operator diff --git a/tests/alien.impure.lisp b/tests/alien.impure.lisp index d60bd10..f97e73c 100644 --- a/tests/alien.impure.lisp +++ b/tests/alien.impure.lisp @@ -319,4 +319,10 @@ '(alien (c-string :not-null t)))) :ok)))))) +(with-test (:name :make-alien-string) + (let ((alien (sb-alien::make-alien-string "This comes from lisp!"))) + (gc :full t) + (assert (equal "This comes from lisp!" (cast alien c-string))) + (free-alien alien))) + ;;; success diff --git a/version.lisp-expr b/version.lisp-expr index fa4e04c..3d414b0 100644 --- a/version.lisp-expr +++ b/version.lisp-expr @@ -20,4 +20,4 @@ ;;; checkins which aren't released. (And occasionally for internal ;;; versions, especially for internal versions off the main CVS ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".) -"1.0.48.33" +"1.0.48.34"