char* to NSData issue - objective-c

I am trying to write a function that takes char* as an input parameter and will serialize it into JSON.
I am running into an issue with converting the input parameter, options to NSData. I used the following line of code:
NSData *data = [NSData dataWithBytes:options length:sizeof(options)];
This did not work. A different set of code did work:
NSString* stringFromChar = [[NSString alloc] initWithUTF8String:options];
NSData * data = [stringFromChar dataUsingEncoding:NSUTF8StringEncoding];
I am curious about why it was necessary to convert my code from char* to an NSString and then to NSData and why I could not do that directly. Is there a way to directly convert char* to NSData without this intermediary step? Thanks.

As the comments indicated, sizeof(options) where options is a char * will produce the size of the pointer, not the length of the string. Also pointed out in comments, strlen(options) counts characters up to the first 0x0, which is what you want...
NSData *data = [NSData dataWithBytes:options length:strlen(options)];
// options must be null-terminated

Related

Signed byte array to UIImage

I am trying to display a picture from a byte-array produced by a web service. Printing out a description it looks like this:
("-119",80,78,71,13,10,26,10,0,0,0,13,3 ... )
From the header it is clear that it's a png encoded in signed integers. It is an __NSCFArray having __NSCFNumber elements.
My code in Objective-C (based on much googling):
NSData *data = [NSData dataWithBytes:(const void *)myImageArray length [myImageArray count]];
UIImage *arrayImage = [UIImage imageWithData:data];
I receive a null UIImage pointer.
I also tried to converting it to unsigned NSNumbers first and then passing it to NSData, though perhaps I did not do this correctly. What am I doing wrong?
You cannot simply cast an NSArray of NSNumber into binary data. Both NSArray and NSNumber are objects; they have their own headers and internal structure that is not the same as the original string of bytes. You'll need to convert it byte-by-byte with something along these lines:
NSArray *bytes = #[#1, #2, #3];
NSMutableData *data = [NSMutableData dataWithLength:bytes.count];
for (NSUInteger i = 0; i < bytes.count; i++) {
char value = [bytes[i] charValue];
[data replaceBytesInRange:NSMakeRange(i, 1) withBytes:&value];
}
char is a signed int8_t, which appears to be the kind of data you're working with. It is often used to mean "an ASCII character," but in C it is commonly also used to mean "byte."

How to print unsigned char* in NSLog()

