3

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!

umop
  • 2,122
  • 2
  • 18
  • 22

4 Answers4

5

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
  • 3,162
  • 4
  • 31
  • 47
  • 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 Aug 05 '10 at 13:26
4

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
  • 2,626
  • 1
  • 26
  • 29
  • I had to edit my posting. I meant to be using (const void *)bytes instead of getBytes:range. – umop Aug 05 '10 at 13:28
  • 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 Aug 05 '10 at 18:14
  • 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 Aug 06 '10 at 01:29
  • 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 Aug 06 '10 at 05:04
3

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.

Ivan Marinov
  • 2,737
  • 1
  • 25
  • 17
2

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)];
Joel Fischer
  • 6,521
  • 5
  • 35
  • 46