6

problem: in video-games there are tons of low precision numbers that can be packed together over the network to dramatically save bandwidth compared to sending strings. Strings are allocated to UTF-8 which uses 1 byte for each character.

Ideally there should be a way to write these numbers together:

  • player id in game- precision 0-1023 range, 10 bits
  • player rotation- quaternion- a few numbers ending up as 24 bits after some math simplification
  • player inputs- 0-1 range x2, 2 bits

How do you take low precision numbers like this and put them in a array buffer / blob?

hydrix
  • 332
  • 2
  • 9
  • 1
    *"Strings are allocated to UTF-8 which uses 1 byte for each character."* Or two, or three, or four, depending on the character. For instance, `ç` (as in François) is two bytes in UTF-8, as are `£` (the Pound Sterling sign) and `¥` (yen); `€` (the Euro sign) is three bytes. – T.J. Crowder Dec 28 '16 at 08:35
  • Notice that the total number of bits in OPs example is 36 which makes this problem a little more complicated (if compact storage is the goal, that is). –  Dec 28 '16 at 23:23
  • yes and the numbers have to align properly despite bit shrinkage too, so some method to ensure a fixated length is required – hydrix Dec 29 '16 at 01:46

3 Answers3

2

You can use a Uint32Array, and then use bit shift and mask operations to store values in that array.

For example, if you wanted to store a 4 bit number, and then a 10 bit number (leaving 18 bits left over for more fields):

array[0] = (num0 & 0x0f) << 0) |
           (num1 & 0x3ff) << 4);

and to extract those fields:

num0 = (array[0] >>> 0) & 0x0f;
num1 = (array[0] >>> 4) & 0x3ff;

The array can be accessed as an ArrayBuffer for serialisation by accessing its .buffer property.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • do you mean use bit mask to add values, then bit shift the number of bits assigned to slide it over? Because bitwise operators are still 32-bit and I'm assuming that's the only way to gracefully remove the preceding zeros – hydrix Dec 29 '16 at 03:11
  • 1
    you should still use fixed width fields, but just use as many bits as are required to store the full range of the number in question. – Alnitak Dec 29 '16 at 07:59
1

based on Alnitak's answer:

function binPush(arr, i, num, max) {
     arr[i] = arr[i] << max.toString(2).length; //shift int32 $max (in base 2) bits to the left to allow for allocation
     arr[i] |= num; // OR bitwise operation, which copies the 1s from $num

}

var myArr = new Uint32Array(1);

binPush(myArr, 0, 3, 3);     // push 11:       00000000000000000000000000000011
binPush(myArr, 0, 10, 15);   // push 1010:     00000000000000000000000000111010
binPush(myArr, 0, 120, 127); // push 1111000:  00000000000000000001110101111000
binPush(myArr, 0, 120, 255); // push 01111000: 00000000000111010111100001111000

notice how the last binPush adds an extra 0 to the front because the max is 255, which is exactly 8 bits whereas 120 is 7 bits

jsfiddle

hydrix
  • 332
  • 2
  • 9
0

Perhaps MathewBarker's bit-stream could be of some help, here.

Haroldo_OK
  • 6,612
  • 3
  • 43
  • 80