Skip to content

Instantly share code, notes, and snippets.

@Yaffle
Created July 18, 2024 06:41
Show Gist options
  • Save Yaffle/09239924d8bfc3ecb2bac50c5c96c7a6 to your computer and use it in GitHub Desktop.
Save Yaffle/09239924d8bfc3ecb2bac50c5c96c7a6 to your computer and use it in GitHub Desktop.
let hex = new Uint8Array(0);
const td = new TextDecoder();
const te = new TextEncoder();
function packBigInt(blocks, blockSize) {
function encode(blocks, length) {
const u32 = new Uint32Array(hex.buffer);
let k = (length >> 2) - 1;//TODO:!?
for (let i = 0; i < blocks.length; i += 1) {
let e = blocks[i] | 0;
e = (e | (e << 24)) & 0xFF00FF00;
e = (e | (e >>> 12)) & 0x0F0F0F0F;
const m = ((e + 0x16161616) & 0x20202020);
e = ((e + 0x30303030) + (m) + (m >>> 2) - (m >>> 5)) | 0;
u32[k] = e;
k = (k - 1) | 0;
}
}
blockSize |= 0;
if (blockSize % 4 !== 0) {
throw new RangeError();
}
blockSize >>= 2;
if (blockSize > 8) {
let s = '';
s += '0x';
const zeros = '0'.repeat(blockSize);
for (let i = blocks.length - 1; i >= 0; i -= 1) {
const x = blocks[i].toString(16);
if (x.length < blockSize) {
s += zeros.slice(0, blockSize - x.length);
}
s += x;
}
return BigInt(s);
}
let length = (blocks.length * blockSize + 4) | 0;
if (hex.length < length) {
hex = new Uint8Array(length + (length % 4 !== 0 ? 4 - length % 4 : 0));
}
hex[0] = 48; // '0'
hex[1] = 120; // 'x'
hex[2] = 48;
hex[3] = 48;
if (blockSize === 4) {
encode(blocks, length);
} else {
let k = length - 1;
for (let i = 0; i < blocks.length; i += 1) {
let e = blocks[i] | 0;
for (let j = blockSize; j !== 0; j = (j - 1) | 0) {
const r = e & 0xF;
hex[k] = (((r + 48) | 0) + (r < 10 ? 0 : 39)) | 0;
k = (k - 1) | 0;
e >>= 4;
}
}
}
return BigInt(td.decode(hex.subarray(0, length)));
}
function unpackBigInt(bigint, blockSize, blocksCount) {
function decode(blocks, length) {
let k = (length >> 2) - 1;
const u32 = new Uint32Array(hex.buffer);
for (let i = 0; i < length; i += 4) {
let a = u32[i >> 2];
const m = ((a + 0x1F1F1F1F) & 0x80808080);
a = ((a - 0x30303030) - (m >>> 2) - (m >>> 4) + (m >>> 7)) | 0;
a = (a | (a << 12)) & 0xFF00FF00;
a = (a | (a >>> 24)) & 0x0000FFFF;
blocks[k] = a;
k = (k - 1) | 0;
}
}
blockSize |= 0;
blocksCount |= 0;
if (blockSize % 4 !== 0) {
throw new RangeError();
}
blockSize >>= 2;
let s = bigint.toString(16);
if (s.length % blockSize !== 0) {
s = '0'.repeat(blockSize - s.length % blockSize) + s;
}
if (blockSize > 8) {
const blocks = new Array(blocksCount);
let k = s.length;
for (let i = 0; i < blocks.length; i += 1) {
blocks[i] = BigInt('0x' + s.slice(k < blockSize ? 0 : k - blockSize, k));
k -= blockSize;
}
return blocks;
}
if (hex.length < s.length) {
hex = new Uint8Array(s.length);
}
te.encodeInto(s, hex);
const length = s.length | 0;
const blocks = new Array(blocksCount);
if (blockSize === 4) {
decode(blocks, length);
} else {
let k = 0;
for (let i = blocksCount - 1; i >= 0; i -= 1) {
let x = 0;
for (let j = blockSize; j !== 0; j = (j - 1) | 0) {
let a = hex[k] | 0;
a = (a - 48) - (a < 97 ? 0 : 39);
x = (x << 4) | a;
k = (k + 1) | 0;
}
blocks[i] = -0 + x;
}
}
return blocks;
}
const arrayToBigInt2 = packBigInt;
const bigIntToArray2 = function (a, b, c) { return unpackBigInt(a, c, b); };
const xxx = new Map();
function smallBigInt(m) {
let x = xxx.get(m);
if (x == null) {
x = BigInt(m);
xxx.set(m, x);
}
return x;
}
function arrayToBigInt0(array, elementSize) {
function internal(start, end) {
const k = end - start;
if (k >= 2) {
const m = Math.ceil(k / 2);
return (internal(start + m, end) << smallBigInt(m * elementSize)) | internal(start, start + m);
} else if (k === 1) {
return BigInt(array[start]);
} else {
return 0n;
}
}
return internal(0, array.length);
}
function bigIntToArray0(bigint, size, elementSize) {
const array = new Array(size);
function internal(bigint, start, end) {
const k = end - start;
if (k >= 2) {
const m = Math.ceil(k / 2);
const r = BigInt.asUintN(m * elementSize, bigint);
internal(r, start, start + m);
const q = bigint >> smallBigInt(m * elementSize);
internal(q, start + m, end);
} else if (k === 1) {
array[start] = bigint;
}
}
internal(bigint, 0, size);
return array;
}
const xxx64 = [];
function smallBigInt64(m) {
while (m >= xxx64.length) {
xxx64.push(BigInt(xxx64.length * 64));
}
return xxx64[m];
}
function arrayToBigInt(array, elementSize) {
if (elementSize !== 16) {
return arrayToBigInt0(array, elementSize);
}
const u16 = new Uint16Array(array.length + (array.length % 4 === 0 ? 0 : 4 - array.length % 4));
for (let i = 0; i < array.length; i++) {
u16[i] = array[i];
}
const u64 = new BigUint64Array(u16.buffer);
function internal(start, end) {
const k = end - start;
if (k >= 2) {
const m = Math.ceil(k / 2);
return (internal(start + m, end) << smallBigInt64(m)) | internal(start, start + m);
} else if (k === 1) {
return u64[start];
} else {
return 0n;
}
}
return internal(0, u64.length);
}
function bigIntToArray(bigint, size, elementSize) {
if (elementSize !== 16) {
return bigIntToArray0(bigint, size, elementSize);
}
const u64 = new BigInt64Array(Math.ceil(size / 4));
function internal(bigint, start, end) {
const k = end - start;
if (k === 2) {
u64[start] = bigint;
u64[start + 1] = bigint >> 64n;
} else if (k >= 2) {
const m = Math.ceil(k / 2);
const r = BigInt.asIntN(m << 6, bigint);
internal(r, start, start + m);
const q = bigint >> smallBigInt64(m);
internal(q, start + m, end);
} else if (k === 1) {
u64[start] = bigint;
}
}
internal(bigint, 0, size);
return new Uint16Array(u64.buffer);
}
/*
if (elementSize === 4) {
for (let i = 0; i < size; i += 1) {
array[i] = u16[size - 1 - i];
}
} else {
let i = size - 1;
let k = 0;
let bits = 0;
let x = 0;
let elementBitSize = elementSize << 2;
while (k < (length >> 2)) {
x = (x << 16) | u16[k];
k = (k + 1) | 0;
bits = (bits + 16) | 0;
while (bits >= elementBitSize) {
bits = (bits - elementBitSize) | 0;
array[i] = (x >> bits);
i -= 1;
x = (x & ~(-1 << bits));
}
}
}
{
let hex = new Uint8Array([1,2,3,4,5,6,7,8].map(e => e + 0x30));
var array = new Array(2);
var k = 0;
for (var i = 2 - 1; i >= 0; i -= 1) {
let x = 0;
for (let j = 4; j !== 0; j = (j - 1) | 0) {
let a = hex[k] | 0;
k = (k + 1) | 0;
a = (((a - 48) | 0) - (a < 97 ? 0 : 39)) | 0;
x = (x << 4) | a;
}
array[i] = x;
}
debugger;
const u32 = new Uint32Array(hex.buffer);
const u16 = new Uint16Array(hex.buffer);
for (let i = 0; i < u32.length; i++) {
let a = u32[i];
//a = (a - 48) - (a < 97 ? 0 : 39);
const m = ((a + 0x1F1F1F1F) & 0x80808080);
a = (a - 0x30303030) - (m >>> 2) - (m >>> 4) + (m >>> 7);
a = (a | (a << 12)) & 0xFF00FF00;
a = (a | (a >> 24)) & 0x0000FFFF;
u16[i] = a;
}
debugger;
console.log(u32, u16);
}
const te = new TextEncoder();
function bigIntToArray2(bigint, size, elementSize) {
size |= 0;
elementSize |= 0;
if (elementSize % 4 !== 0) {
throw new RangeError();
}
elementSize >>= 2;
let s = bigint.toString(16);
if (s.length % 4 !== 0) {
s = '0'.repeat(4 - s.length % 4) + s;
}
if (elementSize > 8) {
const array = new Array(size);
let k = s.length;
for (let i = 0; i < array.length; i += 1) {
array[i] = BigInt('0x' + s.slice(k < elementSize ? 0 : k - elementSize, k));
k -= elementSize;
}
return array;
}
const hex = te.encode(s);
const u32 = new Uint32Array(hex.buffer);
const u16 = new Uint16Array(hex.buffer);
for (let i = 0; i < u32.length; i++) {
let a = u32[i];
//a = (a - 48) - (a < 97 ? 0 : 39);
const m = ((a + 0x1F1F1F1F) & 0x80808080);
a = (a - 0x30303030) - (m >>> 2) - (m >>> 4) + (m >>> 7);
a = (a | (a << 12)) & 0xFF00FF00;
a = (a | (a >> 24)) & 0x0000FFFF;
u16[i] = a;
}
const array = [];//new Array(size);
let j = 0;
let k = u32.length - 1;
let bits = 0;
let x = 0;
let elementBitSize = elementSize << 2;
while (k >= 0) {
x = (x << 16) | u16[k];
bits += 16;
while (bits >= elementBitSize) {
const d = bits === elementBitSize ? x : (x >> (bits - elementBitSize));
//array[j] = d;
array.push(d);
j += 1;
x = bits === elementBitSize ? 0 : (x & ~(-1 << (bits - elementBitSize)));
bits -= elementBitSize;
}
k -= 1;
}
return array;
}
*/
console.assert(arrayToBigInt2([1, 2, 3], 8).toString(16) === '30201');
console.assert(bigIntToArray2(0x030201n, 3, 8).join(',') === '1,2,3' || bigIntToArray2(0x030201n, 3, 8).join(',') === '1,2,3,0');
console.assert(arrayToBigInt([1, 2, 3], 8).toString(16) === '30201');
console.assert(bigIntToArray(0x030201n, 3, 8).join(',') === '1,2,3');
console.assert(arrayToBigInt2([1, 2, 3], 16).toString(16) === '300020001');
console.assert(bigIntToArray2(0x300020001n, 3, 16).join(',') === '1,2,3');
console.assert(arrayToBigInt([1, 2, 3], 16).toString(16) === '300020001');
console.assert(bigIntToArray(0x300020001n, 3, 16).join(',') === '1,2,3' || bigIntToArray(0x300020001n, 3, 16).join(',') === '1,2,3,0');
console.assert(arrayToBigInt2([1, 2, 3], 20).toString(16) === '30000200001');
console.assert(bigIntToArray2(0x30000200001n, 3, 20).join(',') === '1,2,3');
console.assert(arrayToBigInt([1, 2, 3], 20).toString(16) === '30000200001');
console.assert(bigIntToArray(0x30000200001n, 3, 20).join(',') === '1,2,3');
console.assert(arrayToBigInt2([1, 2, 3], 24).toString(16) === '3000002000001');
console.assert(bigIntToArray2(0x3000002000001n, 3, 24).join(',') === '1,2,3');
console.assert(arrayToBigInt([1, 2, 3], 24).toString(16) === '3000002000001');
console.assert(bigIntToArray(0x3000002000001n, 3, 24).join(',') === '1,2,3');
console.assert(arrayToBigInt2([1, 2, 3], 28).toString(16) === '300000020000001');
console.assert(bigIntToArray2(0x300000020000001n, 3, 28).join(',') === '1,2,3');
console.assert(arrayToBigInt([1, 2, 3], 28).toString(16) === '300000020000001');
console.assert(bigIntToArray(0x300000020000001n, 3, 28).join(',') === '1,2,3');
console.assert(arrayToBigInt2([1, 2, 3], 32).toString(16) === '30000000200000001');
console.assert(bigIntToArray2(0x30000000200000001n, 3, 32).join(',') === '1,2,3');
console.assert(arrayToBigInt([1, 2, 3], 32).toString(16) === '30000000200000001');
console.assert(bigIntToArray(0x30000000200000001n, 3, 32).join(',') === '1,2,3');
console.assert(arrayToBigInt2([1, 2, 3], 48).toString(16) === '3000000000002000000000001');
console.assert(bigIntToArray2(0x3000000000002000000000001n, 3, 48).join(',') === '1,2,3');
console.assert(arrayToBigInt([1, 2, 3], 48).toString(16) === '3000000000002000000000001');
console.assert(bigIntToArray(0x3000000000002000000000001n, 3, 48).join(',') === '1,2,3');
console.assert(arrayToBigInt2([1, 2, 3], 96).toString(16) === '3000000000000000000000002000000000000000000000001');
console.assert(bigIntToArray2(0x3000000000000000000000002000000000000000000000001n, 3, 96).join(',') === '1,2,3');
console.assert(arrayToBigInt([1, 2, 3], 96).toString(16) === '3000000000000000000000002000000000000000000000001');
console.assert(bigIntToArray(0x3000000000000000000000002000000000000000000000001n, 3, 96).join(',') === '1,2,3');
const count = 100000;
const size = 2**14;
//const count = 2;
//const size = 2**29;
const maxBigInt = BigInt.asUintN(size, -1n);
//TODO: real world example from the multiplication (?)
//and forget
for (let bits = 16; bits <= 16; bits = bits + 48) {
//console.time('bigIntToArray:' + bits);
//for (let i = 0; i < count; i++) {
//var buffer = bigIntToArray(maxBigInt, Math.ceil(size / bits), bits)
//}
//console.timeEnd('bigIntToArray:' + bits);
console.time('bigIntToArray2:' + bits);
for (let i = 0; i < count; i++) {
var buffer2 = bigIntToArray2(maxBigInt, Math.ceil(size / bits), bits)
}
console.timeEnd('bigIntToArray2:' + bits);
// bigIntToArray2:16: 1379.4541015625 ms
//console.time('arrayToBigInt:' + bits);
//for (let i = 0; i < count; i++) {
//var a = arrayToBigInt(buffer, bits);
//}
//console.timeEnd('arrayToBigInt:' + bits);
console.time('arrayToBigInt2:' + bits);
for (let i = 0; i < count; i++) {
var a2 = arrayToBigInt2(buffer2, bits);
}
console.timeEnd('arrayToBigInt2:' + bits);
// arrayToBigInt2:16: 1523.404052734375 ms
//console.assert(maxBigInt === a);
console.assert(maxBigInt === a2);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment