Last active
January 1, 2024 17:41
-
-
Save cowboy/3b42ce23540bc96b453d20ac6e0905d7 to your computer and use it in GitHub Desktop.
bit-string-encoding.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://codesandbox.io/s/bit-string-compression-zydb3?file=/src/App.js | |
const name = 'test_example' | |
const width = 48 | |
const height = 48 | |
const arr = [ | |
0xffffff000000, | |
0x8000017ffffe, | |
0xbffffd400002, | |
0xa00005400002, | |
0xaffff54ffff2, | |
0xa80015480012, | |
0xabffd5480012, | |
0xaa005549ff92, | |
0xaa4255490092, | |
0xaa7e55490092, | |
0xaa8155493c92, | |
0xaaa555492492, | |
0xaa8155492492, | |
0xaa7e55492492, | |
0xaa3c55492092, | |
0xaaff55492092, | |
0xaa0055493f92, | |
0xabffd5490012, | |
0xa80015490012, | |
0xaffff549fff2, | |
0xa00005480002, | |
0xbffffd480002, | |
0x8000014ffffe, | |
0xffffff000000, | |
0x000000ffffff, | |
0x000000800001, | |
0x3ffffcbffffd, | |
0x200004a00005, | |
0x200004affff5, | |
0x27ffe4a80015, | |
0x240024abffd5, | |
0x25e7a4aa0055, | |
0x25c3a4aa6555, | |
0x2581a4aa9555, | |
0x2518a4aa9555, | |
0x243c24aa9655, | |
0x243c24aa9555, | |
0x2518a4aa9555, | |
0x2581a4aa9555, | |
0x25c3a4aa6555, | |
0x25e7a4aa0055, | |
0x240024abffd5, | |
0x27ffe4a80015, | |
0x200004affff5, | |
0x200004a00005, | |
0x3ffffcbffffd, | |
0x000000800001, | |
0x000000ffffff | |
] | |
const p = (...args) => console.log(...args) | |
// Modified from | |
// https://stackoverflow.com/a/13408680 | |
// https://jsfiddle.net/y2rma/ | |
function bitstringEncode2({ name, width, height, bitStr }) { | |
let chars = '' | |
for (let i = 0; i < bitStr.length; i += 10) { | |
const bitChunk = bitStr.slice(i, i + 10) | |
const binaryStr = `0b${bitChunk}${'0'.repeat(10 - bitChunk.length)}` | |
const base32str = Number(binaryStr).toString(32) | |
chars += `0${base32str}`.slice(-2) | |
} | |
return [name, width, height, chars].join('/') | |
} | |
function bitstringDecode2(str) { | |
const [name, widthStr, heightStr, chars] = str.split('/') | |
let bitStr = '' | |
for (let i = 0; i < chars.length; i += 2) { | |
const charStr = chars.slice(i, i + 2) | |
const binStr = parseInt(charStr, 32).toString(2) | |
bitStr += `${'0'.repeat(10 - binStr.length)}${binStr}` | |
} | |
const width = parseInt(widthStr, 10) | |
const height = parseInt(heightStr, 10) | |
bitStr = bitStr.slice(0, width * height) | |
return { name, width, height, bitStr } | |
} | |
const numArrToBinArr = (arr, width) => { | |
return arr.map((n) => { | |
const s = n.toString(2) | |
return `${'0'.repeat(width - s.length)}${s}` | |
}) | |
} | |
const printBitmapArr = (arr, width) => { | |
console.log( | |
numArrToBinArr(arr, width) | |
.map((s) => s.replace(/1/g, 'x').replace(/0/g, ' ')) | |
.join('\n') | |
) | |
} | |
printBitmapArr(arr, width) | |
p('arr', arr) | |
const bitStrArr = arr.map((n) => n.toString(2)) | |
const bitStr = bitStrArr | |
.map((s) => `${'0'.repeat(width - s.length)}${s}`) | |
.join('') | |
p('bitStr', bitStr) | |
p('bitStr length', bitStr.length) | |
const encodedBitStr = btoa(bitStr) | |
p('base64 encodedBitStr', encodedBitStr) | |
p('base64 encodedBitStr length', encodedBitStr.length) | |
const byteStrArr = bitStr.split('').reduce( | |
(acc, char) => { | |
const prev = acc.slice(0, -1) | |
const last = acc[prev.length] | |
if (last.length < 8) { | |
return [...prev, `${last}${char}`] | |
} else { | |
return [...prev, last, char] | |
} | |
}, | |
[[]] | |
) | |
p('byteStrArr', byteStrArr) | |
const byteArr = byteStrArr.reduce((acc, byteStr, i) => { | |
acc[i] = Number(`0b${byteStr}`) | |
return acc | |
}, new Uint8Array(new ArrayBuffer(byteStrArr.length))) | |
p('byteArr', byteArr) | |
const encodedByteArray = btoa(byteArr) | |
p('base64 encodedByteArray', encodedByteArray) | |
p('base64 encodedByteArray length', encodedByteArray.length) | |
const encoded2 = bitstringEncode2({ name, width, height, bitStr }) | |
p('encoded2', encoded2) | |
p('encoded2 length', encoded2.length) | |
const { bitStr: bitStrOutput, ...rest } = bitstringDecode2(encoded2) | |
p('decoded', rest) | |
p('decoded.bitStr', bitStrOutput) | |
p('decoded.bitStr length', bitStrOutput.length) | |
p('decoded.bitStr === binStr', bitStrOutput === bitStr) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bitstringEncode = ({ width, height, bitString }) => { | |
let chars = '' | |
for (let i = 0; i < bitString.length; i += 10) { | |
const bitChunk = bitString.slice(i, i + 10) | |
const binaryStr = `0b${bitChunk}${'0'.repeat(10 - bitChunk.length)}` | |
const base32str = Number(binaryStr).toString(32) | |
chars += `0${base32str}`.slice(-2) | |
} | |
return [width, height, chars].join('/') | |
} | |
bitstringDecode = (encodedData) => { | |
const [widthStr, heightStr, chars] = Array.isArray(encodedData) | |
? encodedData | |
: encodedData.split('/') | |
let bitString = '' | |
for (let i = 0; i < chars.length; i += 2) { | |
const charStr = chars.slice(i, i + 2) | |
const binStr = parseInt(charStr, 32).toString(2) | |
bitString += `${'0'.repeat(10 - binStr.length)}${binStr}` | |
} | |
const width = parseInt(widthStr, 10) | |
const height = parseInt(heightStr, 10) | |
bitString = bitString.slice(0, width * height) | |
return { width, height, bitString } | |
} | |
getRandomBitString = (width, height) => | |
Array.from({ length: width * height }, () => | |
Math.random() > 0.5 ? '1' : '0' | |
).join('') | |
size = 48 | |
input = [] | |
for (height = 1; height < size; height++) { | |
for (width = 1; width < size; width++) { | |
bitString = getRandomBitString(width, height) | |
input.push({ width, height, bitString }) | |
} | |
} | |
result = input.every((input) => { | |
const encoded = bitstringEncode(input) | |
const decoded = bitstringDecode(encoded) | |
const bitStringMatch = input.bitString === decoded.bitString | |
const widthMatch = input.width === decoded.width | |
const heightMatch = input.height === decoded.height | |
const allMatch = bitStringMatch && widthMatch && heightMatch | |
if (!allMatch) { | |
console.log({ widthMatch, heightMatch, bitStringMatch, input, decoded }) | |
} | |
return allMatch | |
}) | |
console.log(result ? 'all passed' : 'fail') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment