views:

175

answers:

2

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:

  • NSData *source: < a few bytes I want to discard > + < big chunk of bytes I want to keep >
  • NSData *destination: < big chunk of bytes I want to keep >

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!

+1  A: 

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.

alltom
+1 for solving a (completely unrelated) problem I had. :)
Kalle
I just realized that the getBytes that I used was making a copy. I was thinking that it was one of a few accessors for (const void *)bytes. I have edited my code appropriately. Sorry for the confusion. And regarding your answer, unfortunately no. I'm trying to recycle the data in the object itself for use in another object, then dispose of the referencing object.
umop
A: 

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...

Nick
I had to edit my posting. I meant to be using (const void *)bytes instead of getBytes:range.
umop
to replace the ???? part of your post, you can use smth like this: `memcpy(myNewByteArrayOfNewSize, bytes, [source length] - myStart);` ..... I hope I didn't mess up any of the indices
Nick
But that will make a copy. I'm looking to recycle the data chunk pointed to by bytes rather than copy it. Basically, I need to somehow keep the chunk mallocated for when NSData *source inevitably frees it.
umop
i think you are out of luck in this case :(... afaik, once a block of certain size is malloc'ed, you can't free up only a piece of it and remap the rest as a new block. I'd rethink the way you deal with the content though, since if doing a memcpy of a piece of that data is "too much", you are probably pushing the memory limits anyway, which isn't a good thing.
Nick