[Openmcl-devel] [babel-devel] Changes

Gary Byers gb at clozure.com
Sun Apr 12 13:35:38 PDT 2009

On Sun, 12 Apr 2009, Luís Oliveira wrote:

> Again, I'm curious how UTF-8B might be implemented when CODE-CHAR
> returns NIL for #xDC80 through #xDCFF.

Let's assume that we have something that reads a sequence of 1 or more
UTF-8-encoded bytes from a stream (and that we have variants that do
the same for bytes in foreign memory, a lisp vector, etc.)  If it
gets an EOF while trying to read the first byte of a sequence, it
returns NIL; otherwise, it returns an unsigned integer less than
#x110000.  If it can tell that a sequence is malformed (overlong,
whatever), it returns the CHAR-CODE of the Unicode replacement
character (#xfffd), it does not reject encoded values that correspond
to UTF-16 surrogate pairs or other non-character code points.

(defun read-utf-8-code (stream)
   "Try to read 1 or more octets from stream.  Return NIL if EOF is
    encountered when reading the first octet, otherwise, return an
    unsigned integer less than #x110000.  If a malformed UTF-8
    sequence is detected, return the character code of #\Replacement_Character;
    otherwise, return encoded value."
   (let* ((b0 (read-byte stream nil nil)))
     (when b0
       (if (< b0 #x80)
         (if (< b0 #xc2)
           (char-code #\Replacement_Character)
           (let* ((b1 (read-byte stream nil nil)))
             (if (null b1)
               ;;[Lots of other details to get right, not shown]

This (or something very much like it) has to exist in order to support
UTF-8; the elided details are surprisingly complicated (if we want
to reject malformed sequences.)

I wasn't able to find a formal definition of UTF-8B anywhere; the
informal descriptions that I saw suggested that it's a way of
embedding binary data in UTF-8-encoded character data, with the binary
data encoded in the low 8 bits of 16-bit codes whose high 8 bits
contained #xdc.  If the binary data is in fact embedded in the low 7
bits of codes in the range #xdc80-#xdc8f or something else, then the
following parameters would need to change:

(defparameter *utf-8b-binary-data-byte* (byte 8 0))
(defparameter *utf-8b-binary-marker-byte* (byte 13 8))
(defparameter *utf-8b-binary-marker-value* #xdc)

PROCESS-BINARY and PROCESS-CHARACTER do whatever it is that you
want to do with a byte of binary data or a character.  A real
decoder might want to take these functions - or a single function
that processed either a byte or character - as arguments.

This is just #\Replacement_Character in CCL:

(defparameter *replacement-character* (code-char #xfffd))

(defun decode-utf-8b-stream (stream)
   (do* ((code (read-utf-8-code stream) (read-utf-8-code stream)))
        ((null code)) ; eof
     (if (eql *utf-8b-binary-marker-value*
              (ldb *utf-8b-binary-marker-byte* code))
        (process-binary (ldb *utf-8b-binary-data-byte* code))
        (process-character (or (code-char code) *replacement-character*)))))

Isn't that the basic idea, whether the details/parameters are right
or not ?

> -- 
> Luís Oliveira
> http://student.dei.uc.pt/~lmoliv/

More information about the Openmcl-devel mailing list