preface

In actual development, binary is getting further and further away from us, and there are very few scenarios where we need to deal with binary directly. Recently I happened to be working on the WebSocket protocol, which involves a lot of binary related operations, so I took the opportunity to pick up some forgotten things. Without further ado, let’s get straight to today’s business.

Binary operation

symbol describe The rules example
& with If both bits are 1, the result is 1 1100 &1011 = 1000
| or If any digit is 1, it’s going to be 1 1100 &1011 = 1111
^ Exclusive or If the two bits are different, the result is 1. If the two bits are identical, the result is 0 1100 &1011 = 0111
~ The not 0 becomes 1,1 becomes 0 ~ 1100 = 0011
<< Shift to the left All the binary bits move a number of bits left, high discard, low fill 0 1100 << 1 = 1000
>> Moves to the right Each binary bit moves several bits to the right (unsigned number, high-order complement 0; There are sign numbers, some compilers complement sign bits, some compilers complement zeros. 1100 >> 1 = 0110

Here are a few more examples of the calculation process:

And (&)

1 1 1 0 0 0 0 1 & 1 0 0 0 1 1 -- -- -- -- -- -- -- -- -- -- 1 0 0 0 1 0 0 0Copy the code

Or (|)

1 1 1 1 0 0 0 0 | 1 0 0 0 1 1 -- -- -- -- -- -- -- -- -- -- 1 1 0 1 1 1 1 1Copy the code

Xor (^)

1 1 1 1 0 0 0 0 ^ 1 0 0 0 1 1 -- -- -- -- -- -- -- -- -- -- 1 0 0 0 1 1 1 1Copy the code

Invert (~)

1 0 0 1 1 0 0 ~ 1 -- -- -- -- -- -- -- -- -- -- 1 0 0 0 0 1 1 1Copy the code

Shift to the left (< <)

1 1 1 0 0 0 0 1 < < 1 -- -- -- -- -- -- -- -- -- -- -- -- - 1 0 0 1 1 0 0 0Copy the code

Note: All bits are moved one bit to the left, the highest (left) 1 is discarded, and the lowest (right) 1 is added

Moves to the right (> >)

1 1 0 0 1 1 0 0 > > 1 -- -- -- -- -- -- -- -- -- -- -- -- - 0 1 1 1 1 0 0 0Copy the code

Note: All bits are moved to the right by one (unsigned), the highest (left) digit is replaced by a 0, and the lowest (right) digit is discarded

Validate in JavaScript

In JavaScript, binary is expressed literally as: 0b or 0b + binary bits, for example: 0b11001100, so we can try out the bit operations we learned above in JavaScript. Enter the following code on the console to see what it looks like:

console.log( 0b11001100 & 0b10011011 ) // 0b10001000 → 136
console.log( 0b11001100 | 0b10011011 ) // 0b11011111 → 223
console.log( 0b11001100 ^ 0b10011011 ) // 0b01010111 → 87
console.log( ~0b11001100 ) // 0b00110011 → 51
console.log( 0b11001100 << 1 ) // 0b10011000 → 152
console.log( 0b11001100 >> 1 ) // 0b01100110 → 102
Copy the code

The Chrome console looks like this:

There are two places where the results are not what we expected. Don’t panic, let’s break it down one by one:

To start with, let’s analyze why the left shift operation (<<) doesn’t work as expected. First, we need to know that in V8 JS, small integers (-2^30 to 2^ 30-1) take up 4 bytes (32 bits) of space, not 64 bits, as we commonly know. This is mainly for performance optimizations. Read the official blog post for more details. I’m not going to expand it here. But look at our calculation above using 8 bits, resulting in the high 1 being discarded during the left shift, so there is a deviation from the actual result. In that case, try using 32 bits:

00000000 00000000 00000000 11001100 < < 1 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 00000000, 00000000, 00000001 10011000Copy the code

Type the following code in the console to see the result:

console.log( 0b00000000_00000000_00000000_11001100 << 1 ) // 0b00000000_00000000_00000001_10011000 → 408
Copy the code

As expected, the problem is solved 🤷♂️ with no difficulty.

To take a look at the inverse (~) problem, we first use 32 bits to simulate the operation of the computer logic:

To 00000000, 00000000, 00000000, 11001100 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --, 11111111, 11111111, 11111111, 00110011Copy the code

Remove the first sign bit (negative) and the result is: -2147483443. Well, that’s a bit different from the actual calculation of -205… What went wrong? After several twists and turns, I finally figured out the mystery here:

First to understand a few concepts: original code, inverse code, complement code, do not know these knowledge back to the university teacher?

The original code

The sign bit plus the absolute value of the truth value, i.e. the first bit represents the sign (positive number is 0, negative number is 1), and the remaining bits represent the value. For convenience, the following 8-bit examples are used:

[+1] → [0000 0001

[-1] → [1000 0001] original

Radix-minus-one complement

  • The inverse of a positive number is itself
  • The inverse of a negative number is: the sign bit is unchanged, and the other bits are reversed

[+1] → [0000 0001] Original → [0000 0001] inverse

[-1] → [1000 0001] → [1111 1110

complement

  • The complement of a positive number is itself
  • The complement of a negative number is: + 1 on the basis of the inverse

[+1] → [0000 0001] Original → [0000 0001] inverse → [0000 0001] complement

[-1] → [1000 0001] Original → [1111 1110] inverse → [1111 1111] complement

Complement to find the source code

The calculation method is the same as the original code to complement code

  • If it is positive, the source of the complement is itself
  • If it’s negative, take the inverse first, and then add 1

[0000 0001] Complement → [0000 0001] inverse → [0000 0001] original → [+1]

[1111 1111] → [1000 0000] → [1000 0001] → [-1]

With these concepts in mind, it is important to note that in a computer system, all values are stored as complement. The reason, presumably, is that the use of complementary codes allows the sign bits to be involved in the calculation, making it much easier to design a computer’s numerical operations than to consider the sign bits. So let’s go back to this question:

00000000 00000000 00000000 11001100 Source code 00000000 00000000 00000000 11001100 Source code complement to 11111111 11111111 11111111 00110100 Take the inverse complementCopy the code

After the above simulation operation, the 32-bit value stored in the computer memory should be: 111111111111111111111111 00110100 (complement code), but the source code shown to human is readable, and the complement code needs to be converted to the original code (the first digit is 1, indicating a negative number, which needs to be reversed first and then + 1) :

11111111 11111111 11111111 00110100 Complement 10000000 00000000 00000000 11001011 Reverse +1 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 10000000, 00000000, 00000000, 11001100 the original codeCopy the code

Take the original code to the console and turn it to base 10. Sure enough, this is what we want.

In addition, in JavaScript, the following issues need to be noted:

  1. >>Represents a sign shift to the right (the sign bit will not be moved), for example:-0b1100 >> 1 == -0b0110;
  2. >>>Represents an unsigned right shift, for example:-0b1100 >>> 1 == 2147483642The sign bit has been added during the right shift0Replace the)
  3. JavaScript stores numbers as 64-bit floating-point numbers, butAll bit operations are performed in 32-bit binary. Before performing the bit operation, JavaScript converts the number to32 – bit signed integer, and then convert the result to a 64-bit floating-point number, so it is not difficult to explain the following phenomenon:
    • (2 * * 32) | 0 = 0: The high bits are discarded, and the next 32 bits (including the sign bits) are all zeros, so the result is +0
    • 5.5 | 0 = 5: Bit operations are converted to integers first, and decimals are ignored. This property can be used to round down

Create binary data in JavaScript

JavaScript provides the following classes to create binary data:

  • Int8Array: binary array of 8-bit signed integers
  • Uint8Array: a binary array of 8-bit unsigned integers
  • Uint8ClampedArray: a fixed array of 8-bit unsigned integers
  • Int16Array: binary array of 16-bit signed integers
  • Uint16Array: a binary array of 16-bit unsigned integers
  • Int32Array: binary array of 32-bit signed integers
  • Uint32Array: a binary array of 32-bit unsigned integers
  • Float32Array: a 32-bit binary array of signed floating-point numbers
  • Float64Array: binary array of 64-bit signed floating-point numbers
  • BigInt64Array : A binary array of 64-bit signed large integers, Examples of usage:new BigInt64Array([100n])
  • BigUint64Array : A binary array of 64-bit unsigned large integers, Examples of usage:new BigUint64Array([100n])

All of the above classes inherit from the TypedArray class and can use properties and methods from the TypedArray prototype.

For example, to write a string of data into memory as an 8-bit signed integer:

var arr = new Int8Array([10, -20.30.40.50])

/ / or
var arr = Int8Array.from([10, -20.30.40.50])

arr.buffer // -> Return an ArrayBuffer object
arr[0] / / 10
arr[1] / / - 20
arr[2] / / 30
arr[3] / / 40
arr[4] / / 50
Copy the code

The instance property instance.buffer returns an ArrayBuffer object, which is a buffer of raw binary data. Since a byte has 8 bits (1 byte = 8 bits), Int8Array and Uint8Array are common binary data creation classes. Other use of the create class is similar and can be found in the relevant MDN documentation.

ArrayBuffer and Blob

  • The ArrayBuffer object is used to represent a generic, fixed-length buffer of raw binary data
  • A Blob object represents an immutable, raw data-like file object whose data can be read in text or binary format or converted to ReadableStream for data manipulation

The above is the noun explanation for both in MDN. An ArrayBuffer is of a fixed size and must be allocated space when it is created; Blobs, on the other hand, focus on consuming binary data in a way that is similar to a Buffer and a stream in a node.

Create ArrayBuffer

new ArrayBuffer(10); // Create an ArrayBuffer object of 10 bytes

new Uint8Array([10.20.30.40.50]).buffer; // Create an ArrayBuffer object with the Uint8Array class and write data
Copy the code

Create a Blob

Created by passing in an ArrayBuffer object as a construction parameter:

const buffer = new Uint8Array([10.20.30.40.50]).buffer; 
const blob = new Blob(buffer);
Copy the code

You can also specify an appropriate MIME type:

const blob = new Blob([
  JSON.stringify({ foo: 123.bar: 'hello' }, null.2)] and {type : 'application/json'});
Copy the code

Note: File class inherits from Blob class

Some skills

1. Convert binary data into image urls

const blob = new Blob([buffer]); // Blobs or buffers are typically derived from local file uploads or HTTP response binary data
const url = URL.createObjectURL(blob); // The return value URL can be used directly on the SRC attribute of the IMG tag
Copy the code

2. Obtain/process binary data

This scenario is not normally encountered, but in some binary-dependent transport protocols, handling binaries is a very common operation. Usually the header (the first N bits of binary) stores the metadata of the current data (some flags, data length, etc.), followed by the actual data to be transferred.

For example, in the WebSocket protocol, the first bit is the FIN identifier (messages can be transmitted in fragments, and a FIN value of 1 indicates that the current message has been transmitted). How to obtain the first bit in the binary system? In fact, we can simplify the operation by taking the first bit of the first byte:

// nodejs
const buffer = Buffer.from('... ') // Buffer from WebSocket messages

// Tips: the '&' operator results in 1 only if both bits are 1
// the first digit of 0b10000000 is' 1 ', so there are only two results:
// -0b10000000: the first digit is' 1 '
// -0b00000000: the first digit is 0
const FIN = buffer[0] & 0b10000000= = =0b10000000; // If bit 1 is' 1 ', the result is true
Copy the code

conclusion

The above is the author in the process of learning and exploring some summary, personal ability is limited, inevitably there are mistakes or inappropriate place, please correct ~

Further reading

  • What’s so weird about bitwise computing?
  • The story of a V8 performance cliff in React

reference

  • www.runoob.com/w3cnote/bit…
  • www.w3school.com.cn/js/js_bitwi…
  • Segmentfault.com/a/119000002…