* (WIP)-all issues corrected, tests started * first test passing alternative solution for SonarCloud check fixed for to while translation added changelog item adding tests and fixes more tests; avoid compiling BSD Tests on Windows attempting a BSD compile patch more tests borrowing symbol detection from another test more ambitious testing changed the order of assertions for better info more tests fixed clear errors before executing expanding transformation to use string comparison including terminator on translation count a different flag for testing MS Windows differente Windows flag more tests fixing platform encoding match Windows compiler difference on #ifdef Google Test macro usage fix test added added keymap test test and initialisation fixed ms cl issue with named init test for keymap exercising keystate more configuration exercises one more test for config added Unicode test an IPv6 test more portable networking code using our own platform switching code avoiding strcpy * Fixed typo in Changelog. * +appropriate memory handling * one more test for coverage * a tad more coverage * expanded test on Config Co-authored-by: Max <maxmommersteeg@hotmail.com> Co-authored-by: SerhiiGadzhilov <71632867+SerhiiGadzhilov@users.noreply.github.com>
792 lines
19 KiB
C++
792 lines
19 KiB
C++
/*
|
|
* synergy -- mouse and keyboard sharing utility
|
|
* Copyright (C) 2012-2016 Symless Ltd.
|
|
* Copyright (C) 2002 Chris Schoeneman
|
|
*
|
|
* This package is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* found in the file LICENSE that should have accompanied this file.
|
|
*
|
|
* This package is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "arch/Arch.h"
|
|
#include "base/Unicode.h"
|
|
|
|
#include <cstring>
|
|
|
|
//
|
|
// local utility functions
|
|
//
|
|
|
|
inline
|
|
static
|
|
UInt16
|
|
decode16(const UInt8* n, bool byteSwapped)
|
|
{
|
|
union x16 {
|
|
UInt8 n8[2];
|
|
UInt16 n16;
|
|
} c;
|
|
if (byteSwapped) {
|
|
c.n8[0] = n[1];
|
|
c.n8[1] = n[0];
|
|
}
|
|
else {
|
|
c.n8[0] = n[0];
|
|
c.n8[1] = n[1];
|
|
}
|
|
return c.n16;
|
|
}
|
|
|
|
inline
|
|
static
|
|
UInt32
|
|
decode32(const UInt8* n, bool byteSwapped)
|
|
{
|
|
union x32 {
|
|
UInt8 n8[4];
|
|
UInt32 n32;
|
|
} c;
|
|
if (byteSwapped) {
|
|
c.n8[0] = n[3];
|
|
c.n8[1] = n[2];
|
|
c.n8[2] = n[1];
|
|
c.n8[3] = n[0];
|
|
}
|
|
else {
|
|
c.n8[0] = n[0];
|
|
c.n8[1] = n[1];
|
|
c.n8[2] = n[2];
|
|
c.n8[3] = n[3];
|
|
}
|
|
return c.n32;
|
|
}
|
|
|
|
inline
|
|
static
|
|
void
|
|
resetError(bool* errors)
|
|
{
|
|
if (errors != NULL) {
|
|
*errors = false;
|
|
}
|
|
}
|
|
|
|
inline
|
|
static
|
|
void
|
|
setError(bool* errors)
|
|
{
|
|
if (errors != NULL) {
|
|
*errors = true;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Unicode
|
|
//
|
|
|
|
UInt32 Unicode::s_invalid = 0x0000ffff;
|
|
UInt32 Unicode::s_replacement = 0x0000fffd;
|
|
|
|
bool
|
|
Unicode::isUTF8(const String& src)
|
|
{
|
|
// convert and test each character
|
|
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
|
for (UInt32 n = (UInt32)src.size(); n > 0; ) {
|
|
if (fromUTF8(data, n) == s_invalid) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
String
|
|
Unicode::UTF8ToUCS2(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// get size of input string and reserve some space in output
|
|
UInt32 n = (UInt32)src.size();
|
|
String dst;
|
|
dst.reserve(2 * n);
|
|
|
|
// convert each character
|
|
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
|
while (n > 0) {
|
|
UInt32 c = fromUTF8(data, n);
|
|
if (c == s_invalid) {
|
|
c = s_replacement;
|
|
}
|
|
else if (c >= 0x00010000) {
|
|
setError(errors);
|
|
c = s_replacement;
|
|
}
|
|
UInt16 ucs2 = static_cast<UInt16>(c);
|
|
dst.append(reinterpret_cast<const char*>(&ucs2), 2);
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
String
|
|
Unicode::UTF8ToUCS4(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// get size of input string and reserve some space in output
|
|
UInt32 n = (UInt32)src.size();
|
|
String dst;
|
|
dst.reserve(4 * n);
|
|
|
|
// convert each character
|
|
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
|
while (n > 0) {
|
|
UInt32 c = fromUTF8(data, n);
|
|
if (c == s_invalid) {
|
|
c = s_replacement;
|
|
}
|
|
dst.append(reinterpret_cast<const char*>(&c), 4);
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
String
|
|
Unicode::UTF8ToUTF16(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// get size of input string and reserve some space in output
|
|
UInt32 n = (UInt32)src.size();
|
|
String dst;
|
|
dst.reserve(2 * n);
|
|
|
|
// convert each character
|
|
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
|
while (n > 0) {
|
|
UInt32 c = fromUTF8(data, n);
|
|
if (c == s_invalid) {
|
|
c = s_replacement;
|
|
}
|
|
else if (c >= 0x00110000) {
|
|
setError(errors);
|
|
c = s_replacement;
|
|
}
|
|
if (c < 0x00010000) {
|
|
UInt16 ucs2 = static_cast<UInt16>(c);
|
|
dst.append(reinterpret_cast<const char*>(&ucs2), 2);
|
|
}
|
|
else {
|
|
c -= 0x00010000;
|
|
UInt16 utf16h = static_cast<UInt16>((c >> 10) + 0xd800);
|
|
UInt16 utf16l = static_cast<UInt16>((c & 0x03ff) + 0xdc00);
|
|
dst.append(reinterpret_cast<const char*>(&utf16h), 2);
|
|
dst.append(reinterpret_cast<const char*>(&utf16l), 2);
|
|
}
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
String
|
|
Unicode::UTF8ToUTF32(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// get size of input string and reserve some space in output
|
|
UInt32 n = (UInt32)src.size();
|
|
String dst;
|
|
dst.reserve(4 * n);
|
|
|
|
// convert each character
|
|
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
|
while (n > 0) {
|
|
UInt32 c = fromUTF8(data, n);
|
|
if (c == s_invalid) {
|
|
c = s_replacement;
|
|
}
|
|
else if (c >= 0x00110000) {
|
|
setError(errors);
|
|
c = s_replacement;
|
|
}
|
|
dst.append(reinterpret_cast<const char*>(&c), 4);
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
String
|
|
Unicode::UTF8ToText(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// convert to wide char
|
|
UInt32 size;
|
|
wchar_t* tmp = UTF8ToWideChar(src, size, errors);
|
|
|
|
// convert string to multibyte
|
|
int len = ARCH->convStringWCToMB(NULL, tmp, size, errors);
|
|
char* mbs = new char[len + 1];
|
|
ARCH->convStringWCToMB(mbs, tmp, size, errors);
|
|
String text(mbs, len);
|
|
|
|
// clean up
|
|
delete[] mbs;
|
|
delete[] tmp;
|
|
|
|
return text;
|
|
}
|
|
|
|
String
|
|
Unicode::UCS2ToUTF8(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// convert
|
|
UInt32 n = (UInt32)src.size() >> 1;
|
|
return doUCS2ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
|
|
}
|
|
|
|
String
|
|
Unicode::UCS4ToUTF8(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// convert
|
|
UInt32 n = (UInt32)src.size() >> 2;
|
|
return doUCS4ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
|
|
}
|
|
|
|
String
|
|
Unicode::UTF16ToUTF8(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// convert
|
|
UInt32 n = (UInt32)src.size() >> 1;
|
|
return doUTF16ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
|
|
}
|
|
|
|
String
|
|
Unicode::UTF32ToUTF8(const String& src, bool* errors)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// convert
|
|
UInt32 n = (UInt32)src.size() >> 2;
|
|
return doUTF32ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
|
|
}
|
|
|
|
String
|
|
Unicode::textToUTF8(const String& src, bool* errors, IArchString::EWideCharEncoding encoding)
|
|
{
|
|
// default to success
|
|
resetError(errors);
|
|
|
|
// convert string to wide characters
|
|
UInt32 n = (UInt32)src.size();
|
|
int len = ARCH->convStringMBToWC(NULL, src.c_str(), n, errors);
|
|
wchar_t* wcs = new wchar_t[len + 1];
|
|
ARCH->convStringMBToWC(wcs, src.c_str(), n, errors);
|
|
|
|
// convert to UTF8
|
|
String utf8 = wideCharToUTF8(wcs, len, errors, encoding);
|
|
|
|
// clean up
|
|
delete[] wcs;
|
|
|
|
return utf8;
|
|
}
|
|
|
|
wchar_t*
|
|
Unicode::UTF8ToWideChar(const String& src, UInt32& size, bool* errors)
|
|
{
|
|
// convert to platform's wide character encoding
|
|
String tmp;
|
|
switch (ARCH->getWideCharEncoding()) {
|
|
case IArchString::kUCS2:
|
|
tmp = UTF8ToUCS2(src, errors);
|
|
size = (UInt32)tmp.size() >> 1;
|
|
break;
|
|
|
|
case IArchString::kUCS4:
|
|
tmp = UTF8ToUCS4(src, errors);
|
|
size = (UInt32)tmp.size() >> 2;
|
|
break;
|
|
|
|
case IArchString::kUTF16:
|
|
tmp = UTF8ToUTF16(src, errors);
|
|
size = (UInt32)tmp.size() >> 1;
|
|
break;
|
|
|
|
case IArchString::kUTF32:
|
|
tmp = UTF8ToUTF32(src, errors);
|
|
size = (UInt32)tmp.size() >> 2;
|
|
break;
|
|
|
|
default:
|
|
assert(0 && "unknown wide character encoding");
|
|
}
|
|
|
|
// copy to a wchar_t array
|
|
wchar_t* dst = new wchar_t[size];
|
|
::memcpy(dst, tmp.data(), sizeof(wchar_t) * size);
|
|
return dst;
|
|
}
|
|
|
|
String
|
|
Unicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors, IArchString::EWideCharEncoding encoding)
|
|
{
|
|
if (encoding == IArchString::kPlatformDetermined) {
|
|
encoding = ARCH->getWideCharEncoding();
|
|
}
|
|
// convert from platform's wide character encoding.
|
|
// note -- this must include a wide nul character (independent of
|
|
// the String's nul character).
|
|
switch (encoding) {
|
|
case IArchString::kUCS2:
|
|
return doUCS2ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
|
|
|
|
case IArchString::kUCS4:
|
|
return doUCS4ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
|
|
|
|
case IArchString::kUTF16:
|
|
return doUTF16ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
|
|
|
|
case IArchString::kUTF32:
|
|
return doUTF32ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
|
|
|
|
default:
|
|
assert(0 && "unknown wide character encoding");
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String
|
|
Unicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors)
|
|
{
|
|
// make some space
|
|
String dst;
|
|
dst.reserve(n);
|
|
|
|
// check if first character is 0xfffe or 0xfeff
|
|
bool byteSwapped = false;
|
|
if (n >= 1) {
|
|
switch (decode16(data, false)) {
|
|
case 0x0000feff:
|
|
data += 2;
|
|
--n;
|
|
break;
|
|
|
|
case 0x0000fffe:
|
|
byteSwapped = true;
|
|
data += 2;
|
|
--n;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// convert each character
|
|
for (; n > 0; --n) {
|
|
UInt32 c = decode16(data, byteSwapped);
|
|
toUTF8(dst, c, errors);
|
|
data += 2;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
String
|
|
Unicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors)
|
|
{
|
|
// make some space
|
|
String dst;
|
|
dst.reserve(n);
|
|
|
|
// check if first character is 0xfffe or 0xfeff
|
|
bool byteSwapped = false;
|
|
if (n >= 1) {
|
|
switch (decode32(data, false)) {
|
|
case 0x0000feff:
|
|
data += 4;
|
|
--n;
|
|
break;
|
|
|
|
case 0x0000fffe:
|
|
byteSwapped = true;
|
|
data += 4;
|
|
--n;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// convert each character
|
|
for (; n > 0; --n) {
|
|
UInt32 c = decode32(data, byteSwapped);
|
|
toUTF8(dst, c, errors);
|
|
data += 4;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
String
|
|
Unicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors)
|
|
{
|
|
// make some space
|
|
String dst;
|
|
dst.reserve(n);
|
|
|
|
// check if first character is 0xfffe or 0xfeff
|
|
bool byteSwapped = false;
|
|
if (n >= 1) {
|
|
switch (decode16(data, false)) {
|
|
case 0x0000feff:
|
|
data += 2;
|
|
--n;
|
|
break;
|
|
|
|
case 0x0000fffe:
|
|
byteSwapped = true;
|
|
data += 2;
|
|
--n;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// convert each character
|
|
while (n > 0) {
|
|
UInt32 c = decode16(data, byteSwapped);
|
|
if (c < 0x0000d800 || c > 0x0000dfff) {
|
|
toUTF8(dst, c, errors);
|
|
}
|
|
else if (n == 1) {
|
|
// error -- missing second word
|
|
setError(errors);
|
|
toUTF8(dst, s_replacement, NULL);
|
|
}
|
|
else if (c >= 0x0000d800 && c <= 0x0000dbff) {
|
|
UInt32 c2 = decode16(data, byteSwapped);
|
|
data += 2;
|
|
--n;
|
|
if (c2 < 0x0000dc00 || c2 > 0x0000dfff) {
|
|
// error -- [d800,dbff] not followed by [dc00,dfff]
|
|
setError(errors);
|
|
toUTF8(dst, s_replacement, NULL);
|
|
}
|
|
else {
|
|
c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000;
|
|
toUTF8(dst, c, errors);
|
|
}
|
|
}
|
|
else {
|
|
// error -- [dc00,dfff] without leading [d800,dbff]
|
|
setError(errors);
|
|
toUTF8(dst, s_replacement, NULL);
|
|
}
|
|
data += 2;
|
|
--n;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
String
|
|
Unicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors)
|
|
{
|
|
// make some space
|
|
String dst;
|
|
dst.reserve(n);
|
|
|
|
// check if first character is 0xfffe or 0xfeff
|
|
bool byteSwapped = false;
|
|
if (n >= 1) {
|
|
switch (decode32(data, false)) {
|
|
case 0x0000feff:
|
|
data += 4;
|
|
--n;
|
|
break;
|
|
|
|
case 0x0000fffe:
|
|
byteSwapped = true;
|
|
data += 4;
|
|
--n;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// convert each character
|
|
for (; n > 0; --n) {
|
|
UInt32 c = decode32(data, byteSwapped);
|
|
if (c >= 0x00110000) {
|
|
setError(errors);
|
|
c = s_replacement;
|
|
}
|
|
toUTF8(dst, c, errors);
|
|
data += 4;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
UInt32
|
|
Unicode::fromUTF8(const UInt8*& data, UInt32& n)
|
|
{
|
|
assert(data != NULL);
|
|
assert(n != 0);
|
|
|
|
// compute character encoding length, checking for overlong
|
|
// sequences (i.e. characters that don't use the shortest
|
|
// possible encoding).
|
|
UInt32 size;
|
|
if (data[0] < 0x80) {
|
|
// 0xxxxxxx
|
|
size = 1;
|
|
}
|
|
else if (data[0] < 0xc0) {
|
|
// 10xxxxxx -- in the middle of a multibyte character. counts
|
|
// as one invalid character.
|
|
--n;
|
|
++data;
|
|
return s_invalid;
|
|
}
|
|
else if (data[0] < 0xe0) {
|
|
// 110xxxxx
|
|
size = 2;
|
|
}
|
|
else if (data[0] < 0xf0) {
|
|
// 1110xxxx
|
|
size = 3;
|
|
}
|
|
else if (data[0] < 0xf8) {
|
|
// 11110xxx
|
|
size = 4;
|
|
}
|
|
else if (data[0] < 0xfc) {
|
|
// 111110xx
|
|
size = 5;
|
|
}
|
|
else if (data[0] < 0xfe) {
|
|
// 1111110x
|
|
size = 6;
|
|
}
|
|
else {
|
|
// invalid sequence. dunno how many bytes to skip so skip one.
|
|
--n;
|
|
++data;
|
|
return s_invalid;
|
|
}
|
|
|
|
// make sure we have enough data
|
|
if (size > n) {
|
|
data += n;
|
|
n = 0;
|
|
return s_invalid;
|
|
}
|
|
|
|
// extract character
|
|
UInt32 c;
|
|
switch (size) {
|
|
case 1:
|
|
c = static_cast<UInt32>(data[0]);
|
|
break;
|
|
|
|
case 2:
|
|
c = ((static_cast<UInt32>(data[0]) & 0x1f) << 6) |
|
|
((static_cast<UInt32>(data[1]) & 0x3f) );
|
|
break;
|
|
|
|
case 3:
|
|
c = ((static_cast<UInt32>(data[0]) & 0x0f) << 12) |
|
|
((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
|
|
((static_cast<UInt32>(data[2]) & 0x3f) );
|
|
break;
|
|
|
|
case 4:
|
|
c = ((static_cast<UInt32>(data[0]) & 0x07) << 18) |
|
|
((static_cast<UInt32>(data[1]) & 0x3f) << 12) |
|
|
((static_cast<UInt32>(data[2]) & 0x3f) << 6) |
|
|
((static_cast<UInt32>(data[3]) & 0x3f) );
|
|
break;
|
|
|
|
case 5:
|
|
c = ((static_cast<UInt32>(data[0]) & 0x03) << 24) |
|
|
((static_cast<UInt32>(data[1]) & 0x3f) << 18) |
|
|
((static_cast<UInt32>(data[2]) & 0x3f) << 12) |
|
|
((static_cast<UInt32>(data[3]) & 0x3f) << 6) |
|
|
((static_cast<UInt32>(data[4]) & 0x3f) );
|
|
break;
|
|
|
|
case 6:
|
|
c = ((static_cast<UInt32>(data[0]) & 0x01) << 30) |
|
|
((static_cast<UInt32>(data[1]) & 0x3f) << 24) |
|
|
((static_cast<UInt32>(data[2]) & 0x3f) << 18) |
|
|
((static_cast<UInt32>(data[3]) & 0x3f) << 12) |
|
|
((static_cast<UInt32>(data[4]) & 0x3f) << 6) |
|
|
((static_cast<UInt32>(data[5]) & 0x3f) );
|
|
break;
|
|
|
|
default:
|
|
assert(0 && "invalid size");
|
|
return s_invalid;
|
|
}
|
|
|
|
// check that all bytes after the first have the pattern 10xxxxxx.
|
|
// truncated sequences are treated as a single malformed character.
|
|
bool truncated = false;
|
|
switch (size) {
|
|
case 6:
|
|
if ((data[5] & 0xc0) != 0x80) {
|
|
truncated = true;
|
|
size = 5;
|
|
}
|
|
// fall through
|
|
|
|
case 5:
|
|
if ((data[4] & 0xc0) != 0x80) {
|
|
truncated = true;
|
|
size = 4;
|
|
}
|
|
// fall through
|
|
|
|
case 4:
|
|
if ((data[3] & 0xc0) != 0x80) {
|
|
truncated = true;
|
|
size = 3;
|
|
}
|
|
// fall through
|
|
|
|
case 3:
|
|
if ((data[2] & 0xc0) != 0x80) {
|
|
truncated = true;
|
|
size = 2;
|
|
}
|
|
// fall through
|
|
|
|
case 2:
|
|
if ((data[1] & 0xc0) != 0x80) {
|
|
truncated = true;
|
|
size = 1;
|
|
}
|
|
}
|
|
|
|
// update parameters
|
|
data += size;
|
|
n -= size;
|
|
|
|
// invalid if sequence was truncated
|
|
if (truncated) {
|
|
return s_invalid;
|
|
}
|
|
|
|
// check for characters that didn't use the smallest possible encoding
|
|
static UInt32 s_minChar[] = {
|
|
0,
|
|
0x00000000,
|
|
0x00000080,
|
|
0x00000800,
|
|
0x00010000,
|
|
0x00200000,
|
|
0x04000000
|
|
};
|
|
if (c < s_minChar[size]) {
|
|
return s_invalid;
|
|
}
|
|
|
|
// check for characters not in ISO-10646
|
|
if (c >= 0x0000d800 && c <= 0x0000dfff) {
|
|
return s_invalid;
|
|
}
|
|
if (c >= 0x0000fffe && c <= 0x0000ffff) {
|
|
return s_invalid;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
void
|
|
Unicode::toUTF8(String& dst, UInt32 c, bool* errors)
|
|
{
|
|
UInt8 data[6];
|
|
|
|
// handle characters outside the valid range
|
|
if ((c >= 0x0000d800 && c <= 0x0000dfff) || c >= 0x80000000) {
|
|
setError(errors);
|
|
c = s_replacement;
|
|
}
|
|
|
|
// convert to UTF-8
|
|
if (c < 0x00000080) {
|
|
data[0] = static_cast<UInt8>(c);
|
|
dst.append(reinterpret_cast<char*>(data), 1);
|
|
}
|
|
else if (c < 0x00000800) {
|
|
data[0] = static_cast<UInt8>(((c >> 6) & 0x0000001f) + 0xc0);
|
|
data[1] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
|
dst.append(reinterpret_cast<char*>(data), 2);
|
|
}
|
|
else if (c < 0x00010000) {
|
|
data[0] = static_cast<UInt8>(((c >> 12) & 0x0000000f) + 0xe0);
|
|
data[1] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
|
|
data[2] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
|
dst.append(reinterpret_cast<char*>(data), 3);
|
|
}
|
|
else if (c < 0x00200000) {
|
|
data[0] = static_cast<UInt8>(((c >> 18) & 0x00000007) + 0xf0);
|
|
data[1] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
|
|
data[2] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
|
|
data[3] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
|
dst.append(reinterpret_cast<char*>(data), 4);
|
|
}
|
|
else if (c < 0x04000000) {
|
|
data[0] = static_cast<UInt8>(((c >> 24) & 0x00000003) + 0xf8);
|
|
data[1] = static_cast<UInt8>(((c >> 18) & 0x0000003f) + 0x80);
|
|
data[2] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
|
|
data[3] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
|
|
data[4] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
|
dst.append(reinterpret_cast<char*>(data), 5);
|
|
}
|
|
else if (c < 0x80000000) {
|
|
data[0] = static_cast<UInt8>(((c >> 30) & 0x00000001) + 0xfc);
|
|
data[1] = static_cast<UInt8>(((c >> 24) & 0x0000003f) + 0x80);
|
|
data[2] = static_cast<UInt8>(((c >> 18) & 0x0000003f) + 0x80);
|
|
data[3] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
|
|
data[4] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
|
|
data[5] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
|
dst.append(reinterpret_cast<char*>(data), 6);
|
|
}
|
|
else {
|
|
assert(0 && "character out of range");
|
|
}
|
|
}
|