@section Efficiency Hacks
The @code{sb-ext:purify} function causes SBCL first to collect all
-garbage, then to mark all uncollected objects as permanent, never
-again attempting to collect them as garbage. This can cause a large
-increase in efficiency when using a primitive garbage collector, or a
-more moderate increase in efficiency when using a more sophisticated
-garbage collector which is well suited to the program's memory usage
-pattern. It also allows permanent code to be frozen at fixed
-addresses, a precondition for using copy-on-write to share code
-between multiple Lisp processes. it is less important with modern
-generational garbage collectors.
+garbage, then to mark all uncollected objects as permanent, never again
+attempting to collect them as garbage. This can cause a large increase
+in efficiency when using a primitive garbage collector, or a more
+moderate increase in efficiency when using a more sophisticated garbage
+collector which is well suited to the program's memory usage pattern. It
+also allows permanent code to be frozen at fixed addresses, a
+precondition for using copy-on-write to share code between multiple Lisp
+processes. This is less important with modern generational garbage
+collectors, but not all SBCL platforms use such a garbage collector.
@include fun-sb-ext-purify.texinfo
@end lisp
Usually the elimination of tail-recursive frames makes debugging more
-pleasant, since theses frames are mostly uninformative. If there is
-any doubt about how one function called another, it can usually be
+pleasant, since these frames are mostly uninformative. If there is any
+doubt about how one function called another, it can usually be
eliminated by finding the source location in the calling frame.
@xref{Source Location Printing}.
@item
The foreign type specifier @code{sb-alien:c-string} is similar to
-@code{(* char)}, but is interpreted as a null-terminated string, and
-is automatically converted into a Lisp string when accessed; or if the
+@code{(* char)}, but is interpreted as a null-terminated string, and is
+automatically converted into a Lisp string when accessed; or if the
pointer is C @code{NULL} or @code{0}, then accessing it gives Lisp
-@code{nil}. Lisp strings are stored with a trailing NUL
-termination, so no copying (either by the user or the implementation)
-is necessary when passing them to foreign code.
+@code{nil}. Lisp strings of type @code{base-string} are stored with a
+trailing NUL termination, so no copying (either by the user or the
+implementation) is necessary when passing them to foreign code; strings
+of type @code{(simple-array character (*))} are copied by the
+implementation as required.
Assigning a Lisp string to a @code{c-string} structure field or
variable stores the contents of the string to the memory already
order to enable incremental loading with some linkers, you may need to
say @samp{cc -G 0 -c test.c})
-Once the C code has been compiled, you can start up Lisp and load it
-in: @samp{sbcl} Lisp should start up with its normal prompt.
+Once the C code has been compiled, you can start up Lisp and load it in:
+@samp{sbcl}. Lisp should start up with its normal prompt.
Within Lisp, compile the Lisp file. (This step can be done
separately. You don't have to recompile every time.)
(#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
(#.+char-attr-package-delimiter+ (go COLON))
(#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
- (#.+char-attr-invalid+ (%reader-error "invalid constituent"))
+ (#.+char-attr-invalid+ (%reader-error stream "invalid constituent"))
;; can't have eof, whitespace, or terminating macro as first char!
(t (go SYMBOL)))
SIGN ; saw "sign"
(with-open-file (s "/dev/null" :direction :input :external-format xf)
(assert (eq (read-char s nil s) s))))
+;;; Test standard character read-write equivalency over all external formats.
+(let ((standard-characters "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$\"'(),_-./:;?+<=>#%&*@[\\]{|}`^~"))
+ (do-external-formats (xf)
+ (with-open-file (s "external-format-test.txt" :direction :output
+ :if-exists :supersede :external-format xf)
+ (loop for character across standard-characters
+ do (write-char character s)))
+ (with-open-file (s "external-format-test.txt" :direction :input
+ :external-format xf)
+ (loop for character across standard-characters
+ do (assert (eql (read-char s) character))))))
+
+;;; Test UTF-8 writing and reading of 1, 2, 3 and 4 octet characters with
+;;; all possible offsets. Tests for buffer edge bugs. fd-stream buffers are
+;;; 4096 wide.
+(dotimes (width-1 4)
+ (let ((character (code-char (elt '(1 #x81 #x801 #x10001) width-1))))
+ (dotimes (offset (+ width-1 1))
+ (with-open-file (s "external-format-test.txt" :direction :output
+ :if-exists :supersede :external-format :utf-8)
+ (dotimes (n offset)
+ (write-char #\a s))
+ (dotimes (n 4097)
+ (write-char character s)))
+ (with-open-file (s "external-format-test.txt" :direction :input
+ :external-format :utf-8)
+ (dotimes (n offset)
+ (assert (eql (read-char s) #\a)))
+ (dotimes (n 4097)
+ (assert (eql (read-char s) character)))
+ (assert (eql (read-char s nil s) s))))))
+
+;;; Test character decode restarts.
+(with-open-file (s "external-format-test.txt" :direction :output
+ :if-exists :supersede :element-type '(unsigned-byte 8))
+ (write-byte 65 s)
+ (write-byte 66 s)
+ (write-byte #xe0 s)
+ (write-byte 67 s))
+(with-open-file (s "external-format-test.txt" :direction :input
+ :external-format :utf-8)
+ (handler-bind
+ ((sb-int:character-decoding-error #'(lambda (decoding-error)
+ (declare (ignore decoding-error))
+ (invoke-restart
+ 'sb-int:attempt-resync))))
+ (assert (equal (read-line s nil s) "ABC"))
+ (assert (equal (read-line s nil s) s))))
+(with-open-file (s "external-format-test.txt" :direction :input
+ :external-format :utf-8)
+ (handler-bind
+ ((sb-int:character-decoding-error #'(lambda (decoding-error)
+ (declare (ignore decoding-error))
+ (invoke-restart
+ 'sb-int:force-end-of-file))))
+ (assert (equal (read-line s nil s) "AB"))
+ (assert (equal (read-line s nil s) s))))
+
+;;; Test character encode restarts.
+(with-open-file (s "external-format-test.txt" :direction :output
+ :if-exists :supersede :external-format :latin-1)
+ (handler-bind
+ ((sb-int:character-encoding-error #'(lambda (encoding-error)
+ (declare (ignore encoding-error))
+ (invoke-restart
+ 'sb-impl::output-nothing))))
+ (write-char #\A s)
+ (write-char #\B s)
+ (write-char (code-char 322) s)
+ (write-char #\C s)))
+(with-open-file (s "external-format-test.txt" :direction :input
+ :external-format :latin-1)
+ (assert (equal (read-line s nil s) "ABC"))
+ (assert (equal (read-line s nil s) s)))
+
+(with-open-file (s "external-format-test.txt" :direction :output
+ :if-exists :supersede :external-format :latin-1)
+ (handler-bind
+ ((sb-int:character-encoding-error #'(lambda (encoding-error)
+ (declare (ignore encoding-error))
+ (invoke-restart
+ 'sb-impl::output-nothing))))
+ (let ((string (make-array 4 :element-type 'character
+ :initial-contents `(#\A #\B ,(code-char 322)
+ #\C))))
+ (write-string string s))))
+(with-open-file (s "external-format-test.txt" :direction :input
+ :external-format :latin-1)
+ (assert (equal (read-line s nil s) "ABC"))
+ (assert (equal (read-line s nil s) s)))
+
+;;; Test skipping character-decode-errors in comments.
(let ((s (open "external-format-test.lisp" :direction :output
:if-exists :supersede :external-format :latin-1)))
(unwind-protect
(delete-file p)))))
(sb-ext:quit :unix-status 104)
-
\ No newline at end of file
;;; 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".)
-"0.9.0.12"
+"0.9.0.13"