Skip to content

Instantly share code, notes, and snippets.

@diegovgsilva95
Created January 6, 2025 02:12
Show Gist options
  • Save diegovgsilva95/1655b20d13fe645ca7420fe5bf22c0ff to your computer and use it in GitHub Desktop.
Save diegovgsilva95/1655b20d13fe645ca7420fe5bf22c0ff to your computer and use it in GitHub Desktop.
JS - Splitting binary data into k-bit chunks (1<=k<=8)

test.mjs

Initial development of the bitwise approach, debugging and testing it against a lazy-man approach. Uses cli-table and chalk as dependencies for better visualization.

index.mjs

Refined and modularized code, containing just the bitwise approach as a function, followed by an invocation example.

Possible use-case scenarios

  • Base64 encoding/decoding
  • Perhaps experimentation with baudot code?

util.mjs (not included)

  • clear() is a helper function for clearing the console (instead of using console.clear(), it's a function to print the ANSI escape sequence \x1b[H\x1b[2J\x1b[3J to the stdout, effectively clearing both the screen and the scrollback buffer)
  • log() is a redirected export of console.log(), so I don't need to use console.log all the time, nor need to use a second import (e.g. import {log} from "console")
  • sleep() (used only by test.mjs) returns a Promise which fulfills after a given time, effectively delaying a program flow through await sleep(x).
// Better looking code.
import { clear, log } from "./util.mjs"
function ktetize(payload, k = 8){
if(isNaN(k) || k%1 != 0 || k > 8 || k <= 0)
throw new RangeError(`Invalid k = ${k}, must be 1<=k<=8, k being integer`)
if(!(payload instanceof Buffer))
payload = Buffer.from(payload)
let ktets = []
let remSize = 0
let remVal = 0
for(let i = 0; i < payload.length; i++){
/** @type {number} */
let byte = payload[i]
if(k < 8)
while(remSize >= k){
ktets.push(remVal>>(remSize-k))
remSize = remSize - k
remVal = remVal & ((1<<remSize)-1)
}
let remOffset = k-remSize
ktets.push((remVal<<(remOffset)) + (byte >> (8-remOffset)))
remSize = remSize + (8-k)
remVal = byte & ((1<<remSize)-1)
}
while(remSize > 0){
let nex = remVal>>(Math.max(0,remSize-k))
if(remSize - k < 0){
nex = nex << (k-remSize)
}
ktets.push(nex)
remSize = remSize - k
remVal = remVal & ((1<<remSize)-1)
}
return ktets
}
clear()
let ktetization = ktetize(Buffer.from("UUUUUUÿÿÿÿÿÿÿ","ascii"),6)
log("Ktets: ", ktetization)
log("Binary stream: \n" + ktetization.map(ktet => ktet.toString(2).padStart(5,"0")).join(" "))
// Warning: messy code ahead, for testing purposes.
import { clear, log, sleep } from "./util.mjs"
import Table from "cli-table"
import chalk from "chalk"
clear()
// let payload = Buffer.from(Array.from(Array(11),_=>219)) // 219 => 11011011
let payload = Buffer.from("UUUUUUÿÿÿÿÿÿÿ","ascii") // ... however, U (0101 0101) followed by ÿ (1111 1111) seem better to visualize.
let k = 5
const table = initTable()
let xRemTet = ""
let xKtets = []
let ktets = []
let remSize = 0
let remVal = 0
for(let i = 0; i < payload.length; i++){
let deb = []; table.push(deb)
if(k == 0 || k > 8)
throw new Error("Invalid k")
let byte = payload[i]
// Firstly, the algorithm which computes the expected thing in a verbose way
// ;(function lazyman(){
let octet = sbin(byte)
deb.push(octet.replace(new RegExp(`^([01]{${k-xRemTet.length}})([01]*)`), chalk.green("$1") + chalk.yellow("$2")))
let xPushed = []
deb.push(xRemTet.length)
if(k < 8)
while(xRemTet.length >= k){
let xNex = xRemTet.slice(0, k)
xPushed.push(xNex)
xKtets.push(xNex)
xRemTet = xRemTet.slice(k)
}
deb[0]=(octet.replace(new RegExp(`^([01]{${k-xRemTet.length}})([01]*)`), chalk.green("$1") + chalk.yellow("$2")))
deb.push(xRemTet.length)
deb.push(xRemTet)
let xComplTet = octet.slice(0, k-xRemTet.length)
deb.push(chalk.green(xComplTet))
let xFullTet = xRemTet + xComplTet
deb.push(xFullTet.replace(new RegExp(`([01]{${xComplTet.length}})$`), chalk.green("$1")))
if(xFullTet.length > k){
console.log(table.toString())
throw Error("")
}
xKtets.push(xFullTet)
xPushed.push(xFullTet)
let xNextRemTet = (8-xComplTet.length) == 0 ? "" : octet.slice(-(8-xComplTet.length))
deb.push(chalk.yellow(xNextRemTet))
deb.push(xPushed.join(","))
xRemTet = xNextRemTet
// })()
deb.push("")
clear()
console.log(table.toString())
// Then, the bitwise algorithm
// ;(function bitwise(){
let pushed = []
deb.push(remSize)
if(k < 8)
while(remSize >= k){
let nex = remVal>>(remSize-k)
pushed.push(sbin(nex, k, false))
ktets.push(nex)
remSize = remSize - k
remVal = remVal & ((1<<remSize)-1)
}
deb.push(remSize)
deb.push(sbin(remVal,0,false))
let remOffset = k-remSize
deb.push(remOffset)
let complTet = byte >> (8-remOffset)
deb.push(chalk.green(sbin(complTet,remOffset,false)))
let fullTet = (remVal<<(remOffset)) + complTet
deb.push(sbin(fullTet))
ktets.push(fullTet)
pushed.push(sbin(fullTet,k,false))
remSize = remSize + (8-k)
let nextRemTet = byte & ((1<<remSize)-1)
deb.push(sbin(nextRemTet, 0, false))
remVal = nextRemTet
deb.push(pushed.join(","))
// })()
clear()
console.log(table.toString())
}
while(xRemTet.length > 0){
let xNex = xRemTet.slice(0, k)
if(xNex.length < k){
xNex += "0".repeat(k-xNex.length)
}
xKtets.push(xNex)
xRemTet = xRemTet.slice(k)
await sleep(10)
}
clear()
while(remSize > 0){
let nex = remVal>>(Math.max(0,remSize-k))
if(remSize - k < 0){
nex = nex << (k-remSize)
}
ktets.push(nex)
remSize = remSize - k
remVal = remVal & ((1<<remSize)-1)
await sleep(10)
}
console.log(table.toString())
let xKtetsStream = xKtets.join(" ")
let ktetsStream = ktets.map(x=>sbin(x,k,false)).join(" ")
let payloadStream = [...payload].map(b=>b.toString(2).padStart(8,"0")).join("").match(new RegExp(`[01]{1,${k}}`,"g"))?.join(" ") || ""
log(xKtetsStream)
log(payloadStream)
log(ktetsStream)
log("")
log("Lazy == payload?", xKtetsStream.startsWith(payloadStream))
log("Bitw == lazy? ", ktetsStream == xKtetsStream)
function initTable(){
return new Table({
head: [
"Octet", "ERS", "ERS", "ExRem", "ExComp", "ExFull", "ExNRem", "ExOut",
"",
"Rem#", "Rem#", "Rem", "<<", "Comp", "Full", "NRem", "Out"
],
colAligns: [
"middle", "middle", "middle", "right", "left", "middle", "right"
],
chars: {
"mid": "",
"left-mid": "",
"right-mid": "",
"mid-mid": "",
"left": "",
"top-left": "\b",
"bottom-left": "\b",
"right": "",
"top-right": "",
"bottom-right": ""
},
colWidths: [
8,3,3,8,8,8,8,15,
0,
4,4,4,2,8,8,8,15
],
style: {
"padding-left": 0,
"padding-right": 0
}
});
}
function sbin(v = 0, bits = 8,strang=true){
let b = v.toString(2)
if(bits > 0)
b = b.padStart(bits, "0")
if(strang)
b = b.substring(0, bits)
return b
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment