New NSData with range of old NSData maintaining bytes

11,030

Solution 1

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.

Solution 2

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

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

Solution 4

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)];
Share:
11,030
umop
Author by

umop

Primarily a Flash and C/C++/Objective-C Developer. Hardcore veteran of PHP, Perl and VB6 probjects.

Updated on June 05, 2022

Comments

  • umop
    umop almost 2 years

    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
    umop over 13 years
    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
    umop over 13 years
    I had to edit my posting. I meant to be using (const void *)bytes instead of getBytes:range.
  • Nick
    Nick over 13 years
    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
  • umop
    umop over 13 years
    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.
  • Nick
    Nick over 13 years
    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.