News for hackers. Stuff that matters.
Re:UCS vs. UTF-8 as Internal String Encoding by lambda on Thursday May 28, 2015 @12:14PM (#9619153)
Why would you want constant time access to code points? What can you do with constant time access to code points that is actually correct?

Every time someone tries to promote UTF-16, UTF-32, or some imagined encoding like UTF-24, they bring up constant time access to code points, but I have never heard of a reason why you would want that.

Pretty much every use case I have ever heard for constant-time access can be handled just as well by constant-time access at the code unit (byte in UTF-8, 16 bit value in UTF-16, 32 bit value in UTF-32) level. Matching text exactly? Matching works just as well at the code unit level. Doing any kind of fuzzy or regular expression match? You're going to need to iterate over each item anyhow to normalize it or classify it. Need to store offsets? That works just fine at the code unit level.

Most of the other suggestions I've heard for what to do with constant-time access to code points is incorrect when you consider combining characters, normalization, different character widths, need for linguistically appropriate word splitting, etc. If you're doing something that doesn't take these into account, why are you using Unicode instead of just ASCII?

In addition, for the ASCII range, UTF-24 would take up 3 times the space as UTF-8, and the vast majority of text processed is actually in the ASCII range due to verbose markup formats like HTML, XML, etc. Plus if you do anything with the code points in UTF-24, you need to do a bunch of bit-fiddling to move them into 32 bit alignment, so as far as actually decoding the individual code points, it's pretty much a wash with UTF-8.

Re:UCS vs. UTF-8 as Internal String Encoding by vorg on Thursday May 28, 2015 @01:59PM (#9619896)
> UTF-24 would take up 3 times the space as UTF-8, and the vast majority of text processed is actually in the ASCII range due to verbose markup formats like HTML, XML, etc

Sounds like these "verbose" markup formats are the real problem in taking up too much space, not the Unicode transformation formats.

Re:UCS vs. UTF-8 as Internal String Encoding by lambda on Thursday May 28, 2015 @03:12PM (#9620419)
I think that moving the world away from HTML and XML as markup languages is likely to be considerably harder than simply defaulting to using UTF-8 in any new code for which there isn't already a natural encoding to choose based on the platform or APIs you're developing on.

Furthermore, even beyond the markup, the vast majority of written content is in the Roman alphabet, of which most characters are in the ASCII range, and those that aren't fit into 2 bytes of UTF-8. 55% of text on the web is in English, then languages using the Roman script or other scripts in the 2-byte UTF-8 range make up 21 of the next 25 most commonly used languages on the internet; of the top 25 languages, only Chinese, Japanese, Korean, and Thai are in ranges that require 3 bytes in UTF-8 for the majority of their characters (https://en.wikipedia.org/wiki/Languages_used_on_the_Internet).

So for the vast majority of text that is processed, UTF-8 is dramatically more efficient than a hypothetical UTF-24 or the real UTF-32. Now, you might say "well, it should all compress away anyhow", but when dealing with text in RAM, it is generally not compressed (and that would make handling it far more complex than just using UTF-8), and memory capacity, latency, and bandwidth can all be important.

Trying to deal with text as fixed-width characters is simply incorrect. Almost every non-trivial means of handling text needs to support arbitrary length strings, needs to deal with clusters of more than one codepoint as a single unit, and needs to iterate over the characters linearly at least once (and can then store byte offsets for random access later on). Dramatically increasing storage requirements of text for the non-benefit of being able to deal with fixed-width codepoints just doesn't make sense.

Re:UCS vs. UTF-8 as Internal String Encoding by Jweb_Guru on Thursday May 28, 2015 @02:39PM (#9620187)
Okay. But that's the world in which we live. Complaining about the real problem being "things people put in strings" rather than the actual encoding format isn't going to solve any problems people actually have.
Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Thursday May 28, 2015 @12:47PM (#9619385)
> What can you do with constant time access to code points that is actually correct?

Compute an offset into a string in one part of your code, and then pass a representation of that offset to another part of your code that then accesses that part of the string and does something with it.

It is theoretically possible to produce an abstract representation of such an offset that would allow constant-time access into a non-uniformly-represented string, but no programming language that I know of has this feature. Even Common Lisp, which is probably the most flexible and future-proof language ever designed, requires that offsets into strings be integers. (Actually, it's even worse than that: Common Lisp requires that strings be arrays of characters, which is truly ironic if you think about it.)

Re:UCS vs. UTF-8 as Internal String Encoding by rectang on Thursday May 28, 2015 @12:52PM (#9619426)
But as lambda points out, that offset could be measured in code units rather than code points. You still don't need random access into variable-width data.
Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Thursday May 28, 2015 @01:01PM (#9619474)
Read what I wrote more carefully. You have to take that offset, represent it somehow, and then pass that representation off to another part of the code, which then uses that representation to access the part of the string that was found earlier. How do you do that without forcing a re-scan when the only facility that the language provides for accessing a character in a string requires you to represent the location of that character as an integer?

Actually, you can do it in C because you can use a raw pointer, but that is fraught with all manner of other perils.

Re:UCS vs. UTF-8 as Internal String Encoding by jcranmer on Thursday May 28, 2015 @01:46PM (#9619802)
Just use the octet index instead of the code point index. To use UTF-8-based strings, you do need to distinguish between octet indexes (calling them iterators is probably saner to most people) and character indexes.

With UCS-4 (and UCS-2 as implemented in practice), the iterator representation and character index are the same, which makes retrofitting those implementations to use UTF-8-based strings impossible. But for new environments, where UCS-2-backwards-compatibility isn't a requirement, it's not hard to make a string API that properly exposes the difference between iterators and character indexes, which is why newer environments are gravitating towards UTF-8 strings.

Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Thursday May 28, 2015 @01:58PM (#9619884)
> Just use the octet index instead of the code point index.

And how, exactly, are you going to do that in any extant programming language other than C or assembler?

Re:UCS vs. UTF-8 as Internal String Encoding by lambda on Thursday May 28, 2015 @02:31PM (#9620112)
This discussion is about what encoding is best to choose for the low-level string types in languages and libraries, as that's what the original article was about.

If your language already uses a Unicode-compatible string type, you should probably use that.

If you are designing a new language, rewriting a language's basic string primitives, or writing an API or application in a language that doesn't have a well-standardized Unicode string type like C or C++, then it is probably better to pick UTF-8; if your app is necessarily single platform and tied to a platform API, choose that API's encoding, but for anything cross platform, the encoding is going to differ between the platforms so choosing UTF-8 and then transcoding at the API boundaries on the different platforms is fine.

By the way, most languages do have the concept of a byte array, and you can provide UTF-8 string operations on top of that; but as I said, in general, you should be using the language's native string type unless you have a very good reason not to.

The original article that we're discussing does mention one of those very good reasons; in Python, it uses an 8-bit Latin-1 encoding if the characters can fit, upgrades to 16-bit UCS-2 if they can fit in that range, and finally upgrades to 32-bit UTF-32 if they don't fit in the UCS-2 range. But that means that if you're appending strings, and one of them contains an emoji (for instance, in a web templating system that encounters an emoji in a comment), that can mean that all of a sudden your string has to grow to four times its original size, which can cause serious problems; so one workaround for that may be to actually write everything out to a UTF-8 encoded string (which in Python can be represented as a byte array) rather than trying to append strings together and hitting that behavior.

Furthermore, several languages already do use UTF-8 as their internal string type; Rust, Go, and Ruby all use UTF-8 as their default or only string type.

And by the way, Rust and Go both use slices to handle the use case you describe; a slice is represented as a pointer to the first byte of interest, and a number representing how many bytes it extends. I don't know about in Go, but in Rust the API guarantees that all string slices created from a string begin and end on proper codepoint boundaries. Go: https://blog.golang.org/slices, Rust: https://doc.rust-lang.org/std/primitive.str.html.

Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Thursday May 28, 2015 @05:38PM (#9621348)
I think we're in violent agreement here. Keep in mind that the question I was responding to was:

> Why would you want constant time access to code points?

And the answer is: because you might want to compute an offset into a string in one place in your code, and then use that offset to access the string at that offset somewhere else in your code in constant time.

Re:UCS vs. UTF-8 as Internal String Encoding by lambda on Thursday May 28, 2015 @06:40PM (#9621647)
But you don't need constant time access to code points for that, you just need constant time access to code units, which UTF-8 can do just fine. That's why I had asked that question; every reason I have ever seen given for wanting constant time access by code point can either be done simply by indexing by code unit, or is misguided as it assumes that a single code point is an interesting unit of processing on its own.

A code point (http://unicode.org/glossary/#code_point) is a value in the range from 0x0 to 0x10FFFF. In a fixed-width type, you need at least 21 bits to represent that, which is why 32 bits is the most common form for representing any arbitrary code point; but if you don't mind the alignment issues, you could even pack it to 24 bits or even 21 bits if you don't mind a little bit twiddling to extract it.

A code unit (http://unicode.org/glossary/#code_unit) is the smallest unit of encoding in the encoding that you are using. In UTF-8, that's a single byte; in UTF-16, it's two bytes, in UTF-32, four bytes.

In order to be able to have constant time offsets, you only need to store a code unit index, not a code point index.

The thing is, individual code points are not really all that interesting from the perspective of text processing. Almost all meaningful processing of them could potentially involve multiple code points, splitting on arbitrary code point boundaries without regards to higher level semantics can cause all kinds of problems, etc. So a desire to have constant time indexing by code points is misguided.

All offsets can be stored by code unit (byte in UTF-8). That gives you all of the constant time access to given points of interest in a string that constant time indexing by code point would have given you.

Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Friday May 29, 2015 @11:22AM (#9625102)
I wrote:

> (And yes, I know that UTF-8 is self-synchronizing. But that doesn't help with the problem I'm talking about, and if you think it does then you haven't understood the problem.)

I would like to retract this, and apologize for the snarky tone. I had mis-remembered the design of UTF-8, and so pretty much everything I said was wrong (well, not all of it was wrong, but the parts that weren't were exactly what you were saying).

I'd edit or delete my comment if I could, but the deadline has passed so this is the best I can do.

Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Thursday May 28, 2015 @07:30PM (#9621850)
> But you don't need constant time access to code points for that, you just need constant time access to code units

Well, sort of. What you really want is constant-time access to (semantically meaningful) substrings. Not all code points are semantically meaningful, but the situation is even worse for code units. A pointer to a random code point may or may not designate the boundary of a semantically meaningful substring. But whatever problems that causes, the situation for code units is even worse, because a pointer to a random code unit may or may not designate the start of a code point, and even if it does then you still have the previous problem that a random code point may or may not designate yada yada yada. (And yes, I know that UTF-8 is self-synchronizing. But that doesn't help with the problem I'm talking about, and if you think it does then you haven't understood the problem.)

What you really want is constant-time dereferencing of designators for semantically meaningful substrings. But no language AFAIK actually has that. The fundamental problem is that most languages have painted themselves into a corner by carving into stone the fact that strings can be dereferenced by integers. Once you've done that, you're pretty much screwed. It's not that you can't make it work, it's just that it requires an awful lot of machinery. You basically need to build an index for every string you construct, and that can get very expensive. Fixed-width representations are a less-than-perfect-but-still-not-entirely-unreasonable engineering solution to this problem.

Re:UCS vs. UTF-8 as Internal String Encoding by wtetzner on Thursday May 28, 2015 @02:18PM (#9620024)
Notice he said code units, not code points. Code units are fixed size. So for example, in Java, you have 16-bit code units, and have constant time indexing into them.

So you could use a BreakIterator [1] to iterate through grapheme clusters. The iterator returns the index to the cluster though, not the cluster itself. The index is in terms of code units, not in terms of grapheme clusters.

[1] https://docs.oracle.com/javase/6/docs/api/java/text/BreakIte...

Re:UCS vs. UTF-8 as Internal String Encoding by TheCoelacanth on Thursday May 28, 2015 @02:00PM (#9619906)
Why couldn't the string implementation simply provide a string offset class? Internally, it could be represented as a byte offset but at the point of use it wouldn't have to know that.
Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Thursday May 28, 2015 @05:36PM (#9621341)
Yes, this is the Right Thing. But don't forget the question I was responding to:

> Why would you want constant time access to code points?

The answer is: because you might want to compute an offset into a string in one place in your code, and then pass that offset to another place in your code which then accesses the string at that offset in constant time.

My point is that to make this work you must have string offset be an abstract class, not an integer.

Re:UCS vs. UTF-8 as Internal String Encoding by TheCoelacanth on Thursday May 28, 2015 @10:30PM (#9622389)
It only has to be an abstract class if you want to prevent the existence of invalid offsets, but the same is true if you have constant width code points. For instance, INT_MAX is not a valid offset into almost any string.
Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Friday May 29, 2015 @02:14AM (#9622998)
> It only has to be an abstract class if you want to prevent the existence of invalid offsets

Wouldn't you want that? I would.

> the same is true if you have constant width code points

True, but the validity check for the constant-width case can be done in constant time.

Re:UCS vs. UTF-8 as Internal String Encoding by TheCoelacanth on Friday May 29, 2015 @09:25AM (#9624351)
> True, but the validity check for the constant-width case can be done in constant time.

The same is true of UTF-8 because it is self-synchronizing. You only have to check that the byte at the given offset is either 0xxxxxxx or 11xxxxxx.

Re:UCS vs. UTF-8 as Internal String Encoding by lisper on Friday May 29, 2015 @11:06AM (#9624978)
Hm, that's a good point.
Re:UCS vs. UTF-8 as Internal String Encoding by maxlybbert on Thursday May 28, 2015 @01:23PM (#9619647)
> Why would you want constant time access to code points? What can you do with constant time access to code points that is actually correct?

Well, it makes it easier to avoid putting an ellipsis in the middle of a multi-byte character, which could crash the user's phone when something else tries to display your now-invalid string.

Re:UCS vs. UTF-8 as Internal String Encoding by lambda on Thursday May 28, 2015 @02:18PM (#9620018)
Note that my question included "...that is actually correct" as a condition.

Truncating between code points to insert an ellipsis is not correct. Not all characters have the same width, even in a fixed-width font; for instance, combining characters have zero width. Truncating between a base character and a following combining character makes no sense. If you're truncating the word "exposé" to fit into six characters, it makes no sense to count that as 7 characters, truncate the accent, and wind up with "expose...". (Yes, normally that will be represented as a pre-composed character, but there are base-character/diacritic combinations that have no pre-composed character, so you will necessarily get a combining diacritic).

Truncating based on character count can also mean that you exceed your width requirement; CJK characters are twice the width of most alphabetic characters.

Additionally, most text is displayed using variable width fonts, where truncation should depend on the precise sum of the widths of characters, but even text that isn't can have the previous two issues. These mean that to do truncation properly, you need to iterate over the characters counting width until you exceed your required width in order to truncate, then truncate at the previous grapheme boundary, which requires walking through the string in order and can be done just as efficiently in a variable width encoding as in fixed.

Re:UCS vs. UTF-8 as Internal String Encoding by maxlybbert on Friday May 29, 2015 @11:49AM (#9625296)
> Note that my question included "...that is actually correct" as a condition.

I think it's valid to pick an approach that makes it more likely I'll do things correctly.