During the development of Bluetooth project, some problems related to data processing are encountered

  1. Introduction to data size side
  2. Conversion of small – and large-side data schemas
  3. By bit operation, left shift, right shift operation

First, the introduction of data size

A. The size side indicates the order in which data is stored in the computer. B. Big-endian mode conforms to normal human thinking, with high bytes stored at low addresses in memory. C. The small-endian mode is convenient for computer processing, and the high bytes are stored in the high address of memory. D. By default, iOS uses small – end storage.

You can run the following two lines of code in Xcode to print out the size mode.

short int number = 0x8866;
NSLog(@ "% @"[NSString stringWithFormat:@"%x"((,char *)&number)[0]].intValue == 66 ? @" Small end mode" : @" Big-endian mode");
Copy the code

Second, the conversion of data mode at the large and small end

During Bluetooth communication, the data received from the hardware is of NSData type, so we need to parse the data to get the data that is really convenient to use. However, the received data may be stored in the opposite order in memory, so the size conversion problem is involved in the process of parsing.

In fact, iOS is very easy to convert from one side to the other, and apple’s Core Fundation provides a way to do this. Apple Official Documents

Here are a few examples to look at the basic use of methods related to the size side in Fundation.

CFByteOrderGetCurrent() returns the size mode of the current computer

CFByteOrderGetCurrentThe value returned by () is an enumeration as followsenum __CFByteOrder {
    CFByteOrderUnknown./ / of the unknown
    CFByteOrderLittleEndian.// Small endian mode
    CFByteOrderBigEndian      // Big-endian mode
};
Copy the code

CFSwapInt16() converts a 16-bit integer number

// Convert the number 15 to mode
CFSwapInt16(15)

The result is 3840 in decimal and 0xF00 in hexadecimal.
// 0xF00 reverses is 0xF = 15, so this method does reverse 15.
Copy the code

CFSwapInt16BigToHost() converts a 16-bit integer number from big-ended mode to local data store mode. If the local host is in big-endian mode, the original value remains unchanged.

// Change the Number of the big-endian mode to the local data store mode
CFSwapInt16BigToHost(Number)
Copy the code

CFSwapInt32HostToBig() converts a 32-bit native mode data to big-endian mode. If the local host is in big-endian mode, the original value remains unchanged.

// Convert the Number in local storage mode to big-endian mode
CFSwapInt32HostToBig(Number)
Copy the code

There are many other methods (see the official documentation), all of which are similar and can be interpreted literally.

Usually we only have two or three. The general requirement is to convert the big end to local mode, that is, small end mode.

CFSwapInt16BigToHost
CFSwapInt32BigToHost
Copy the code

Here are two methods encapsulated that can be used directly to parse data during development.

The two methods return Signed and Unsigned data, respectively. In the code, location represents the location of the data to be parsed, and offset represents the number of bits to be parsed.

  • Note that there is no need to use methods like CFSwapInt16BigToHost when parsing only 1-bit data. See the code for details.
// Converting to local size-side mode returns Signed data+ (signed int)signedDataTointWithData:(NSData *)data Location:(NSInteger)location Offset:(NSInteger)offset {
    signed int value=0;
    NSData *intdata= [data subdataWithRange:NSMakeRange(location, offset)];
    if (offset==2) {
        value=CFSwapInt16BigToHost(* (int*)([intdata bytes]));
    }
    else if (offset==4) {
        value = CFSwapInt32BigToHost(* (int*)([intdata bytes]));
    }
    else if (offset==1) {
        signed char *bs = (signed char *)[[data subdataWithRange:NSMakeRange(location, 1) ] bytes];
        value = *bs;
    }
    return value;
}
Copy the code
// Converting to local size mode returns data of Unsigned type+ (unsigned int)unsignedDataTointWithData:(NSData *)data Location:(NSInteger)location Offset:(NSInteger)offset {
    unsigned int value=0;
    NSData *intdata= [data subdataWithRange:NSMakeRange(location, offset)];
    
    if (offset==2) {
        value=CFSwapInt16BigToHost(* (int*)([intdata bytes]));
    }
    else if (offset==4) {
        value = CFSwapInt32BigToHost(* (int*)([intdata bytes]));
    }
    else if (offset==1) {
        unsigned char *bs = (unsigned char *)[[data subdataWithRange:NSMakeRange(location, 1) ] bytes];
        value = *bs;
    }
    return value;
}
Copy the code

