(return-from fd-stream-resync
(funcall (symbol-function (eighth entry)) stream)))))
+(defun get-fd-stream-character-sizer (stream)
+ (dolist (entry *external-formats*)
+ (when (member (fd-stream-external-format stream) (first entry))
+ (return-from get-fd-stream-character-sizer (ninth entry)))))
+
+(defun fd-stream-character-size (stream char)
+ (let ((sizer (get-fd-stream-character-sizer stream)))
+ (when sizer (funcall sizer char))))
+
+(defun fd-stream-string-size (stream string)
+ (let ((sizer (get-fd-stream-character-sizer stream)))
+ (when sizer
+ (loop for char across string summing (funcall sizer char)))))
+
;;; FIXME: OAOOM here vrt. *EXTERNAL-FORMAT-FUNCTIONS* in fd-stream.lisp
(defmacro define-external-format (external-format size output-restart
out-expr in-expr)
(out-function (symbolicate "OUTPUT-BYTES/" name))
(format (format nil "OUTPUT-CHAR-~A-~~A-BUFFERED" (string name)))
(in-function (symbolicate "FD-STREAM-READ-N-CHARACTERS/" name))
- (in-char-function (symbolicate "INPUT-CHAR/" name)))
+ (in-char-function (symbolicate "INPUT-CHAR/" name))
+ (size-function (symbolicate "BYTES-FOR-CHAR/" name)))
`(progn
+ (defun ,size-function (byte)
+ (declare (ignore byte))
+ ,size)
(defun ,out-function (stream string flush-p start end)
(let ((start (or start 0))
(end (or end (length string))))
(cons '(,external-format ,in-function ,in-char-function ,out-function
,@(mapcar #'(lambda (buffering)
(intern (format nil format (string buffering))))
- '(:none :line :full)))
+ '(:none :line :full))
+ nil ; no resync-function
+ ,size-function)
*external-formats*)))))
(defmacro define-external-format/variable-width
(format (format nil "OUTPUT-CHAR-~A-~~A-BUFFERED" (string name)))
(in-function (symbolicate "FD-STREAM-READ-N-CHARACTERS/" name))
(in-char-function (symbolicate "INPUT-CHAR/" name))
- (resync-function (symbolicate "RESYNC/" name)))
+ (resync-function (symbolicate "RESYNC/" name))
+ (size-function (symbolicate "BYTES-FOR-CHAR/" name)))
`(progn
+ (defun ,size-function (byte)
+ ,out-size-expr)
(defun ,out-function (stream string flush-p start end)
(let ((start (or start 0))
(end (or end (length string))))
,@(mapcar #'(lambda (buffering)
(intern (format nil format (string buffering))))
'(:none :line :full))
- ,resync-function)
+ ,resync-function
+ ,size-function)
*external-formats*)))))
;;; Multiple names for the :ISO{,-}8859-* families are needed because on
(if (zerop mode)
nil
(truncate size (fd-stream-element-size fd-stream)))))
- ;; FIXME: I doubt this is correct in the presence of Unicode,
- ;; since fd-stream FILE-POSITION is measured in bytes.
(:file-string-length
(etypecase arg1
- (character 1)
- (string (length arg1))))
+ (character (fd-stream-character-size fd-stream arg1))
+ (string (fd-stream-string-size fd-stream arg1))))
(:file-position
(fd-stream-file-position fd-stream arg1))))
:external-format :koi8-r)
(let ((char (read-char s)))
(assert (= (char-code (eval char)) #xB0))))
+(delete-file "external-format-test.txt")
+\f
+;;; tests of FILE-STRING-LENGTH
+(let ((standard-characters "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$\"'(),_-./:;?+<=>#%&*@[\\]{|}`^~"))
+ (do-external-formats (xf)
+ (with-open-file (s "external-format-test.txt" :direction :output
+ :external-format xf)
+ (loop for x across standard-characters
+ for position = (file-position s)
+ for char-length = (file-string-length s x)
+ do (write-char x s)
+ do (assert (= (file-position s) (+ position char-length))))
+ (let ((position (file-position s))
+ (string-length (file-string-length s standard-characters)))
+ (write-string standard-characters s)
+ (assert (= (file-position s) (+ position string-length)))))
+ (delete-file "external-format-test.txt")))
+(let ((char-codes '(0 1 255 256 511 512 1023 1024 2047 2048 4095 4096
+ 8191 8192 16383 16384 32767 32768 65535 65536 131071
+ 131072 262143 262144)))
+ (with-open-file (s "external-format-test.txt" :direction :output
+ :external-format :utf-8)
+ (dolist (code char-codes)
+ (let* ((char (code-char code))
+ (position (file-position s))
+ (char-length (file-string-length s char)))
+ (write-char char s)
+ (assert (= (file-position s) (+ position char-length)))))
+ (let* ((string (map 'string #'code-char char-codes))
+ (position (file-position s))
+ (string-length (file-string-length s string)))
+ (write-string string s)
+ (assert (= (file-position s) (+ position string-length))))))
\f
-(delete-file "external-format-test.txt")
+;;;; success
\ No newline at end of file