- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
int _Mbtowcx(wchar_t *pwc, const char *s, size_t nin, mbstate_t *pst, _Statab *pmbstate)
char state = (char)pst->_State;
unsigned char *su = (unsigned char *)s;
wchar_t wc = (wchar_t)pst->_Wchar;
static const mbstate_t initial = {0};
if (pmbstate->_Tab[0] == 0) { /* no table, convert from UTF8 */
if (s == 0)
{ /* set initial state */
*pst = initial;
return (0);
}
for (; ; ++su, --nin) { /* consume an input byte */
if (nin == 0) { /* report incomplete conversion */
pst->_Wchar = wc;
pst->_State = state;
return (-2);
}
else if (0 < state) { /* fold in a successor byte */
if ((*su & 0xc0) != 0x80) { /* report invalid sequence */
errno = EILSEQ;
return (-1);
}
wc = (wchar_t)((wc << 6) | (*su & 0x3f));
--state;
}
else if ((*su & 0x80) == 0)
wc = *su; /* consume a single byte */
else if ((*su & 0xe0) == 0xc0) { /* consume first of two bytes */
wc = (wchar_t)(*su & 0x1f);
state = 1;
}
else if ((*su & 0xf0) == 0xe0) { /* consume first of three bytes */
wc = (wchar_t)(*su & 0x0f);
state = 2;
}
else{ /* report invalid sequence */
errno = EILSEQ;
return (-1);
}
if (state == 0) { /* produce an output wchar */
if (pwc != 0)
*pwc = wc;
pst->_State = 0;
return (wc == 0 ? 0 : (const char *)++su - s);
}
}
}
else
{ /* run finite state machine */
int limit = 0;
if (s == 0) { /* set initial state */
*pst = initial;
return (pmbstate->_Tab[0][0] & _ST_STATE);
}
for (; ; ) { /* perform a state transformation */
unsigned short code;
const unsigned short *stab;
if (nin == 0)
{ /* report incomplete conversion */
pst->_Wchar = wc;
pst->_State = state;
return (-2);
}
else if (_NSTATE <= state
|| (stab = pmbstate->_Tab[state]) == 0
|| (_NSTATE*UCHAR_MAX) <= ++limit
|| (code = stab[*su]) == 0)
{ /* report invalid sequence */
errno = EILSEQ;
return (-1);
}
state = (char)((code & _ST_STATE) >> _ST_STOFF);
if (code & _ST_FOLD)
wc = (wchar_t)(wc & ~UCHAR_MAX | code & _ST_CH);
if (code & _ST_ROTATE)
wc = (wchar_t)(wc << CHAR_BIT | UCHAR_MAX
& wc >> CHAR_BIT * (sizeof (wchar_t) - 1));
if (code & _ST_INPUT && *su != '\0')
++su, --nin, limit = 0;
if (code & _ST_OUTPUT)
{ /* produce an output wchar */
int nused = (const char *)su - s;
if (pwc)
*pwc = wc;
pst->_Wchar = wc;
pst->_State = state;
return (wc == 0 ? 0 : nused == 0 ? -3 : nused);
}
}
}
}
Аналогичная функция с MS Visual Studio делает примерно так
Во вторых: Реализация. Вот список кодовых таблиц поддерживаемых Виндой
http://msdn.microsoft.com/en-us/library/dd317756(v=VS.85).aspx
А в данная функция, вроде бы, поддерживает UTF8 и OEM.
Зачем придумывать такой алгоритм если надежней воспользоваться функцией MultiByteToWideChar?
Эта функция как раз и реализует перекодировку из многобайтной кодировки в wchar. Для этого используется конечный автомат, управляемый таблицами (а кроме 8-битных кодировок существуют и многобайтные азиатские кодировки). В частном случае UTF-8 автомат закодирован явно (таблицы были бы слишком большими). Загляните в код iconv (или MultiByteToWideChar) — там увидите ещё сложнее.
Почему не используется MultiByteToWideChar? Ну, возможно, когда писался этот код, её ещё не было. Или она поддерживала очень ограниченный набор кодировок. Или медленнее. Или часть рантайма MS VС, а не системная.
P. S. Код mbtowc и _Mbtowc приведен полностью
Для работы этой перекодировщику нужны ещё таблицы, на которые ссылается pmbstate (они могут занимать сотни килобайт и даже мегабайты). Полюбопытствуйте, разберитесь, как оно работает.