Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -666,11 +666,11 @@ ]) AS_IF([test x"$GOBJC" = x"yes"], [ OBJCFLAGS="$OBJCFLAGS -Wwrite-strings -Wpointer-arith -Werror" - AC_MSG_CHECKING(whether gcc has bug objc/27438) + AC_MSG_CHECKING(whether we have gcc bug objc/27438) AC_TRY_COMPILE([ @interface OFConstantString { struct objc_class *isa; const char *string; Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -834,19 +834,39 @@ * @return The string as an array of Unicode characters */ - (const of_unichar_t*)characters OF_RETURNS_INNER_POINTER; /*! - * @brief Returns the string in big endian UTF-16 encoding. + * @brief Returns the string in UTF-16 encoding with native byte order. + * + * 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 UTF-16 encoding with native byte order + */ +- (const uint16_t*)UTF16String OF_RETURNS_INNER_POINTER; + +/*! + * @brief Returns the string in UTF-16 encoding with the specified byte order. * * 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 + * @param byteOrder The byte order for the UTF-16 encoding + * @return The string in UTF-16 encoding with the specified byte order + */ +- (const uint16_t*)UTF16StringWithByteOrder: (of_byte_order_t)byteOrder + OF_RETURNS_INNER_POINTER; + +/*! + * @brief Returns the length of the string in UTF-16 characters. + * + * @return The length of string in UTF-16 characters */ -- (const uint16_t*)UTF16String OF_RETURNS_INNER_POINTER; +- (size_t)UTF16StringLength; /*! * @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 @@ -646,11 +646,11 @@ - initWithUTF16String: (const uint16_t*)string length: (size_t)length { return [self initWithUTF16String: string length: length - byteOrder: OF_BYTE_ORDER_BIG_ENDIAN]; + byteOrder: OF_BYTE_ORDER_NATIVE]; } - initWithUTF16String: (const uint16_t*)string length: (size_t)length byteOrder: (of_byte_order_t)byteOrder @@ -1996,16 +1996,22 @@ return ret; } - (const uint16_t*)UTF16String { + return [self UTF16StringWithByteOrder: OF_BYTE_ORDER_NATIVE]; +} + +- (const uint16_t*)UTF16StringWithByteOrder: (of_byte_order_t)byteOrder +{ OFObject *object = [[[OFObject alloc] init] autorelease]; void *pool = objc_autoreleasePoolPush(); const of_unichar_t *characters = [self characters]; size_t length = [self length]; uint16_t *ret; size_t i, j; + BOOL swap = (byteOrder != OF_BYTE_ORDER_NATIVE); /* Allocate memory for the worst case */ ret = [object allocMemoryWithSize: sizeof(uint16_t) count: length * 2]; @@ -2016,16 +2022,25 @@ if (c > 0x10FFFF) @throw [OFInvalidEncodingException exceptionWithClass: [self class]]; - 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); + if (swap) { + if (c > 0xFFFF) { + c -= 0x10000; + ret[j++] = OF_BSWAP16(0xD800 | (c >> 10)); + ret[j++] = OF_BSWAP16(0xDC00 | (c & 0x3FF)); + } else + ret[j++] = OF_BSWAP16(c); + } else { + if (c > 0xFFFF) { + c -= 0x10000; + ret[j++] = 0xD800 | (c >> 10); + ret[j++] = 0xDC00 | (c & 0x3FF); + } else + ret[j++] = c; + } } @try { ret = [object resizeMemory: ret size: sizeof(uint16_t) @@ -2036,10 +2051,24 @@ objc_autoreleasePoolPop(pool); return ret; } + +- (size_t)UTF16StringLength +{ + const of_unichar_t *characters = [self characters]; + size_t i, length, UTF16StringLength; + + length = UTF16StringLength = [self length]; + + for (i = 0; i < length; i++) + if (characters[i] > 0xFFFF) + UTF16StringLength++; + + return UTF16StringLength; +} - (void)writeToFile: (OFString*)path { void *pool = objc_autoreleasePoolPush(); OFFile *file; Index: src/OFTLSSocket.h ================================================================== --- src/OFTLSSocket.h +++ src/OFTLSSocket.h @@ -37,11 +37,11 @@ - (BOOL)socket: (id )socket shouldAcceptKeychain: (OFArray*)keychain; @end /*! - * @brief A protocol that should be implemented by 3rd party libraries + * @brief A protocol that should be implemented by 3rd-party libraries * implementing TLS. */ @protocol OFTLSSocket #ifdef OF_HAVE_PROPERTIES @property (assign) id delegate; Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -412,15 +412,21 @@ TEST(@"-[characters]", (ua = [@"fööbär🀺" characters]) && !memcmp(ua, ucstr + 1, sizeof(ucstr) / sizeof(*ucstr))) TEST(@"-[UTF16String]", (u16a = [@"fööbär🀺" UTF16String]) && -#ifdef OF_BIG_ENDIAN !memcmp(u16a, utf16str + 1, sizeof(utf16str) - sizeof(uint16_t))) + + TEST(@"-[UTF16String]", (u16a = [@"fööbär🀺" +#ifdef OF_BIG_ENDIAN + UTF16StringWithByteOrder: OF_BYTE_ORDER_LITTLE_ENDIAN]) && #else - !memcmp(u16a, sutf16str + 1, sizeof(sutf16str) - sizeof(uint16_t))) + UTF16StringWithByteOrder: OF_BYTE_ORDER_BIG_ENDIAN]) && #endif + !memcmp(u16a, sutf16str + 1, sizeof(sutf16str) - sizeof(uint16_t))) + + TEST(@"-[UTF16StringLength]", [@"fööbär🀺" UTF16StringLength] == 8) TEST(@"-[MD5Hash]", [[@"asdfoobar" MD5Hash] isEqual: @"184dce2ec49b5422c7cfd8728864db4c"]) TEST(@"-[SHA1Hash]", [[@"asdfoobar" SHA1Hash]