Extracting binary data out of an NSData object - objective-c

I have an NSData object with a chunk of binary data and I'm trying to use getBytes:range: to set a primitive.
I can do this:
NSData* data = [...get record from file...]
double value = 0;
[data getBytes:&value range:NSMakeRange(42, 8)];
[someObject setValue:value];
I would rather not have to use a temporary variable as I have many fields in this file I need to read.
Is there an easy way (objc or c) to extract bytes out of an NSData object, cast these bytes to some primitive value, and assign it to an object without using some temporary?
Thanks!

It's not the prettiest, but you can get the raw data pointer used by NSData and dereference it in the proper location.
UInt8 *rawData = [theData bytes];
[someObject setValue:*((double*)(&rawData[42]))];
This involves lots of casting, but you only need to use one temporary variable instead of one for each data type, and it will be slightly faster as you don't repeatedly ask the NSData object for a specific range of data.

Related

Accessing NSData bytes - Not the same value on debugger

I'm facing a problem that I don't understand. Before beginning to explain it, even if I have worked on a Swift project this year that was using some Objective-C, I am new to this language and its concepts.
So, there is my problem : I want to access the bytes of an NSData object. I know there several ways to do so :
[data bytes];
data.bytes;
[data getBytes: dest, length: [data length]];
But each method doesn't return the same value as the console, when I'm using po [data bytes].
Can you explain me why this happens ? I don't really understand what I'm missing.
Thanks.
data and data.bytes are of two totally different types. data is an instance of NSData, while data.bytes is a raw pointer (const void *). When you call po in the debugger (short for "print object"), it will call -description on things which inherit from NSObject, or just print the value if they do not.
In this case, since data is an NSData (which has -description), if you po data, it calls [data description] and prints the result of that out; since NSData knows how to nicely format its contents, it will print nicely.
However, since data.bytes is a void *, there is no way for the debugger to know how to print it (void * can point to anything; how to interpret it is totally up to you) so it just prints out the pointer itself.
If you want to print the data from the debugger directly, you can tell it how to interpret the pointer and print it out. If you know that the data blob is n bytes long, you can run the following command:
p/x *(uint8_t (*)[<n>])data.bytes
where <n> is replaced with the literal length of the data (e.g. uint8_t (*)[8])). *(uint8_t (*)[<n>])data.bytes tells the debugger to reinterpret data.bytes as an array of n bytes (giving it the length so it knows how much data to read from memory) while p/x tells it to print the hex values of the bytes it finds.

NSString value as NSData output

I have a NSString which is #"15".
I want my NSData to be 15 also. I know how to convert it to get the value 31 35 but I would like my NSData to be 15 if I use NSLog on it. I'm not asking for a conversion but more for a translation. I don't wanna change the NSLog print but the NSData value. Is there anyway to do it ?
Parse the string to an integer (lets assume a signed 32-bit integer):
NSString *str = #"15";
int32_t i = (int32_t)[str intValue];
To encode it in native endian:
NSData *data = [NSData dataWithBytes:&i length:sizeof(i)];
Note: if you intend to transmit that data to another computer then you need to decide on a common endianness of primitive types. Big endian is traditionally used and facilitated with functions like htonl(), ntohl(), etc. If the computers are all the same platform then you can use the native endianness, for a slight performance boost and code simplification.
You need to convert the string to a byte first (by parsing it). Then you can build the NSData from the byte.

Convert NSValue to NSData and back again, with the correct type

I would like to be able to convert an objective-c object, such as an NSArray or UIImage into an NSData object, which I can then use to write to disk. I first converted them to an NSValue, which I then planned on converting to NSData. This question provided part of the answer, but as they were working with NSNumber, they didn't really have a need to convert it back to NSValue.
I have seen other questions such as this one that relies on NSKeyedArchiver, but I would like to steer away from this due to the vast size inflation that occurs.
Therefore my code at the moment for encoding an NSData object from an NSValue, from the first question, is as follows:
+(NSData*) dataWithValue:(NSValue*)value {
NSUInteger size;
const char* encoding = [value objCType];
NSGetSizeAndAlignment(encoding, &size, NULL);
void* ptr = malloc(size);
[value getValue:ptr];
NSData* data = [NSData dataWithBytes:ptr length:size];
free(ptr);
return data;
}
My question is how would I go about decoding an NSData object that has been encoded in this manner and get back the original objCType from the NSValue?
I would assume I would be using something along these lines
[NSValue valueWithBytes:[data bytes] objCType:typeHere];
How would I get back the type information?
Use NSKeyedArchiver to save the items and NSKeyedUnarchiver to restore the object. The object must conform to NSCoding and in the case of a collection all contained items must also conform to NSCoding.
See the Apple documentation of NSKeyedArchiver and NSCoder
Your approach will only work for primitive types (int, float, structs without pointers, ...) inside your NSValue. Otherwise you will only get the meaningless pointer value but not the actual data in your NSData object.
To also pass the actual type string along you would have to figure out a way to get this inside your NSData object as well. Not impossible, but it will not solve your actual problem.
Using a keyed archiver as zaph suggests is much better.

