Last active
April 21, 2021 07:53
-
-
Save iczero/a03255b5d5de1f22cbc21e323622347e to your computer and use it in GitHub Desktop.
stupid unicode things
This file contains 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
// @ts-check | |
const { Transform } = require('stream'); | |
const zlib = require('zlib'); | |
// abuse varwidth fonts by encoding bytes to very thin unicode characters | |
// for best results run calculateWidths() with different fonts and devices | |
/* run this on about:blank | |
let root = document.createElement('div'); | |
document.body.appendChild(root); | |
for (let i = 0; i < 65536; i++) { | |
let el = document.createElement('span'); | |
el.dataset.index = i; | |
el.innerText = String.fromCharCode(i); | |
root.appendChild(el); | |
} | |
function calculateWidths() { | |
let dimensions = []; | |
for (let el of root.children) { | |
let width = el.getBoundingClientRect().width; | |
if (width < 1) continue; | |
dimensions.push([width, +el.dataset.index, el.innerText]); | |
} | |
return dimensions.sort((a, b) => a[0] - b[0]); | |
} | |
*/ | |
const CHARS = [ | |
6211, 6130, 4170, 5194, 737, 8343, 8260, 8725, | |
6154, 690, 3856, 2417, 7502, 7522, 8305, 10649 | |
].map(i => String.fromCharCode(i)); | |
/* use the following instead for zero-width control characters: | |
const CHARS = [ | |
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143 | |
].map(i => String.fromCharCode(i)); | |
*/ | |
const CHARS_REVERSE = new Map(CHARS.map((char, idx) => [char, idx])); | |
const CHARS_SET = new Set(CHARS); | |
/** | |
* Encode byte to string | |
* @param {number} n | |
* @return {string} | |
*/ | |
function encode(n) { | |
return CHARS[n >> 4] + CHARS[n & 0xf]; | |
} | |
/** | |
* Decode string to byte | |
* @param {string|[string, string]} s | |
* @return {number} | |
*/ | |
function decode(s) { | |
return (CHARS_REVERSE.get(s[0]) << 4) + CHARS_REVERSE.get(s[1]); | |
} | |
/** | |
* Encode buffer to string | |
* @param {Buffer} buf | |
* @return {string} | |
*/ | |
function encodeBuffer(buf) { | |
return Array.prototype.map.call(buf, encode).join(''); | |
} | |
/** | |
* Decode string to buffer | |
* @param {string} s | |
* @return {Buffer} | |
*/ | |
function decodeBuffer(s) { | |
let encoded = s.split('').filter(Set.prototype.has.bind(CHARS_SET)); | |
let decoded = []; | |
for (let i = 0; i < (encoded.length - encoded.length % 2); i += 2) { | |
decoded.push(decode(/** @type {[string, string]} */ (encoded.slice(i, i + 2)))); | |
} | |
return Buffer.from(decoded); | |
} | |
/** Transform stream for encoding */ | |
class EncodeStream extends Transform { | |
/** | |
* stream.Transform _transform method | |
* @param {Buffer} data | |
* @param {string} _encoding | |
* @param {(err: Error) => void} callback | |
*/ | |
_transform(data, _encoding, callback) { | |
this.push(Buffer.from(encodeBuffer(data), 'utf-8')) | |
callback(null); | |
} | |
} | |
/** Transform stream for decoding */ | |
class DecodeStream extends Transform { | |
/** | |
* The constructor | |
* @param {object} [opts] | |
*/ | |
constructor(opts) { | |
super(opts); | |
this.partial = false; | |
this.byte = 0; | |
} | |
/** | |
* stream.Transform _transform method | |
* @param {Buffer} data | |
* @param {string} _encoding | |
* @param {(err: Error) => void} callback | |
*/ | |
_transform(data, _encoding, callback) { | |
let dataString = data.toString('utf-8'); | |
let out = []; | |
for (let i = 0; i < dataString.length; i++) { | |
let n = CHARS_REVERSE.get(dataString[i]); | |
if (typeof n === 'undefined') continue; | |
if (this.partial) { | |
out.push(this.byte | n); | |
this.partial = false; | |
} else { | |
this.byte = n << 4; | |
this.partial = true; | |
} | |
} | |
if (out.length) this.push(Buffer.from(out)); | |
callback(null); | |
} | |
} | |
if (require.main === module) { | |
let args = process.argv.slice(2); | |
/** @type {import('stream').Stream} */ | |
let s = process.stdin; | |
if (args.includes('-d')) { | |
s = s.pipe(new DecodeStream()); | |
if (args.includes('-c')) s = s.pipe(zlib.createBrotliDecompress()); | |
} else { | |
if (args.includes('-c')) s = s.pipe(zlib.createBrotliCompress()); | |
s = s.pipe(new EncodeStream()); | |
} | |
s.pipe(process.stdout); | |
} | |
exports.encode = encode; | |
exports.decode = decode; | |
exports.encodeBuffer = encodeBuffer; | |
exports.decodeBuffer = decodeBuffer; | |
exports.EncodeStream = EncodeStream; | |
exports.DecodeStream = DecodeStream; |
This file contains 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
// @ts-check | |
const { Transform } = require('stream'); | |
const zlib = require('zlib'); | |
// abuse all fonts by encoding bytes to combining diacriticals | |
const CHARS = [ | |
768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783 | |
].map(i => String.fromCharCode(i)); | |
const CHARS_REVERSE = new Map(CHARS.map((char, idx) => [char, idx])); | |
const CHARS_SET = new Set(CHARS); | |
/** | |
* Encode byte to string | |
* @param {number} n | |
* @return {string} | |
*/ | |
function encode(n) { | |
return CHARS[n >> 4] + CHARS[n & 0xf]; | |
} | |
/** | |
* Decode string to byte | |
* @param {string|[string, string]} s | |
* @return {number} | |
*/ | |
function decode(s) { | |
return (CHARS_REVERSE.get(s[0]) << 4) + CHARS_REVERSE.get(s[1]); | |
} | |
/** | |
* Encode buffer to string | |
* @param {Buffer} buf | |
* @return {string} | |
*/ | |
function encodeBuffer(buf) { | |
return Array.prototype.map.call(buf, encode).join(''); | |
} | |
/** | |
* Decode string to buffer | |
* @param {string} s | |
* @return {Buffer} | |
*/ | |
function decodeBuffer(s) { | |
let encoded = s.normalize('NFKD').split('').filter(Set.prototype.has.bind(CHARS_SET)); | |
let decoded = []; | |
for (let i = 0; i < (encoded.length - encoded.length % 2); i += 2) { | |
decoded.push(decode(/** @type {[string, string]} */ (encoded.slice(i, i + 2)))); | |
} | |
return Buffer.from(decoded); | |
} | |
/** Transform stream for encoding */ | |
class EncodeStream extends Transform { | |
constructor(...args) { | |
super(...args); | |
this.push('a'); | |
} | |
/** | |
* stream.Transform _transform method | |
* @param {Buffer} data | |
* @param {string} _encoding | |
* @param {(err: Error) => void} callback | |
*/ | |
_transform(data, _encoding, callback) { | |
this.push(Buffer.from(encodeBuffer(data), 'utf-8')) | |
callback(null); | |
} | |
} | |
/** Transform stream for decoding */ | |
class DecodeStream extends Transform { | |
/** | |
* The constructor | |
* @param {object} [opts] | |
*/ | |
constructor(opts) { | |
super(opts); | |
this.initial = true; | |
this.partial = false; | |
this.byte = 0; | |
} | |
/** | |
* stream.Transform _transform method | |
* @param {Buffer} data | |
* @param {string} _encoding | |
* @param {(err: Error) => void} callback | |
*/ | |
_transform(data, _encoding, callback) { | |
let dataString = data.toString('utf-8'); | |
if (this.initial) { | |
this.initial = false; | |
dataString = dataString.normalize('NFKD'); | |
} | |
let out = []; | |
for (let i = 0; i < dataString.length; i++) { | |
let n = CHARS_REVERSE.get(dataString[i]); | |
if (typeof n === 'undefined') continue; | |
if (this.partial) { | |
out.push(this.byte | n); | |
this.partial = false; | |
} else { | |
this.byte = n << 4; | |
this.partial = true; | |
} | |
} | |
if (out.length) this.push(Buffer.from(out)); | |
callback(null); | |
} | |
} | |
if (require.main === module) { | |
let args = process.argv.slice(2); | |
/** @type {import('stream').Stream} */ | |
let s = process.stdin; | |
if (args.includes('-d')) { | |
s = s.pipe(new DecodeStream()); | |
if (args.includes('-c')) s = s.pipe(zlib.createBrotliDecompress()); | |
} else { | |
if (args.includes('-c')) s = s.pipe(zlib.createBrotliCompress()); | |
s = s.pipe(new EncodeStream()); | |
} | |
s.pipe(process.stdout); | |
} | |
exports.encode = encode; | |
exports.decode = decode; | |
exports.encodeBuffer = encodeBuffer; | |
exports.decodeBuffer = decodeBuffer; | |
exports.EncodeStream = EncodeStream; | |
exports.DecodeStream = DecodeStream; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment