How do you convert an int to a byte in Objective-C? - objective-c

I have an int value which needs to be converted into a byte array.
How do you go about doing this in Objective-C? Are there methods to do this?
Thank you,

Converted in what way? Do you want it in little endian, big endian, or native byte order? If you want it in native byte order, then all you need to do is:
int val = //... initialize integer somehow
char* bytes = (char*) &val;
int len = sizeof(int);
That said, the best way to manipulate the bytes of an integer is to do bitwise operations. For example, to get the lowest order byte, you can use val&0xFF, to get the next you use (val>>8)&0xFF, then (val>>16)&0xFF, then (val>>24)&0xFF, etc.
Of course, it depends on the size of your data type. If you do those kinds of things, you really should include <inttypes.h>;, and use uint8_t, uint16_t, uint32_t, or uint64_t, depending on how large an integer you want; otherwise, you cannot reliably play around with larger numbers of bytes.

I suspect that what you want to do is to pass this byte array somewhere possibly to a NSMutableData object. You just pass the address &val
Example:
[myData appendBytes:&myInteger length:sizeof(myInteger)];
This link is more complete and deals with endianness:
Append NSInteger to NSMutableData

Related

Objective-C: How to store typedef enum in a plist?

Plists seem to have a limited number of data types that you can enter in. However, I know there are loopholes, such as storing an int as a Number and then using [x integerValue] to set it back to an int. Is there a similar workaround for storing typedef enums in a property list?
Since they are really just ints, is there some way to store them as Numbers and then casting them as their typedef?
You would have to store the raw enum integer values and then load them out again as integers. Once you've loaded them out again as NSNumbers you can then use [num intValue] to get the integer. Then you can compare it to your enum, for example:
intRep = [loadedNumber intValue];
if (intRep == MY_ENUM_CONSTANT) {
// Do something...
}
// ... etc.
To store the value "MY_ENUM_CONSTANT" as a string inside the plist doesn't have any meaning - and reverse engineering it if -isEqualToString is definitely fighting the system. If you need more flexibility though you may be able to store binary data objects inside the plist, this is probably overdoing it a bit for what you need though.
You will need to translate them into a format suitable for plist, you can use the integer value is the simplest and use NSNumber, but you can alternatively write a couple of functions to convert to and from a string value. If you use the default integers for for enum, 0, 1, 2 etc. You can then use the enum as an index into an array of string, to get from string back to enum/int, use a for loop thru your string array and return the matching index.

NSData bytes from integer

I have an integer, with a value of 2. I append that to an NSMutableData object with:
[data appendBytes:&intVal length:2];
The number 2 is the number of bytes I want from the int. When I log the data, what I want to see is <0002> (one empty byte followed by one non-empty byte), but what I get is <0200>.
Am I missing something? The order and length of the bytes needs to be very specific. This is for a direct socket connection API. I'm not really sure what I'm doing wrong here. Maybe I'm just reading it wrong.
Thanks for the help.
Am I missing something?
Yes, the endianness of your system doesn't match what you need. Convert it to either little or big endian (the POSIX C library has functions for this purpose somewhere in the <netinet.h> or <inet.h> headers).
NSData's description method prints it's values in hexadecimal format. This means that it needs 4 digits to represent 2 bytes, every byte may map 2^8=256 different value, every hexadecimal digit may map 16 possibile values, so 16x16x16x16 = 2^16, which is exactly what you can map with 2 bytes.
Here is the answer, It works great!
uint16_t intVal = 2;
Byte *byteData = (Byte*)malloc(2);
byteData[1] = majorValue & 0xff;
byteData[0] = (majorValue & 0xff00) >> 8;
NSData * result = [NSData dataWithBytes:byteData length:sizeof(uint16_t)];
NSLog(#"result=%#",result);

How to get byte size from objective-c type encoding

Using NSMethodSignature I can get a methods argument types via getArgumentTypeAtIndex:. Which returns a c-string based off of this documentation. So like "i" for int and "I" for unsigned.
Is there a function somewhere that takes in this encoding and returns the types size in bytes?
Something like this:
int paramSize = typeEncodingSize("i");
NSLog(#"%s is %d bytes", "i", paramSize);
//this would be the encoding for a struct that has three fields. An id, a pointer and an int.
paramSize = typeEncodingSize("{example=#*i}"); //two 8 byte pointers & one 4 byte int
NSLog(#"%s is %d bytes", "{example=#*i}", paramSize);
which would output:
i is 4 bytes
{example=#*i} is 20 bytes
I figure there must be an api function for this somewhere since the docs for [NSInvocation setArgument:atIndex:] say
The number of bytes copied is determined by the argument size.
I understand that this is old, but I've hit the same wall.
The solution seems to be method NSGetSizeAndAlignment.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSGetSizeAndAlignment
Have you tried sizeof()? That's the usual way to determine the size of a struct or other type.
Caleb's sizeof() is correct, because basically you can pass anything to #encode only if it's also accepted by sizeof().There's nothing like #decode. You can download class-dump source code and look into CDTypeParser.m file for example how to parse it.

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.

How can I reverse the byte order of an NSInteger or NSUInteger in objective-c

This is a somewhat of a follow up to this posting but with a different question so I felt I should ask in a separate thread.
I am at the point where I have four consecutive bytes in memory that I have read in from a file. I'd like to store these as a bit array (the actual int value of them does not matter until later). When I print out what is in my int, I notice that it seems to be stored in reverse order (little endian).
Does anyone have a good method for reversing the order of the bytes. Then once reversed, picking out consecutive bits spanning two bytes and converting back to an int?
unsigned char data[] = { 0x00, 0x02, 0x45, 0x28 };
NSInteger intData = *((NSInteger *)data);
NSLog(#"data:%08x", intData); // data:28450200
Cocoa (or to be exact the Foundation framework) has functions to swap the endianness of bytes: NSSwapInt, NSSwapShort, NSSwapLong, and NSSwapLongLong. These swap around the bytes no matter what - they make big-endian integers from small-endian integers and vice versa.
If you know which format you have there are other functions that swap it to the native endianness: NSSwapLittleIntToHost and NSSwapBigIntToHost. There are also the reverse functions which swap from the native format to little or big endian format: NSSwapHostIntToLittle and NSSwapHostIntToBig. Those are available for the other integer types and floating point types as well. What they do is they call the primitive swap functions on the values if necessary. So NSSwapLittleIntToHost doesn’t do anything while NSSwapBigIntToHost returns the result of NSSwapInt on a little endian machine.
Note that these take parameters of the compilers integer types and not the NSInteger type. So depending on wether you’re generating 32bit or 64bit code you have to use different functions if you are using NSInteger.
You also should not cast your byte array to an integer pointer and dereference that. It would be better to assemble the integer using bit shift operations. Your code will only work if NSInteger is 32 bit wide. If it is 64 bit then your number will be garbage or your program might even crash. But even if you are using an integer type that is always 32 bit wide (int32_t from the C99 <stdint.h> header for example) this might not work as expected.