Difference between NSMutableData's mutableBytes and bytes methods

Both return the same pointer. I know - bytes belongs to NSData, why does NSMutableData introduce - mutableBytes? Is it just for code clarity so it is more obvious you are accessing mutable data? Does it really matter which one is used?
NSMutableData* mydata = [[NSMutableData alloc] init];
[mydata appendData: [#"hello" dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"%p", [mydata mutableBytes]);
NSLog(#"%p", [mydata bytes]);
Thanks.
There are a couple of reasons why NSMutableData might provide a separate mutableBytes method:
As you suggested in your question, using mutableBytes makes it clear to the reader that you want to change the data.
The bytes method returns a const void *. The mutableBytes method returns a void *. If you want to change the bytes, you need a void * with no const qualifier. The mutableBytes method eliminates the need to cast away the const qualifier.
In theory there could be a third reason: the -[NSData mutableCopy] method could return an NSMutableData that points to the same buffer as the original NSData, and only create a new, mutable copy of the buffer when you call mutableBytes. However, I don't think it's implemented this way based on my very limited testing.
One addition to the rob's answer and his comment:
#Dabbu NSData and NSMutableData store their contents as one contiguous
array of bytes.
The thing to keep in mind here is that this behavior was changed in iOS7: now NSData/NSMutableData are not guaranteed to keep contents as one contiguous array. It could be stored as multiple chunks.
So when you call bytes/mutableBytes, they will copy and flatten contents into one contiguous array of bytes, if needed, and then return a pointer to this contiguous chunk.
Depending of what you're trying to do, it may cause an unexpected performance penalty or excessive memory consumption for large buffers.

Objective-C Packing Data using NSMutableData?

I need to put a short and integer at the begging of a message that i am sending to a java server. The server is expecting to read a short (message id) then an integer (message length). I've read in the stackoverflow that NSMutableData is similar to java ByteBuffer.
I am trying to pack the message into NSMutableData then send it.
So this is what I have but is not working !.
NSMutableData *data = [NSMutableData dataWithLength:(sizeof(short) + sizeof(int))];
short msg_id = 2;
int length = 198;
[data appendBytes:&msg_id length:sizeof(short)];
[data appendBytes:&length length:sizeof(int)];
send(sock, data, 6, 0);
The server is using Java ByteBuffer to read in the received data. So the bytes coming in is:
32,120,31,0,2,0
which is invalid.
The correct value so the ByteBuffer can read them as .getShort() and .getInt()
0,2,0,0,0,-66
You're basically putting stuff into the NSData object correctly, but you're not using it with the send function correctly. First off, as dreamlax suggests, use NSMutableData's -initWithCapacity initializer to get a capacity, not zeroed bytes.
Your data pointer is a pointer to an Objective-C (NSData) object, not a the actual raw byte buffer. The send function is a classic UNIX-y C function, and doesn't know anything about Objective-C objects. It expects a pointer to the actual bytes:
send(sock, [data bytes], [data length], 0);
Also, FWIW, note that endianness matters here if you're expecting to recover the multibyte fields on the server. Consider using HTONL and HTONS on the short and int values before putting them in the NSData buffer, assuming the server expects "network" byte order for its packet format (though maybe you control that).
I think your use of dataWithLength: will give you an NSMutableData object with 6 bytes all initialised to 0, but then you append 6 more bytes with actual values (so you'll end up with 12 bytes all up). I'm assuming here that short is 2 bytes and int is 4. I believe you want to use dataWithCapacity: to hint how much memory to reserve for your data that you are packing.
As quixoto has pointed out, you need to use the bytes method, which returns a pointer to the first byte of the actual data. The length method will return the number of bytes you have.
Another thing you need to watch out for is endianness. The position of the most significant byte is dependent on the underlying architecture.