Unicode implementation concept

"Mark Evans" <[email protected]> 13 Jan 2004 23:54:19 -0000
Newsgroups gmane.comp.lib.icu.general
Message-ID <[email protected]>
Hello, I am a newcomer to this library and not a Unicode expert.
What I have is a rather low-level development concept/question
and I believe this is the right place for it.  I wish to
float a Unicode implementation concept and ask (a) whether ICU
does anything like it and (b) whether you think it could fly.
First a little background.

Some months ago I exchanged ideas with a language designer so
obsessed with performance that he would not consider my proposal
to build Unicode strings directly into his language.  I suggested
that the language should use UTF-16 internally for all strings,
much as the ICU library does.  There could also be intrinsic
UTF-8 and UTF-32 string types, too.

I will say that I believe his performance concerns are
largely misguided, but there they are anyway.  For example, he
forgets that Asian-language strings actually have a smaller
memory footprint in UTF-16 than in UTF-8.  And he tends to
neglect the fact that intrinsic Unicode support provides him
with direct control over the performance of his language.

One can argue whether Unicode support should live in libraries
or in the language.  With respect to this particular language,
its author is already supporting a "wchar_t" type (wide char)
along with C chars, in a half-baked attempt at intrinsic Unicode
support.  He claims existing UTF-8 and UTF-16 support
on that very basis (wrongly).  So in a sense he is persuaded
that Unicode should be intrinsic, but lacks the willingness
to break with fixed character sizes.  His claim for existing
UTF-8 and UTF-16 support is a bit like claiming that language
bit manipulation features imply language support for JPEG, since
JPEG images are composed of bits.

While he cares very much about internationalization, he has a
mental block on the problem of indexing into strings.  He wants
it to work just like C at the language implementation level.  So
for example, his internal array slicing and dicing routines will
also work with strings, modulo minor tweaks - and just as fast.
My proposal, below, was intended to address that requirement.
What do you think of it?

Please forgive in advance any mistakes in terminology on my part,
or any lack of Unicode understanding - I do not claim expertise.
Feel free to enlighten me about ICU which looks like a fabulous
library.

- Mark

==============================================================

There's no hard requirement for serial bytewise storage of the
proposed intrinsic Unicode strings.  Other ways to build Unicode
strings exist.  The one offered here would do little or no damage to
the current compiler.  Really it's just a set of small additions.

Consider a Unicode string made of two data structures:  a C-style
array, and a lookup table.  The C-style array holds the first code
point for each character. The table holds only characters needing more
than one code point.  (A 'code point' meaning 8/16/32 bits for UTF
8/16/32 respectively.)  The keys to the table are the indices of the
string.  So if character #100 has extra code points, they are accessed
via some function like table_access(100).  If a character is in the
table, its first code point is still in the array, but only the first
code point.  So there is some redundancy in storage, but it is small,
and easily pays for itself with runtime efficiency and elegance.

This setup unifies C array indices with Unicode character indices.  So
the language can employ straight pointer arithmetic to find any
character in the string. Character index = array index.  String length
(in chars) = implementation array size (in elements).  These features
may address your hesitation over implementation issues that are
complex in the serial case.

Having found the character, the language need only check the high
bit(s) which flag additional code points.  Unicode requires such a
test in any case; it's unavoidable.  If flagged, the language performs
a table lookup.  This table lookup is the only serious runtime cost.
The table could take whatever form is most efficient.

* UTF-32 has no extended codes, so UTF-32 strings don't need tables.
* UTF-16 characters involve only a few percent with extended codes.
  Ergo - the table is small, and the runtime cost is, say, 2-3%.
* UTF-8 needs the biggest and most table entries, but manageably so.

A downside might be file and network serialization - but we might
skate by.  The language could supply streams on demand, without an
intermediate serialized format.  If I tell the language "write(myFile,
myString)" no intermediate format is required.  The language can just
empty the internal array and table to disk in proper byte sequence.
The disk or network won't care how the language gets the bytes from
memory.

The only hard serialization requirement would be actual user
conversion to byte arrays or Unicode APIs expecting serial data.

Serialization at choke points has a cost of (a) zero, because the
string has no extended codes (say typ. 95%+ of UTF-16 and by
definition 100% of UTF-32), or (b) an alloc plus copy equivalent,
which is acceptable for small to medium strings (another statistically
large class in software programs).

You run into problems only with large UTF-8 strings that are
frequently passed to/from Unicode APIs.  Windows uses UTF-16 so it's
no problem.  Where you find UTF-8 happening is on the web, but that
has inherent delays of its own, so the cost might go unnoticed.
Consider for example that plenty of web sites are driven with UTF-8 by
slower languages.

This scheme supports 7-bit ASCII.  An optimization could yield raw C
speed.  Put an extra boolean flag inside each string structure.  This
flag is the logical OR of all contained Unicode bit flags.  If the
string has no extended chars, the flag is FALSE, and the language can
use alternate string code on that basis.  (No bit tests, no table
lookups.)  That works for UTF-32, 7-bit ASCII, and the majority of
UTF-16 strings.

The idea can be nitpicked to death, but it's a concept.  Unicode
strings and characters will never enjoy the simplicity or speed of
7-bit ASCII.  That's a fact of life, meaning that implementation
concepts cannot be faulted on such a basis.

What would be nice is to make Unicode maximally simple and maximally
efficient for the language users.