Skip to content

Instantly share code, notes, and snippets.

@junderw
Created July 19, 2020 00:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save junderw/f59264aede3dc5df4bc393f926dbad35 to your computer and use it in GitHub Desktop.
Save junderw/f59264aede3dc5df4bc393f926dbad35 to your computer and use it in GitHub Desktop.
A CBOR encoder in TypeScript
class CBOREncoder {
private entryCount: number = 0;
private data: Uint8Array = Uint8Array.from([]);
pushBool(key: string, value: boolean): void {
this.entryCount++;
this.pushTextInternal(key);
this.pushBoolInternal(value);
}
pushText(key: string, value: string): void {
this.entryCount++;
this.pushTextInternal(key);
this.pushTextInternal(value);
}
pushBytes(key: string, value: Uint8Array): void {
this.entryCount++;
this.pushTextInternal(key);
this.pushBytesInternal(value);
}
serialise(): Uint8Array {
const size = this.data.length + 1;
if (size > 0xffff) throw new Error('Metadata too large.');
if (this.entryCount > 0x1f) throw new Error('Too many map entries.');
const ret = appendBytes(
Uint8Array.from([0xa0 + this.entryCount]),
this.data,
Uint8Array.from([0, 0]),
);
ret[size] = size >> 8;
ret[size + 1] = size & 0xff;
return ret;
}
private appendBytes(...bufs: Uint8Array[]): void {
this.data = appendBytes(this.data, ...bufs);
}
private pushBoolInternal(value: boolean): void {
this.appendBytes(Uint8Array.from([value ? 0xf5 : 0xf4]));
}
private pushBytesInternal(value: Uint8Array): void {
const len = value.length;
if (len < 24) {
this.appendBytes(Uint8Array.from([0x40 + len]), value);
} else if (len <= 256) {
this.appendBytes(Uint8Array.from([0x58, len]), value);
} else {
throw new Error('Byte string too large.');
}
}
private pushTextInternal(value: string): void {
const valueBuf = new TextEncoder().encode(value);
const len = valueBuf.length;
if (len < 24) {
this.appendBytes(Uint8Array.from([0x60 + len]), valueBuf);
} else if (len <= 256) {
this.appendBytes(Uint8Array.from([0x78, len]), valueBuf);
} else {
throw new Error('Text string too large.');
}
}
}
function appendBytes(main: Uint8Array, ...bufs: Uint8Array[]): Uint8Array {
const bufsLen = bufs.reduce((total, buf) => total + buf.length, 0);
let skipLen = main.length;
const ret = new Uint8Array(bufsLen + skipLen);
ret.set(main);
bufs.forEach(buf => {
ret.set(buf, skipLen);
skipLen += buf.length;
});
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment