Reimplement :REPLACEMENT external-formats

Don't wrap a handler around every stream or octet function.  Instead,
thread the replacement from the external format through the various
functions all the way to the coding error site, and inline handling a
coding error if a replacement is provided.

In the process, also somewhat unify the behaviour of restarts around
stream and octet encoding- and decoding-errors: provide a USE-VALUE
restart whose argument should be a character, a string, an octet or an
octet sequence, which will be used as a replacement (with encoding or
decoding if necessary) to the operation that caused the error.

Note a couple of TODOs in the process.
This commit is contained in:
Christophe Rhodes 2023-06-20 21:43:28 +01:00
parent 7484a14c41
commit cf4d7a4e5c
16 changed files with 372 additions and 348 deletions

View file

@ -21,28 +21,28 @@
(string-to-octets-name/crlf (symbolicate string-to-octets-name '/crlf)))
`(progn
(declaim (inline ,get-bytes-name ,get-bytes-name/cr))
(defun ,get-bytes-name (string pos)
(defun ,get-bytes-name (string pos replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range pos))
(get-latin-bytes #',code->-name ',external-format string pos))
(defun ,string-to-octets-name (string sstart send null-padding)
(get-latin-bytes #',code->-name ',external-format replacement string pos))
(defun ,string-to-octets-name (string sstart send null-padding replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send))
(values (string->latin% string sstart send #',get-bytes-name null-padding)))
(defun ,get-bytes-name/cr (string pos)
(values (string->latin% string sstart send #',get-bytes-name null-padding replacement)))
(defun ,get-bytes-name/cr (string pos replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range pos))
(get-latin-bytes (lambda (code) (,code->-name (if (= code 10) 13 code)))
',external-format string pos))
(defun ,string-to-octets-name/cr (string sstart send null-padding)
'(,external-format :newline :cr) replacement string pos))
(defun ,string-to-octets-name/cr (string sstart send null-padding replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send))
(values (string->latin% string sstart send #',get-bytes-name/cr null-padding)))
(defun ,string-to-octets-name/crlf (string sstart send null-padding)
(values (string->latin% string sstart send #',get-bytes-name/cr null-padding replacement)))
(defun ,string-to-octets-name/crlf (string sstart send null-padding replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send))
@ -57,7 +57,8 @@
(vector-push-extend (,code->-name 13) array)
(vector-push-extend byte array))
((null byte)
(let ((replacement (encoding-error ',external-format string i)))
(let ((replacement (encoding-error '(,external-format :newline :crlf)
replacement string i)))
(declare (type (simple-array (unsigned-byte 8) (*)) replacement))
(dotimes (j (length replacement))
(vector-push-extend (aref replacement j) array))))
@ -79,10 +80,11 @@
``((define-condition ,',decoding-condition-name (octet-decoding-error) ())))
,,(if invalid-bytes-p
``(progn
(defun ,(make-od-name ',octets-to-string-name accessor) (array astart aend)
(defun ,(make-od-name ',octets-to-string-name accessor) (array astart aend replacement)
(declare (optimize speed)
(type ,type array)
(type array-range astart aend))
(type array-range astart aend)
(ignorable replacement))
(let ((string (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t)))
(loop for apos from astart below aend
do (let* ((byte (,accessor array apos))
@ -92,6 +94,7 @@
(code-char code)
(decoding-error array apos (1+ apos)
,',external-format
replacement
',',decoding-condition-name
apos))))
(if (characterp string-content)
@ -99,10 +102,11 @@
(loop for c across string-content
do (vector-push-extend c string))))
finally (return (coerce string 'simple-string)))))
(defun ,(make-od-name ',octets-to-string-name/cr accessor) (array astart aend)
(defun ,(make-od-name ',octets-to-string-name/cr accessor) (array astart aend replacement)
(declare (optimize speed)
(type ,type array)
(type array-range astart aend))
(type array-range astart aend)
(ignorable replacement))
(let ((string (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t)))
(loop for apos from astart below aend
do (let* ((byte (,accessor array apos))
@ -111,7 +115,8 @@
(cond
((null code)
(decoding-error array apos (1+ apos)
,',external-format
'(,',external-format :newline :cr)
replacement
',',decoding-condition-name
apos))
((= code 13) (code-char 10))
@ -122,15 +127,18 @@
do (vector-push-extend c string))))
finally (return (coerce string 'simple-string))))))
``(progn
(defun ,(make-od-name ',octets-to-string-name accessor) (array astart aend)
(defun ,(make-od-name ',octets-to-string-name accessor) (array astart aend replacement)
(declare (ignore replacement))
(,(make-od-name 'latin->string accessor) array astart aend #',',->code-name))
(defun ,(make-od-name ',octets-to-string-name/cr accessor) (array astart aend)
(defun ,(make-od-name ',octets-to-string-name/cr accessor) (array astart aend replacement)
(declare (ignore replacement))
(,(make-od-name 'latin->string accessor) array astart aend
(lambda (x) (let ((code (,',->code-name x))) (if (= code 13) 10 code)))))))
(defun ,(make-od-name ',octets-to-string-name/crlf accessor) (array astart aend)
(defun ,(make-od-name ',octets-to-string-name/crlf accessor) (array astart aend replacement)
(declare (optimize speed)
(type ,type array)
(type array-range astart aend))
(type array-range astart aend)
(ignorable replacement))
(let ((string (make-array (- aend astart) :element-type 'character
:fill-pointer 0 :adjustable t)))
(loop for apos from astart below aend
@ -141,7 +149,8 @@
,@',(when invalid-bytes-p
`(((null code)
(decoding-error array apos (1+ apos)
,external-format
'(,external-format :newline :crlf)
replacement
',decoding-condition-name
apos))))
((= code 13)
@ -331,7 +340,7 @@
(add-byte (logior #x80 (ldb (byte 6 6) code)))
(add-byte (logior #x80 (ldb (byte 6 0) code))))))
(def (name newline)
`(defun ,name (string sstart send null-padding)
`(defun ,name (string sstart send null-padding replacement)
(declare (optimize (speed 3) #.*safety-0*)
(type simple-string string)
(type (integer 0 1) null-padding)
@ -370,7 +379,11 @@
:fill-pointer index))
(replace new-array array)
:error
(let ((replacement (encoding-error :utf-8 string index)))
(let ((replacement (encoding-error
,(if (eql newline :lf)
:utf-8
`'(:utf-8 :newline ,newline))
replacement string index)))
(flet ((add-byte (b) (vector-push-extend b new-array)))
(dotimes (i (length replacement))
(add-byte (aref replacement i)))
@ -395,7 +408,7 @@
;;; from UTF-8
(macrolet ((def (crlfp definer-name base-name)
(macrolet ((def (crlfp newline definer-name base-name)
`(progn
(defmacro ,definer-name (accessor type)
(let ((name (make-od-name ',base-name accessor)))
@ -403,9 +416,9 @@
;;(declaim (inline ,name))
(let ((lexically-max
(string->utf8 (string (code-char ,(1- char-code-limit)))
0 1 0)))
0 1 0 nil)))
(declare (type (simple-array (unsigned-byte 8) (#+sb-unicode 4 #-sb-unicode 2)) lexically-max))
(defun ,name (array pos end)
(defun ,name (array pos end replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range pos end))
@ -523,11 +536,14 @@
(character-out-of-range (+ pos maybe-len))))
(bad-len (- bad-end pos)))
(declare (type array-range bad-end bad-len))
(let ((replacement (decoding-error array pos bad-end :utf-8 reject-reason reject-position)))
(let ((replacement (decoding-error array pos bad-end
,,(if newline ``'(:utf-8 :newline ,,newline) '':utf-8)
replacement reject-reason reject-position)))
(values bad-len replacement)))))))))))))
(instantiate-octets-definition ,definer-name))))
(def nil define-bytes-per-utf8-character bytes-per-utf8-character)
(def t define-bytes-per-utf8-character/crlf bytes-per-utf8-character/crlf))
(def nil nil define-bytes-per-utf8-character bytes-per-utf8-character)
(def nil :cr define-bytes-per-utf8-character/clr bytes-per-utf8-character/cr)
(def t :crlf define-bytes-per-utf8-character/crlf bytes-per-utf8-character/crlf))
(macrolet ((def (newline definer-name base-name)
`(progn
@ -574,7 +590,7 @@
(defmacro ,definer-name (accessor type)
(let ((name (make-od-name ',base-name accessor)))
`(progn
(defun ,name (array astart aend)
(defun ,name (array astart aend replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range astart aend))
@ -582,7 +598,7 @@
(loop with pos = astart
while (< pos aend)
do (multiple-value-bind (bytes invalid)
(,(make-od-name ',bytes-per-base-name accessor) array pos aend)
(,(make-od-name ',bytes-per-base-name accessor) array pos aend replacement)
(declare (type (or null string) invalid))
(cond
((null invalid)

View file

@ -85,7 +85,7 @@
;;; Conversion to UCS-2{LE,BE}
(declaim (inline char->ucs-2le))
(defun char->ucs-2le (char dest string pos)
(defun char->ucs-2le (char dest string pos replacement)
(declare (optimize speed #.*safety-0*)
(type (array (unsigned-byte 8) (*)) dest))
(let ((code (char-code char)))
@ -96,13 +96,13 @@
(declare (inline add-byte))
(add-byte (ldb (byte 8 0) code))
(add-byte (ldb (byte 8 8) code)))
(let ((replacement (encoding-error :ucs-2le string pos)))
(let ((replacement (encoding-error :ucs-2le replacement string pos)))
(declare (type (simple-array (unsigned-byte 8) (*)) replacement))
(dotimes (i (length replacement))
(vector-push-extend (aref replacement i) dest))))))
(declaim (inline char->ucs-2be))
(defun char->ucs-2be (char dest string pos)
(defun char->ucs-2be (char dest string pos replacement)
(declare (optimize speed #.*safety-0*)
(type (array (unsigned-byte 8) (*)) dest))
(let ((code (char-code char)))
@ -113,12 +113,12 @@
(declare (inline add-byte))
(add-byte (ldb (byte 8 8) code))
(add-byte (ldb (byte 8 0) code)))
(let ((replacement (encoding-error :ucs-2be string pos)))
(let ((replacement (encoding-error :ucs-2be replacement string pos)))
(declare (type (simple-array (unsigned-byte 8) (*)) replacement))
(dotimes (i (length replacement))
(vector-push-extend (aref replacement i) dest))))))
(defun string->ucs-2le (string sstart send additional-space)
(defun string->ucs-2le (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
@ -126,12 +126,12 @@
:element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
(loop for i from sstart below send
do (char->ucs-2le (char string i) array string i))
do (char->ucs-2le (char string i) array string i replacement))
(dotimes (i (* 2 additional-space))
(vector-push-extend 0 array))
(coerce array '(simple-array (unsigned-byte 8) (*)))))
(defun string->ucs-2be (string sstart send additional-space)
(defun string->ucs-2be (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
@ -139,7 +139,7 @@
:element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
(loop for i from sstart below send
do (char->ucs-2be (char string i) array string i))
do (char->ucs-2be (char string i) array string i replacement))
(dotimes (i (* 2 additional-space))
(vector-push-extend 0 array))
(coerce array '(simple-array (unsigned-byte 8) (*)))))
@ -289,10 +289,11 @@
(add-byte (ldb (byte 8 8) code))
(add-byte (ldb (byte 8 0) code)))))
(defun string->ucs-4le (string sstart send additional-space)
(defun string->ucs-4le (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
(type array-range sstart send additional-space)
(ignore replacement))
(let ((array (make-array (* 4 (+ additional-space (- send sstart)))
:element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
@ -302,10 +303,11 @@
(vector-push-extend 0 array))
(coerce array '(simple-array (unsigned-byte 8) (*)))))
(defun string->ucs-4be (string sstart send additional-space)
(defun string->ucs-4be (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
(type array-range sstart send additional-space)
(ignore replacement))
(let ((array (make-array (* 4 (+ additional-space (- send sstart)))
:element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
@ -333,7 +335,7 @@
(let ((name-le (make-od-name 'simple-get-ucs-4le-char accessor))
(name-be (make-od-name 'simple-get-ucs-4be-char accessor)))
`(progn
(defun ,name-le (array pos bytes)
(defun ,name-le (array pos bytes replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range pos)
@ -351,9 +353,9 @@
(dpb (cref 1) (byte 8 8) (cref 0))))))))
(if (< code char-code-limit)
(code-char code)
(decoding-error array pos (+ pos bytes) :ucs-4le
(decoding-error array pos (+ pos bytes) :ucs-4le replacement
'octet-decoding-error pos))))
(defun ,name-be (array pos bytes)
(defun ,name-be (array pos bytes replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range pos)
@ -370,7 +372,7 @@
(dpb (cref 2) (byte 8 8) (cref 3))))))))
(if (< code char-code-limit)
(code-char code)
(decoding-error array pos (+ pos bytes) :ucs-4be
(decoding-error array pos (+ pos bytes) :ucs-4be replacement
'octet-decoding-error pos)))))))
(eval-when (:compile-toplevel)
@ -381,7 +383,7 @@
(let ((name-le (make-od-name 'ucs-4le->string accessor))
(name-be (make-od-name 'ucs-4be->string accessor)))
`(progn
(defun ,name-le (array astart aend)
(defun ,name-le (array astart aend replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range astart aend))
@ -392,14 +394,14 @@
(,(make-od-name 'bytes-per-ucs-4le-character accessor) array pos aend)
(declare (type (or null string) invalid))
(aver (null invalid))
(let ((thing (,(make-od-name 'simple-get-ucs-4le-char accessor) array pos bytes)))
(let ((thing (,(make-od-name 'simple-get-ucs-4le-char accessor) array pos bytes replacement)))
(typecase thing
(character (vector-push-extend thing string))
(string (dotimes (i (length thing))
(vector-push-extend (char thing i) string)))))
(incf pos bytes)))
string))
(defun ,name-be (array astart aend)
(defun ,name-be (array astart aend replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range astart aend))
@ -410,7 +412,7 @@
(,(make-od-name 'bytes-per-ucs-4be-character accessor) array pos aend)
(declare (type (or null string) invalid))
(aver (null invalid))
(let ((thing (,(make-od-name 'simple-get-ucs-4be-char accessor) array pos bytes)))
(let ((thing (,(make-od-name 'simple-get-ucs-4be-char accessor) array pos bytes replacement)))
(typecase thing
(character (vector-push-extend thing string))
(string (dotimes (i (length thing))

View file

@ -25,12 +25,12 @@
;;; Conversion to UTF-16{LE,BE}
(declaim (inline char->utf-16le))
(defun char->utf-16le (char dest string pos)
(defun char->utf-16le (char dest string pos replacement)
(declare (optimize speed #.*safety-0*)
(type (array (unsigned-byte 8) (*)) dest))
(let ((code (char-code char)))
(if (utf-noncharacter-code-p code)
(let ((replacement (encoding-error :utf-16le string pos)))
(let ((replacement (encoding-error :utf-16le replacement string pos)))
(declare (type (simple-array (unsigned-byte 8) (*)) replacement))
(dotimes (i (length replacement))
(vector-push-extend (aref replacement i) dest)))
@ -52,12 +52,12 @@
(add-byte (ldb (byte 8 8) low)))))))))
(declaim (inline char->utf-16be))
(defun char->utf-16be (char dest string pos)
(defun char->utf-16be (char dest string pos replacement)
(declare (optimize speed #.*safety-0*)
(type (array (unsigned-byte 8) (*)) dest))
(let ((code (char-code char)))
(if (utf-noncharacter-code-p code)
(let ((replacement (encoding-error :utf-16be string pos)))
(let ((replacement (encoding-error :utf-16be replacement string pos)))
(declare (type (simple-array (unsigned-byte 8) (*)) replacement))
(dotimes (i (length replacement))
(vector-push-extend (aref replacement i) dest)))
@ -78,7 +78,7 @@
(add-byte (ldb (byte 8 8) low))
(add-byte (ldb (byte 8 0) low)))))))))
(defun string->utf-16le (string sstart send additional-space)
(defun string->utf-16le (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
@ -86,12 +86,12 @@
:element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
(loop for i from sstart below send
do (char->utf-16le (char string i) array string i))
do (char->utf-16le (char string i) array string i replacement))
(dotimes (i (* 2 additional-space))
(vector-push-extend 0 array))
(coerce array '(simple-array (unsigned-byte 8) (*)))))
(defun string->utf-16be (string sstart send additional-space)
(defun string->utf-16be (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
@ -99,7 +99,7 @@
:element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
(loop for i from sstart below send
do (char->utf-16be (char string i) array string i))
do (char->utf-16be (char string i) array string i replacement))
(dotimes (i (* 2 additional-space))
(vector-push-extend 0 array))
(coerce array '(simple-array (unsigned-byte 8) (*)))))
@ -110,45 +110,45 @@
(let ((name-le (make-od-name 'bytes-per-utf-16le-character accessor))
(name-be (make-od-name 'bytes-per-utf-16be-character accessor)))
`(progn
(defun ,name-le (array pos end)
(defun ,name-le (array pos end replacement)
(let ((remaining (- end pos)))
(when (< remaining 2)
(return-from ,name-le (values remaining (decoding-error array pos end :utf-16le 'octet-decoding-error pos))))
(return-from ,name-le (values remaining (decoding-error array pos end :utf-16le replacement 'octet-decoding-error pos))))
(let ((low (dpb (,accessor array (+ pos 1)) (byte 8 8) (,accessor array pos))))
(if (<= #xd800 low #xdbff)
(if (< remaining 4)
(values remaining (decoding-error array pos end :utf-16le 'octet-decoding-error pos))
(values remaining (decoding-error array pos end :utf-16le replacement 'octet-decoding-error pos))
(let ((high (dpb (,accessor array (+ pos 3)) (byte 8 8) (,accessor array (+ pos 2)))))
(if (<= #xdc00 high #xdfff)
(let ((code (dpb (ldb (byte 10 0) low) (byte 10 10) (ldb (byte 10 0) high))))
(if (= (logand code #xfffe) #xfffe)
(values 4 (decoding-error array pos (+ pos 4) :utf-16le 'octet-decoding-error pos))
(values 4 (decoding-error array pos (+ pos 4) :utf-16le replacement 'octet-decoding-error pos))
(values 4 nil)))
(values 2 (decoding-error array pos (+ pos 2) :utf-16le 'octet-decoding-error pos)))))
(values 2 (decoding-error array pos (+ pos 2) :utf-16le replacement 'octet-decoding-error pos)))))
(if (or (<= #xdc00 low #xdfff)
(<= #xfdd0 low #xfdef)
(= (logand low #xfffe) #xfffe))
(values 2 (decoding-error array pos (+ pos 2) :utf-16le 'octet-decoding-error pos))
(values 2 (decoding-error array pos (+ pos 2) :utf-16le replacement 'octet-decoding-error pos))
(values 2 nil))))))
(defun ,name-be (array pos end)
(defun ,name-be (array pos end replacement)
(let ((remaining (- end pos)))
(when (< remaining 2)
(return-from ,name-be (values remaining (decoding-error array pos end :utf-16le 'octet-decoding-error pos))))
(return-from ,name-be (values remaining (decoding-error array pos end :utf-16le replacement 'octet-decoding-error pos))))
(let ((low (dpb (,accessor array pos) (byte 8 8) (,accessor array (+ pos 1)))))
(if (<= #xd800 low #xdbff)
(if (< remaining 4)
(values remaining (decoding-error array pos end :utf-16le 'octet-decoding-error pos))
(values remaining (decoding-error array pos end :utf-16le replacement 'octet-decoding-error pos))
(let ((high (dpb (,accessor array (+ pos 2)) (byte 8 8) (,accessor array (+ pos 3)))))
(if (<= #xdc00 high #xdfff)
(let ((code (dpb (ldb (byte 10 0) low) (byte 10 10) (ldb (byte 10 0) high))))
(if (= (logand code #xfffe) #xfffe)
(values 4 (decoding-error array pos (+ pos 4) :utf-16le 'octet-decoding-error pos))
(values 4 (decoding-error array pos (+ pos 4) :utf-16le replacement 'octet-decoding-error pos))
(values 4 nil)))
(values 2 (decoding-error array pos (+ pos 2) :utf-16le 'octet-decoding-error pos)))))
(values 2 (decoding-error array pos (+ pos 2) :utf-16le replacement 'octet-decoding-error pos)))))
(if (or (<= #xdc00 low #xdfff)
(<= #xfdd0 low #xfdef)
(= (logand low #xfffe) #xfffe))
(values 2 (decoding-error array pos (+ pos 2) :utf-16le 'octet-decoding-error pos))
(values 2 (decoding-error array pos (+ pos 2) :utf-16le replacement 'octet-decoding-error pos))
(values 2 nil)))))))))
(instantiate-octets-definition define-bytes-per-utf16-character)
@ -212,7 +212,7 @@
(let ((name-le (make-od-name 'utf-16le->string accessor))
(name-be (make-od-name 'utf-16be->string accessor)))
`(progn
(defun ,name-le (array astart aend)
(defun ,name-le (array astart aend replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range astart aend))
@ -220,7 +220,7 @@
(loop with pos = astart
while (< pos aend)
do (multiple-value-bind (bytes invalid)
(,(make-od-name 'bytes-per-utf-16le-character accessor) array pos aend)
(,(make-od-name 'bytes-per-utf-16le-character accessor) array pos aend replacement)
(declare (type (or null string) invalid))
(cond
((null invalid)
@ -232,7 +232,7 @@
(vector-push-extend (char invalid i) string))))
(incf pos bytes)))
string))
(defun ,name-be (array astart aend)
(defun ,name-be (array astart aend replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range astart aend))
@ -240,7 +240,7 @@
(loop with pos = astart
while (< pos aend)
do (multiple-value-bind (bytes invalid)
(,(make-od-name 'bytes-per-utf-16be-character accessor) array pos aend)
(,(make-od-name 'bytes-per-utf-16be-character accessor) array pos aend replacement)
(declare (type (or null string) invalid))
(cond
((null invalid)
@ -328,12 +328,12 @@
:char-encodable-p (not (utf-noncharacter-code-p (char-code |ch|))))
(declaim (inline char->utf-32le))
(defun char->utf-32le (char dest string pos)
(defun char->utf-32le (char dest string pos replacement)
(declare (optimize speed #.*safety-0*)
(type (array (unsigned-byte 8) (*)) dest))
(let ((code (char-code char)))
(if (utf-noncharacter-code-p code)
(let ((replacement (encoding-error :utf-32le string pos)))
(let ((replacement (encoding-error :utf-32le replacement string pos)))
(declare (type (simple-array (unsigned-byte 8) (*)) replacement))
(dotimes (i (length replacement))
(vector-push-extend (aref replacement i) dest)))
@ -347,12 +347,12 @@
(add-byte (ldb (byte 8 24) code))))))
(declaim (inline char->utf-32be))
(defun char->utf-32be (char dest string pos)
(defun char->utf-32be (char dest string pos replacement)
(declare (optimize speed #.*safety-0*)
(type (array (unsigned-byte 8) (*)) dest))
(let ((code (char-code char)))
(if (utf-noncharacter-code-p code)
(let ((replacement (encoding-error :utf-32be string pos)))
(let ((replacement (encoding-error :utf-32be replacement string pos)))
(declare (type (simple-array (unsigned-byte 8) (*)) replacement))
(dotimes (i (length replacement))
(vector-push-extend (aref replacement i) dest)))
@ -365,7 +365,7 @@
(add-byte (ldb (byte 8 8) code))
(add-byte (ldb (byte 8 0) code))))))
(defun string->utf-32le (string sstart send additional-space)
(defun string->utf-32le (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
@ -373,12 +373,12 @@
:element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
(loop for i from sstart below send
do (char->utf-32le (char string i) array string i))
do (char->utf-32le (char string i) array string i replacement))
(dotimes (i (* 4 additional-space))
(vector-push-extend 0 array))
(coerce array '(simple-array (unsigned-byte 8) (*)))))
(defun string->utf-32be (string sstart send additional-space)
(defun string->utf-32be (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
@ -386,7 +386,7 @@
:element-type '(unsigned-byte 8)
:fill-pointer 0 :adjustable t)))
(loop for i from sstart below send
do (char->utf-32be (char string i) array string i))
do (char->utf-32be (char string i) array string i replacement))
(dotimes (i (* 4 additional-space))
(vector-push-extend 0 array))
(coerce array '(simple-array (unsigned-byte 8) (*)))))
@ -409,7 +409,7 @@
(let ((name-le (make-od-name 'simple-get-utf-32le-char accessor))
(name-be (make-od-name 'simple-get-utf-32be-char accessor)))
`(progn
(defun ,name-le (array pos bytes)
(defun ,name-le (array pos bytes replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range pos)
@ -428,9 +428,9 @@
(if (and (< code char-code-limit)
(not (utf-noncharacter-code-p code)))
(code-char code)
(decoding-error array pos (+ pos bytes) :utf-32le
(decoding-error array pos (+ pos bytes) :utf-32le replacement
'octet-decoding-error pos))))
(defun ,name-be (array pos bytes)
(defun ,name-be (array pos bytes replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range pos)
@ -448,7 +448,7 @@
(if (and (< code char-code-limit)
(not (utf-noncharacter-code-p code)))
(code-char code)
(decoding-error array pos (+ pos bytes) :utf-32be
(decoding-error array pos (+ pos bytes) :utf-32be replacement
'octet-decoding-error pos)))))))
(declaim (muffle-conditions compiler-note))
@ -458,7 +458,7 @@
(let ((name-le (make-od-name 'utf-32le->string accessor))
(name-be (make-od-name 'utf-32be->string accessor)))
`(progn
(defun ,name-le (array astart aend)
(defun ,name-le (array astart aend replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range astart aend))
@ -469,14 +469,14 @@
(,(make-od-name 'bytes-per-utf-32le-character accessor) array pos aend)
(declare (type (or null string) invalid))
(aver (null invalid))
(let ((thing (,(make-od-name 'simple-get-utf-32le-char accessor) array pos bytes)))
(let ((thing (,(make-od-name 'simple-get-utf-32le-char accessor) array pos bytes replacement)))
(typecase thing
(character (vector-push-extend thing string))
(string (dotimes (i (length thing))
(vector-push-extend (char thing i) string)))))
(incf pos bytes)))
string))
(defun ,name-be (array astart aend)
(defun ,name-be (array astart aend replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range astart aend))
@ -487,7 +487,7 @@
(,(make-od-name 'bytes-per-utf-32be-character accessor) array pos aend)
(declare (type (or null string) invalid))
(aver (null invalid))
(let ((thing (,(make-od-name 'simple-get-utf-32be-char accessor) array pos bytes)))
(let ((thing (,(make-od-name 'simple-get-utf-32be-char accessor) array pos bytes replacement)))
(typecase thing
(character (vector-push-extend thing string))
(string (dotimes (i (length thing))

View file

@ -45,7 +45,7 @@
(make-od-name-list 'invalid format 'continuation-byte)))
`(progn
;;(declaim (inline ,name))
(defun ,name (array pos end)
(defun ,name (array pos end replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range pos end))
@ -108,7 +108,7 @@
reject-position)))
(bad-len (- bad-end pos)))
(declare (type array-range bad-end bad-len))
(let ((replacement (decoding-error array pos bad-end ,format reject-reason reject-position)))
(let ((replacement (decoding-error array pos bad-end ,format replacement reject-reason reject-position)))
(values bad-len replacement))))))))))))
(defun define-simple-get-mb-char-1 (accessor type format mb-to-ucs)
@ -116,7 +116,7 @@
(malformed (make-od-name 'malformed format)))
`(progn
(declaim (inline ,name))
(defun ,name (array pos bytes)
(defun ,name (array pos bytes replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range pos)
@ -133,7 +133,7 @@
(if code
(code-char code)
(decoding-error array pos (+ pos bytes) ,format
',malformed pos))))))))
replacement ',malformed pos))))))))
(defun define-mb->string-1 (accessor type format)
(let ((name
@ -143,7 +143,7 @@
(simple-get-mb-char
(make-od-name-list 'simple-get format 'char accessor)))
`(progn
(defun ,name (array astart aend)
(defun ,name (array astart aend replacement)
(declare (optimize speed #.*safety-0*)
(type ,type array)
(type array-range astart aend))
@ -151,11 +151,11 @@
(loop with pos = astart
while (< pos aend)
do (multiple-value-bind (bytes invalid)
(,bytes-per-mb-character array pos aend)
(,bytes-per-mb-character array pos aend replacement)
(declare (type (or null string) invalid))
(cond
((null invalid)
(let ((thing (,simple-get-mb-char array pos bytes)))
(let ((thing (,simple-get-mb-char array pos bytes replacement)))
(typecase thing
(character (vector-push-extend thing string))
(string
@ -199,7 +199,7 @@
(octet-decoding-error) ())
(declaim (inline ,char->mb))
(defun ,char->mb (char dest string pos)
(defun ,char->mb (char dest string pos replacement)
(declare (optimize speed #.*safety-0*)
(type (array (unsigned-byte 8) (*)) dest))
(let ((code (,ucs-to-mb (char-code char))))
@ -219,9 +219,12 @@
(add-byte (ldb (byte 8 16) code))
(add-byte (ldb (byte 8 8) code))
(add-byte (ldb (byte 8 0) code)))))
(encoding-error ,format string pos))))
;; TODO: it looks like this doesn't actually
;; participate in the protocol: we should emit the
;; octets that this returns.
(encoding-error ,format replacement string pos))))
(defun ,string->mb (string sstart send additional-space)
(defun ,string->mb (string sstart send additional-space replacement)
(declare (optimize speed #.*safety-0*)
(type simple-string string)
(type array-range sstart send additional-space))
@ -230,7 +233,7 @@
:adjustable t
:fill-pointer 0)))
(loop for i from sstart below send
do (,char->mb (char string i) array string i))
do (,char->mb (char string i) array string i replacement))
(dotimes (i additional-space)
(vector-push-extend 0 array))
(coerce array '(simple-array (unsigned-byte 8) (*)))))

View file

@ -161,6 +161,7 @@
(external-format :latin-1)
;; fixed width, or function to call with a character
(char-size 1 :type (or fixnum function))
(replacement nil :type (or null character string (simple-array (unsigned-byte 8) 1)))
(output-bytes #'ill-out :type function))
(defun fd-stream-bivalent-p (stream)
@ -527,62 +528,109 @@
;;; Returning true goes into end of file handling, false will enter another
;;; round of input buffer filling followed by re-entering character decode.
(defun stream-decoding-error-and-handle (stream octet-count)
(restart-case
(error 'stream-decoding-error
:external-format (stream-external-format stream)
:stream stream
:octets (let ((buffer (fd-stream-ibuf stream)))
(sap-ref-octets (buffer-sap buffer)
(buffer-head buffer)
octet-count)))
(attempt-resync ()
:report (lambda (stream)
(format stream
"~@<Attempt to resync the stream at a ~
(let ((external-format (stream-external-format stream))
(replacement (fd-stream-replacement stream)))
(labels ((replacement (thing)
(let* ((string (decoding-replacement-stringify thing external-format))
(reversed (reverse string))
(instead (fd-stream-instead stream)))
(dotimes (i (length reversed))
(vector-push-extend (char reversed i) instead))
(when (> (length reversed) 0)
(setf (fd-stream-listen stream) t))
(resync)))
(resync ()
(fd-stream-resync stream)
nil))
(if replacement
(replacement replacement)
(restart-case
(error 'stream-decoding-error
:external-format external-format
:stream stream
:octets (let ((buffer (fd-stream-ibuf stream)))
(sap-ref-octets (buffer-sap buffer)
(buffer-head buffer)
octet-count)))
(attempt-resync ()
:report (lambda (stream)
(format stream
"~@<Attempt to resync the stream at a ~
character boundary and continue.~@:>"))
(fd-stream-resync stream)
nil)
(force-end-of-file ()
:report (lambda (stream)
(format stream "~@<Force an end of file.~@:>"))
(setf (fd-stream-eof-forced-p stream) t))
(input-replacement (string)
:report (lambda (stream)
(format stream "~@<Use string as replacement input, ~
(resync))
(force-end-of-file ()
:report (lambda (stream)
(format stream "~@<Force an end of file.~@:>"))
(setf (fd-stream-eof-forced-p stream) t))
(use-value (replacement)
:report (lambda (stream)
(format stream "~@<Use datum as replacement input, ~
attempt to resync at a character ~
boundary and continue.~@:>"))
:interactive (lambda ()
(read-evaluated-form
"Replacement byte, bytes, character, or string (evaluated): "))
(replacement replacement))
(input-replacement (thing)
:report (lambda (stream)
(format stream "~@<Use string as replacement input, ~
attempt to resync at a character ~
boundary and continue.~@:>"))
:interactive (lambda ()
(format *query-io* "~@<Enter a string: ~@:>")
(finish-output *query-io*)
(list (read *query-io*)))
(let ((string (reverse (string string)))
(instead (fd-stream-instead stream)))
(dotimes (i (length string))
(vector-push-extend (char string i) instead))
(fd-stream-resync stream)
(when (> (length string) 0)
(setf (fd-stream-listen stream) t)))
nil)))
:interactive (lambda ()
(format *query-io* "~@<Enter a string: ~@:>")
(finish-output *query-io*)
(list (read *query-io*)))
(replacement thing)))))))
) ; end MACROLET
(defun encoding-replacement-adjust-charpos (replacement stream)
(typecase replacement
(character (if (char= replacement #\Newline)
(setf (fd-stream-output-column stream) 0)
(incf (fd-stream-output-column stream) 1)))
(string (let ((newline-pos (position #\Newline replacement :from-end t)))
(if newline-pos
(setf (fd-stream-output-column stream) (- (length replacement) newline-pos 1))
(incf (fd-stream-output-column stream) (length replacement)))))
((unsigned-byte 8))
((simple-array (unsigned-byte 8) 1))))
(defun stream-encoding-error-and-handle (stream code)
(restart-case
(error 'stream-encoding-error
:external-format (stream-external-format stream)
:stream stream
:code code)
(output-nothing ()
:report (lambda (stream)
(format stream "~@<Skip output of this character.~@:>")))
(output-replacement (string)
:report (lambda (stream)
(format stream "~@<Output replacement string.~@:>"))
:interactive (lambda ()
(format *query-io* "~@<Enter a string: ~@:>")
(finish-output *query-io*)
(list (read *query-io*)))
(let ((string (string string)))
(fd-sout stream (string string) 0 (length string))))))
(let ((external-format (stream-external-format stream))
(replacement (fd-stream-replacement stream)))
(labels ((replacement (thing)
(let ((octets (encoding-replacement-octetify thing external-format)))
(ecase (fd-stream-buffering stream)
(:full (buffer-output stream octets 0 (length octets)))
(:line (buffer-output stream octets 0 (length octets)))
(:none (write-or-buffer-output stream octets 0 (length octets))))
(encoding-replacement-adjust-charpos thing stream))))
(if replacement
(replacement replacement)
(restart-case
(error 'stream-encoding-error
:external-format external-format
:stream stream
:code code)
(output-nothing ()
:report (lambda (stream)
(format stream "~@<Skip output of this character.~@:>")))
(use-value (replacement)
:report (lambda (stream)
(format stream "~@<Use datum as replacement output.~@:>"))
:interactive (lambda ()
(read-evaluated-form
"Replacement byte, bytes, character, or string (evaluated): "))
(replacement replacement))
(output-replacement (string)
:report (lambda (stream)
(format stream "~@<Output replacement string.~@:>"))
:interactive (lambda ()
(format *query-io* "~@<Enter a string: ~@:>")
(finish-output *query-io*)
(list (read *query-io*)))
(let ((string (string string)))
(fd-sout stream (string string) 0 (length string)))))))))
(defun %external-format-encoding-error (stream code)
(if (streamp stream)
@ -833,6 +881,7 @@
(names (missing-arg) :type list :read-only t)
(newline-variant (missing-arg) :type (member :crlf :lf :cr) :read-only t)
(default-replacement-character (missing-arg) :type character)
(replacement nil :type (or null character string (simple-array (unsigned-byte 8) 1)))
(read-n-chars-fun (missing-arg) :type function)
(read-char-fun (missing-arg) :type function)
(write-n-bytes-fun (missing-arg) :type function)
@ -903,6 +952,7 @@
1
(ef-write-n-bytes-fun entry)
(ef-char-size entry)
(ef-replacement entry)
(canonize-external-format external-format entry)))))
(dolist (entry *output-routines*)
(when (and (subtypep type (first entry))
@ -1261,6 +1311,7 @@
1
(ef-read-n-chars-fun entry)
(ef-char-size entry)
(ef-replacement entry)
(canonize-external-format external-format entry)))))
(dolist (entry *input-routines*)
(when (and (subtypep type (first entry))
@ -1777,6 +1828,7 @@
(bivalent-stream-p (eq element-type :default))
normalized-external-format
char-size
replacement
(bin-routine #'ill-bin)
(bin-type nil)
(bin-size nil)
@ -1831,14 +1883,14 @@
target-type)))
(when (or (not character-stream-p) bivalent-stream-p)
(setf (values bin-routine bin-type bin-size read-n-characters
char-size normalized-external-format)
char-size replacement normalized-external-format)
(pick-input-routine (if bivalent-stream-p '(unsigned-byte 8)
target-type)
external-format))
(unless bin-routine (no-input-routine)))
(when character-stream-p
(setf (values cin-routine cin-type cin-size read-n-characters
char-size normalized-external-format)
char-size replacement normalized-external-format)
(pick-input-routine target-type external-format))
(unless cin-routine (no-input-routine))))
(setf (fd-stream-in fd-stream) cin-routine
@ -1848,7 +1900,8 @@
(setf input-type (or cin-type bin-type))
(when normalized-external-format
(setf (fd-stream-external-format fd-stream) normalized-external-format
(fd-stream-char-size fd-stream) char-size))
(fd-stream-char-size fd-stream) char-size
(fd-stream-replacement fd-stream) replacement))
(when (= (or cin-size 1) (or bin-size 1) 1)
(setf (fd-stream-n-bin fd-stream) ;XXX
(if (and character-stream-p (not bivalent-stream-p))
@ -1879,7 +1932,7 @@
(when output-p
(when (or (not character-stream-p) bivalent-stream-p)
(setf (values bout-routine bout-type bout-size output-bytes
char-size normalized-external-format)
char-size replacement normalized-external-format)
(let ((buffering (fd-stream-buffering fd-stream)))
(if bivalent-stream-p
(pick-output-routine '(unsigned-byte 8)
@ -1894,7 +1947,7 @@
target-type)))
(when character-stream-p
(setf (values cout-routine cout-type cout-size output-bytes
char-size normalized-external-format)
char-size replacement normalized-external-format)
(pick-output-routine target-type
(fd-stream-buffering fd-stream)
external-format))
@ -1904,6 +1957,7 @@
target-type)))
(when normalized-external-format
(setf (fd-stream-external-format fd-stream) normalized-external-format
(fd-stream-replacement fd-stream) replacement
(fd-stream-char-size fd-stream) char-size))
(when character-stream-p
(setf (fd-stream-output-bytes fd-stream) output-bytes))

View file

@ -36,31 +36,35 @@
(octets-encoding-error-position c)))
(octets-encoding-error-external-format c)))))
(declaim (ftype (sfunction (t t t) (simple-array (unsigned-byte 8) 1))
(defun encoding-replacement-octetify (thing external-format)
(etypecase thing
;; TODO: make sure EXTERNAL-FORMAT here does not have a REPLACEMENT, and handle
;; encoding errors explicitly
(character
(string-to-octets (string thing) :external-format external-format))
(string
(string-to-octets thing :external-format external-format))
((unsigned-byte 8) (make-array 1 :element-type '(unsigned-byte 8) :initial-element thing))
(sequence (coerce thing '(simple-array (unsigned-byte 8) 1)))))
(declaim (ftype (sfunction (t t t t) (simple-array (unsigned-byte 8) 1))
encoding-error))
(defun encoding-error (external-format string pos)
(restart-case
(error 'octets-encoding-error
:external-format external-format
:string string
:position pos)
(use-value (replacement)
:report "Supply a set of bytes to use in place of the invalid one."
:interactive
(lambda ()
(read-evaluated-form
"Replacement byte, bytes, character, or string (evaluated): "))
(typecase replacement
((unsigned-byte 8)
(make-array 1 :element-type '(unsigned-byte 8) :initial-element replacement))
(character
(string-to-octets (string replacement)
:external-format external-format))
(string
(string-to-octets replacement
:external-format external-format))
(t
(coerce replacement '(simple-array (unsigned-byte 8) (*))))))))
(defun encoding-error (external-format replacement string pos)
(flet ((replacement (replacement) (encoding-replacement-octetify replacement external-format)))
(if replacement
(replacement replacement)
(restart-case
(error 'octets-encoding-error
:external-format external-format
:string string
:position pos)
(use-value (replacement)
:report "Supply a set of bytes to use in place of the invalid one."
:interactive
(lambda ()
(read-evaluated-form
"Replacement byte, bytes, character, or string (evaluated): "))
(replacement replacement))))))
;;; decoding condition
@ -98,21 +102,37 @@
(define-condition invalid-utf8-continuation-byte (octet-decoding-error) ())
(define-condition overlong-utf8-sequence (octet-decoding-error) ())
(defun decoding-error (array start end external-format reason pos)
(restart-case
(error reason
:external-format external-format
:array array
:start start
:end end
:pos pos)
(use-value (s)
:report "Supply a replacement string designator."
:interactive
(lambda ()
(read-evaluated-form
"Enter a replacement string designator (evaluated): "))
(string s))))
(defun decoding-replacement-stringify (thing external-format)
(etypecase thing
(character (string thing))
(string thing)
;; TODO: make sure EXTERNAL-FORMAT here does not have a REPLACEMENT, and handle
;; decoding errors explicitly
((unsigned-byte 8)
(let ((octets (make-array 1 :element-type '(unsigned-byte 8) :initial-element thing)))
(octets-to-string octets :external-format external-format)))
(sequence
(let ((octets (coerce thing '(simple-array (unsigned-byte 8) 1))))
(octets-to-string octets :external-format external-format)))))
(defun decoding-error (array start end external-format replacement reason pos)
(flet ((replacement (thing) (decoding-replacement-stringify thing external-format)))
(if replacement
(replacement replacement)
(restart-case
(error reason
:external-format external-format
:array array
:start start
:end end
:pos pos)
(use-value (replacement)
:report "Supply a replacement string."
:interactive
(lambda ()
(read-evaluated-form
"Replacement byte, bytes, character, or string (evaluated): "))
(replacement replacement))))))
;;; Utilities used in both to-string and to-octet conversions
@ -222,17 +242,17 @@
nil))))))))
(declaim (inline get-latin-bytes))
(defun get-latin-bytes (mapper external-format string pos)
(defun get-latin-bytes (mapper external-format replacement string pos)
(let ((code (funcall mapper (char-code (char string pos)))))
(declare (type (or null char-code) code))
(values (cond
((and code (< code 256)) code)
(t
(encoding-error external-format string pos)))
(encoding-error external-format replacement string pos)))
1)))
(declaim (inline string->latin%))
(defun string->latin% (string sstart send get-bytes null-padding)
(defun string->latin% (string sstart send get-bytes null-padding replacement)
(declare (optimize speed)
(type simple-string string)
(type index sstart send)
@ -251,7 +271,7 @@
(tagbody
:no-error
(loop for pos of-type index from sstart below send
do (let ((byte (funcall get-bytes string pos)))
do (let ((byte (funcall get-bytes string pos replacement)))
(typecase byte
((unsigned-byte 8)
(locally (declare (optimize (sb-c:insert-array-bounds-checks 0)))
@ -280,7 +300,7 @@
(vector-push-extend (aref thing i) new-octets))))))
(extend error-replacement)
(loop for pos of-type index from (1+ error-position) below send
do (extend (funcall get-bytes string pos))
do (extend (funcall get-bytes string pos replacement))
finally (return-from string->latin%
(progn
(unless (zerop null-padding)
@ -365,8 +385,9 @@ subsequence bounded by START and END)."
(end end)
:check-fill-pointer t)
(declare (type (simple-array (unsigned-byte 8) (*)) vector))
(let ((ef (maybe-defaulted-external-format external-format)))
(funcall (ef-octets-to-string-fun ef) vector start end))))
(let* ((ef (maybe-defaulted-external-format external-format))
(replacement (ef-replacement ef)))
(funcall (ef-octets-to-string-fun ef) vector start end replacement))))
(declaim (ftype (sfunction (string &key (:external-format t)
(:start index)
@ -399,9 +420,10 @@ STRING (or the subsequence bounded by START and END)."
(end end)
:check-fill-pointer t)
(declare (type simple-string string))
(let ((ef (maybe-defaulted-external-format external-format)))
(let* ((ef (maybe-defaulted-external-format external-format))
(replacement (ef-replacement ef)))
(funcall (ef-string-to-octets-fun ef) string start end
(if null-terminate 1 0)))))
(if null-terminate 1 0) replacement))))
;;; Vector of all available EXTERNAL-FORMAT instances. Each format is named
;;; by one or more keyword symbols. The mapping from symbol to index into this
@ -433,101 +455,64 @@ STRING (or the subsequence bounded by START and END)."
(setf (get name :external-format) free-index))
(setf (aref table free-index) ef)))
(defun wrap-external-format-functions (external-format fun)
(let ((result (%copy-external-format external-format)))
(macrolet ((frob (accessor)
`(setf (,accessor result) (funcall fun (,accessor result)))))
(frob ef-read-n-chars-fun)
(frob ef-read-char-fun)
(frob ef-write-n-bytes-fun)
(frob ef-write-char-none-buffered-fun)
(frob ef-write-char-line-buffered-fun)
(frob ef-write-char-full-buffered-fun)
(frob ef-resync-fun)
(frob ef-bytes-for-char-fun)
(frob ef-read-c-string-fun)
(frob ef-write-c-string-fun)
(frob ef-octets-to-string-fun)
(frob ef-string-to-octets-fun))
result))
;;; This function was moved from 'fd-stream' because it depends on
;;; the various error classes, two of which are defined just above.
;;; XXX: Why does this get called with :DEFAULT and NIL when neither is
;;; the name of any format? Shouldn't those be handled higher up,
;;; or else this should return the actual default?
(defun get-external-format (external-format)
(flet ((replacement-handlerify (entry replacement)
(wrap-external-format-functions
entry
(lambda (fun)
(and fun
(lambda (&rest rest)
(declare (dynamic-extent rest))
(handler-bind
((stream-decoding-error
(lambda (c)
(declare (ignore c))
(invoke-restart 'input-replacement replacement)))
(stream-encoding-error
(lambda (c)
(declare (ignore c))
(invoke-restart 'output-replacement replacement)))
(octets-encoding-error
(lambda (c) (use-value replacement c)))
(octet-decoding-error
(lambda (c) (use-value replacement c))))
(apply fun rest))))))))
(binding*
(((format-name newline replacement)
(etypecase external-format
;; This seem like such an unnecessarily general way to
;; pass optional parameters. What's up with that? (I
;; think it's because BINDING* uses MULTIPLE-VALUE-BIND
;; but optionals are most easily handled with
;; DESTRUCTURING-BIND)
((cons keyword)
(values (car external-format)
(getf (cdr external-format) :newline :lf)
(getf (cdr external-format) :replacement)))
(symbol
(values external-format :lf nil))))
(table-index (get format-name :external-format) :exit-if-null)
(formats *external-formats*)
(table-entry
;; The table entry can be one of:
;; 1. #<external-format>
;; 2. (#<external-format> ((:crlf #\char) . #<modified-ef>) ...)
;; for a list of modified formats that alter the
;; newline encoding and choice of replacement character.
;; 3. "namestring" - to autoload from a fasl containing
;; the named format.
(let ((ef (svref formats table-index)))
(etypecase ef
((or instance list) ef)
#+nil
(string
;; Theoretically allow demand-loading the external-format
;; from a fasl of this name. (Not done yet)
;; (module-provide-contrib ef)
(let ((ef (svref formats table-index)))
(aver (external-format-p ef))
ef)))))
((base-format variations)
(if (listp table-entry)
(values (car table-entry) (cdr table-entry))
(values table-entry nil)))
(newline-base-format
(if (eql newline :lf)
base-format
(cdr (assoc (list newline) variations :test #'equal)))))
(binding*
(((format-name newline replacement)
(etypecase external-format
;; This seem like such an unnecessarily general way to
;; pass optional parameters. What's up with that? (I
;; think it's because BINDING* uses MULTIPLE-VALUE-BIND
;; but optionals are most easily handled with
;; DESTRUCTURING-BIND)
((cons keyword)
(values (car external-format)
(getf (cdr external-format) :newline :lf)
(getf (cdr external-format) :replacement)))
(symbol
(values external-format :lf nil))))
(table-index (get format-name :external-format) :exit-if-null)
(formats *external-formats*)
(table-entry
;; The table entry can be one of:
;; 1. #<external-format>
;; 2. (#<external-format> ((:crlf #\char) . #<modified-ef>) ...)
;; for a list of modified formats that alter the
;; newline encoding and choice of replacement character.
;; 3. "namestring" - to autoload from a fasl containing
;; the named format.
(let ((ef (svref formats table-index)))
(etypecase ef
((or instance list) ef)
#+nil
(string
;; Theoretically allow demand-loading the external-format
;; from a fasl of this name. (Not done yet)
;; (module-provide-contrib ef)
(let ((ef (svref formats table-index)))
(aver (external-format-p ef))
ef)))))
((base-format variations)
(if (listp table-entry)
(values (car table-entry) (cdr table-entry))
(values table-entry nil)))
(newline-base-format
(if (eql newline :lf)
base-format
(cdr (assoc (list newline) variations :test #'equal)))))
(when (or (not newline-base-format) (not replacement))
(return-from get-external-format newline-base-format))
(loop
(let ((key (cons newline replacement)))
(awhen (assoc key variations :test #'equal)
(return (cdr it)))
(let* ((new-ef (replacement-handlerify newline-base-format replacement))
(let* ((new-ef (let ((copy (copy-structure newline-base-format)))
(setf (ef-replacement copy) replacement)
copy))
(new-table-entry
(cons base-format (acons key new-ef variations)))
(old (cas (svref formats table-index) table-entry new-table-entry)))
@ -536,7 +521,7 @@ STRING (or the subsequence bounded by START and END)."
;; CAS failure -> some other thread added an entry. It's probably
;; for the same replacement char which is usually #\ufffd.
;; So try again. At worst this conses some more garbage.
(setq table-entry old)))))))
(setq table-entry old))))))
(push
`("SB-IMPL"

View file

@ -101,9 +101,7 @@
(with-test (:name (,(macroexpand 'name env) :file ,inxf))
(with-open-file (s *test-path* :external-format ',inxf)
(handler-bind ((sb-int:character-decoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::attempt-resync c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let* ((string (make-string 20))
(count (read-sequence string s)))
(assert (equal (map 'list 'identity (subseq string 0 count)) ,expected))))))
@ -129,9 +127,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(write-sequence ,chars s)))
(with-test (:name (,(macroexpand 'name env) :file ,outxf))
(with-open-file (s *test-path* :element-type '(unsigned-byte 8))
@ -140,7 +136,7 @@
(assert (equal (map 'list 'identity (subseq vector 0 count)) ,expected)))))
(with-test (:name (,(macroexpand 'name env) :octets ,outxf))
(handler-bind ((sb-int:character-encoding-error
(lambda (c) (use-value nil c))))
(lambda (c) (use-value "" c))))
(let* ((string (coerce chars 'string))
(octets (sb-ext:string-to-octets string :external-format ',outxf)))
(assert (typep octets '(simple-array (unsigned-byte 8) 1)))
@ -305,9 +301,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual

View file

@ -97,9 +97,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(write-sequence ,chars s)))
(with-test (:name (,(macroexpand 'name env) :file ,outxf))
(with-open-file (s *test-path* :element-type '(unsigned-byte 8))
@ -108,7 +106,7 @@
(assert (equal (map 'list 'identity (subseq vector 0 count)) ,expected)))))
(with-test (:name (,(macroexpand 'name env) :octets ,outxf))
(handler-bind ((sb-int:character-encoding-error
(lambda (c) (use-value nil c))))
(lambda (c) (use-value "" c))))
(let* ((string (coerce chars 'string))
(octets (sb-ext:string-to-octets string :external-format ',outxf)))
(assert (typep octets '(simple-array (unsigned-byte 8) 1)))

View file

@ -97,9 +97,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(write-sequence ,chars s)))
(with-test (:name (,(macroexpand 'name env) :file ,outxf))
(with-open-file (s *test-path* :element-type '(unsigned-byte 8))
@ -108,7 +106,7 @@
(assert (equal (map 'list 'identity (subseq vector 0 count)) ,expected)))))
(with-test (:name (,(macroexpand 'name env) :octets ,outxf))
(handler-bind ((sb-int:character-encoding-error
(lambda (c) (use-value nil c))))
(lambda (c) (use-value "" c))))
(let* ((string (coerce chars 'string))
(octets (sb-ext:string-to-octets string :external-format ',outxf)))
(assert (typep octets '(simple-array (unsigned-byte 8) 1)))
@ -284,9 +282,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual

View file

@ -98,9 +98,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(write-sequence ,chars s)))
(with-test (:name (,(macroexpand 'name env) :file ,outxf))
(with-open-file (s *test-path* :element-type '(unsigned-byte 8))
@ -109,7 +107,7 @@
(assert (equal (map 'list 'identity (subseq vector 0 count)) ,expected)))))
(with-test (:name (,(macroexpand 'name env) :octets ,outxf))
(handler-bind ((sb-int:character-encoding-error
(lambda (c) (use-value nil c))))
(lambda (c) (use-value "" c))))
(let* ((string (coerce chars 'string))
(octets (sb-ext:string-to-octets string :external-format ',outxf)))
(assert (typep octets '(simple-array (unsigned-byte 8) 1)))
@ -255,9 +253,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual

View file

@ -97,9 +97,7 @@
(with-test (:name (,(macroexpand 'name env) :file ,inxf))
(with-open-file (s *test-path* :external-format ',inxf)
(handler-bind ((sb-int:character-decoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::attempt-resync c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let* ((string (make-string 20))
(count (read-sequence string s)))
(assert (equal (map 'list 'identity (subseq string 0 count)) ,expected))))))
@ -125,9 +123,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(write-sequence ,chars s)))
(with-test (:name (,(macroexpand 'name env) :file ,outxf))
(with-open-file (s *test-path* :element-type '(unsigned-byte 8))
@ -136,7 +132,7 @@
(assert (equal (map 'list 'identity (subseq vector 0 count)) ,expected)))))
(with-test (:name (,(macroexpand 'name env) :octets ,outxf))
(handler-bind ((sb-int:character-encoding-error
(lambda (c) (use-value nil c))))
(lambda (c) (use-value "" c))))
(let* ((string (coerce chars 'string))
(octets (sb-ext:string-to-octets string :external-format ',outxf)))
(assert (typep octets '(simple-array (unsigned-byte 8) 1)))
@ -301,9 +297,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual

View file

@ -26,9 +26,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual

View file

@ -26,9 +26,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual

View file

@ -26,9 +26,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual

View file

@ -26,9 +26,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual

View file

@ -131,9 +131,7 @@
(with-test (:name (,(macroexpand 'name env) :file ,inxf))
(with-open-file (s *test-path* :external-format ',inxf)
(handler-bind ((sb-int:character-decoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::attempt-resync c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let* ((string (make-string 20))
(count (read-sequence string s)))
(assert (equal (map 'list 'identity (subseq string 0 count)) ,expected))))))
@ -159,9 +157,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(write-sequence ,chars s)))
(with-test (:name (,(macroexpand 'name env) :file ,outxf))
(with-open-file (s *test-path* :element-type '(unsigned-byte 8))
@ -170,7 +166,7 @@
(assert (equal (map 'list 'identity (subseq vector 0 count)) ,expected)))))
(with-test (:name (,(macroexpand 'name env) :octets ,outxf))
(handler-bind ((sb-int:character-encoding-error
(lambda (c) (use-value nil c))))
(lambda (c) (use-value "" c))))
(let* ((string (coerce chars 'string))
(octets (sb-ext:string-to-octets string :external-format ',outxf)))
(assert (typep octets '(simple-array (unsigned-byte 8) 1)))
@ -336,9 +332,7 @@
:external-format ',outxf
:direction :output :if-exists :supersede)
(handler-bind ((sb-int:character-encoding-error
(lambda (c)
(let ((restart (find-restart 'sb-impl::output-nothing c)))
(invoke-restart restart)))))
(lambda (c) (use-value "" c))))
(let ((pos (file-position s))
(len (file-string-length s string)))
(let ((actual