Three, according to the bit operation, left shift, right shift operation

So before we talk about bit operations and left and right shift, let’s just remember the basic units of measurement.

A byte is an 8-bit piece of data that can represent 256 digits ranging from 0 to 255.

1B (byte) = 8 bits.

Simulate a process of parsing data:

  1. If bluetooth sends 32 bytes of data each time, the data Log in NSData type will look like this: < 0AA60000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000059 9DB56800 00260b01>
  2. Each two digits represents a hexadecimal number, for example, the leftmost 0a represents a byte, which is 0x0A = 10.
  3. Now we need to intercept the two bytes (16 bits) of 0aa6 on the far left, which is of the UInt16 type, so the first thing to do is to use the size end conversion method encapsulated above to intercept these two bytes, and result in the code below is the data needed.
// From bit 0, 2 bytes are taken, so location is 0, offset is 2
UInt16 result = [self unsignedDataTointWithData:data Location:0 Offset:2];
Copy the code

However, the work is not finished after the result.

  • Requirement: the binary of result is 0000 1010 1010 0110, a 16-bit number, if agreed with the hardware engineer in advance, the lower four bits (0110) represent the number of groups, and the lower five to eight bits (1010) represent the number of people in each group.

How do you separate out the data you need?

This is where bit operations come in handy. Take a look at the basic uses and scenarios for bitwise operations and left and right shifts. the required answers are also included.

1, by bit and &

1 for both, 0 for none

For example: 3&5 0000 0011 0000 0101 0000 0001 =1 so 3&5 =1

Features: (1) clear: any number and 0 phase, the result is 0. (2) remove the value of the specified bit. So if I pick the digit, I’m going to make the corresponding digit 1.

For example, if you get a 16-bit result = 0000 1010 1010 0110, how do you get the lower 4 bits of the result? You can use bitwise and, and the code is as follows

// 0x000f == 0000 0000 0000 1111
// number == 0000 0000 0000 0110
int number = result & 0x000f;
Copy the code

2, bitwise or |

As long as one of them is 1, it is 1. Negative numbers participate in bitwise or operations in the form of complement

For example: 3 | 5 0000 0011 0000 0101 0000 0111 = 7 so 3 | 5 = 7

Features: (1) To some position of data 1.

For example: 1 1010 0000 0000 0000 0000 1111 1010 1111 so that the last four bits are all 1

The xor operation ^

If the corresponding bit is different, the value is 1; if the corresponding bit is the same, the value is 0

For example, 3 ^ 5 0000 0011 0000 0101 0000 0110 so 3 ^ 5= 6

Features: (1) specific bit flip, which bit need to flip the corresponding bit set to 1 (2) any number and 0 xor, the original value unchanged. (3) xor operations can swap positions: 3 ^ 5 ^ 6 == 3 ^ 6 ^ 5 (4) The same number xor is equal to 0:9 ^ 9 == 0 (5) A ^ b ^ a == b

4, take the inverse ~

0 becomes 1,1 becomes 0

For example, ~3 0000 0011 1111 1100

X=1011 0111 = (~1) X & (~1) =1011 0110 so that the last digit is 0

5. Left shift operation <<

The binary bits are all moved to the left by several bits, the left is discarded, and the right is filled with zeros

For example, 3<<2 0000 0011 = 3 0000 1100 = 12 (after left shift) 3<<2 == 12

Features: If the highest bit discarded when moving left does not contain 1, it is multiplied by 2 for every left bit. So a<<n is a times 2 to the n

6, right shift operation >>

Binary right shift a number of bits, positive number left complement 0, negative number left complement 1, right discard.

For example, 12>>2 0000 1100 = 12 0000 0011 = 2 (after right shift) 12>>2 == 3

A >>n is a divided by 2 to the n

For example, if we get a 16-bit result = 0000 1010 1010 0110, how do we get the 5-8 bits of this data? First use bitwise and set everything except 5-8 bits to 0, then use right shift to get the exact number. The following code

// 0x00f0 == 0000 0000 1111 0000, result is 0000 0000 1010 0000 after bit and 0xf0
Number == 0000 0000 0000 1010
int number = (result & 0x00f0) > >4;
Copy the code