Index: src/OFConstantString.m ================================================================== --- src/OFConstantString.m +++ src/OFConstantString.m @@ -85,10 +85,15 @@ - initWithString: (OFString*)str { @throw [OFNotImplementedException newWithClass: isa selector: _cmd]; } + +- (BOOL)isUTF8 +{ + return YES; +} - (void)addMemoryToPool: (void*)ptr { @throw [OFNotImplementedException newWithClass: isa selector: _cmd]; Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -265,10 +265,14 @@ /** * \return The length of the string which cString would return */ - (size_t)cStringLength; + +/// \cond internal +- (BOOL)isUTF8; +/// \endcond /** * Compares the OFString to another OFString. * * \param str A string to compare with Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -14,10 +14,11 @@ #define _GNU_SOURCE #include #include #include #include +#include #include #ifdef HAVE_MADVISE # include #else @@ -34,10 +35,25 @@ #import "asprintf.h" #import "unicode.h" extern const uint16_t of_iso_8859_15[256]; extern const uint16_t of_windows_1252[256]; + +static inline int +memcasecmp(const char *s1, const char *s2, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (tolower(s1[i]) > tolower(s2[i])) + return OF_ORDERED_DESCENDING; + if (tolower(s1[i]) < tolower(s2[i])) + return OF_ORDERED_ASCENDING; + } + + return OF_ORDERED_SAME; +} /* References for static linking */ void _references_to_categories_of_OFString() { _OFString_Hashing_reference = 1; @@ -700,10 +716,15 @@ - (size_t)cStringLength { return length; } + +- (BOOL)isUTF8 +{ + return isUTF8; +} - (BOOL)isEqual: (OFObject*)obj { if (![obj isKindOfClass: [OFString class]]) return NO; @@ -723,12 +744,12 @@ return [[OFMutableString alloc] initWithString: self]; } - (of_comparison_result_t)compare: (OFString*)str { - int cmp; size_t str_len, min_len; + int cmp; if (![str isKindOfClass: [OFString class]]) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; @@ -750,18 +771,36 @@ } - (of_comparison_result_t)caseInsensitiveCompare: (OFString*)str { const char *str_cstr; - size_t i, j, str_len; + size_t i, j, str_len, min_len; + int cmp; if (![str isKindOfClass: [OFString class]]) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; str_cstr = [str cString]; str_len = [str cStringLength]; + + if (![self isUTF8]) { + min_len = (length > str_len ? str_len : length); + + if ((cmp = memcasecmp(string, [str cString], min_len)) == 0) { + if (length > str_len) + return OF_ORDERED_DESCENDING; + if (length < str_len) + return OF_ORDERED_ASCENDING; + return OF_ORDERED_SAME; + } + + if (cmp > 0) + return OF_ORDERED_DESCENDING; + else + return OF_ORDERED_ASCENDING; + } i = j = 0; while (i < length && j < str_len) { of_unichar_t c1, c2; @@ -820,10 +859,17 @@ } - (of_unichar_t)characterAtIndex: (size_t)index { of_unichar_t c; + + if (![self isUTF8]) { + if (index >= length) + @throw [OFOutOfRangeException newWithClass: isa]; + + return string[index]; + } index = of_string_index_to_position(string, index, length); if (index >= length) @throw [OFOutOfRangeException newWithClass: isa]; @@ -876,12 +922,14 @@ } - (OFString*)substringFromIndex: (size_t)start toIndex: (size_t)end { - start = of_string_index_to_position(string, start, length); - end = of_string_index_to_position(string, end, length); + if ([self isUTF8]) { + start = of_string_index_to_position(string, start, length); + end = of_string_index_to_position(string, end, length); + } if (start > end) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -72,11 +72,13 @@ [@"a" caseInsensitiveCompare: @"A"] == OF_ORDERED_SAME && [@"Ä" caseInsensitiveCompare: @"ä"] == OF_ORDERED_SAME && [@"я" caseInsensitiveCompare: @"Я"] == OF_ORDERED_SAME && [@"€" caseInsensitiveCompare: @"ß"] == OF_ORDERED_DESCENDING && [@"ß" caseInsensitiveCompare: @"→"] == OF_ORDERED_ASCENDING && - [@"AA" caseInsensitiveCompare: @"z"] == OF_ORDERED_ASCENDING) + [@"AA" caseInsensitiveCompare: @"z"] == OF_ORDERED_ASCENDING && + [[OFString stringWithCString: "ABC"] caseInsensitiveCompare: + [OFString stringWithCString: "AbD"]] == [@"abc" compare: @"abd"]) TEST(@"-[hash] is the same if -[isEqual:] is YES", [s[0] hash] == [s[2] hash]) TEST(@"-[appendString:] and -[appendCString:]",