Код, как написано здесь, все еще глючит - когда вы комбинируете c1 и c2, вам нужно использовать c2! То есть в строках:
c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) +
(c1 - UNI_SUR_LOW_START ) + UNI_BASE;
Третье вхождение c1 на самом деле должно быть c2.
Кроме того, кажется глупым инициализировать указатель src_end для нуля, а затем для src + src_size. Почему бы не добраться туда сразу?
Кроме того, cp_size может быть избыточным, если начало строки было сохранено; тогда он будет таким же, как (dst - initial_dst).
Тестовый код - с исправлением c1 - c2 - с использованием первого примера кода на Solaris 10 с GCC 4.3.3. Результаты для 32-битной и 64-битной компиляции показаны. Данные из таблицы 3.4 в главе 3 стандарта Unicode (технически Unicode 5.0, а не 5.1.0, но я не думаю, что это имеет значение).
enum { NULL = 0 };
enum { UNI_SUR_HIGH_START = 0xD800, UNI_SUR_HIGH_END = 0xDBFF,
UNI_SUR_LOW_START = 0xDC00, UNI_SUR_LOW_END = 0xDFFF,
UNI_SHIFT = 10, UNI_BASE = 0x10000 };
int StringCopy2to4bytes(const unsigned short* src, int src_size,
unsigned int* dst, int dst_size)
{
int cp_size = 0;
const unsigned short *src_end = NULL;
const unsigned int *dst_end = NULL;
unsigned int c1, c2;
src_end = src + src_size;
dst_end = dst + dst_size;
while (src < src_end)
{
c1 = *src++;
if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
{
if (src < src_end)
{
c2 = *src;
if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
{
c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) +
(c2 - UNI_SUR_LOW_START ) + UNI_BASE; /* Fixed */
++src;
}
}
else
return -1;
}
if (dst >= dst_end) return -2;
*dst++ = c1;
cp_size++;
}
return cp_size;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
unsigned short w2_chars[] = { 0x004D, 0x0430, 0x4E8C, 0xD800, 0xDF02, 0x004D };
unsigned int w4_wanted[] = { 0x00004D, 0x000430, 0x004E8C, 0x010302, 0x00004D };
unsigned int w4_actual[5];
int w2_len = 6;
int w4_len = 5;
int w4_actlen;
int i;
int failed = 0;
w4_actlen = StringCopy2to4bytes(w2_chars, w2_len, w4_actual, w4_len);
if (w4_actlen != w4_len)
{
failed = 1;
printf("Length mismatch: got %d, wanted %d\n", w4_actlen, w4_len);
}
for (i = 0; i < w4_len; i++)
{
if (w4_actual[i] != w4_wanted[i])
{
printf("Mismatch: index %d: wanted 0x%06X, actual 0x%06X\n",
i, w4_wanted[i], w4_actual[i]);
failed = 1;
}
}
if (failed == 0)
printf("No problem observed\n");
return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
$ gcc -m32 -O utf.c -o utf32 && ./utf32
No problem observed
$ gcc -m64 -O utf.c -o utf64 && ./utf64
No problem observed
$
Мне интересно, что случилось с вашим компилятором или с вашим тестовым примером.
Вот пересмотренная версия функции StringCopy2to4bytes (). Он обнаруживает и сообщает об ошибке, которой не было в оригинале, а именно, когда второе слово суррогатной пары не является допустимым низким суррогатным кодом, он возвращает статус -3.
int StringCopy2to4bytes(const unsigned short *src, int src_size,
unsigned int *dst, int dst_size)
{
const unsigned short *src_end = src + src_size;
const unsigned int *dst_end = dst + dst_size;
const unsigned int *dst0 = dst;
while (src < src_end)
{
unsigned int c1 = *src++;
if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
{
if (src >= src_end)
return -1;
unsigned int c2 = *src++;
if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
{
c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) +
(c2 - UNI_SUR_LOW_START ) + UNI_BASE; /* Fixed */
}
else
return -3; /* Invalid second code point in surrogate pair */
}
if (dst >= dst_end)
return -2;
*dst++ = c1;
}
return dst - dst0;
}
Тот же тестовый код дает тот же чистый счет здоровья. Объявление c2 предполагает, что вы используете C99, а не C89.