Index: src/OFConstantString.m ================================================================== --- src/OFConstantString.m +++ src/OFConstantString.m @@ -344,22 +344,24 @@ return [self getCharacters: buffer inRange: range]; } -- (size_t)indexOfFirstOccurrenceOfString: (OFString*)string +- (of_range_t)rangeOfString: (OFString*)string { [self finishInitialization]; - return [self indexOfFirstOccurrenceOfString: string]; + return [self rangeOfString: string]; } -- (size_t)indexOfLastOccurrenceOfString: (OFString*)string +- (of_range_t)rangeOfString: (OFString*)string + options: (of_string_search_options_t)options { [self finishInitialization]; - return [self indexOfLastOccurrenceOfString: string]; + return [self rangeOfString: string + options: options]; } - (BOOL)containsString: (OFString*)string { [self finishInitialization]; Index: src/OFHTTPRequest.m ================================================================== --- src/OFHTTPRequest.m +++ src/OFHTTPRequest.m @@ -400,24 +400,24 @@ bytesReceived = 0; @try { if (chunked) { for (;;) { void *pool2 = objc_autoreleasePoolPush(); - size_t pos, toRead; + size_t toRead; + of_range_t range; @try { line = [sock readLine]; } @catch (OFInvalidEncodingException *e) { @throw [OFInvalidServerReplyException exceptionWithClass: [self class]]; } - pos = [line - indexOfFirstOccurrenceOfString: @";"]; - if (pos != OF_INVALID_INDEX) + range = [line rangeOfString: @";"]; + if (range.start != OF_INVALID_INDEX) line = [line substringWithRange: - of_range(0, pos)]; + of_range(0, range.start)]; @try { toRead = (size_t)[line hexadecimalValue]; } @catch (OFInvalidFormatException *e) { Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -42,10 +42,15 @@ OF_STRING_ENCODING_ISO_8859_15, OF_STRING_ENCODING_WINDOWS_1252, OF_STRING_ENCODING_AUTODETECT = 0xFF } of_string_encoding_t; + +typedef enum of_string_search_options_t { + OF_STRING_SEARCH_BACKWARDS = 1 +} of_string_search_options_t; + /* FIXME */ #define OF_STRING_ENCODING_NATIVE OF_STRING_ENCODING_UTF_8 #ifdef OF_HAVE_BLOCKS typedef void (^of_string_line_enumeration_block_t)(OFString *line, BOOL *stop); @@ -608,26 +613,28 @@ */ - (void)getCharacters: (of_unichar_t*)buffer inRange: (of_range_t)range; /** - * \brief Returns the index of the first occurrence of the string. - * - * \param string The string to search - * \return The index of the first occurrence of the string or OF_INVALID_INDEX - * if it was not found - */ -- (size_t)indexOfFirstOccurrenceOfString: (OFString*)string; - -/** - * \brief Returns the index of the last occurrence of the string. - * - * \param string The string to search - * \return The index of the last occurrence of the string or OF_INVALID_INDEX if - * it was not found - */ -- (size_t)indexOfLastOccurrenceOfString: (OFString*)string; + * \brief Returns the range of the first occurrence of the string. + * + * \param string The string to search + * \return The range of the first occurrence of the string or a range with + * OF_INVALID_INDEX as start position if it was not found + */ +- (of_range_t)rangeOfString: (OFString*)string; + +/** + * \brief Returns the range of the string. + * + * \param string The string to search + * \param options Options modifying search behaviour. + * \return The range of the first occurrence of the string or a range with + * OF_INVALID_INDEX as start position if it was not found + */ +- (of_range_t)rangeOfString: (OFString*)string + options: (of_string_search_options_t)options; /** * \brief Returns whether the string contains the specified string. * * \param string The string to search Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -1317,72 +1317,59 @@ [JSON makeImmutable]; return JSON; } -- (size_t)indexOfFirstOccurrenceOfString: (OFString*)string +- (of_range_t)rangeOfString: (OFString*)string +{ + return [self rangeOfString: string + options: 0]; +} + +- (of_range_t)rangeOfString: (OFString*)string + options: (of_string_search_options_t)options { void *pool; const of_unichar_t *unicodeString, *searchString; size_t i, length, searchLength; if ((searchLength = [string length]) == 0) - return [self length]; + return of_range(0, 0); if (searchLength > (length = [self length])) - return OF_INVALID_INDEX; + return of_range(OF_INVALID_INDEX, 0); pool = objc_autoreleasePoolPush(); unicodeString = [self unicodeString]; searchString = [string unicodeString]; - for (i = 0; i <= length - searchLength; i++) { - if (!memcmp(unicodeString + i, searchString, - searchLength * sizeof(of_unichar_t))) { - objc_autoreleasePoolPop(pool); - return i; - } - } - - objc_autoreleasePoolPop(pool); - - return OF_INVALID_INDEX; -} - -- (size_t)indexOfLastOccurrenceOfString: (OFString*)string -{ - void *pool; - const of_unichar_t *unicodeString, *searchString; - size_t i, length, searchLength; - - if ((searchLength = [string length]) == 0) - return [self length]; - - if (searchLength > (length = [self length])) - return OF_INVALID_INDEX; - - pool = objc_autoreleasePoolPush(); - - unicodeString = [self unicodeString]; - searchString = [string unicodeString]; - - for (i = length - searchLength;; i--) { - if (!memcmp(unicodeString + i, searchString, - searchLength * sizeof(of_unichar_t))) { - objc_autoreleasePoolPop(pool); - return i; - } - - /* Did not match and we're at the last character */ - if (i == 0) - break; - } - - objc_autoreleasePoolPop(pool); - - return OF_INVALID_INDEX; + if (options & OF_STRING_SEARCH_BACKWARDS) { + for (i = length - searchLength;; i--) { + if (!memcmp(unicodeString + i, searchString, + searchLength * sizeof(of_unichar_t))) { + objc_autoreleasePoolPop(pool); + return of_range(i, searchLength); + } + + /* Did not match and we're at the last character */ + if (i == 0) + break; + } + } else { + for (i = 0; i <= length - searchLength; i++) { + if (!memcmp(unicodeString + i, searchString, + searchLength * sizeof(of_unichar_t))) { + objc_autoreleasePoolPop(pool); + return of_range(i, searchLength); + } + } + } + + objc_autoreleasePoolPop(pool); + + return of_range(OF_INVALID_INDEX, 0); } - (BOOL)containsString: (OFString*)string { void *pool; Index: src/OFString_UTF8.m ================================================================== --- src/OFString_UTF8.m +++ src/OFString_UTF8.m @@ -876,48 +876,42 @@ range.length * sizeof(of_unichar_t)); objc_autoreleasePoolPop(pool); } -- (size_t)indexOfFirstOccurrenceOfString: (OFString*)string -{ - const char *cString = [string UTF8String]; - size_t i, cStringLength = [string UTF8StringLength]; - - if (cStringLength == 0) - return 0; - - if (cStringLength > s->cStringLength) - return OF_INVALID_INDEX; - - for (i = 0; i <= s->cStringLength - cStringLength; i++) - if (!memcmp(s->cString + i, cString, cStringLength)) - return of_string_position_to_index(s->cString, i); - - return OF_INVALID_INDEX; -} - -- (size_t)indexOfLastOccurrenceOfString: (OFString*)string -{ - const char *cString = [string UTF8String]; - size_t i, cStringLength = [string UTF8StringLength]; - - if (cStringLength == 0) - return of_string_position_to_index(s->cString, - s->cStringLength); - - if (cStringLength > s->cStringLength) - return OF_INVALID_INDEX; - - for (i = s->cStringLength - cStringLength;; i--) { - if (!memcmp(s->cString + i, cString, cStringLength)) - return of_string_position_to_index(s->cString, i); - - /* Did not match and we're at the last char */ - if (i == 0) - return OF_INVALID_INDEX; - } +- (of_range_t)rangeOfString: (OFString*)string + options: (of_string_search_options_t)options +{ + const char *cString = [string UTF8String]; + size_t i, cStringLength = [string UTF8StringLength]; + + if (cStringLength == 0) + return of_range(0, 0); + + if (cStringLength > s->cStringLength) + return of_range(OF_INVALID_INDEX, 0); + + if (options & OF_STRING_SEARCH_BACKWARDS) { + for (i = s->cStringLength - cStringLength;; i--) { + if (!memcmp(s->cString + i, cString, cStringLength)) + return of_range( + of_string_position_to_index(s->cString, i), + [string length]); + + /* Did not match and we're at the last char */ + if (i == 0) + return of_range(OF_INVALID_INDEX, 0); + } + } else { + for (i = 0; i <= s->cStringLength - cStringLength; i++) + if (!memcmp(s->cString + i, cString, cStringLength)) + return of_range( + of_string_position_to_index(s->cString, i), + [string length]); + } + + return of_range(OF_INVALID_INDEX, 0); } - (BOOL)containsString: (OFString*)string { const char *cString = [string UTF8String]; Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -219,21 +219,24 @@ TEST(@"-[appendFormat:]", R(([s[0] appendFormat: @"%02X", 15])) && [s[0] isEqual: @"test:1230F"]) - TEST(@"-[indexOfFirstOccurrenceOfString:]", - [@"π„žΓΆΓΆ" indexOfFirstOccurrenceOfString: @"ΓΆΓΆ"] == 1 && - [@"π„žΓΆΓΆ" indexOfFirstOccurrenceOfString: @"ΓΆ"] == 1 && - [@"π„žΓΆΓΆ" indexOfFirstOccurrenceOfString: @"π„ž"] == 0 && - [@"π„žΓΆΓΆ" indexOfFirstOccurrenceOfString: @"x"] == OF_INVALID_INDEX) - - TEST(@"-[indexOfLastOccurrenceOfString:]", - [@"π„žΓΆΓΆ" indexOfLastOccurrenceOfString: @"ΓΆΓΆ"] == 1 && - [@"π„žΓΆΓΆ" indexOfLastOccurrenceOfString: @"ΓΆ"] == 2 && - [@"π„žΓΆΓΆ" indexOfLastOccurrenceOfString: @"π„ž"] == 0 && - [@"π„žΓΆΓΆ" indexOfLastOccurrenceOfString: @"x"] == OF_INVALID_INDEX) + TEST(@"-[rangeOfString:]", + [@"π„žΓΆΓΆ" rangeOfString: @"ΓΆΓΆ"].start == 1 && + [@"π„žΓΆΓΆ" rangeOfString: @"ΓΆ"].start == 1 && + [@"π„žΓΆΓΆ" rangeOfString: @"π„ž"].start == 0 && + [@"π„žΓΆΓΆ" rangeOfString: @"x"].start == OF_INVALID_INDEX && + [@"π„žΓΆΓΆ" rangeOfString: @"ΓΆΓΆ" + options: OF_STRING_SEARCH_BACKWARDS].start == 1 && + [@"π„žΓΆΓΆ" rangeOfString: @"ΓΆ" + options: OF_STRING_SEARCH_BACKWARDS].start == 2 && + [@"π„žΓΆΓΆ" rangeOfString: @"π„ž" + options: OF_STRING_SEARCH_BACKWARDS].start == 0 && + [@"π„žΓΆΓΆ" rangeOfString: @"x" + options: OF_STRING_SEARCH_BACKWARDS].start == + OF_INVALID_INDEX) TEST(@"-[substringWithRange:]", [[@"π„žΓΆΓΆ" substringWithRange: of_range(1, 1)] isEqual: @"ΓΆ"] && [[@"π„žΓΆΓΆ" substringWithRange: of_range(3, 0)] isEqual: @""])