SameBoy/HexFiend/HFSharedMemoryByteSlice.m

210 lines
9.6 KiB
Objective-C

//
// HFSharedMemoryByteSlice.m
// HexFiend_2
//
// Copyright 2008 ridiculous_fish. All rights reserved.
//
#import <HexFiend/HFByteSlice_Private.h>
#import <HexFiend/HFSharedMemoryByteSlice.h>
#define MAX_FAST_PATH_SIZE (1 << 13)
#define MAX_TAIL_LENGTH (sizeof ((HFSharedMemoryByteSlice *)NULL)->inlineTail / sizeof *((HFSharedMemoryByteSlice *)NULL)->inlineTail)
@implementation HFSharedMemoryByteSlice
- (instancetype)initWithUnsharedData:(NSData *)unsharedData {
self = [super init];
REQUIRE_NOT_NULL(unsharedData);
NSUInteger dataLength = [unsharedData length];
NSUInteger inlineAmount = MIN(dataLength, MAX_TAIL_LENGTH);
NSUInteger sharedAmount = dataLength - inlineAmount;
HFASSERT(inlineAmount <= UCHAR_MAX);
inlineTailLength = (unsigned char)inlineAmount;
length = sharedAmount;
if (inlineAmount > 0) {
[unsharedData getBytes:inlineTail range:NSMakeRange(dataLength - inlineAmount, inlineAmount)];
}
if (sharedAmount > 0) {
data = [[NSMutableData alloc] initWithBytes:[unsharedData bytes] length:sharedAmount];
}
return self;
}
// retains, does not copy
- (instancetype)initWithData:(NSMutableData *)dat {
REQUIRE_NOT_NULL(dat);
return [self initWithData:dat offset:0 length:[dat length]];
}
- (instancetype)initWithData:(NSMutableData *)dat offset:(NSUInteger)off length:(NSUInteger)len {
self = [super init];
REQUIRE_NOT_NULL(dat);
HFASSERT(off + len >= off); //check for overflow
HFASSERT(off + len <= [dat length]);
offset = off;
length = len;
data = [dat retain];
return self;
}
- (instancetype)initWithSharedData:(NSMutableData *)dat offset:(NSUInteger)off length:(NSUInteger)len tail:(const void *)tail tailLength:(NSUInteger)tailLen {
self = [super init];
if (off || len) REQUIRE_NOT_NULL(dat);
if (tailLen) REQUIRE_NOT_NULL(tail);
HFASSERT(tailLen <= MAX_TAIL_LENGTH);
HFASSERT(off + len >= off);
HFASSERT(off + len <= [dat length]);
offset = off;
length = len;
data = [dat retain];
HFASSERT(tailLen <= UCHAR_MAX);
inlineTailLength = (unsigned char)tailLen;
memcpy(inlineTail, tail, tailLen);
HFASSERT([self length] == tailLen + len);
return self;
}
- (void)dealloc {
[data release];
[super dealloc];
}
- (unsigned long long)length {
return length + inlineTailLength;
}
- (void)copyBytes:(unsigned char *)dst range:(HFRange)lrange {
HFASSERT(HFSum(length, inlineTailLength) >= HFMaxRange(lrange));
NSRange requestedRange = NSMakeRange(ll2l(lrange.location), ll2l(lrange.length));
NSRange dataRange = NSMakeRange(0, length);
NSRange tailRange = NSMakeRange(length, inlineTailLength);
NSRange dataRangeToCopy = NSIntersectionRange(requestedRange, dataRange);
NSRange tailRangeToCopy = NSIntersectionRange(requestedRange, tailRange);
HFASSERT(HFSum(dataRangeToCopy.length, tailRangeToCopy.length) == lrange.length);
if (dataRangeToCopy.length > 0) {
HFASSERT(HFSum(NSMaxRange(dataRangeToCopy), offset) <= [data length]);
const void *bytes = [data bytes];
memcpy(dst, bytes + dataRangeToCopy.location + offset, dataRangeToCopy.length);
}
if (tailRangeToCopy.length > 0) {
HFASSERT(tailRangeToCopy.location >= length);
HFASSERT(NSMaxRange(tailRangeToCopy) - length <= inlineTailLength);
memcpy(dst + dataRangeToCopy.length, inlineTail + tailRangeToCopy.location - length, tailRangeToCopy.length);
}
}
- (HFByteSlice *)subsliceWithRange:(HFRange)lrange {
if (HFRangeEqualsRange(lrange, HFRangeMake(0, HFSum(length, inlineTailLength)))) return [[self retain] autorelease];
HFByteSlice *result;
HFASSERT(lrange.length > 0);
HFASSERT(HFSum(length, inlineTailLength) >= HFMaxRange(lrange));
NSRange requestedRange = NSMakeRange(ll2l(lrange.location), ll2l(lrange.length));
NSRange dataRange = NSMakeRange(0, length);
NSRange tailRange = NSMakeRange(length, inlineTailLength);
NSRange dataRangeToCopy = NSIntersectionRange(requestedRange, dataRange);
NSRange tailRangeToCopy = NSIntersectionRange(requestedRange, tailRange);
HFASSERT(HFSum(dataRangeToCopy.length, tailRangeToCopy.length) == lrange.length);
NSMutableData *resultData = NULL;
NSUInteger resultOffset = 0;
NSUInteger resultLength = 0;
const unsigned char *tail = NULL;
NSUInteger tailLength = 0;
if (dataRangeToCopy.length > 0) {
resultData = data;
HFASSERT(resultData != NULL);
resultOffset = offset + dataRangeToCopy.location;
resultLength = dataRangeToCopy.length;
HFASSERT(HFSum(resultOffset, resultLength) <= [data length]);
}
if (tailRangeToCopy.length > 0) {
tail = inlineTail + tailRangeToCopy.location - length;
tailLength = tailRangeToCopy.length;
HFASSERT(tail >= inlineTail && tail + tailLength <= inlineTail + inlineTailLength);
}
HFASSERT(resultLength + tailLength == lrange.length);
result = [[[[self class] alloc] initWithSharedData:resultData offset:resultOffset length:resultLength tail:tail tailLength:tailLength] autorelease];
HFASSERT([result length] == lrange.length);
return result;
}
- (HFByteSlice *)byteSliceByAppendingSlice:(HFByteSlice *)slice {
REQUIRE_NOT_NULL(slice);
const unsigned long long sliceLength = [slice length];
if (sliceLength == 0) return self;
const unsigned long long thisLength = [self length];
HFASSERT(inlineTailLength <= MAX_TAIL_LENGTH);
NSUInteger spaceRemainingInTail = MAX_TAIL_LENGTH - inlineTailLength;
if (sliceLength <= spaceRemainingInTail) {
/* We can do our work entirely within the tail */
NSUInteger newTailLength = (NSUInteger)sliceLength + inlineTailLength;
unsigned char newTail[MAX_TAIL_LENGTH];
memcpy(newTail, inlineTail, inlineTailLength);
[slice copyBytes:newTail + inlineTailLength range:HFRangeMake(0, sliceLength)];
HFByteSlice *result = [[[[self class] alloc] initWithSharedData:data offset:offset length:length tail:newTail tailLength:newTailLength] autorelease];
HFASSERT([result length] == HFSum(sliceLength, thisLength));
return result;
}
else {
/* We can't do our work entirely in the tail; see if we can append some shared data. */
HFASSERT(offset + length >= offset);
if (offset + length == [data length]) {
/* We can append some shared data. But impose some reasonable limit on how big our slice can get; this is 16 MB */
if (HFSum(thisLength, sliceLength) < (1ULL << 24)) {
NSUInteger newDataOffset = offset;
NSUInteger newDataLength = length;
unsigned char newDataTail[MAX_TAIL_LENGTH];
unsigned char newDataTailLength = MAX_TAIL_LENGTH;
NSMutableData *newData = (data ? data : [[[NSMutableData alloc] init] autorelease]);
NSUInteger sliceLengthInt = ll2l(sliceLength);
NSUInteger newTotalTailLength = sliceLengthInt + inlineTailLength;
HFASSERT(newTotalTailLength >= MAX_TAIL_LENGTH);
NSUInteger amountToShiftIntoSharedData = newTotalTailLength - MAX_TAIL_LENGTH;
NSUInteger amountToShiftIntoSharedDataFromTail = MIN(amountToShiftIntoSharedData, inlineTailLength);
NSUInteger amountToShiftIntoSharedDataFromNewSlice = amountToShiftIntoSharedData - amountToShiftIntoSharedDataFromTail;
if (amountToShiftIntoSharedDataFromTail > 0) {
HFASSERT(amountToShiftIntoSharedDataFromTail <= inlineTailLength);
[newData appendBytes:inlineTail length:amountToShiftIntoSharedDataFromTail];
newDataLength += amountToShiftIntoSharedDataFromTail;
}
if (amountToShiftIntoSharedDataFromNewSlice > 0) {
HFASSERT(amountToShiftIntoSharedDataFromNewSlice <= [slice length]);
NSUInteger dataLength = offset + length + amountToShiftIntoSharedDataFromTail;
HFASSERT([newData length] == dataLength);
[newData setLength:dataLength + amountToShiftIntoSharedDataFromNewSlice];
[slice copyBytes:[newData mutableBytes] + dataLength range:HFRangeMake(0, amountToShiftIntoSharedDataFromNewSlice)];
newDataLength += amountToShiftIntoSharedDataFromNewSlice;
}
/* We've updated our data; now figure out the tail */
NSUInteger amountOfTailFromNewSlice = sliceLengthInt - amountToShiftIntoSharedDataFromNewSlice;
HFASSERT(amountOfTailFromNewSlice <= MAX_TAIL_LENGTH);
[slice copyBytes:newDataTail + MAX_TAIL_LENGTH - amountOfTailFromNewSlice range:HFRangeMake(sliceLengthInt - amountOfTailFromNewSlice, amountOfTailFromNewSlice)];
/* Copy the rest, if any, from the end of self */
NSUInteger amountOfTailFromSelf = MAX_TAIL_LENGTH - amountOfTailFromNewSlice;
HFASSERT(amountOfTailFromSelf <= inlineTailLength);
if (amountOfTailFromSelf > 0) {
memcpy(newDataTail, inlineTail + inlineTailLength - amountOfTailFromSelf, amountOfTailFromSelf);
}
HFByteSlice *result = [[[[self class] alloc] initWithSharedData:newData offset:newDataOffset length:newDataLength tail:newDataTail tailLength:newDataTailLength] autorelease];
HFASSERT([result length] == HFSum([slice length], [self length]));
return result;
}
}
}
return nil;
}
@end