- Added utf8 to unicode support thanks to Axel. :-)

- Rounded out new double-character-set support.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5603 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Tyler Dauwalder
2003-12-07 08:20:14 +00:00
parent f5f5f2ecce
commit cd0f0e996d
2 changed files with 159 additions and 10 deletions
@@ -4,9 +4,21 @@
/*! \brief Converts the given unicode character to utf8.
\param c The unicode character.
\param out Pointer to a C-string of at least 4 characters
long into which the output utf8 characters will
be written. The string that is pointed to will
be incremented to reflect the number of characters
written, i.e. if \a out initially points to a pointer
to the first character in string named \c str, and
the function writes 4 characters to \c str, then
upon returning, out will point to a pointer to
the fifth character in \c str.
*/
static
void
Udf::unicode_to_utf8(uint32 c, char **out)
unicode_to_utf8(uint32 c, char **out)
{
char *s = *out;
@@ -28,6 +40,57 @@ Udf::unicode_to_utf8(uint32 c, char **out)
*out = s;
}
/*! \brief Converts the given utf8 character to 4-byte unicode.
\param in Pointer to a C-String from which utf8 characters
will be read. *in will be incremented to reflect
the number of characters read, similarly to the
\c out parameter for Udf::unicode_to_utf8().
\return The 4-byte unicode character, or **in if passed an
invalid character, or 0 if passed any NULL pointers.
*/
static
uint32
utf8_to_unicode(const char **in)
{
if (!in)
return 0;
uint8 *bytes = (uint8 *)*in;
if (!bytes)
return 0;
int32 length;
uint8 mask = 0x1f;
switch (bytes[0] & 0xf0) {
case 0xc0:
case 0xd0: length = 2; break;
case 0xe0: length = 3; break;
case 0xf0:
mask = 0x0f;
length = 4;
break;
default:
// valid 1-byte character
// and invalid characters
(*in)++;
return bytes[0];
}
uint32 c = bytes[0] & mask;
int32 i = 1;
for (;i < length && (bytes[i] & 0x80) > 0;i++)
c = (c << 6) | (bytes[i] & 0x3f);
if (i < length) {
// invalid character
(*in)++;
return (uint32)bytes[0];
}
*in += length;
return c;
}
using namespace Udf;
/*! \brief Creates an empty string object.
@@ -68,6 +131,84 @@ String::~String()
void
String::SetTo(const char *utf8)
{
DEBUG_INIT_ETC("String", ("utf8: %p, strlen(utf8): %ld", utf8,
utf8 ? strlen(utf8) : 0));
_Clear();
if (!utf8) {
PRINT(("passed NULL utf8 string\n"));
return;
}
uint32 length = strlen(utf8);
// First copy the utf8 string
fUtf8String = new char[length+1];
if (!fUtf8String){
PRINT(("new fUtf8String[%ld] allocation failed\n", length+1));
return;
}
// Next convert to raw 4-byte unicode. Then we'll do some
// analysis to figure out if we have any invalid characters,
// and whether we can get away with compressed 8-bit unicode,
// or have to use burly 16-bit unicode.
uint32 *raw = new uint32[length];
if (!raw) {
PRINT(("new uint32 raw[%ld] temporary string allocation failed\n", length));
_Clear();
return;
}
const char *in = utf8;
uint32 rawLength = 0;
for (uint32 i = 0; i < length && uint32(in-utf8) < length; i++, rawLength++)
raw[i] = utf8_to_unicode(&in);
// Check for invalids.
uint32 mask = 0xffff0000;
for (uint32 i = 0; i < rawLength; i++) {
if (raw[i] & mask) {
PRINT(("WARNING: utf8 string contained a multi-byte sequence which "
"was converted into a unicode character larger than 16-bits; "
"character will be converted to an underscore character for "
"safety.\n"));
raw[i] = '_';
}
}
// See if we can get away with 8-bit compressed unicode
mask = 0xffffff00;
bool canUse8bit = true;
for (uint32 i = 0; i < rawLength; i++) {
if (raw[i] & mask) {
canUse8bit = false;
break;
}
}
// Build our cs0 string
if (canUse8bit) {
fCs0Length = rawLength+1;
fCs0String = new char[fCs0Length];
if (fCs0String) {
fCs0String[0] = '\x08'; // 8-bit compressed unicode
for (uint32 i = 0; i < rawLength; i++)
fCs0String[i+1] = raw[i] % 256;
} else {
PRINT(("new fCs0String[%ld] allocation failed\n", fCs0Length));
_Clear();
return;
}
} else {
fCs0Length = rawLength*2+1;
fCs0String = new char[fCs0Length];
if (fCs0String) {
fCs0String[0] = '\x10'; // 16-bit unicode
uint16 *string = reinterpret_cast<uint16*>(&fCs0String[1]);
for (uint32 i = 0; i < rawLength; i++)
string[i+1] = uint16(raw[i]);
} else {
PRINT(("new fCs0String[%ld] allocation failed\n", fCs0Length));
_Clear();
return;
}
}
// Clean up
delete [] raw;
raw = NULL;
}
/*! \brief Assignment from a Cs0 string.
@@ -78,7 +219,22 @@ String::SetTo(const char *cs0, uint32 length)
DEBUG_INIT_ETC("String", ("cs0: %p, length: %ld", cs0, length));
_Clear();
if (!cs0) {
PRINT(("passed NULL cs0 string\n"));
return;
}
// First copy the Cs0 string and length
fCs0String = new char[length];
if (fCs0String) {
memcpy(fCs0String, cs0, length);
} else {
PRINT(("new fCs0String[%ld] allocation failed\n", length));
return;
}
// Now convert to utf8
// The first byte of the CS0 string is the compression ID.
// - 8: 1 byte characters
// - 16: 2 byte, big endian characters
@@ -40,26 +40,19 @@ public:
template <uint32 length>
String& operator=(const array<char, length> &cs0);
// const char* Cs0() const { return fCs0String; }
const char* Cs0() const { return kTempCs0String; }
const char* Cs0() const { return fCs0String; }
const char* Utf8() const { return fUtf8String; }
// uint32 Cs0Length() const { return fCs0Length; }
uint32 Cs0Length() const { return kTempCs0Length; }
uint32 Cs0Length() const { return fCs0Length; }
uint32 Utf8Length() const { return fUtf8String ? strlen(fUtf8String) : 0; }
private:
void _Clear();
static const char * const kTempCs0String = "\x08Ih8Unicode";
static const uint32 kTempCs0Length = 12;
char *fCs0String;
uint32 fCs0Length;
char *fUtf8String;
};
void unicode_to_utf8(uint32 c, char **out);
/*! \brief Creates a new String object from the given Cs0 string.
*/
template <uint32 length>