mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-26 10:11:03 +00:00
enumeration methods that take blocks as arguments. This allows us to implement the NSEnumerationConcurrent option of those methods with minimal effort. The searching methods on NSIndexSet and all the sorting methods are still missing. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@35010 72102866-910b-0410-8b05-ffd578937521
1457 lines
32 KiB
Objective-C
1457 lines
32 KiB
Objective-C
/** Implementation for NSIndexSet, NSMutableIndexSet for GNUStep
|
|
Copyright (C) 2004 Free Software Foundation, Inc.
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
|
Created: Feb 2004
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02111 USA.
|
|
|
|
*/
|
|
|
|
#import "common.h"
|
|
#define EXPOSE_NSIndexSet_IVARS 1
|
|
#import "Foundation/NSCoder.h"
|
|
#import "Foundation/NSData.h"
|
|
#import "Foundation/NSIndexSet.h"
|
|
#import "Foundation/NSException.h"
|
|
#import "GSDispatch.h"
|
|
|
|
#define GSI_ARRAY_TYPE NSRange
|
|
|
|
#define GSI_ARRAY_NO_RELEASE 1
|
|
#define GSI_ARRAY_NO_RETAIN 1
|
|
|
|
#include "GNUstepBase/GSIArray.h"
|
|
|
|
#define _array ((GSIArray)(self->_data))
|
|
#define _other ((GSIArray)(aSet->_data))
|
|
|
|
#ifdef SANITY_CHECKS
|
|
static void sanity(GSIArray array)
|
|
{
|
|
if (array != 0)
|
|
|
|
{
|
|
NSUInteger c = GSIArrayCount(array);
|
|
NSUInteger i;
|
|
NSUInteger last = 0;
|
|
|
|
for (i = 0; i < c; i++)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(array, i).ext;
|
|
|
|
if (i > 0)
|
|
{
|
|
NSCAssert(r.location > last, @"Overlap or touching ranges");
|
|
}
|
|
else
|
|
{
|
|
NSCAssert(r.location >= last, @"Overlap ranges");
|
|
}
|
|
NSCAssert(NSMaxRange(r) > r.location, @"Bad range length");
|
|
last = NSMaxRange(r);
|
|
}
|
|
}
|
|
}
|
|
#define SANITY() sanity(_array)
|
|
#else
|
|
#define SANITY()
|
|
#endif
|
|
|
|
/*
|
|
* Returns the position in the array at which the index should be inserted.
|
|
* This may be the position of a range containing the index if it is already
|
|
* present, otherwise it is the position of the first range containing an
|
|
* index greater than the argument (or a position beyond the end of the
|
|
* array).
|
|
*/
|
|
static NSUInteger posForIndex(GSIArray array, NSUInteger index)
|
|
{
|
|
NSUInteger upper = GSIArrayCount(array);
|
|
NSUInteger lower = 0;
|
|
NSUInteger pos;
|
|
|
|
/*
|
|
* Binary search for an item equal to the one to be inserted.
|
|
*/
|
|
for (pos = upper/2; upper != lower; pos = (upper+lower)/2)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(array, pos).ext;
|
|
|
|
if (index < r.location)
|
|
{
|
|
upper = pos;
|
|
}
|
|
else if (index > NSMaxRange(r))
|
|
{
|
|
lower = pos + 1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* Now skip past any item containing no values as high as the index.
|
|
*/
|
|
while (pos < GSIArrayCount(array)
|
|
&& index >= NSMaxRange(GSIArrayItemAtIndex(array, pos).ext))
|
|
{
|
|
pos++;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
@implementation NSIndexSet
|
|
+ (id) indexSet
|
|
{
|
|
id o = [self allocWithZone: NSDefaultMallocZone()];
|
|
|
|
o = [o init];
|
|
return AUTORELEASE(o);
|
|
}
|
|
|
|
+ (id) indexSetWithIndex: (NSUInteger)anIndex
|
|
{
|
|
id o = [self allocWithZone: NSDefaultMallocZone()];
|
|
|
|
o = [o initWithIndex: anIndex];
|
|
return AUTORELEASE(o);
|
|
}
|
|
|
|
+ (id) indexSetWithIndexesInRange: (NSRange)aRange
|
|
{
|
|
id o = [self allocWithZone: NSDefaultMallocZone()];
|
|
|
|
o = [o initWithIndexesInRange: aRange];
|
|
return AUTORELEASE(o);
|
|
}
|
|
|
|
- (BOOL) containsIndex: (NSUInteger)anIndex
|
|
{
|
|
NSUInteger pos;
|
|
NSRange r;
|
|
|
|
if (_array == 0 || GSIArrayCount(_array) == 0
|
|
|| (pos = posForIndex(_array, anIndex)) >= GSIArrayCount(_array))
|
|
{
|
|
return NO;
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
return NSLocationInRange(anIndex, r);
|
|
}
|
|
|
|
- (BOOL) containsIndexes: (NSIndexSet*)aSet
|
|
{
|
|
NSUInteger count = _other ? GSIArrayCount(_other) : 0;
|
|
|
|
if (count > 0)
|
|
{
|
|
NSUInteger i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_other, i).ext;
|
|
|
|
if ([self containsIndexesInRange: r] == NO)
|
|
{
|
|
return NO;
|
|
}
|
|
}
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL) containsIndexesInRange: (NSRange)aRange
|
|
{
|
|
NSUInteger pos;
|
|
NSRange r;
|
|
|
|
if (NSNotFound - aRange.length < aRange.location)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"[%@-%@]: Bad range",
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
}
|
|
if (_array == 0 || GSIArrayCount(_array) == 0
|
|
|| (pos = posForIndex(_array, aRange.location)) >= GSIArrayCount(_array))
|
|
{
|
|
return NO; // Empty ... contains no indexes.
|
|
}
|
|
if (aRange.length == 0)
|
|
{
|
|
return YES; // No indexes needed.
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
if (NSLocationInRange(aRange.location, r)
|
|
&& NSLocationInRange(NSMaxRange(aRange)-1, r))
|
|
{
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone*)aZone
|
|
{
|
|
if (NSShouldRetainWithZone(self, aZone))
|
|
{
|
|
return RETAIN(self);
|
|
}
|
|
else
|
|
{
|
|
NSIndexSet *c = [NSIndexSet allocWithZone: aZone];
|
|
|
|
return [c initWithIndexSet: self];
|
|
}
|
|
}
|
|
|
|
- (NSUInteger) count
|
|
{
|
|
if (_array == 0 || GSIArrayCount(_array) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
NSUInteger count = GSIArrayCount(_array);
|
|
NSUInteger total = 0;
|
|
NSUInteger i = 0;
|
|
|
|
while (i < count)
|
|
{
|
|
total += GSIArrayItemAtIndex(_array, i).ext.length;
|
|
i++;
|
|
}
|
|
return total;
|
|
}
|
|
}
|
|
|
|
- (NSUInteger) countOfIndexesInRange: (NSRange)range
|
|
{
|
|
if (_array == 0 || GSIArrayCount(_array) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
NSUInteger count = GSIArrayCount(_array);
|
|
NSUInteger total = 0;
|
|
NSUInteger i = 0;
|
|
|
|
while (i < count)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, i).ext;
|
|
|
|
r = NSIntersectionRange(r, range);
|
|
total += r.length;
|
|
i++;
|
|
}
|
|
return total;
|
|
}
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
if (_array != 0)
|
|
{
|
|
GSIArrayClear(_array);
|
|
NSZoneFree([self zone], _array);
|
|
_data = 0;
|
|
}
|
|
[super dealloc];
|
|
}
|
|
|
|
- (NSString*) description
|
|
{
|
|
NSMutableString *m;
|
|
NSUInteger c = (_array == 0) ? 0 : GSIArrayCount(_array);
|
|
NSUInteger i;
|
|
|
|
if (c == 0)
|
|
{
|
|
return [NSString stringWithFormat: @"%@(no indexes)",
|
|
[super description]];
|
|
}
|
|
m = [NSMutableString stringWithFormat:
|
|
@"%@[number of indexes: %u (in %u ranges), indexes:",
|
|
[super description], [self count], c];
|
|
for (i = 0; i < c; i++)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, i).ext;
|
|
|
|
if (r.length > 1)
|
|
{
|
|
[m appendFormat: @" (%u-%u)", r.location, NSMaxRange(r) - 1];
|
|
}
|
|
else
|
|
{
|
|
[m appendFormat: @" %u", r.location];
|
|
}
|
|
}
|
|
[m appendString: @"]"];
|
|
return m;
|
|
}
|
|
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
{
|
|
NSUInteger rangeCount = 0;
|
|
|
|
if (_array != 0)
|
|
{
|
|
rangeCount = GSIArrayCount(_array);
|
|
}
|
|
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
[aCoder encodeInt: rangeCount forKey: @"NSRangeCount"];
|
|
}
|
|
else
|
|
{
|
|
[aCoder encodeValueOfObjCType: @encode(NSUInteger)
|
|
at: &rangeCount];
|
|
}
|
|
|
|
if (rangeCount == 0)
|
|
{
|
|
// Do nothing
|
|
}
|
|
else if (rangeCount == 1)
|
|
{
|
|
NSRange r;
|
|
|
|
r = GSIArrayItemAtIndex(_array, 0).ext;
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
[aCoder encodeInt: r.location forKey: @"NSLocation"];
|
|
[aCoder encodeInt: r.length forKey: @"NSLength"];
|
|
}
|
|
else
|
|
{
|
|
[aCoder encodeValueOfObjCType: @encode(NSUInteger)
|
|
at: &r.location];
|
|
[aCoder encodeValueOfObjCType: @encode(NSUInteger)
|
|
at: &r.length];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NSMutableData *m = [NSMutableData dataWithCapacity: rangeCount*2];
|
|
NSUInteger i;
|
|
|
|
for (i = 0; i < rangeCount; i++)
|
|
{
|
|
NSRange r;
|
|
NSUInteger v;
|
|
uint8_t b;
|
|
|
|
r = GSIArrayItemAtIndex(_array, i).ext;
|
|
v = r.location;
|
|
do
|
|
{
|
|
if (v > 0x7f)
|
|
{
|
|
b = (v & 0x7f) | 0x80;
|
|
}
|
|
else
|
|
{
|
|
b = v;
|
|
}
|
|
v >>= 7;
|
|
[m appendBytes: &b length: 1];
|
|
}
|
|
while (v > 0);
|
|
v = r.length;
|
|
do
|
|
{
|
|
if (v > 0x7f)
|
|
{
|
|
b = (v & 0x7f) | 0x80;
|
|
}
|
|
else
|
|
{
|
|
b = v;
|
|
}
|
|
v >>= 7;
|
|
[m appendBytes: &b length: 1];
|
|
}
|
|
while (v > 0);
|
|
}
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
[aCoder encodeObject: m forKey: @"NSRangeData"];
|
|
}
|
|
else
|
|
{
|
|
[aCoder encodeObject: m];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (NSUInteger) firstIndex
|
|
{
|
|
if (_array == 0 || GSIArrayCount(_array) == 0)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
return GSIArrayItemAtIndex(_array, 0).ext.location;
|
|
}
|
|
|
|
- (NSUInteger) getIndexes: (NSUInteger*)aBuffer
|
|
maxCount: (NSUInteger)aCount
|
|
inIndexRange: (NSRangePointer)aRange
|
|
{
|
|
NSUInteger pos;
|
|
NSUInteger i = 0;
|
|
NSRange r;
|
|
NSRange fullRange;
|
|
|
|
if (aBuffer == 0)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"[%@-%@]: nul pointer argument",
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
}
|
|
if (aRange == 0)
|
|
{
|
|
fullRange = (NSRange){0, NSNotFound};
|
|
aRange = &fullRange;
|
|
}
|
|
else if (NSNotFound - aRange->length < aRange->location)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"[%@-%@]: Bad range",
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
}
|
|
if (_array == 0 || GSIArrayCount(_array) == 0
|
|
|| (pos = posForIndex(_array, aRange->location)) >= GSIArrayCount(_array))
|
|
{
|
|
*aRange = NSMakeRange(NSMaxRange(*aRange), 0);
|
|
return 0;
|
|
}
|
|
|
|
while (aRange->length > 0 && i < aCount && pos < GSIArrayCount(_array))
|
|
{
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
if (aRange->location < r.location)
|
|
{
|
|
NSUInteger skip = r.location - aRange->location;
|
|
|
|
if (skip > aRange->length)
|
|
{
|
|
skip = aRange->length;
|
|
}
|
|
aRange->location += skip;
|
|
aRange->length -= skip;
|
|
}
|
|
if (NSLocationInRange(aRange->location, r))
|
|
{
|
|
while (aRange->length > 0 && i < aCount
|
|
&& aRange->location < NSMaxRange(r))
|
|
{
|
|
aBuffer[i++] = aRange->location++;
|
|
aRange->length--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
}
|
|
pos++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
- (NSUInteger) hash
|
|
{
|
|
return [self count];
|
|
}
|
|
|
|
- (NSUInteger) indexGreaterThanIndex: (NSUInteger)anIndex
|
|
{
|
|
NSUInteger pos;
|
|
NSRange r;
|
|
|
|
if (anIndex++ == NSNotFound)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
if (_array == 0 || GSIArrayCount(_array) == 0
|
|
|| (pos = posForIndex(_array, anIndex)) >= GSIArrayCount(_array))
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
if (NSLocationInRange(anIndex, r))
|
|
{
|
|
return anIndex;
|
|
}
|
|
return r.location;
|
|
}
|
|
|
|
- (NSUInteger) indexGreaterThanOrEqualToIndex: (NSUInteger)anIndex
|
|
{
|
|
NSUInteger pos;
|
|
NSRange r;
|
|
|
|
if (anIndex == NSNotFound)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
if (_array == 0 || GSIArrayCount(_array) == 0
|
|
|| (pos = posForIndex(_array, anIndex)) >= GSIArrayCount(_array))
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
if (NSLocationInRange(anIndex, r))
|
|
{
|
|
return anIndex;
|
|
}
|
|
return r.location;
|
|
}
|
|
|
|
- (NSUInteger) indexLessThanIndex: (NSUInteger)anIndex
|
|
{
|
|
NSUInteger pos;
|
|
NSRange r;
|
|
|
|
if (anIndex-- == 0)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
if (_array == 0 || GSIArrayCount(_array) == 0
|
|
|| (pos = posForIndex(_array, anIndex)) >= GSIArrayCount(_array))
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
if (NSLocationInRange(anIndex, r))
|
|
{
|
|
return anIndex;
|
|
}
|
|
if (pos-- == 0)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
return NSMaxRange(r) - 1;
|
|
}
|
|
|
|
- (NSUInteger) indexLessThanOrEqualToIndex: (NSUInteger)anIndex
|
|
{
|
|
NSUInteger pos;
|
|
NSRange r;
|
|
|
|
if (_array == 0 || GSIArrayCount(_array) == 0
|
|
|| (pos = posForIndex(_array, anIndex)) >= GSIArrayCount(_array))
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
if (NSLocationInRange(anIndex, r))
|
|
{
|
|
return anIndex;
|
|
}
|
|
if (pos-- == 0)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
return NSMaxRange(r) - 1;
|
|
}
|
|
|
|
- (id) init
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (id) initWithCoder: (NSCoder*)aCoder
|
|
{
|
|
NSUInteger rangeCount = 0;
|
|
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
if ([aCoder containsValueForKey: @"NSRangeCount"])
|
|
{
|
|
rangeCount = [aCoder decodeIntForKey: @"NSRangeCount"];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
[aCoder decodeValueOfObjCType: @encode(NSUInteger)
|
|
at: &rangeCount];
|
|
}
|
|
|
|
if (rangeCount == 0)
|
|
{
|
|
// Do nothing
|
|
}
|
|
else if (rangeCount == 1)
|
|
{
|
|
NSUInteger len = 0;
|
|
NSUInteger loc = 0;
|
|
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
if ([aCoder containsValueForKey: @"NSLocation"])
|
|
{
|
|
loc = [aCoder decodeIntForKey: @"NSLocation"];
|
|
}
|
|
if ([aCoder containsValueForKey: @"NSLength"])
|
|
{
|
|
len = [aCoder decodeIntForKey: @"NSLength"];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
[aCoder decodeValueOfObjCType: @encode(NSUInteger)
|
|
at: &loc];
|
|
[aCoder decodeValueOfObjCType: @encode(NSUInteger)
|
|
at: &len];
|
|
}
|
|
self = [self initWithIndexesInRange: NSMakeRange(loc, len)];
|
|
}
|
|
else
|
|
{
|
|
NSMutableIndexSet *other = [NSMutableIndexSet new];
|
|
NSData *data = nil;
|
|
const uint8_t *bytes;
|
|
NSUInteger length;
|
|
NSUInteger index = 0;
|
|
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
if ([aCoder containsValueForKey: @"NSRangeData"])
|
|
{
|
|
data = [aCoder decodeObjectForKey: @"NSRangeData"];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data = [aCoder decodeObject];
|
|
}
|
|
bytes = (const uint8_t*)[data bytes];
|
|
length = [data length];
|
|
while (index < length)
|
|
{
|
|
NSRange range;
|
|
NSUInteger offset;
|
|
NSUInteger value;
|
|
NSUInteger next;
|
|
|
|
for (offset = 0; index + offset < length; offset++)
|
|
{
|
|
if (bytes[index + offset] < 0x80)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
NSAssert(index + offset < length && bytes[index + offset] < 0x80,
|
|
NSInternalInconsistencyException);
|
|
next = index + offset + 1;
|
|
value = bytes[index + offset];
|
|
while (offset-- > 0)
|
|
{
|
|
value <<= 7;
|
|
value += (bytes[index + offset] & 0x7f);
|
|
}
|
|
range.location = value;
|
|
index = next;
|
|
for (offset = 0; index + offset < length; offset++)
|
|
{
|
|
if (bytes[index + offset] < 0x80)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
NSAssert(index + offset < length && bytes[index + offset] < 0x80,
|
|
NSInternalInconsistencyException);
|
|
next = index + offset + 1;
|
|
value = bytes[index + offset];
|
|
while (offset-- > 0)
|
|
{
|
|
value <<= 7;
|
|
value += (bytes[index + offset] & 0x7f);
|
|
}
|
|
range.length = value;
|
|
index = next;
|
|
[other addIndexesInRange: range];
|
|
}
|
|
self = [self initWithIndexSet: other];
|
|
RELEASE(other);
|
|
/*
|
|
FIXME:
|
|
NSLog(@"Decoded count %d, data %@", rangeCount, data);
|
|
This is a very strange format:
|
|
|
|
5 + 6 + 9 gives <05020901>
|
|
5 + 6 + 23 gives <05021701>
|
|
155 + 156 + 223 gives <9b0102df 0101>
|
|
*/
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) initWithIndex: (NSUInteger)anIndex
|
|
{
|
|
if (anIndex == NSNotFound)
|
|
{
|
|
DESTROY(self); // NSNotFound is not legal
|
|
}
|
|
else
|
|
{
|
|
self = [self initWithIndexesInRange: NSMakeRange(anIndex, 1)];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (id) initWithIndexesInRange: (NSRange)aRange
|
|
{
|
|
if (aRange.length > 0)
|
|
{
|
|
if (NSMaxRange(aRange) == NSNotFound)
|
|
{
|
|
DESTROY(self); // NSNotFound is not legal
|
|
}
|
|
else
|
|
{
|
|
#if GS_WITH_GC
|
|
_data = (GSIArray)NSAllocateCollectable(sizeof(GSIArray_t),
|
|
NSScannedOption);
|
|
GSIArrayInitWithZoneAndCapacity(_array, 0, 1);
|
|
#else
|
|
_data = (GSIArray)NSZoneMalloc([self zone], sizeof(GSIArray_t));
|
|
GSIArrayInitWithZoneAndCapacity(_array, [self zone], 1);
|
|
#endif
|
|
GSIArrayAddItem(_array, (GSIArrayItem)aRange);
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (id) initWithIndexSet: (NSIndexSet*)aSet
|
|
{
|
|
if (aSet == nil || [aSet isKindOfClass: [NSIndexSet class]] == NO)
|
|
{
|
|
DESTROY(self);
|
|
}
|
|
else
|
|
{
|
|
NSUInteger count = _other ? GSIArrayCount(_other) : 0;
|
|
|
|
if (count > 0)
|
|
{
|
|
NSUInteger i;
|
|
|
|
#if GS_WITH_GC
|
|
_data = (GSIArray)NSAllocateCollectable(sizeof(GSIArray_t),
|
|
NSScannedOption);
|
|
GSIArrayInitWithZoneAndCapacity(_array, 0, 1);
|
|
#else
|
|
_data = (GSIArray)NSZoneMalloc([self zone], sizeof(GSIArray_t));
|
|
GSIArrayInitWithZoneAndCapacity(_array, [self zone], count);
|
|
#endif
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
GSIArrayAddItem(_array, GSIArrayItemAtIndex(_other, i));
|
|
}
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL) intersectsIndexesInRange: (NSRange)aRange
|
|
{
|
|
NSUInteger p1;
|
|
NSUInteger p2;
|
|
|
|
if (NSNotFound - aRange.length < aRange.location)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"[%@-%@]: Bad range",
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
}
|
|
if (aRange.length == 0 || _array == 0 || GSIArrayCount(_array) == 0)
|
|
{
|
|
return NO; // Empty
|
|
}
|
|
p1 = posForIndex(_array, aRange.location);
|
|
p2 = posForIndex(_array, NSMaxRange(aRange) - 1);
|
|
if (p1 != p2)
|
|
{
|
|
return YES;
|
|
}
|
|
if (p1 >= GSIArrayCount(_array))
|
|
{
|
|
return NO;
|
|
}
|
|
if (NSLocationInRange(aRange.location, GSIArrayItemAtIndex(_array, p1).ext))
|
|
{
|
|
return YES;
|
|
}
|
|
if (NSLocationInRange(NSMaxRange(aRange)-1,
|
|
GSIArrayItemAtIndex(_array, p1).ext))
|
|
{
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL) isEqual: (id)aSet
|
|
{
|
|
if ([aSet isKindOfClass: [NSIndexSet class]] == YES)
|
|
{
|
|
return [self isEqualToIndexSet: aSet];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL) isEqualToIndexSet: (NSIndexSet*)aSet
|
|
{
|
|
NSUInteger count = _other ? GSIArrayCount(_other) : 0;
|
|
|
|
if (count != (_array ? GSIArrayCount(_array) : 0))
|
|
{
|
|
return NO;
|
|
}
|
|
if (count > 0)
|
|
{
|
|
NSUInteger i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
NSRange rself = GSIArrayItemAtIndex(_array, i).ext;
|
|
NSRange rother = GSIArrayItemAtIndex(_other, i).ext;
|
|
|
|
if (NSEqualRanges(rself, rother) == NO)
|
|
{
|
|
return NO;
|
|
}
|
|
}
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
- (NSUInteger) lastIndex
|
|
{
|
|
if (_array == 0 || GSIArrayCount(_array) == 0)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
return NSMaxRange(GSIArrayItemAtIndex(_array, GSIArrayCount(_array)-1).ext)-1;
|
|
}
|
|
|
|
- (id) mutableCopyWithZone: (NSZone*)aZone
|
|
{
|
|
NSMutableIndexSet *c = [NSMutableIndexSet allocWithZone: aZone];
|
|
|
|
return [c initWithIndexSet: self];
|
|
}
|
|
|
|
|
|
- (void)enumerateIndexesInRange: (NSRange)range
|
|
options: (NSEnumerationOptions)opts
|
|
usingBlock: (GSIndexSetEnumerationBlock)aBlock
|
|
{
|
|
NSUInteger lastInRange = (NSMaxRange(range) - 1);
|
|
NSUInteger startArrayIndex = posForIndex(_array, range.location);
|
|
NSUInteger endArrayIndex = MIN(posForIndex(_array, lastInRange), (GSIArrayCount(_array) - 1));
|
|
NSUInteger i;
|
|
NSUInteger c;
|
|
BOOL isReverse = opts & NSEnumerationReverse;
|
|
BLOCK_SCOPE BOOL shouldStop = NO;
|
|
|
|
if ((0 == [self count]) || (NSNotFound == range.location))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (NSNotFound == startArrayIndex)
|
|
{
|
|
startArrayIndex = 0;
|
|
}
|
|
|
|
if (NSNotFound == endArrayIndex)
|
|
{
|
|
endArrayIndex = GSIArrayCount(_array) - 1;
|
|
}
|
|
|
|
if (isReverse)
|
|
{
|
|
i = endArrayIndex;
|
|
c = startArrayIndex;
|
|
}
|
|
else
|
|
{
|
|
i = startArrayIndex;
|
|
c = endArrayIndex;
|
|
}
|
|
|
|
GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
|
while (isReverse ? i >= c : i <= c)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, i).ext;
|
|
NSUInteger innerI;
|
|
NSUInteger innerC;
|
|
if (isReverse)
|
|
{
|
|
innerI = NSMaxRange(r) - 1;
|
|
innerC = r.location;
|
|
}
|
|
else
|
|
{
|
|
innerI = r.location;
|
|
innerC = NSMaxRange(r) - 1;
|
|
}
|
|
while (isReverse ? innerI >= innerC : innerI <= innerC)
|
|
{
|
|
if ((innerI <= lastInRange) && (innerI >= range.location))
|
|
{
|
|
GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (shouldStop) {return;}, return;, aBlock, innerI, &shouldStop);
|
|
}
|
|
if (shouldStop)
|
|
{
|
|
break;
|
|
}
|
|
if (isReverse)
|
|
{
|
|
if (0 == innerI)
|
|
{
|
|
break;
|
|
}
|
|
innerI--;
|
|
}
|
|
else
|
|
{
|
|
innerI++;
|
|
}
|
|
}
|
|
|
|
if (shouldStop)
|
|
{
|
|
break;
|
|
}
|
|
if (isReverse)
|
|
{
|
|
if (0 == i)
|
|
{
|
|
break;
|
|
}
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
|
|
|
|
}
|
|
|
|
- (void)enumerateIndexesWithOptions: (NSEnumerationOptions)opts
|
|
usingBlock: (GSIndexSetEnumerationBlock)aBlock
|
|
{
|
|
NSUInteger firstIndex = [self firstIndex];
|
|
NSUInteger lastIndex = [self lastIndex];
|
|
if (NSNotFound == firstIndex)
|
|
{
|
|
return;
|
|
}
|
|
[self enumerateIndexesInRange: NSMakeRange(firstIndex, ((lastIndex - firstIndex) + 1))
|
|
options: opts
|
|
usingBlock: aBlock];
|
|
}
|
|
|
|
- (void)enumerateIndexesUsingBlock: (GSIndexSetEnumerationBlock)aBlock
|
|
{
|
|
[self enumerateIndexesWithOptions: 0
|
|
usingBlock: aBlock];
|
|
}
|
|
@end
|
|
|
|
|
|
@implementation NSMutableIndexSet
|
|
|
|
#undef _other
|
|
#define _other ((GSIArray)(((NSMutableIndexSet*)aSet)->_data))
|
|
|
|
- (void) addIndex: (NSUInteger)anIndex
|
|
{
|
|
[self addIndexesInRange: NSMakeRange(anIndex, 1)];
|
|
}
|
|
|
|
- (void) addIndexes: (NSIndexSet*)aSet
|
|
{
|
|
NSUInteger count = _other ? GSIArrayCount(_other) : 0;
|
|
|
|
if (count > 0)
|
|
{
|
|
NSUInteger i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_other, i).ext;
|
|
|
|
[self addIndexesInRange: r];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) addIndexesInRange: (NSRange)aRange
|
|
{
|
|
NSUInteger pos;
|
|
|
|
if (NSNotFound - aRange.length < aRange.location)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"[%@-%@]: Bad range",
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
}
|
|
if (aRange.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
if (_array == 0)
|
|
{
|
|
#if GS_WITH_GC
|
|
_data = (GSIArray)NSAllocateCollectable(sizeof(GSIArray_t),
|
|
NSScannedOption);
|
|
GSIArrayInitWithZoneAndCapacity(_array, 0, 1);
|
|
#else
|
|
_data = (GSIArray)NSZoneMalloc([self zone], sizeof(GSIArray_t));
|
|
GSIArrayInitWithZoneAndCapacity(_array, [self zone], 1);
|
|
#endif
|
|
}
|
|
|
|
pos = posForIndex(_array, aRange.location);
|
|
if (pos >= GSIArrayCount(_array))
|
|
{
|
|
/*
|
|
* The start of the range to add lies beyond the existing
|
|
* ranges, so we can simply append it.
|
|
*/
|
|
GSIArrayAddItem(_array, (GSIArrayItem)aRange);
|
|
}
|
|
else
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
|
|
if (NSLocationInRange(aRange.location, r))
|
|
{
|
|
pos++;
|
|
}
|
|
GSIArrayInsertItem(_array, (GSIArrayItem)aRange, pos);
|
|
}
|
|
|
|
/*
|
|
* Combine with the preceding ranges if possible.
|
|
*/
|
|
while (pos > 0)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, pos-1).ext;
|
|
|
|
if (NSMaxRange(r) < aRange.location)
|
|
{
|
|
break;
|
|
}
|
|
if (NSMaxRange(r) >= NSMaxRange(aRange))
|
|
{
|
|
GSIArrayRemoveItemAtIndex(_array, pos--);
|
|
}
|
|
else
|
|
{
|
|
r.length += (NSMaxRange(aRange) - NSMaxRange(r));
|
|
GSIArrayRemoveItemAtIndex(_array, pos--);
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Combine with any following ranges where possible.
|
|
*/
|
|
while (pos + 1 < GSIArrayCount(_array))
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, pos+1).ext;
|
|
|
|
if (NSMaxRange(aRange) < r.location)
|
|
{
|
|
break;
|
|
}
|
|
GSIArrayRemoveItemAtIndex(_array, pos + 1);
|
|
if (NSMaxRange(r) > NSMaxRange(aRange))
|
|
{
|
|
int offset = NSMaxRange(r) - NSMaxRange(aRange);
|
|
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
r.length += offset;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos);
|
|
}
|
|
}
|
|
SANITY();
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone*)aZone
|
|
{
|
|
NSIndexSet *c = [NSIndexSet allocWithZone: aZone];
|
|
|
|
return [c initWithIndexSet: self];
|
|
}
|
|
|
|
- (void) removeAllIndexes
|
|
{
|
|
if (_array != 0)
|
|
{
|
|
GSIArrayRemoveAllItems(_array);
|
|
}
|
|
}
|
|
|
|
- (void) removeIndex: (NSUInteger)anIndex
|
|
{
|
|
[self removeIndexesInRange: NSMakeRange(anIndex, 1)];
|
|
}
|
|
|
|
- (void) removeIndexes: (NSIndexSet*)aSet
|
|
{
|
|
NSUInteger count = _other ? GSIArrayCount(_other) : 0;
|
|
|
|
if (count > 0)
|
|
{
|
|
NSUInteger i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_other, i).ext;
|
|
|
|
[self removeIndexesInRange: r];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) removeIndexesInRange: (NSRange)aRange
|
|
{
|
|
NSUInteger pos;
|
|
NSRange r;
|
|
|
|
if (NSNotFound - aRange.length < aRange.location)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"[%@-%@]: Bad range",
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
}
|
|
if (aRange.length == 0 || _array == 0
|
|
|| (pos = posForIndex(_array, aRange.location)) >= GSIArrayCount(_array))
|
|
{
|
|
return; // Already empty
|
|
}
|
|
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
if (r.location <= aRange.location)
|
|
{
|
|
if (r.location == aRange.location)
|
|
{
|
|
if (NSMaxRange(r) <= NSMaxRange(aRange))
|
|
{
|
|
/*
|
|
* Found range is entirely within range to remove,
|
|
* leaving next range to check at current position.
|
|
*/
|
|
GSIArrayRemoveItemAtIndex(_array, pos);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Range to remove is entirely within found range and
|
|
* overlaps the start of the found range ... shrink it
|
|
* and then we are finished.
|
|
*/
|
|
r.location += aRange.length;
|
|
r.length -= aRange.length;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos);
|
|
pos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (NSMaxRange(r) <= NSMaxRange(aRange))
|
|
{
|
|
/*
|
|
* Range to remove overlaps the end of the found range.
|
|
* May also overlap next range ... so shorten found
|
|
* range and move on.
|
|
*/
|
|
r.length = aRange.location - r.location;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos);
|
|
pos++;
|
|
}
|
|
else
|
|
{
|
|
NSRange next = r;
|
|
|
|
/*
|
|
* Range to remove is entirely within found range and
|
|
* overlaps the middle of the found range ... split it.
|
|
* Then we are finished.
|
|
*/
|
|
next.location = NSMaxRange(aRange);
|
|
next.length = NSMaxRange(r) - next.location;
|
|
r.length = aRange.location - r.location;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos);
|
|
pos++;
|
|
GSIArrayInsertItem(_array, (GSIArrayItem)next, pos);
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* At this point we are guaranteed that, if there is a range at pos,
|
|
* it does not start before aRange.location
|
|
*/
|
|
while (pos < GSIArrayCount(_array))
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
|
|
if (NSMaxRange(r) <= NSMaxRange(aRange))
|
|
{
|
|
/*
|
|
* Found range is entirely within range to remove ...
|
|
* delete it.
|
|
*/
|
|
GSIArrayRemoveItemAtIndex(_array, pos);
|
|
}
|
|
else
|
|
{
|
|
if (r.location < NSMaxRange(aRange))
|
|
{
|
|
/*
|
|
* Range to remove overlaps start of found range ...
|
|
* shorten it.
|
|
*/
|
|
r.length = NSMaxRange(r) - NSMaxRange(aRange);
|
|
r.location = NSMaxRange(aRange);
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos);
|
|
}
|
|
/*
|
|
* Found range extends beyond range to remove ... finished.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
SANITY();
|
|
}
|
|
|
|
- (void) shiftIndexesStartingAtIndex: (NSUInteger)anIndex by: (NSInteger)amount
|
|
{
|
|
if (amount != 0 && _array != 0 && GSIArrayCount(_array) > 0)
|
|
{
|
|
NSUInteger c;
|
|
NSUInteger pos;
|
|
|
|
if (amount > 0)
|
|
{
|
|
c = GSIArrayCount(_array);
|
|
pos = posForIndex(_array, anIndex);
|
|
|
|
if (pos < c)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
|
|
/*
|
|
* If anIndex is within an existing range, we split
|
|
* that range so we have one starting at anIndex.
|
|
*/
|
|
if (r.location < anIndex)
|
|
{
|
|
NSRange t;
|
|
|
|
/*
|
|
* Split the range.
|
|
*/
|
|
t = NSMakeRange(r.location, anIndex - r.location);
|
|
GSIArrayInsertItem(_array, (GSIArrayItem)t, pos);
|
|
c++;
|
|
r.length = NSMaxRange(r) - anIndex;
|
|
r.location = anIndex;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, ++pos);
|
|
}
|
|
|
|
/*
|
|
* Shift all higher ranges to the right.
|
|
*/
|
|
while (c > pos)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, --c).ext;
|
|
|
|
if (NSNotFound - amount <= r.location)
|
|
{
|
|
GSIArrayRemoveItemAtIndex(_array, c);
|
|
}
|
|
else if (NSNotFound - amount < NSMaxRange(r))
|
|
{
|
|
r.location += amount;
|
|
r.length = NSNotFound - r.location;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, c);
|
|
}
|
|
else
|
|
{
|
|
r.location += amount;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
amount = -amount;
|
|
|
|
/*
|
|
* Delete range which will be overwritten.
|
|
*/
|
|
if (amount >= anIndex)
|
|
{
|
|
[self removeIndexesInRange: NSMakeRange(0, anIndex)];
|
|
}
|
|
else
|
|
{
|
|
[self removeIndexesInRange:
|
|
NSMakeRange(anIndex - amount, amount)];
|
|
}
|
|
pos = posForIndex(_array, anIndex);
|
|
|
|
/*
|
|
* Now shift everything left into the hole we made.
|
|
*/
|
|
c = GSIArrayCount(_array);
|
|
while (c > pos)
|
|
{
|
|
NSRange r = GSIArrayItemAtIndex(_array, --c).ext;
|
|
|
|
if (NSMaxRange(r) <= amount)
|
|
{
|
|
GSIArrayRemoveItemAtIndex(_array, c);
|
|
}
|
|
else if (r.location <= amount)
|
|
{
|
|
r.length += (r.location - amount);
|
|
r.location = 0;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, c);
|
|
}
|
|
else
|
|
{
|
|
r.location -= amount;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, c);
|
|
}
|
|
}
|
|
if (pos > 0)
|
|
{
|
|
c = GSIArrayCount(_array);
|
|
if (pos < c)
|
|
{
|
|
NSRange r0 = GSIArrayItemAtIndex(_array, pos - 1).ext;
|
|
NSRange r1 = GSIArrayItemAtIndex(_array, pos).ext;
|
|
|
|
if (NSMaxRange(r0) == r1.location)
|
|
{
|
|
r0.length += r1.length;
|
|
GSIArraySetItemAtIndex(_array, (GSIArrayItem)r0, pos - 1);
|
|
GSIArrayRemoveItemAtIndex(_array, pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SANITY();
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation NSIndexSet (NSCharacterSet)
|
|
/* Extra method to let NSCharacterSet play with index sets more efficiently.
|
|
*/
|
|
- (NSUInteger) _gapGreaterThanIndex: (NSUInteger)anIndex
|
|
{
|
|
NSUInteger pos;
|
|
NSRange r;
|
|
|
|
if (anIndex++ == NSNotFound)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
if (_array == 0 || GSIArrayCount(_array) == 0)
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
|
|
if ((pos = posForIndex(_array, anIndex)) >= GSIArrayCount(_array))
|
|
{
|
|
r = GSIArrayItemAtIndex(_array, pos-1).ext;
|
|
if (anIndex > NSMaxRange(r))
|
|
{
|
|
return NSNotFound;
|
|
}
|
|
return anIndex; // anIndex is the gap after the last index.
|
|
}
|
|
r = GSIArrayItemAtIndex(_array, pos).ext;
|
|
if (r.location > anIndex)
|
|
{
|
|
return anIndex; // anIndex is in a gap between index ranges.
|
|
}
|
|
return NSMaxRange(r); // Return start of gap after the index range.
|
|
}
|
|
|
|
@end
|
|
|
|
/* A subclass used to access a pre-generated table of information on the
|
|
* stack or in other non-heap allocated memory.
|
|
*/
|
|
@interface _GSStaticIndexSet : NSIndexSet
|
|
@end
|
|
|
|
@implementation _GSStaticIndexSet
|
|
- (void) dealloc
|
|
{
|
|
if (_array != 0)
|
|
{
|
|
/* Free the array without freeing its static content.
|
|
*/
|
|
NSZoneFree([self zone], _array);
|
|
_data = 0;
|
|
}
|
|
[super dealloc];
|
|
}
|
|
|
|
- (id) _initWithBytes: (const void*)bytes length: (NSUInteger)length
|
|
{
|
|
NSAssert(length % sizeof(GSIArrayItem) == 0, NSInvalidArgumentException);
|
|
NSAssert(length % __alignof__(GSIArrayItem) == 0, NSInvalidArgumentException);
|
|
length /= sizeof(NSRange);
|
|
_data = NSZoneMalloc([self zone], sizeof(GSIArray_t));
|
|
_array->ptr = (GSIArrayItem*)bytes;
|
|
_array->count = length;
|
|
_array->cap = length;
|
|
_array->old = length;
|
|
_array->zone = 0;
|
|
return self;
|
|
}
|
|
|
|
- (id) init
|
|
{
|
|
return [self _initWithBytes: 0 length: 0];
|
|
}
|
|
@end
|
|
|