1.0.48.34: MAKE-ALIEN-STRING
authorNikodemus Siivola <nikodemus@random-state.net>
Mon, 30 May 2011 11:59:18 +0000 (11:59 +0000)
committerNikodemus Siivola <nikodemus@random-state.net>
Mon, 30 May 2011 11:59:18 +0000 (11:59 +0000)
 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.

NEWS
doc/manual/ffi.texinfo
package-data-list.lisp-expr
src/code/target-alieneval.lisp
tests/alien.impure.lisp
version.lisp-expr

diff --git a/NEWS b/NEWS
index f8eb769..aead0d4 100644 (file)
--- 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.
index d6b6d05..95390dd 100644 (file)
@@ -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
index b77f7de..f821934 100644 (file)
@@ -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"
index 2113489..e95abe3 100644 (file)
 
 (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)))
 \f
 ;;;; the SLOT operator
 
index d60bd10..f97e73c 100644 (file)
                                     '(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
index fa4e04c..3d414b0 100644 (file)
@@ -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"