spica
Version:
Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.
78 lines (70 loc) • 2.29 kB
text/typescript
import { min } from './alias';
/*
ASCII文字のパーセントエンコーディングを避ければマルチバイト文字のみのエンコーディングとなるため
常に2単位以上の圧縮となる。2単位での圧縮率でもデルタ25%、ハフマン33%となるためハフマンを採用。
*/
const ASCII = [...Array(256)].reduce<string>((acc, _, i) => acc + String.fromCharCode(i), '');
function isHEX(code: number): boolean {
return 0x41 <= code && code < 0x47
|| 0x30 <= code && code < 0x3a;
}
interface Options {
start: number;
next: number;
}
export function encode(input: string, table: Uint8Array, opts: Options): string {
let output = '';
let buffer = 0;
let count = 0;
for (let i = opts.next = opts.start; i + 2 < input.length; opts.next = ++i) {
if (input[i] !== '%') break;
const code1 = input.charCodeAt(++i);
if (!isHEX(code1)) break;
const code2 = input.charCodeAt(++i);
if (!isHEX(code2)) break;
const delta1 = table[code1];
assert(delta1 >>> 4 === 0);
const delta2 = table[code2];
assert(delta2 >>> 4 === 0);
const hcode = 1 << 8 | delta1 << 4 | delta2;
let hlen = 9;
while (hlen !== 0) {
assert(count < 8);
const cnt = min(hlen, 8 - count);
assert(cnt > 0);
buffer |= (hcode >>> hlen - cnt & (1 << cnt) - 1) << 8 - count - cnt;
assert(buffer >>> 8 === 0);
count += cnt;
assert(count <= 8);
hlen -= cnt;
assert(hlen >= 0);
if (count !== 8) continue;
output += ASCII[buffer];
buffer = 0;
count = 0;
}
}
if (count !== 0) {
assert(count < 8);
output += ASCII[buffer];
}
assert(output.length <= input.length);
return output;
}
export function decode(input: string, table: Uint8Array, opts: Options): string {
let output = '';
let buffer = 0;
let count = 0;
for (let i = opts.next = opts.start; i < input.length; opts.next = ++i) {
buffer <<= 8;
buffer |= input.charCodeAt(i);
count += 8;
assert(count <= 16);
if ((buffer >>> count - 1 & 1) === 0) break;
if (count < 9) continue;
const delta = buffer >>> count - 9 & 0xff;
output += `%${ASCII[table[delta >>> 4]]}${ASCII[table[delta & 0x0f]]}`;
count -= 9;
}
return output;
}