Index: src/OFDataArray.m ================================================================== --- src/OFDataArray.m +++ src/OFDataArray.m @@ -154,29 +154,31 @@ } - (of_comparison_result_t)compare: (OFDataArray*)ary { int cmp; + size_t ary_count, min_count; if (![ary isKindOfClass: [OFDataArray class]]) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; if ([ary itemsize] != itemsize) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; - if ([ary count] == count) { - if ((cmp = memcmp(data, [ary cArray], count * itemsize)) == 0) - return OF_ORDERED_SAME; + ary_count = [ary count]; + min_count = (count > ary_count ? ary_count : count); - if (cmp > 0) + if ((cmp = memcmp(data, [ary cArray], min_count * itemsize)) == 0) { + if (count > ary_count) return OF_ORDERED_DESCENDING; - else + if (count < ary_count) return OF_ORDERED_ASCENDING; + return OF_ORDERED_SAME; } - if (count > [ary count]) + if (cmp > 0) return OF_ORDERED_DESCENDING; else return OF_ORDERED_ASCENDING; } Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -206,10 +206,18 @@ * \param str A string to compare with * \return An of_comparison_result_t */ - (of_comparison_result_t)compare: (OFString*)str; +/** + * Compares the OFString to another OFString without caring about the case. + * + * \param str A string to compare with + * \return An of_comparison_result_t + */ +- (of_comparison_result_t)caseInsensitiveCompare: (OFString*)str; + /** * \param index The index of the Unicode character to return * \return The Unicode character at the specified index */ - (of_unichar_t)characterAtIndex: (size_t)index; Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -26,10 +26,11 @@ #import "OFAutoreleasePool.h" #import "OFExceptions.h" #import "OFMacros.h" #import "asprintf.h" +#import "unicode.h" extern const uint16_t of_iso_8859_15[256]; extern const uint16_t of_windows_1252[256]; /* References for static linking */ @@ -568,36 +569,87 @@ } - (of_comparison_result_t)compare: (OFString*)str { int cmp; + size_t str_len, min_len; + + if (![str isKindOfClass: [OFString class]]) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + str_len = [str cStringLength]; + min_len = (length > str_len ? str_len : length); + + if ((cmp = memcmp(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; +} + +- (of_comparison_result_t)caseInsensitiveCompare: (OFString*)str +{ + const char *str_cstr; + size_t i, j, str_len; if (![str isKindOfClass: [OFString class]]) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; - if ([str length] == [self length]) { - if (length != [str cStringLength]) { - if (length > [str cStringLength]) - return OF_ORDERED_DESCENDING; - else - return OF_ORDERED_ASCENDING; - } - - if ((cmp = memcmp(string, [str cString], length)) == 0) - return OF_ORDERED_SAME; - - if (cmp > 0) - return OF_ORDERED_DESCENDING; - else - return OF_ORDERED_ASCENDING; - } - - if ([self length] > [str length]) - return OF_ORDERED_DESCENDING; - else - return OF_ORDERED_ASCENDING; + str_cstr = [str cString]; + str_len = [str cStringLength]; + + i = j = 0; + + while (i < length && j < str_len) { + of_unichar_t c1, c2; + size_t l1, l2; + of_unichar_t tmp; + + l1 = of_string_utf8_to_unicode(string + i, length - i, &c1); + l2 = of_string_utf8_to_unicode(str_cstr + j, str_len - j, &c2); + + if (l1 == 0 || l2 == 0 || c1 > 0x10FFFF || c2 > 0x10FFFF) + @throw [OFInvalidEncodingException newWithClass: isa]; + + if (c1 >> 8 < OF_UNICODE_CASEFOLDING_TABLE_SIZE) { + tmp = of_unicode_casefolding_table[c1 >> 8][c1 & 0xFF]; + + if (tmp != 0) + c1 = tmp; + } + + if (c2 >> 8 < OF_UNICODE_CASEFOLDING_TABLE_SIZE) { + tmp = of_unicode_casefolding_table[c2 >> 8][c2 & 0xFF]; + + if (tmp != 0) + c2 = tmp; + } + + if (c1 > c2) + return OF_ORDERED_DESCENDING; + if (c1 < c2) + return OF_ORDERED_ASCENDING; + + i += l1; + j += l2; + } + + if (length - i > str_len - j) + return OF_ORDERED_DESCENDING; + else if (length - i < str_len - j) + return OF_ORDERED_ASCENDING; + + return OF_ORDERED_SAME; } - (uint32_t)hash { uint32_t hash; Index: tests/OFDataArray.m ================================================================== --- tests/OFDataArray.m +++ tests/OFDataArray.m @@ -24,11 +24,11 @@ const char *str = "Hello!"; static void do_tests(Class class) { - OFDataArray *array[2]; + OFDataArray *array[4]; void *data[2]; Class other; TEST(@"+[dataArrayWithItemSize:]", (array[0] = [class dataArrayWithItemSize: 4096])) @@ -59,14 +59,20 @@ [array[1] removeNItems: 1] && ![array[0] isEqual: array[1]]) TEST(@"-[copy]", (array[1] = [[array[0] copy] autorelease]) && [array[0] isEqual: array[1]]) + array[2] = [OFDataArray dataArrayWithItemSize: 1]; + array[3] = [OFDataArray dataArrayWithItemSize: 1]; + [array[2] addItem: "a"]; + [array[2] addItem: "a"]; + [array[3] addItem: "z"]; TEST(@"-[compare]", [array[0] compare: array[1]] == 0 && [array[1] removeNItems: 1] && [array[0] compare: array[1]] == OF_ORDERED_DESCENDING && - [array[1] compare: array[0]] == OF_ORDERED_ASCENDING) + [array[1] compare: array[0]] == OF_ORDERED_ASCENDING && + [array[2] compare: array[3]] == OF_ORDERED_ASCENDING) TEST(@"-[hash]", [array[0] hash] == 0xC54621B6) TEST(@"-[removeNItems:]", [array[0] removeNItems: 1]) Index: tests/OFString.m ================================================================== --- tests/OFString.m +++ tests/OFString.m @@ -52,11 +52,25 @@ TEST(@"-[isEqual:]", [s[0] isEqual: s[2]] && ![s[0] isEqual: [[[OFObject alloc] init] autorelease]]) TEST(@"-[compare:]", [s[0] compare: s[2]] == OF_ORDERED_SAME && - [s[0] compare: @""] != OF_ORDERED_SAME) + [s[0] compare: @""] != OF_ORDERED_SAME && + [@"" compare: @"a"] == OF_ORDERED_ASCENDING && + [@"a" compare: @"b"] == OF_ORDERED_ASCENDING && + [@"cd" compare: @"bc"] == OF_ORDERED_DESCENDING && + [@"ä" compare: @"ö"] == OF_ORDERED_ASCENDING && + [@"€" compare: @"ß"] == OF_ORDERED_DESCENDING && + [@"aa" compare: @"z"] == OF_ORDERED_ASCENDING) + + TEST(@"-[caseInsensitiveCompare:]", + [@"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) TEST(@"-[hash] is the same if -[isEqual:] is YES", [s[0] hash] == [s[2] hash]) TEST(@"-[appendString:] and -[appendCString:]",