У меня есть часть моей библиотеки Unicode, которая декодирует UTF-16 в необработанные кодовые точки Unicode.Тем не менее, он работает не так, как ожидалось.
Вот соответствующая часть кода (исключая UTF-8 и материал для работы со строками):
typedef struct string {
unsigned long length;
unsigned *data;
} string;
string *upush(string *s, unsigned c) {
if (!s->length) s->data = (unsigned *) malloc((s->length = 1) * sizeof(unsigned));
else s->data = (unsigned *) realloc(s->data, ++s->length * sizeof(unsigned));
s->data[s->length - 1] = c;
return s;
}
typedef struct string16 {
unsigned long length;
unsigned short *data;
} string16;
string u16tou(string16 old) {
unsigned long i, cur = 0, need = 0;
string new;
new.length = 0;
for (i = 0; i < old.length; i++)
if (old.data[i] < 0xd800 || old.data[i] > 0xdfff) upush(&new, old.data[i]);
else
if (old.data[i] > 0xdbff && !need) {
cur = 0; continue;
} else if (old.data[i] < 0xdc00) {
need = 1;
cur = (old.data[i] & 0x3ff) << 10;
printf("cur 1: %lx\n", cur);
} else if (old.data[i] > 0xdbff) {
cur |= old.data[i] & 0x3ff;
upush(&new, cur);
printf("cur 2: %lx\n", cur);
cur = need = 0;
}
return new;
}
Как это работает?
string
- это структура, которая содержит 32-битные значения, а string16
для 16-битных значений, таких как UTF-16.Все, что делает upush
, это добавляет полную кодовую точку Unicode к string
, перераспределяя память по мере необходимости.
u16tou
- это та часть, на которой я сосредоточен.Он проходит по string16
, пропуская несуррогатные значения как обычно и преобразуя суррогатные пары в полные кодовые точки.Неправильные суррогаты игнорируются.
Первый суррогат в паре имеет самые младшие 10 битов, сдвинутые на 10 бит влево, в результате чего он формирует старшие 10 битов конечной кодовой точки.У другого суррогата младшие 10 бит добавляются к финалу, а затем добавляются к строке.
Проблема?
Давайте попробуем самую высокую кодовую точку,мы будем?
U+10FFFD
, последняя действительная кодовая точка Unicode, кодируется как 0xDBFF 0xDFFD
в UTF-16.Давайте попробуем расшифровать это.
string16 b;
b.length = 2;
b.data = (unsigned short *) malloc(2 * sizeof(unsigned short));
b.data[0] = 0xdbff;
b.data[1] = 0xdffd;
string a = u16tou(b);
puts(utoc(a));
Используя функцию utoc
(не показана; я знаю, что она работает (см. Ниже)), чтобы преобразовать ее обратно в UTF-8 char *
для печати, я могуПосмотрите в моем терминале, что я получаю U+0FFFFD
, а не U+10FFFD
в результате.
В калькуляторе
Выполнение всех преобразований вручную в gcalctool приводит к тому же неправильному ответу.Так что сам мой синтаксис не ошибочен, но алгоритм таков.Хотя алгоритм мне кажется правильным, и все же он заканчивается неправильным ответом.
Что я делаю не так?