Implementation of [NSData rangeOfData:options:range], adapted from [NSString rangeOfString:options:range].

This commit is contained in:
Adam Fox 2020-10-12 12:22:00 -06:00
parent 79f738ceb1
commit 0b69d88ee2
4 changed files with 212 additions and 0 deletions

View file

@ -1,3 +1,7 @@
2020-10-11 Adam Fox <adam.fox@eggplantsoftware.com>
* Source/NSData.m: Implement rangeOfData:options:range.
2020-08-30 Fred Kiefer <fredkiefer@gmx.de>
* Source/NSDateComponentsFormatter.m: Fix use of wrong operator.

View file

@ -40,6 +40,14 @@ extern "C" {
@class NSURL;
#endif
#if OS_API_VERSION(MAC_OS_X_VERSION_10_6,GS_API_LATEST)
enum {
NSDataSearchBackwards = (1UL << 0),
NSDataSearchAnchored = (1UL << 1),
};
typedef NSUInteger NSDataSearchOptions;
#endif
#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
enum {
NSDataBase64DecodingIgnoreUnknownCharacters = (1UL << 0)
@ -136,6 +144,12 @@ DEFINE_BLOCK_TYPE(GSDataDeallocatorBlock, void, void*, NSUInteger);
range: (NSRange)aRange;
- (NSData*) subdataWithRange: (NSRange)aRange;
#if OS_API_VERSION(MAC_OS_X_VERSION_10_6,GS_API_LATEST)
- (NSRange) rangeOfData: (NSData *)dataToFind
options: (NSDataSearchOptions)mask
range: (NSRange)searchRange;
#endif
// base64
#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
- (NSData *) base64EncodedDataWithOptions: (NSDataBase64EncodingOptions)options;

View file

@ -1082,6 +1082,119 @@ failure:
return [NSData dataWithBytesNoCopy: buffer length: aRange.length];
}
/**
* Finds and returns the range of the first occurrence of the given data, within the given range, subject to given options.
*/
- (NSRange) rangeOfData: (NSData *)dataToFind
options: (NSDataSearchOptions)mask
range: (NSRange)searchRange
{
NSUInteger length = [self length];
NSUInteger countOther = [dataToFind length];
const void* bytesSelf = [self bytes];
const void* bytesOther = [dataToFind bytes];
NSRange result;
GS_RANGE_CHECK(searchRange, length);
if (dataToFind == nil)
[NSException raise: NSInvalidArgumentException format: @"range of nil"];
/* Zero length data is always found at the start of the given range.
*/
if (0 == countOther)
{
if ((mask & NSDataSearchBackwards) == NSDataSearchBackwards)
{
searchRange.location += searchRange.length;
}
searchRange.length = 0;
return searchRange;
}
if (searchRange.length < countOther)
{
/* Range to search is smaller than data to look for.
*/
result = NSMakeRange(NSNotFound, 0);
}
else
{
if ((mask & NSDataSearchAnchored) == NSDataSearchAnchored
|| searchRange.length == countOther)
{
/* Range to search is same size as data to look for.
*/
if ((mask & NSDataSearchBackwards) == NSDataSearchBackwards)
{
searchRange.location = NSMaxRange(searchRange) - countOther;
searchRange.length = countOther;
}
else
{
searchRange.length = countOther;
}
if (memcmp(&bytesSelf[0], &bytesOther[0], countOther) == 0)
{
result = searchRange;
}
else
{
result = NSMakeRange(NSNotFound, 0);
}
}
else
{
/* Range to search is bigger than data to look for.
*/
NSUInteger pos;
NSUInteger end;
end = searchRange.length - countOther + 1;
if ((mask & NSDataSearchBackwards) == NSDataSearchBackwards)
{
pos = end;
}
else
{
pos = 0;
}
if ((mask & NSDataSearchBackwards) == NSDataSearchBackwards)
{
while (pos-- > 0)
{
if (memcmp(&bytesSelf[searchRange.location + pos], bytesOther, countOther) == 0)
{
break;
}
}
}
else
{
while (pos < end)
{
if (memcmp(&bytesSelf[searchRange.location + pos], bytesOther, countOther) == 0)
{
break;
}
pos++;
}
}
if (pos >= end)
{
result = NSMakeRange(NSNotFound, 0);
}
else
{
result = NSMakeRange(searchRange.location + pos, countOther);
}
}
}
return result;
}
- (NSData *) base64EncodedDataWithOptions: (NSDataBase64EncodingOptions)options
{
void *srcBytes = (void*)[self bytes];

View file

@ -0,0 +1,81 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
static BOOL rangesEqual(NSRange r1, NSRange r2)
{
if (&r1 == &r2)
return YES;
if (r1.length == 0 && r2.length == 0)
return YES;
return (r1.length == r2.length && r1.location == r2.location);
}
static void dataRange(char *s0, char *s1, NSDataSearchOptions opts,
NSRange range, NSRange want)
{
NSData *d0 = [NSData dataWithBytes:s0 length:strlen(s0)];
NSData *d1 = [NSData dataWithBytes:s1 length:strlen(s1)];
NSRange res = [d0 rangeOfData:d1 options:opts range:range];
PASS(rangesEqual(res,want), "NSData range for '%s' and '%s' is ok",s0,s1);
}
int main()
{
NSAutoreleasePool *arp = [NSAutoreleasePool new];
/* Borrowed from NSString/test00.m
*/
dataRange("hello", "hello", NSDataSearchAnchored,
NSMakeRange(0,5), NSMakeRange(0,5));
dataRange("hello", "hello", NSDataSearchAnchored | NSDataSearchBackwards,
NSMakeRange(0,5), NSMakeRange(0,5));
dataRange("hello", "hElLo", 0,
NSMakeRange(0,5), NSMakeRange(NSNotFound,0));
dataRange("hello", "hell", NSDataSearchAnchored,
NSMakeRange(0,5), NSMakeRange(0,4));
dataRange("hello", "hell", NSDataSearchAnchored | NSDataSearchBackwards,
NSMakeRange(0,4), NSMakeRange(0,4));
dataRange("hello", "ello", NSDataSearchAnchored,
NSMakeRange(0,5), NSMakeRange(NSNotFound,0));
dataRange("hello", "hel", NSDataSearchBackwards,
NSMakeRange(0,5), NSMakeRange(0,3));
dataRange("hello", "he", 0,
NSMakeRange(0,5), NSMakeRange(0,2));
dataRange("hello", "h", 0,
NSMakeRange(0,5), NSMakeRange(0,1));
dataRange("hello", "l", 0,
NSMakeRange(0,5), NSMakeRange(2,1));
dataRange("hello", "l", NSDataSearchBackwards,
NSMakeRange(0,5), NSMakeRange(3,1));
dataRange("hello", "", 0,
NSMakeRange(0,5), NSMakeRange(0,0));
dataRange("hello", "el", 0,
NSMakeRange(0,5), NSMakeRange(1,2));
dataRange("hello", "el", 0,
NSMakeRange(0,2), NSMakeRange(0,0));
dataRange("hello", "el", 0,
NSMakeRange(2,3), NSMakeRange(0,0));
dataRange("hello", "ell", 0,
NSMakeRange(0,5), NSMakeRange(1,3));
dataRange("hello", "lo", 0,
NSMakeRange(2,3), NSMakeRange(3,2));
dataRange("boaboaboa", "abo", 0,
NSMakeRange(0,9), NSMakeRange(2,3));
dataRange("boaboaboa", "abo", NSDataSearchBackwards,
NSMakeRange(0,9), NSMakeRange(5,3));
dataRange("", "", 0,
NSMakeRange(0,0), NSMakeRange(0,0));
dataRange("x", "", 0,
NSMakeRange(0,1), NSMakeRange(0,0));
dataRange("x", "", NSDataSearchBackwards,
NSMakeRange(0,1), NSMakeRange(1,0));
[arp release];
arp = nil;
return 0;
}