/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <objmng/drm_i18n.h>
#define IS_GB2312_HIGH_BYTE(c) ((c) >= 0xA1 && (c) <= 0xF7)
#define IS_GB2312_LOW_BYTE(c) ((c) >= 0xA1 && (c) <= 0xFE)
#define IS_GBK_HIGH_BYTE(c) ((c) >= 0x81 && (c) <= 0xFE)
#define IS_GBK_LOW_BYTE(c) ((c) >= 0x40 && (c) <= 0xFE && (c) != 0x7F)
#define IS_BIG5_HIGH_BYTE(c) ((c) >= 0xA1 && (c) <= 0xF9)
#define IS_BIG5_LOW_BYTE(c) (((c) >= 0x40 && (c) <= 0x7E) \
|| ((c) >= 0xA1 && (c) <= 0xFE))
#define IS_ASCII(c) ((c) <= 127)
#define INVALID_UNICODE 0xFFFD
#define I18N_LATIN1_SUPPORT
#define I18N_UTF8_UTF16_SUPPORT
/**
* Simply convert ISO 8859-1 (latin1) to unicode
*/
static int32_t latin1ToWcs(const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed);
/**
* Convert one unicode char to ISO 8859-1 (latin1) byte
*/
static int32_t wcToLatin1(uint16_t wc, uint8_t * mbs, int32_t bufSize);
/**
* Convert UTF-8 to unicode
*/
static int32_t utf8ToWcs(const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed);
/**
* Convert one unicode char to UTF-8 bytes
*/
static int32_t wcToUtf8(uint16_t wc, uint8_t * mbs, int32_t bufSize);
/**
* Convert UTF-16 BE to unicode
*/
static int32_t utf16beToWcs(const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed);
/**
* Convert one unicode char to UTF-16 BE bytes
*/
static int32_t wcToUtf16be(uint16_t wc, uint8_t * mbs, int32_t bufSize);
/**
* Convert UTF-16 LE to unicode
*/
static int32_t utf16leToWcs(const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed);
/**
* Convert one unicode char to UTF-16 LE bytes
*/
static int32_t wcToUtf16le(uint16_t wc, uint8_t * mbs, int32_t bufSize);
/*
* see drm_i18n.h
*/
int32_t DRM_i18n_mbsToWcs(DRM_Charset_t charset,
const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed)
{
switch (charset)
{
#ifdef I18N_GB2312_SUPPORT
case DRM_CHARSET_GB2312:
return gb2312ToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
#endif
#ifdef I18N_GBK_SUPPORT
case DRM_CHARSET_GBK:
return gbkToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
#endif
#ifdef I18N_BIG5_SUPPORT
case DRM_CHARSET_BIG5:
return big5ToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
#endif
#ifdef I18N_LATIN1_SUPPORT
case DRM_CHARSET_LATIN1:
return latin1ToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
#endif
#ifdef I18N_ISO8859X_SUPPORT
case DRM_CHARSET_LATIN2:
case DRM_CHARSET_LATIN3:
case DRM_CHARSET_LATIN4:
case DRM_CHARSET_CYRILLIC:
case DRM_CHARSET_ARABIC:
case DRM_CHARSET_GREEK:
case DRM_CHARSET_HEBREW:
case DRM_CHARSET_LATIN5:
case DRM_CHARSET_LATIN6:
case DRM_CHARSET_THAI:
case DRM_CHARSET_LATIN7:
case DRM_CHARSET_LATIN8:
case DRM_CHARSET_LATIN9:
case DRM_CHARSET_LATIN10:
return iso8859xToWcs(charset, mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
#endif
#ifdef I18N_UTF8_UTF16_SUPPORT
case DRM_CHARSET_UTF8:
return utf8ToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
case DRM_CHARSET_UTF16BE:
return utf16beToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
case DRM_CHARSET_UTF16LE:
return utf16leToWcs(mbs, mbsLen, wcsBuf, bufSizeInWideChar, bytesConsumed);
#endif
default:
return -1;
}
}
/*
* see drm_i18n.h
*/
int32_t DRM_i18n_wcsToMbs(DRM_Charset_t charset,
const uint16_t *wcs, int32_t wcsLen,
uint8_t *mbsBuf, int32_t bufSizeInByte)
{
int32_t (* wcToMbFunc)(uint16_t, uint8_t *, int32_t);
int32_t charIndex = 0;
int32_t numMultiBytes = 0;
switch (charset)
{
#ifdef I18N_LATIN1_SUPPORT
case DRM_CHARSET_LATIN1:
wcToMbFunc = wcToLatin1;
break;
#endif
#ifdef I18N_UTF8_UTF16_SUPPORT
case DRM_CHARSET_UTF8:
wcToMbFunc = wcToUtf8;
break;
case DRM_CHARSET_UTF16BE:
wcToMbFunc = wcToUtf16be;
break;
case DRM_CHARSET_UTF16LE:
wcToMbFunc = wcToUtf16le;
break;
#endif
#ifdef I18N_ISO8859X_SUPPORT
case DRM_CHARSET_LATIN2:
case DRM_CHARSET_LATIN3:
case DRM_CHARSET_LATIN4:
case DRM_CHARSET_CYRILLIC:
case DRM_CHARSET_ARABIC:
case DRM_CHARSET_GREEK:
case DRM_CHARSET_HEBREW:
case DRM_CHARSET_LATIN5:
case DRM_CHARSET_LATIN6:
case DRM_CHARSET_THAI:
case DRM_CHARSET_LATIN7:
case DRM_CHARSET_LATIN8:
case DRM_CHARSET_LATIN9:
case DRM_CHARSET_LATIN10:
return wcsToIso8859x(charset, wcs, wcsLen, mbsBuf, bufSizeInByte);
#endif
default:
return -1;
}
if (mbsBuf) {
while (numMultiBytes < bufSizeInByte && charIndex < wcsLen) {
/* TODO: handle surrogate pair values here */
int32_t mbLen = wcToMbFunc(wcs[charIndex],
&mbsBuf[numMultiBytes], bufSizeInByte - numMultiBytes);
if (numMultiBytes + mbLen > bufSizeInByte) {
/* Insufficient buffer. Don't update numMultiBytes */
break;
}
charIndex++;
numMultiBytes += mbLen;
}
} else {
while (charIndex < wcsLen) {
/* TODO: handle surrogate pair values here */
numMultiBytes += wcToMbFunc(wcs[charIndex], NULL, 0);
charIndex++;
}
}
return numMultiBytes;
}
#ifdef I18N_LATIN1_SUPPORT
int32_t latin1ToWcs(const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed)
{
int32_t charsToConvert;
int32_t len;
if (wcsBuf == NULL) {
return mbsLen;
}
len = charsToConvert = mbsLen > bufSizeInWideChar ? bufSizeInWideChar : mbsLen;
if (len < 0)
return 0;
while (len--) {
*wcsBuf++ = *mbs++;
}
if (bytesConsumed)
*bytesConsumed = charsToConvert;
return charsToConvert;
}
int32_t wcToLatin1(uint16_t wc, uint8_t * mbs, int32_t bufSize)
{
uint8_t ch;
if (wc < 0x100) {
ch = (uint8_t)(wc & 0xff);
} else {
ch = '?';
}
if (mbs && bufSize > 0)
*mbs = ch;
return 1;
}
#endif /* I18N_LATIN1_SUPPORT */
#ifdef I18N_UTF8_UTF16_SUPPORT
int32_t utf8ToWcs(const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed)
{
int32_t charsConverted = 0;
int32_t i = 0;
int32_t wideChar;
if (wcsBuf == NULL) {
/* No conversion but we're still going to calculate bytesConsumed */
bufSizeInWideChar = mbsLen * 2;
}
while((i < mbsLen) && (charsConverted < bufSizeInWideChar)) {
uint8_t ch = mbs[i];
uint8_t ch2, ch3, ch4;
wideChar = -1;
if(IS_ASCII(ch)) {
wideChar = ch;
i++;
} else if ((ch & 0xc0) == 0xc0) {
int utfStart = i;
if ((ch & 0xe0) == 0xc0) {
/* 2 byte sequence */
if (i + 1 < mbsLen && ((ch2 = mbs[i + 1]) & 0xc0) == 0x80) {
wideChar = (uint16_t)(((ch & 0x1F) << 6) | (ch2 & 0x3F));
i += 2;
} else {
/* skip incomplete sequence */
i++;
}
} else if ((ch & 0xf0) == 0xe0) {
/* 3 byte sequence */
if (i + 2 < mbsLen
&& ((ch2 = mbs[i + 1]) & 0xc0) == 0x80
&& ((ch3 = mbs[i + 2]) & 0xc0) == 0x80) {
wideChar = (uint16_t)(((ch & 0x0F) << 12) | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F));
i += 3;
} else {
/* skip incomplete sequence (up to 2 bytes) */
i++;
if (i < mbsLen && (mbs[i] & 0xc0) == 0x80)
i++;
}
} else if ((ch & 0xf8) == 0xf0) {
/* 4 byte sequence */
if (i + 3 < mbsLen
&& ((ch2 = mbs[i + 1]) & 0xc0) == 0x80
&& ((ch3 = mbs[i + 2]) & 0xc0) == 0x80
&& ((ch4 = mbs[i + 3]) & 0xc0) == 0x80) {
/* FIXME: we do NOT support U+10000 - U+10FFFF for now.
* leave it as 0xFFFD. */
wideChar = INVALID_UNICODE;
i += 4;
} else {
/* skip incomplete sequence (up to 3 bytes) */
i++;
if (i < mbsLen && (mbs[i] & 0xc0) == 0x80) {
i++;
if (i < mbsLen && (mbs[i] & 0xc0) == 0x80) {
i++;
}
}
}
} else {
/* invalid */
i++;
}
if (i >= mbsLen && wideChar == -1) {
/* Possible incomplete UTF-8 sequence at the end of mbs.
* Leave it to the caller.
*/
i = utfStart;
break;
}
} else {
/* invalid */
i++;
}
if(wcsBuf) {
if (wideChar == -1)
wideChar = INVALID_UNICODE;
wcsBuf[charsConverted] = (uint16_t)wideChar;
}
charsConverted++;
}
if (bytesConsumed)
*bytesConsumed = i;
return charsConverted;
}
int32_t wcToUtf8(uint16_t wc, uint8_t * mbs, int32_t bufSize)
{
if (wc <= 0x7f) {
if (mbs && (bufSize >= 1)) {
*mbs = (uint8_t)wc;
}
return 1;
} else if (wc <= 0x7ff) {
if (mbs && (bufSize >= 2)) {
*mbs++ = (uint8_t)((wc >> 6) | 0xc0);
*mbs = (uint8_t)((wc & 0x3f) | 0x80);
}
return 2;
} else {
if (mbs && (bufSize >= 3)) {
*mbs++ = (uint8_t)((wc >> 12) | 0xe0);
*mbs++ = (uint8_t)(((wc >> 6) & 0x3f)| 0x80);
*mbs = (uint8_t)((wc & 0x3f) | 0x80);
}
return 3;
}
}
int32_t utf16beToWcs(const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed)
{
int32_t charsToConvert;
int32_t len;
if (wcsBuf == NULL) {
return mbsLen / 2;
}
len = charsToConvert = (mbsLen / 2) > bufSizeInWideChar ? bufSizeInWideChar : (mbsLen / 2);
while (len--) {
/* TODO: handle surrogate pair values */
*wcsBuf++ = (uint16_t)((*mbs << 8) | *(mbs + 1));
mbs += 2;
}
if (bytesConsumed)
*bytesConsumed = charsToConvert * 2;
return charsToConvert;
}
int32_t wcToUtf16be(uint16_t wc, uint8_t * mbs, int32_t bufSize)
{
if (mbs && bufSize >= 2) {
/* TODO: handle surrogate pair values */
*mbs = (uint8_t)(wc >> 8);
*(mbs + 1) = (uint8_t)(wc & 0xff);
}
return 2;
}
int32_t utf16leToWcs(const uint8_t *mbs, int32_t mbsLen,
uint16_t *wcsBuf, int32_t bufSizeInWideChar,
int32_t *bytesConsumed)
{
int32_t charsToConvert;
int32_t len;
if (wcsBuf == NULL) {
return mbsLen / 2;
}
len = charsToConvert = (mbsLen / 2) > bufSizeInWideChar ? bufSizeInWideChar : (mbsLen / 2);
while (len--) {
/* TODO: handle surrogate pair values */
*wcsBuf++ = (uint16_t)(*mbs | (*(mbs + 1) << 8));
mbs += 2;
}
if (bytesConsumed)
*bytesConsumed = charsToConvert * 2;
return charsToConvert;
}
int32_t wcToUtf16le(uint16_t wc, uint8_t * mbs, int32_t bufSize)
{
if (mbs && bufSize >= 2) {
/* TODO: handle surrogate pair values */
*mbs = (uint8_t)(wc & 0xff);
*(mbs + 1) = (uint8_t)(wc >> 8);
}
return 2;
}
#endif /* I18N_UTF8_UTF16_SUPPORT */