// // HFSharedMemoryByteSlice.m // HexFiend_2 // // Copyright 2008 ridiculous_fish. All rights reserved. // #import #import #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