How to convert NSData to struct accurately [closed] - ios

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I Got a data from device(BLE):<840100ec d5045715 00010014 00240018 00>
but the second byte can not convert accurately. Like these:
But I can do it use Uint8 array, Why? Thank you.
code like these:
// I got the data:<840100ec d5045715 00010014 00240018 00>
case SPK_FEEDBACK_HistoryDataPort:
// Log
NSLog(#"receive data:%#", [NSData dataWithBytes:originalCommandBytes length:sizeof(D2MHistoryDataPort)]);
// originalCommandBytes dataType:UInt8 *
D2MHistoryDataPort *historyData = (D2MHistoryDataPort *)originalCommandBytes;
// Log
NSLog(#"收到硬件返回的0x%x指令(历史数据体): 历史数据包的索引:%d; 时间戳:%d; 步数:%d; 卡路里:%d; 距离:%d; 睡眠:%d; 运动时长:%d",
historyData->cmd,
historyData->index,
(unsigned int)historyData->timeStamp,
historyData->steps,
historyData->calories,
historyData->distance,
historyData->sleep,
historyData->duration);
break;
// I declare this struct in another class
typedef struct {
UInt8 cmd;
UInt16 index;
UInt32 timeStamp;
UInt16 steps;// 步数
UInt16 calories;// 卡路里
UInt16 distance;// 距离,单位m
UInt16 sleep;// 睡眠
UInt16 duration;// 运动时长,单位minute
} D2MHistoryDataPort;

How the compiler lays out the individual fields of a struct in memory is implementation dependent. Usually the compiler has to add padding to properly align the fields and it might even reorder them (by grouping fields of the same size) to reduce the required padding and the overall size of the struct.
You can turn this behavior off using __attribute__((packed)):
typedef struct __attribute__((packed)) {
UInt8 cmd;
UInt16 index;
UInt32 timeStamp;
UInt16 steps;// 步数
UInt16 calories;// 卡路里
UInt16 distance;// 距离,单位m
UInt16 sleep;// 睡眠
UInt16 duration;// 运动时长,单位minute
} D2MHistoryDataPort;

What you are doing is quite guaranteed to not work. You are trying to take a struct, assume that you can interpret it as a sequence of bytes, write it and read it back. That's not going to work. Starting with structs having different layouts between compiler versions, between 32 and 64 bit compilers, and so on. People knew in the '90s that this was a bad idea.
Use the NSCoding protocol. Or convert the data to JSON. Do NOT try to interpret structs as a sequence of bytes.
If you absolutely cannot avoid using NSData, this is how it works safely:
Step 1: Define the external data format. The external data format is NOT "whatever the compiler decided to layout my struct". The external data format is "One unsigned byte cmd; two unsigned bytes index, most significant byte first. 4 unsigned bytes time stamp, most significant byte first, meaning the number of seconds since Jan 1st 1904, ... " and so on.
Then to read the struct, get a pointer to the first byte, check that you have enough bytes, and write
mystruct.cmd = p [0];
mystruct.index = (p [1] << 8) | p [2];
mystruct.timeStamp = (p [3] << 24) | (p [4] << 16) ...
and so on.

Related

Dartlang: How to convert int from network to host order (and vice versa)

How to convert int from network to host order (and vice versa).
In cpp it would be the ntohl and htonl.
Use ByteData, which is a ...
sequence of bytes that also provides random and unaligned access to
the fixed-width integers and floating point numbers represented by
those bytes
For example:
ByteData byteData = ByteData(4);
byteData.setUint32(0, netValue, Endian.big);
int hostValue = byteData.getUint32(0);
Chances are you'll actually want to use the underlying bytes - maybe to send in some network message. You could extract a int from a byte array like this:
Uint8List bytes = Uint8List(4); // 4 byte list
// read the network order int in bytes [0-3].
int value = bytes.buffer.asByteData().getUint32(0, Endian.big);

How to convert switch state into integer in ios

I am using five switches for handling different types of notifications. To remember the state of the switch, I am thinking of converting state of five switches into an integer. For example, if my switches status is as follow, 01010 then the integer should be 10. Please help me how to achieve this.
At first extract each switch value and store it in a single string
Now convert the string to decimal /integer value like this:-
NSString * binarystring = #"01010";
long decimalValue = strtol([binarystring UTF8String], NULL, 2);
NSLog(#"%ld", decimalValue );
Edit
Get all switch control value in single string:-
NSString *binarystring = [[NSString alloc] initWithFormat:#"%i%i%i%i%i",self.switch1.isOn,self.switch2.isOn,self.switch3.isOn,self.switch4.isOn,self.switch5.isOn];
(Why bother encoding your 5 switch values into a single integer? Storing 5 Booleans is not hard. That said the question is how to do it...)
Important aside: BOOL values are not 0 and 1
Objective-C is a superset of C, and in the original C there was no Boolean type - instead it just used an integer type with the interpretation that 0 was false and anything else was true.
Objective-C defines BOOL as a signed char, that is an 8-bit signed integer type (as characters are just an integer type in C). So in Objective-C 0 is false, and -128..-1, 1..127 are all true. NO is defined as 0 and YES as 1, but various operations may result in other values.
To get a 0 or 1 from a BOOL b you can use the conditional operator:
b ? 1 : 0
However the built in logical operators by definition will always return 0 or 1 and never any of the other possible values. The ! operator is logical not, and two not's get you back to where you started so:
!!b
will also give you a 0 or 1.
In any code that takes a BOOL and tries to use it as a 0 or 1 you should really use one of the above (or an equivalent).
One way to solve it: using strings
Your question has been interpreted as using a string as an intermediary during the encoding. First assume the class has your five buttons stored in an instance variable as a simple array (it will allow us to loop):
const int kSWITCH_COUNT = 5; // let's not hard code it everywhere
#implemention MyClass
{
Switch *switches[kSWITCH_COUNT];
}
then the string method goes something like:
- (void) stringMethod
{
NSMutableString *binarystring = NSMutableString.new;
// build up the string one value at a time, note the !! so we only get 0 or 1 values
for (int ix = 0; ix < kSWITCH_COUNT; ix++)
[binarystring appendFormat:#"%d", !!switches[ix].isOn];
long decimalValue = strtol([binarystring UTF8String], NULL, 2);
NSLog(#"Encoded: 0x%lx", decimalValue);
}
This method works, but it is rather a circuitous way of getting to the result - you have 5 integer (Boolean) values and you want to combine them into an integer, why involve strings?
A better way to solve it: using integers
(Objective-)C provides bitwise operators to do shifts, or, and, etc. operations which treat integer types as an ordered collection of bits - which is what they are on a computer.
The << operator shifts left, e.g. 0x1 << 1 produces 0x2, i.e. << 1 is equivalent to multiplication by 2. The | operator is bitwise or, e.g. 0x1 << 1 | 1produces0x3`. The answer to your question now follows easily:
- (void) shiftMethod
{
unsigned int encoded = 0;
for (int ix = 0; ix < kSWITCH_COUNT; ix++)
encoded = (encoded << 1) | !!switches[ix].isOn;
NSLog(#"Encoded: 0x%x", encoded);
}
If you don't like shifts and ors you can use multiplication and addition:
encoded = encoded * 2 + !!switches[ix].isOn;
The above solves the problem directly, no converting to/from intermediate strings. It happens to be a lot faster as well, but in the overall scheme of an application neither approach is probably going to take a significant proportion of the execution time and you shouldn't select based on that.
A Third Way
If you are going to wish to set/get the individual bits of an integer a lot you can use struct types with bit-field widths. These let you set/get the bits of an integer directly - no shifting etc. required - and you may find them useful, but they are rather "low level". Any good book on C will show you how to use these.
HTH

How to turn 4 bytes into a float in objective-c from NSData

Here is an example of turning 4 bytes into a 32bit integer in objective-c. The function readInt grabs 4 bytes from the read function and then converts it into a single 32 bit int. Does anyone know how I would convert 4 bytes to a float? I believe it is big endian. Basically I need a readFloat function. I can never grasp these bitwise operations.
EDIT:
I forgot to mention that the original data comes from Java's DataOutputStream class. The writeFloat function accordign to java doc is
Converts the float argument to an int using the floatToIntBits method
in class Float, and then writes that int value to the underlying
output stream as a 4-byte quantity, high byte first.
This is Objective c trying to extract the data written by java.
- (int32_t)read{
int8_t v;
[data getBytes:&v range:NSMakeRange(length,1)];
length++;
return ((int32_t)v & 0x0ff);
}
- (int32_t)readInt {
int32_t ch1 = [self read];
int32_t ch2 = [self read];
int32_t ch3 = [self read];
int32_t ch4 = [self read];
if ((ch1 | ch2 | ch3 | ch4) < 0){
#throw [NSException exceptionWithName:#"Exception" reason:#"EOFException" userInfo:nil];
}
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
OSByteOrder.h contains functions for reading, writing, and converting integer data.
You can use OSSwapBigToHostInt32() to convert a big-endian integer to the native representation, then copy the bits into a float:
NSData* data = [NSData dataWithContentsOfFile:#"/tmp/java/test.dat"];
int32_t bytes;
[data getBytes:&bytes length:sizeof(bytes)];
bytes = OSSwapBigToHostInt32(bytes);
float number;
memcpy(&number, &bytes, sizeof(bytes));
NSLog(#"Float %f", number);
[data getBytes:&myFloat range:NSMakeRange(locationWhereFloatStarts, sizeof(float)] ought to do the trick.
Given that the data comes from DataOutputStream's writeFloat() method, then that is documented to use Float.floatToIntBits() to create the integer representation. intBitsToFloat() further documents how to interpret that representation.
I'm not sure if it's the same thing, but the xdr API seems like it might handle that representation. The credits on the man page refer to Sun Microsystems standards/specifications, so it seems likely it's related to Java.
So, it may work to do something like:
// At top of file:
#include <rpc/types.h>
#include <rpc/xdr.h>
// In some function or method:
XDR xdr;
xdrmem_create(&xdr, (char*)data.bytes + offset, data.length - offset, XDR_DECODE);
float f;
if (!xdr_float(&xdr, &f))
/* handle error */;
xdr_destroy(&xdr);
If the data consists of a whole stream in eXternal Data Representation, then you would create one XDR stream for the whole task of extracting items from it, and use many xdr_...() calls between creating and destroying it to extract all of the items.

get int value including zeros (incase if it starts with 0) from string

I have a question regarding the converting string to intvalue. My question and issue is in case if I have string called "001223" I am getting 1223 as intvalue. But I want to get the 001223 as final int value. Please let me know if my question is not clear. Thanks for your time
There is no difference in value between the numbers 001223 , 1223, 2446/2 or 1223.000. They all refer to the same number.
If you want to keep leading zeroes, then you need to either keep it as a string or maintain another piece of information so it can be rebuilt later, basically the number of zeroes at the front, such as:
struct sNumWithLeadingZeros {
size_t zeroCount;
unsigned int actualValue;
};
I'd probably suggest the former (keeping it as a string) since that's likely to be less effort.
"Leading zeros" are to do with the textual representation of an integer, when stored as integer values in a computer the leading zeros do not exist.
However, if what you want to do is display the number with the same number of digits it had before being converted from text then: if the string contains only the digits of the number, e.g. you have #"001223" then you can take the length of this string to determine the number of digits. Later when converting the number back to string format you can use a formatted conversion, e.g. stringWithFormat:, and a format specifier which specifies the required number of digits. You'll need to read up on formats in the documentation, but here is an example:
NSString *input = #"001223";
int x = [input intValue];
int digits = (int)input.length;
NSString *output = [NSString stringWithFormat:#"%0*d", digits, x];
The value of output will be the same as input. The format broken down is: 0 - leading zeros; * use a dynamic field with, will use the value of digits; d - int.
HTH
One cannot prefix leading 0s in int data type. But if you see 0 prefix then the number is octal not decimal. Octal value can be created by changing base. For this you can use wrapper class like Integer.
But if one wants leading 0s for displaying data then he/she can use following code
public class Sample
{
public static void main(final String[] argv)
{
System.out.printf("%06d", 1223);
System.out.println();
}
}

NSData Packet Interpretation

I have a fairly complex issue regarding the interpretation of packets in an app that I am making. A host app sends a packet to client apps with the following structure:
[Header of 10 bytes][peerID of selected client of variable byte length][empty byte][peerID of a client of variable byte length][empty byte][int of 4 bytes][peerID of client of variable byte length][empty byte][int of 4 bytes]
Here is a sample packet that is produced under this structure:
434e4c50 00000000 006a3134 31303837 34393634 00313233 38313638 35383900 000003e8 31343130 38373439 36340000 0003e8
Converted it looks like this:
CNLP j1410874964 1238168589 Ë1410874964 Ë
"CNLP j" is the packet header of 10 bytes. "1410874964" is the peerID of the selected client. "1238168589" is the peerID of another client. " Ë" has an int value of 1000. "1410874964" is the peerID of the other client (in this case, the selected client). " Ë" also has an int value of 1000. Basically, in this packet I am communicating 2 things - who the selected client is and the int value associated with each client.
My problem exists on the interpretation side (client side). To interpret this particular type of packet, I use the following method:
+ (NSMutableDictionary *)infoFromData:(NSData *)data atOffset:(size_t) offset
{
size_t count;
NSMutableDictionary *info = [NSMutableDictionary dictionaryWithCapacity:8];
while (offset < [data length])
{
NSString *peerID = [data cnl_stringAtOffset:offset bytesRead:&count];
offset += count;
NSNumber *number = [NSNumber numberWithInteger:[data cnl_int32AtOffset:offset]];
offset += 4;
[info setObject:number forKey:peerID];
}
return info;
}
Typically, each of these packets range between 49 and 51 bytes. "offset" is set in a previous method to reflect the byte number after the packet header plus the empty byte after the selected player (in the case of the above packet, 21). "count" is initialized with a value of 1. In the case of this particular example, length is 51. The following method is passed the above arguments:
- (NSString *)cnl_stringAtOffset:(size_t)offset bytesRead:(size_t *)amount
{
const char *charBytes = (const char *)[self bytes];
NSString *string = [NSString stringWithUTF8String:charBytes + offset];
*amount = strlen(charBytes + offset) + 1;
return string;
}
This method is supposed to read through a variable length string in the packet, set the offset to the byte immediately after the empty byte pad behind the peerID string, and return the string that was read. "amount" is then set to the number of bytes the method read through for the string (this is becomes the new value of count after returning to the first method). "offset" and "count" are then added together to become the new "offset" - where interpretation of the int portion of the packet will begin. The above arguments are passed to the following method:
- (int)cnl_int32AtOffset:(size_t)offset
{
const int *intBytes = (const int *)[self bytes];
return ntohl(intBytes[offset / 4]);
}
This method is intended to return the 32 bit (4 byte) int value read at the current offset value of the packet. I believe that the problem exists in this method when the offset is a number that is not divisible by 4. In this case, the first int value of 1000 was correctly interpreted, and 32 was returned as the offset during the first iteration of the while loop. However, during the second iteration, the int value interpreted was 909377536 (obtained from reading bytes 36340000 in the packet instead of bytes 000003E8) This was likely due to the fact that the offset during this iteration was set to 47 (not divisible by 4). After interpreting the 32 bit int in the category above, 4 is added to the offset in the first method to account for a 4 byte (32 bit int). If my intuition about an offset not divisible by zero is correct, any suggestions to get around this problem are greatly appreciated. I have been looking for a way to solve this problem for quite some time and perhaps fresh eyes may help. Thanks for any help!!!
The unportable version (undefined behaviour for many reasons):
return ntohl(*(const int *)([self bytes]+offset));
A semi-portable version is somewhat trickier, but in C99 it appears that you can assume int32_t is "the usual" two's complement representation (no trap representations, no padding bits), thus:
// The cast is necessary to prevent arithmetic on void* which is nonstandard.
const uint8_t * p = (const uint8_t *)[self bytes]+offset;
// The casts ensure the result type is big enough to hold the shifted value.
// We use uint32_t to prevent UB when shifting into the sign bit.
uint32_t n = ((uint32_t)p[0]<<24) | ((uint32_t)p[1]<<16) | ((uint32_t)p[2]<<8) | ((uint32_t)p[3]);
// Jump through some hoops to prevent UB on "negative" numbers.
// An equivalent to the third expression is -(int32_t)~n-1.
// A good compiler should be able to optimize this into nothing.
return (n <= INT32_MAX) ? (int32_t)n : -(int32_t)(UINT32_MAX-n)-1;
This won't work on architectures without 8-bit bytes, but such architectures probably have different conventions for how things are passed over the network.
A good compiler should be able to optimize this into a single (possibly byte-swapped) load on suitable architectures.

Resources