Skip to content

Instantly share code, notes, and snippets.

@cowboy
Last active January 1, 2024 17:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cowboy/3b42ce23540bc96b453d20ac6e0905d7 to your computer and use it in GitHub Desktop.
Save cowboy/3b42ce23540bc96b453d20ac6e0905d7 to your computer and use it in GitHub Desktop.
bit-string-encoding.js
// 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)
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