Index: src/OFConstantString.m ================================================================== --- src/OFConstantString.m +++ src/OFConstantString.m @@ -635,10 +635,18 @@ if (initialized != SIZE_MAX) [self finishInitialization]; return [super unicodeString]; } + +- (const uint16_t*)UTF16String +{ + if (initialized != SIZE_MAX) + [self finishInitialization]; + + return [super UTF16String]; +} - (void)writeToFile: (OFString*)path { if (initialized != SIZE_MAX) [self finishInitialization]; Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -795,10 +795,21 @@ * * \return The string as an array of Unicode characters */ - (const of_unichar_t*)unicodeString; +/** + * \brief Returns the string in big endian UTF-16 encoding. + * + * The result is valid until the autorelease pool is released. If you want to + * use the result outside the scope of the current autorelease pool, you have to + * copy it. + * + * \return The string in big endian UTF-16 encoding + */ +- (const uint16_t*)UTF16String; + /** * \brief Writes the string into the specified file using UTF-8 encoding. * * \param path The path of the file to write to */ Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -1897,10 +1897,57 @@ ret[j++] = c; i += cLen; } ret[j] = 0; + + return ret; +} + +- (const uint16_t*)UTF16String +{ + OFObject *object = [[[OFObject alloc] init] autorelease]; + uint16_t *ret; + size_t i, j; + + /* Allocate memory for the worst case */ + ret = [object allocMemoryForNItems: s->length * 2 + 1 + ofSize: sizeof(uint16_t)]; + + i = 0; + j = 0; + + while (i < s->cStringLength) { + of_unichar_t c; + size_t cLen; + + cLen = of_string_utf8_to_unicode(s->cString + i, + s->cStringLength - i, &c); + + if (cLen == 0 || c > 0x10FFFF) + @throw [OFInvalidEncodingException + exceptionWithClass: isa]; + + if (c > 0xFFFF) { + c -= 0x10000; + ret[j++] = of_bswap16_if_le(0xD800 | (c >> 10)); + ret[j++] = of_bswap16_if_le(0xDC00 | (c & 0x3FF)); + } else + ret[j++] = of_bswap16_if_le(c); + + i += cLen; + } + + ret[j] = 0; + + @try { + ret = [object resizeMemory: ret + toNItems: j + 1 + ofSize: sizeof(uint16_t)]; + } @catch (OFOutOfMemoryException *e) { + /* We don't care, as we only tried to make it smaller */ + } return ret; } - (void)writeToFile: (OFString*)path Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -72,10 +72,11 @@ OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFMutableString *s[3]; OFArray *a; int i; const of_unichar_t *ua; + const uint16_t *u16a; EntityHandler *h; #ifdef OF_HAVE_BLOCKS __block BOOL ok; #endif @@ -380,10 +381,17 @@ @"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" hexadecimalValue]) TEST(@"-[unicodeString]", (ua = [@"fööbär🀺" unicodeString]) && !memcmp(ua, ucstr + 1, sizeof(ucstr) - sizeof(of_unichar_t))) + + TEST(@"-[UTF16String]", (u16a = [@"fööbär🀺" UTF16String]) && +#ifdef OF_BIG_ENDIAN + !memcmp(u16a, utf16str + 1, sizeof(utf16str) - sizeof(uint16_t))) +#else + !memcmp(u16a, sutf16str + 1, sizeof(sutf16str) - sizeof(uint16_t))) +#endif TEST(@"-[MD5Hash]", [[@"asdfoobar" MD5Hash] isEqual: @"184dce2ec49b5422c7cfd8728864db4c"]) TEST(@"-[SHA1Hash]", [[@"asdfoobar" SHA1Hash]