Title pretty much says everything.
would like to print (NOT in decimal), but in unsigned char value (HEX).
example
unsigned char data[6] = {70,AF,80,1A, 01,7E};
NSLog(#"?",data); //need this output : 70 AF 80 1A 01 7E
Any idea? Thanks in advance.
There is no format specifier for an char array. One option would be to create an NSData object from the array and then log the NSData object.
NSData *dataData = [NSData dataWithBytes:data length:sizeof(data)];
NSLog(#"data = %#", dataData);
Nothing in the standard libraries will do it, so you could write a small hex dump function, or you could use something else that prints non-ambigious full data. Something like:
char buf[1 + 3*dataLength];
strvisx(buf, data, dataLength, VIS_WHITE|VIS_HTTPSTYLE);
NSLog(#"data=%s", buf);
For smallish chunks of data you could try to make a NSData and use the debugDescription method. That is currently a hex dump, but nothing promises it will always be one.
To print char* in NSLog try the following:
char data[6] = {'H','E','L','L','0','\n'};
NSString *string = [[NSString alloc] initWithUTF8String:data];
NSLog(#"%#", string);
You will need to null terminate your string.
From Apple Documentation:
- (instancetype)initWithUTF8String:(const char *)nullTerminatedCString;
Returns an NSString object initialized by copying the characters from a given C array of UTF8-encoded bytes.

Increasing Length of NSData

Basically, I have an NSString of 46 characters which i convert to NSData. I need to pad the string to 48 characters. It does not work via just adding ' ' to the end of the NSString. So, i just increased the length of NSData using this:
NSString *string = #"__46characterlongstring__";
NSData *d = [string dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"d: %#", d);
NSData *data = [NSData dataWithBytes:[d bytes] length:48];
NSLog(#"data: %#", data);
The NSData called 'd' returns <723d6c67 6e267573 65726e61 6d653d64 61766964 77617473 6f6e3936 26706173 73776f72 643d736e 30307079 6f32>
The NSData called 'data' returns <723d6c67 6e267573 65726e61 6d653d64 61766964 77617473 6f6e3936 26706173 73776f72 643d736e 30307079 6f32_>, where _ is 4 random characters (usually numbers)
How can i make sure that 'data' returns <723d6c67 6e267573 65726e61 6d653d64 61766964 77617473 6f6e3936 26706173 73776f72 643d736e 30307079 6f320000> - 4 0's instead of 4 random characters?
Thanks.
You want to use an NSMutableData, which you make from the NSData you get back from the string, then add some zeros:
NSMutableData *paddedData = [NSMutableData dataWithData:d];
[paddedData increaseLengthBy:4];

Sending hexadecimal data to devices (Converting NSString to hexadecimal data)

I'm trying to send hexadecimal data via WiFi.
The code is something like this:
NSString *abc = #"0x1b 0x50";
NSData *data = [[[NSData alloc] initWithData:[abc dataUsingEncoding:NSASCIIStringEncoding]]autorelease];
[outputStream write:[data bytes] maxLength:[data length]]];
Instead of sending the hexadecimal data, it's sending it in text format.
I tried with NSUTF8StringEncoding, but it's the same. I'm using it with the NSStream class.
You're not getting what you expect with NSString *abc = #"0x1b 0x50". It's almost the same as having NSString *abc = #"cat dog 123 0x0x0x"; just a bunch of words separated by spaces. So when you create your NSData object, you're just initializing it with a string of characters, not a series of actual numbers.
If you can get your numbers into an NSArray, this question/answer should help you: How to convert NSArray to NSData?
The data that you probably want to send is simply 0x1b50, which is the decimal number 6992 (assuming big-endian), and fits into two bytes. This is not the same as a string (which could contain anything) even if it happens to contain some human-readable representation of those numbers.
I'm assuming you want to send this as binary data, and if so one way would be to simply send a buffer formed by a single UInt16 instead of a string. I'm not very familiar with the relevant APIs, but look to see if you can populate the NSData with an integer, perhaps something like:
UInt16 i = 0x1b50; // Or = 6992
[[NSData alloc] initWithBytes: &i length: sizeof(i)]
[outputStream write: [data bytes] maxLength: [data length]]];
Again, I'm not fluent with Objective C, but this should be the general approach to sending the number 0x1b50 as binary data.

Why does this NSString created from an NSData object fail to show it has contents?

Why does the following code produce the logging at the bottom ?
Here is the anomaly- my second NSLog should print the chrStr but produces nothing, empty, which is verified by this debug command:
(gdb) po chrStr
object returns empty description
However, the third NSString where I re-convert the NSString back to NSData object DOES display the the data, the same value as in the first NSLog, as it should. This would indicate to me that chrStr must have actual contents. But it seems not to be so from the NSLOG or the po command. Why ?
NSString *login;
NSString *pass;
// Purpose: NSString *loginString = [NSString stringWithFormat:#"\000%#\000%#", login, pass];
login = #"Loginname"; // text string1
pass = #"Password"; // text string2
// convert text strings to data objects
NSData *subData1 = [login dataUsingEncoding:NSUTF8StringEncoding];
NSData *subData2 = [pass dataUsingEncoding:NSUTF8StringEncoding];
// embed a NULL into new NSData object
NSMutableData *data = [NSMutableData data];
unsigned char zeroByte = 0;
[data appendBytes:&zeroByte length:1];
// append string1, NULL, string2 to data object
[data appendData:subData1];
[data appendBytes:&zeroByte length:1];
[data appendData:subData2];
NSLog(#"1.NSData: %#", data); // print data object
// create a character string from data object
NSString *chrStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"2.NSString: %#", chrStr); // print character string
// create data object from string object
NSData *chrData = [chrStr dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"3.NSDATA: %#", chrData); // print data object
Produces:
[1071:207] 1.NSData: 004c6f67 696e6e61 6d650050 61737377 6f7264
[1071:207] 2.NSString:
[1071:207] 3.NSDATA: 004c6f67 696e6e61 6d650050 61737377 6f7264
This is a real mystery to me. If chrStr is empty then 3-NSDATA could not display its info, but it does !
What am I trying to accomplish ? Well, check my very first comment line: // purpose:
That line when uncommented produces a warning, even though it actually works, so I was trying to do it another way that allowed me to have a clean compile. If you see a better way to accomplish that objective, I all eyes and ears. But please don't dwell on why that #"\000%#\000%#" string is necessary, start out accepting that it is. Thanks.
In C (and therefore objective-c), a null byte is used to represent the end of a string. When you create the string object, it takes all of the data you have given it without parsing, which is why you can convert it back to data successfully. However, when you display the string, the system reads the string up to the first null byte, which is the first byte. Therefore, the string contains data, but any system functions which read byte by byte instead of using the strings returned length will think it is empty. When you work with non-displayable characters, you should try to use data objects over string objects as often as possible.