|
|
Message-ID: <20260826224942.GP23438@brightrain.aerifal.cx> Date: Wed, 26 Aug 2026 18:49:43 -0400 From: Rich Felker <dalias@...c.org> To: musl@...ts.openwall.com Subject: Re: Collation data binary format conclusions On Sun, Aug 16, 2026 at 11:01:37PM -0400, Rich Felker wrote: > Some follow-ups on this, to go with implementation of the generating > code I'm working on now: > > On Thu, Jun 18, 2026 at 05:52:56PM -0400, Rich Felker wrote: > > On Sat, May 23, 2026 at 05:02:18PM -0400, Rich Felker wrote: > > > However, what it also suggests is maybe having a data-defined > > > dictionary of header byte values. which would avoid locking in any > > > assumptions about the FractionalUCA.txt implementation of root data > > > and instead allow any assignment (e.g. not even necessarily > > > variable-length/fractional) of weights as non-null byte sequences > > > compatible with strxfrm. > > > > > > From an immediate practical standpoint, this would facilitate eliding > > > not just one common secondary/tertiary byte value (05), but basically > > > all of the common secondary/tertiary weight bytes. So that, instead of > > > most entries in the table being one of (4-6 bytes): > > > > > > - hh pp pp pp ss tt > > > - hh pp pp ss tt > > > - hh pp ss tt > > > > > > most would be (2-4 bytes): > > > > > > - hh pp pp pp > > > - hh pp pp > > > - hh pp > > > > > > We could probably take this even further and let header byte represent > > > a lead primary byte too. This would drop the 87k ideographic collation > > > elements from 4 bytes each to 3 bytes each. > > > > I don't really see a lot of advantage in special-casing > > > > "header dictionary defines the weights for a given level entirely > > or just defines the length in bytes and the weight bytes > > themselves are in the rules" > > > > versus just doing: > > > > "header dictionary defines the total length and a shared prefix of > > the weight bytes". > > I'm going with the latter, more general form. There were no reasons > not to and it just makes more sense. And, the variant that doesn't > require scanning for terminators: Some small addenda that came up during implementation: The format so far just covered the header dictionary and collation elements themselves. There are a couple more things needed outside that: 1. Number of weight levels. Normally this is 3, and I think that's generally fine for anything built on the UCA root data. However there's no good reason to hard-code it in the data representation. The header dictionary entry and direct-coded collation element formats specified work as-is with the number of weight levels being an arbitrary parameter (below some ridiculous level where single-byte offsets and counts no longer work), so let's put that parameter in a global field in the collation hierarchy of the locale file. 2. Forward/reverse direction flag for each weight level. In practice only level 2 needs to be reversible, but any non-initial level is just as easy as level 2, so allow it. Supporting reversal of the very first level requires special-casing and an extra pass, so it's probably not worth doing. It doesn't make sense, and if you really wanted to make a locale that works that way, you could make a dummy initial level where all collation elements are full-ignoreable. So, we need one global key whose contents are an array of direction flags for each level. 3. Delimiting multiple collation elements in a one-to-many or many-to-many mapping. Since mapping to multiple CEs is generally rare, encoding space should not be spent optimizing for the multi-CE case. The most reasonable choices seem to be a single reserved lead byte to indicate multi-CE sequence, either followed by a count byte then the sequence of CEs, or just the sequence of CEs "null terminated" by a full-ignoreable CE. Null termination turns out to be a bad choice in that it requires decoding the header byte at the CE-iteration layer to determine if the CE is "null", so it turns out an explicit count is a better choice. Specifics of encoding. First, the multilevel table tree keys: /3/0: count of weight levels and array of per-weight-level flags /3/1: collation mappings table [added lead byte for multi-CEs] /3/2: header dictionary table [no changes] Contents of /3/0: First byte is number of levels; we can say values above 10 (way more than ever needed) are invalid. After that, an array consisting of one flags byte per level. The only bit defined at this point is bit 0 for direction (0=forward, 1=reverse). Reverse is invalid for first level. Special lead bytes in mappings in /3/1: 0x00 - cannot appear [collision with subtable] 0xfd - direct-coded CE (not using header dict) 0xfe - multi-CE sequence follows 0xff - cannot appear [collision with subtable] Form of multi-CE maps with lead byte 0xfe: Immediately following byte is a count of CEs present in the mapping. The sequence of CEs follows with no further delimiting. Contents of /3/2: Header dictionary is itself a (sub)table, mapping byte values 0x01-0xfc (any lead byte that is not special) to the format described before. Extensibility considerations: Any extension to collation capability, if used in a locale's data, would inherently give a mismatching collation order with a version of the implementation incapable of interpreting the new data. So in general, this should be avoided. If there is a need to extend collation functionality at some point, however, there are two ways in which it can be done. Neither of these requires version labeling now; both rely on the multi-level table data structure: 1. In the case where a meaningful but imperfect collation order can be had with old versions of the implementation by ignoring the unsupported extensions, these extensions can be expressed alongside the existing data structure, in previously-unused flag bits, an extended header dictionary in a new key alongside the existing basic header dictionary at /3/2, or some other new key. 2. In the case where the new extended data cannot be meaningfully processed with old implementations, it may be placed in completely new keys (e.g. a new collation mappings table in /3/3). In this case, the old keys could be left undefined (causing old versions of the implementation not to use the locale for collation at all), or an entire parallel collation table in the old form could be placed in the locale file alongside the new one, with a suitable approximation for old versions of the implementation to use. Neither of these is something we should set out planning to do, and no extensions should be made merely for the sake of optimizing representation for size or performance. The above should only be considered if/when there is a legitimate need for new capabilities to correctly represent an important part of the expected collation ordering for a locale where Unicode has defined these new capabilities.
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.