umop
umop

Reputation: 2192

New NSData with range of old NSData maintaining bytes

I have a fairly large NSData (or NSMutableData if necessary) object which I want to take a small chunk out of and leave the rest. Since I'm working with large amounts of NSData bytes, I don't want to make a big copy, but instead just truncate the existing bytes. Basically:

There are truncation methods in NSMutableData, but they only truncate the end of it, whereas I want to truncate the beginning. My thoughts are to do this with the methods:

Note that I used the wrong (copying) method in the original posting. I've edited and fixed it

- (const void *)bytes

and

- initWithBytesNoCopy:length:freeWhenDone:

However, I'm trying to figure out how to manage memory with these. I'm guessing the process will be like this (I've placed ????s where I don't know what to do):

// Get bytes
const unsigned char *bytes = (const unsigned char *)[source bytes];

// Offset the start
bytes += myStart;

// Somehow (m)alloc the memory which will be freed up in the following step
?????

// Release the source, now that I've allocated the bytes
[source release];

// Create a new data, recycling the bytes so they don't have to be copied
NSData destination = [[NSData alloc]
                      initWithBytesNoCopy:bytes
                      length:myLength
                      freeWhenDone:YES];

Thanks for the help!

Upvotes: 3

Views: 11489

Answers (4)

Joel Fischer
Joel Fischer

Reputation: 6597

There's also an NSData method -[subdataWithRange:(NSRange)range] that could do the trick. I have no idea what the performance looks like (I'd imagine it does a copy or two, but I don't know for certain). It can be used like:

NSData *destination = [source subdataWithRange:NSMakeRange(0, lengthIWant)];

Upvotes: 2

Ivan Marinov
Ivan Marinov

Reputation: 2786

If you want to avoid copying memory blocks, you can use the dataWithBytesNoCopy to keep the old buffer with a certain offset. In this example we "remove" the first 2 bytes:

source = [NSData dataWithBytesNoCopy:(char*)source.bytes + 2 length:source.length - 2];

For the sake of example simplicity, boundary check is skipped, please add it as it convenient for you. Available in iOS 2.0 and later.

Upvotes: 3

Nick
Nick

Reputation: 2646

depending on the context, the solutions can be different. I will assume that you need a method that would return an autoreleased NSData object with the specified range:

- (NSData *)getSubData:(NSData *)source withRange:(NSRange)range
{
    UInt8 bytes[range.length];
    [source getBytes:&bytes range:range];
    NSData *result = [[NSData alloc] initWithBytes:bytes length:sizeof(bytes)];
    return [result autorelease];
}

Of course, you can make it a class method and put it into some kind of "utils" class or create an extension over NSData...

Upvotes: 4

alltom
alltom

Reputation: 3252

Is this what you want?

NSData *destination = [NSData dataWithBytes:((char *)source.bytes) + myStart
                                     length:myLength];

I know you said "I don't want to make a big copy," but this only does the same copy you were doing with getBytes:length: in your example, so this may be okay to you.

There's also replaceBytesInRange:withBytes:length:, which you might use like this:

[source setLength:myStart + myLength];
[source replaceBytesInRange:NSMakeRange(0, myStart)
                  withBytes:NULL
                     length:0];

But the doc's don't say how that method works (no performance characteristics), and source needs to be an NSMutableData.

Upvotes: 5

Related Questions