5

With reference to the following question: Convert NSData into HEX NSString

I have solved the problem using the solution provided by Erik Aigner which is:

NSData *data = ...;
NSUInteger capacity = [data length] * 2;
NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:capacity];
const unsigned char *dataBuffer = [data bytes];
NSInteger i;
for (i=0; i<[data length]; ++i) {
  [stringBuffer appendFormat:@"%02X", (NSUInteger)dataBuffer[i]];
}

However, there is one small problem in that if there are extra zeros at the back, the string value would be different. For eg. if the hexa data is of a string @"3700000000000000", when converted using a scanner to integer:

unsigned result = 0;
NSScanner *scanner = [NSScanner scannerWithString:stringBuffer];
[scanner scanHexInt:&result];
NSLog(@"INTEGER: %u",result);

The result would be 4294967295, which is incorrect. Shouldn't it be 55 as only the hexa 37 is taken?

So how do I get rid of the zeros?

EDIT: (In response to CRD)

Hi, thanks for clarifying my doubts. So what you're doing is to actually read the 64-bit integer directly from a byte pointer right? However I have another question. How do you actually cast NSData to a byte pointer?

To make it easier for you to understand, I'll explain what I did originally.

First, I displayed the data of the file I have (data is in hexadecimal)

NSData *file = [NSData dataWithContentsOfFile:@"file path here"];
NSLog(@"Patch File: %@",file);

Output:

enter image description here

Next, I read and offset the first 8 bytes of the file and converted them into a string.

// 0-8 bytes
[file seekToFileOffset:0];
NSData *b = [file readDataOfLength:8];
NSUInteger capacity = [b length] * 2;
NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:capacity];
const unsigned char *dataBuffer = [b bytes];
NSInteger i;
for (i=0; i<[b length]; ++i) {
    [stringBuffer appendFormat:@"%02X", (NSUInteger)dataBuffer[i]];
}
NSLog(@"0-8 bytes HEXADECIMAL: %@",stringBuffer);

As you can see, 0x3700000000000000 is the next 8 bytes. The only changes I would have to make to access the next 8 bytes would be to change the value of SeekFileToOffset to 8, so as to access the next 8 bytes of data.

All in all, the solution you gave me is useful, however it would not be practical to enter the hexadecimal values manually. If formatting the bytes as a string and then parsing them is not the way to do it, then how do I access the first 8 bytes of the data directly and cast them into a byte pointer?

armcknight
  • 149
  • 6
Dawson
  • 117
  • 1
  • 9

2 Answers2

3

4,294,967,295 is ULONG_MAX, so it appears that the scanner simply overflows. (The scanHexInt method eats all the hexa digits it finds, and since zero is a hexa digit, too, the method slurps the whole number, getting past ULONG_MAX.)

Community
  • 1
  • 1
zoul
  • 102,279
  • 44
  • 260
  • 354
  • the problem seems to be with the most significant and least significant bit. If I use the little endian encoding, the string would most likely take only 37 and ignore the zeros behind. But how do I achieve this? – Dawson Nov 21 '12 at 09:46
  • What are you really trying to do, from the high-level view? – zoul Nov 21 '12 at 10:22
  • I'm trying to convert 8 bytes into a 64-bit integer but I'm not sure what is the appropriate way to do this. It seems like my method of achieving this is wrong. Hence, I'm seeking for some advice. – Dawson Nov 22 '12 at 00:55
3

You appear to be trying to convert 8 bytes stored in an NSData into an 64-bit integer (there are 8 bytes in 0x3700000000000000).

If this is your goal then doing it by formatting the bytes as a string and then parsing them is not the way to do it. You need to find out what endian format (i.e. least or most significant byte first) your data is in and then use the appropriate endian conversion functions.

BTW the reason why you're getting 4294967295 (0xFFFFFFFF) is because you are asking the NSScanner to read a 32-bit integer and your number (0x3700000000000000) overflows.

Response to comment

As the x86 is little-endian you can read a little-endian 64-bit integer direct from a byte pointer by just casting:

Byte bytes[] = { 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
NSLog(@"%lld", *(int64_t *)bytes);

If you need to read big-endian data then you can use an endian conversion function such as Endian64_Swap - which just flips what you have, so read then flip.

Note that the x86 does not require data access to be aligned, but will perform better if it is.

CRD
  • 52,522
  • 5
  • 70
  • 86
  • This is what I'm trying to achieve. I've previously tried initialising the string using the NSData with NSUTF16LittleEndianStringEncoding, but it gave me 7. However, after reading your answer and seeking advice externally, I realised that this way of doing it is flawed and completely wrong. Any idea on how to find out what endian format my data is in before converting it? – Dawson Nov 22 '12 at 01:03
  • @Dawson - There is know way to really determine the endian format, you could guess based on the values but you wouldn't really *know*. So unless you know the expect range of values you need to find the answer from whoever is producing the data. – CRD Nov 22 '12 at 02:03
  • Assuming that the data is of little endian format. What appropriate conversion functions could I use to convert (0x3700000000000000) into a 64-bit integer, which in this case is 55? From what I know, NSScanner doesn't seem to have an appropriate function for that. – Dawson Nov 22 '12 at 02:52
  • @Dawson - I've added to the answer above. – CRD Nov 22 '12 at 03:23
  • @Dawson - you've essentially written the answer yourself `NSData *fileContents = [NSData dataWithContentsOfFile:@"file path here"]; Byte *ptrToBytes = [fileContents bytes];`. Now cast & increment `ptrToBytes` as needed to work through your data. – CRD Nov 22 '12 at 05:26
  • Hi, thanks so much for all the help. My code now runs perfectly. One last question, there is a small semantic issue when initializing the byte pointer, it seems that an expression of type 'const void *' discards qualifiers – Dawson Nov 22 '12 at 05:48
  • I have found the reason and solved it, thanks. void const *ptrToBytes = [...] should be used instead as when the [fileContents bytes] returns a "void const *", it points to data that it does not want you to mess with. – Dawson Nov 22 '12 at 07:47