Index: src/OFData.h ================================================================== --- src/OFData.h +++ src/OFData.h @@ -40,10 +40,11 @@ OFSerialization, OFMessagePackRepresentation> { unsigned char *_items; size_t _count, _itemSize; bool _freeWhenDone; + OFData *_parentData; } /*! * @brief The size of a single item in the OFData in bytes. */ @@ -277,10 +278,18 @@ * @param index The number of the item to return * @return The specified item of the OFData */ - (const void *)itemAtIndex: (size_t)index OF_RETURNS_INNER_POINTER; +/*! + * @brief Returns the data in the specified range as a new OFData. + * + * @param range The range of the data for the new data + * @return The data in the specified range as a new OFData + */ +- (OFData *)subdataWithRange: (of_range_t)range; + /*! * @brief Returns the range of the data. * * @param data The data to search for * @param options Options modifying search behavior.@n Index: src/OFData.m ================================================================== --- src/OFData.m +++ src/OFData.m @@ -382,10 +382,12 @@ - (void)dealloc { if (_freeWhenDone) free(_items); + [_parentData release]; + [super dealloc]; } - (size_t)count { @@ -497,10 +499,27 @@ OF_HASH_FINALIZE(hash); return hash; } + +- (OFData *)subdataWithRange: (of_range_t)range +{ + OFData *ret; + + if (range.length > SIZE_MAX - range.location || + range.location + range.length > _count) + @throw [OFOutOfRangeException exception]; + + ret = [OFData dataWithItemsNoCopy: _items + (range.location * _itemSize) + itemSize: _itemSize + count: range.length + freeWhenDone: false]; + ret->_parentData = [(_parentData != nil ? _parentData : self) copy]; + + return ret; +} - (OFString *)description { OFMutableString *ret = [OFMutableString stringWithString: @"<"]; Index: src/OFMutableData.m ================================================================== --- src/OFMutableData.m +++ src/OFMutableData.m @@ -139,10 +139,21 @@ _capacity = _count; return self; } + +- (OFData *)subdataWithRange: (of_range_t)range +{ + if (range.length > SIZE_MAX - range.location || + range.location + range.length > _count) + @throw [OFOutOfRangeException exception]; + + return [OFData dataWithItems: _items + (range.location * _itemSize) + itemSize: _itemSize + count: range.length]; +} - (void)addItem: (const void *)item { if (SIZE_MAX - _count < 1) @throw [OFOutOfRangeException exception]; Index: tests/OFDataTests.m ================================================================== --- tests/OFDataTests.m +++ tests/OFDataTests.m @@ -148,18 +148,33 @@ count: 1] options: 0 range: of_range(0, 1)]) EXPECT_EXCEPTION( - @"-[rangeOfString:options:range:] failing on out of range", + @"-[rangeOfData:options:range:] failing on out of range", OFOutOfRangeException, [immutable rangeOfData: [OFData dataWithItems: "" itemSize: 2 count: 0] options: 0 range: of_range(8, 1)]) + TEST(@"-[subdataWithRange:]", + [[immutable subdataWithRange: of_range(2, 4)] + isEqual: [OFData dataWithItems: "accdacaa" + itemSize: 2 + count: 4]] && + [[mutable subdataWithRange: of_range(2, 3)] + isEqual: [OFData dataWithItems: "cde" + count: 3]]) + + EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #1", + OFOutOfRangeException, [immutable subdataWithRange: of_range(7, 1)]) + + EXPECT_EXCEPTION(@"-[subdataWithRange:] failing on out of range #2", + OFOutOfRangeException, [mutable subdataWithRange: of_range(6, 1)]) + TEST(@"-[MD5Hash]", [[mutable MD5Hash] isEqual: [@"abcde" MD5Hash]]) TEST(@"-[RIPEMD160Hash]", [[mutable RIPEMD160Hash] isEqual: [@"abcde" RIPEMD160Hash]])