I've been doing some work on logical arithmetic recently. I want to encode unsigned integers (Uint's) and signed integers (Int's) as bytes.
Below is a code snippet that currently translates UInts to bytes and vice versa. Unfortunately I got some problems while encoding signed integers with my tactic.
Encoding for Integers will be performed using this shift and mask method for both - unsigned and signed integers.
for (let i=0; i<n; i++)
buffer.push((value >>> (i*8)) & 0xFF);
So what is wrong with my readInt-method and how to fix this?
const buffer = [];
const writeIntN = (value, n) => {
for (let i=0; i<n; i++)
buffer.push((value >>> (i*8)) & 0xFF);
}
const readUint = (n, offset) => {
let v=0;
for (let i=0; i<n; i++)
v |= (buffer[offset++] << (i*8));
return v >>> 0;
}
const readInt = (n, offset) => {
return readUint(n, offset) << (8*n) >> (8*n); // error is here
}
const writeUInt16 = (value) => writeIntN(value, 2);
const readUInt16 = (offset) => readUint(2, offset);
const writeInt16 = (value) => writeIntN(value, 2);
const readInt16 = (offset) => readInt(2, offset);
const writeInt8 = (value) => writeIntN(value, 1);
const readInt8 = (offset) => readInt(1, offset);
writeUInt16(20);
writeInt16(-20);
writeInt8(-42);
console.log(buffer);
console.log(readUInt16(0));
console.log(readInt16(2));
console.log(readInt8(4)); // how?
Note: I know about the basic operations to decode 8,16-bit signed ints but how to get this inside the for loop in readUint more efficient?
Decoding 8-bit signed integers: (buffer[offset++] << 24) >> 24
Decoding 16-bit signed integers: (((buffer[offset++] << 0) | (buffer[offset++] << 8)) << 16) >> 16
So what is wrong with my readInt-method and how to fix this?
The fix: change (8*n) to (8*(4-n)).
const readInt = (n, offset) => {
return readUint(n, offset) << (8*(4-n)) >> (8*(4-n));
}
Or, equivalently,
const readInt = (n, offset) => {
let v =0;
for (let i=4-n; i<4; i++)
v |= (buffer[offset++] << (i*8));
return v >> (8*(4-n));
}
JavaScript bitwise operators use 32-bit (4-byte) integers. The left shift (<<) operator discards bits shifted off to the left. The right shift operator (>>) propagates the leftmost (sign) bit.
console.log();
writeUInt16(20);
writeInt16(-20);
writeInt8(-42);
console.log(buffer);
console.log(readUInt16(0));
console.log(readInt16(2));
console.log(readInt8(4));
[ 20, 0, 236, 255, 214 ]
20
-20
-42
const buffer = [];
const writeIntN = (value, n) => {
for (let i=0; i<n; i++)
buffer.push((value >>> (i*8)) & 0xFF);
}
const readUint = (n, offset) => {
let v=0;
for (let i=0; i<n; i++)
v |= (buffer[offset++] << (i*8));
return v >>> 0;
}
const readInt = (n, offset) => {
return readUint(n, offset) << (8*(4-n)) >> (8*(4-n));
}
const writeUInt32 = (value) => writeIntN(value, 4);
const readUInt32 = (offset) => readUint(4, offset);
const writeInt32 = (value) => writeIntN(value, 4);
const readInt32 = (offset) => readInt(4, offset);
const writeUInt16 = (value) => writeIntN(value, 2);
const readUInt16 = (offset) => readUint(2, offset);
const writeInt16 = (value) => writeIntN(value, 2);
const readInt16 = (offset) => readInt(2, offset);
const writeUInt8 = (value) => writeIntN(value, 1);
const readUInt8 = (offset) => readUint(1, offset);
const writeInt8 = (value) => writeIntN(value, 1);
const readInt8 = (offset) => readInt(1, offset);
writeUInt32(Math.pow(2,32)-1);
writeInt32((1<<31)-1);
writeInt32(-(1<<31));
writeUInt16((1<<16)-1);
writeInt16((1<<15)-1);
writeInt16(-(1<<15));
writeUInt8((1<<8)-1);
writeInt8((1<<7)-1);
writeInt8(-(1<<7));
console.log(buffer);
console.log(readUInt32(0));
console.log(readInt32(4));
console.log(readInt32(8));
console.log(readUInt16(12));
console.log(readInt16(14));
console.log(readInt16(16));
console.log(readUInt8(18));
console.log(readInt8(19));
console.log(readInt8(20));
[
255, 255, 255, 255, 255, 255,
255, 127, 0, 0, 0, 128,
255, 255, 255, 127, 0, 128,
255, 127, 128
]
4294967295
2147483647
-2147483648
65535
32767
-32768
255
127
-128