Skip to content

Commit

Permalink
feat(base-n): add en/decodeBytes(), add BASE16_XX
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Jan 13, 2021
1 parent 67a2e61 commit d6205d7
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 20 deletions.
3 changes: 3 additions & 0 deletions packages/base-n/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
"typedoc": "^0.20.4",
"typescript": "^4.1.3"
},
"dependencies": {
"@thi.ng/hex": "^0.1.3"
},
"files": [
"*.js",
"*.d.ts",
Expand Down
11 changes: 11 additions & 0 deletions packages/base-n/src/16.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defBase } from "./base";

/**
* Digits: 0-9 A-F
*/
export const BASE16_UC = defBase("0123456789ABCDEF");

/**
* Digits: 0-9 a-f
*/
export const BASE16_LC = defBase("0123456789abcdef");
20 changes: 20 additions & 0 deletions packages/base-n/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export interface IBase {
readonly N: number;
readonly base: string;

/**
* Encodes `x` into a baseN encoded string. `x` MUST be < 2^53. Use
* `encodeBigInt()` for arbitrary values.
Expand All @@ -12,6 +15,14 @@ export interface IBase {
* @param x
*/
encodeBigInt(x: bigint): string;

/**
* Encodes given byte array into a bigint and then baseN encodes it.
*
* @param buf
*/
encodeBytes(buf: Uint8Array): string;

/**
* Decodes baseN encoded string `x` into a numeric value. Assumes the
* resulting `x` will be < 2^53. Use `decodeBigInt()` for arbitrary values.
Expand All @@ -25,4 +36,13 @@ export interface IBase {
* @param x
*/
decodeBigInt(x: string): bigint;
/**
* Decodes given string in a byte array. The byte values will be big endian
* order, with the LSB aligned to end of the given array. If `buf` is
* shorter than the space required by the encoded source string, the most
* significant bytes will be ignored.
*
* @param buf
*/
decodeBytes(x: string, buf: Uint8Array): Uint8Array;
}
22 changes: 22 additions & 0 deletions packages/base-n/src/base.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { U8 } from "@thi.ng/hex";
import type { IBase } from "./api";

export const defBase = (chars: string) => new BaseN(chars);
Expand Down Expand Up @@ -38,6 +39,12 @@ export class BaseN implements IBase {
return res;
}

encodeBytes(buf: Uint8Array) {
let hex = "";
for (let i = 0, n = buf.length; i < n; i++) hex += U8(buf[i]);
return this.encodeBigInt(BigInt(`0x${hex}`));
}

decode(x: string) {
const { index, N } = this;
let res = 0;
Expand All @@ -57,7 +64,22 @@ export class BaseN implements IBase {
return res;
}

decodeBytes(x: string, buf: Uint8Array): Uint8Array {
let y = this.decodeBigInt(x);
const M = BigInt(255);
const SHIFT = BigInt(8);
for (let i = buf.length; --i >= 0; ) {
buf[i] = Number(y & M);
y >>= SHIFT;
}
return buf;
}

validate(x: string) {
return new RegExp(`^[${this.base}]+$`).test(x);
}

size(x: number) {
return Math.ceil(Math.log(x) / Math.log(this.N));
}
}
1 change: 1 addition & 0 deletions packages/base-n/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./api";
export * from "./base";

export * from "./16";
export * from "./32";
export * from "./36";
export * from "./58";
Expand Down
37 changes: 17 additions & 20 deletions packages/base-n/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,31 @@ import {
BASE32_RFC4648,
BASE36,
BASE58,
BASE62,
BASE64,
BASE85,
IBase,
} from "../src";

describe("base-n", () => {
it("roundtrip", () => {
const X = BigInt(2) ** BigInt(128) - BigInt(1);

// prettier-ignore
assert.strictEqual(BASE32_RFC4648.encodeBigInt(X), "H7777777777777777777777777");
// prettier-ignore
assert.strictEqual(BASE32_RFC4648.decodeBigInt("H7777777777777777777777777"), X);
const check = (
base: IBase,
expected: string,
id: string | number = base.N
) => {
assert.strictEqual(base.encodeBigInt(X), expected, `encode: ${id}`);
assert.strictEqual(base.decodeBigInt(expected), X, `decode: ${id}`);
};

// prettier-ignore
assert.strictEqual(BASE32_HEX.encodeBigInt(X), "7VVVVVVVVVVVVVVVVVVVVVVVVV");
// prettier-ignore
assert.strictEqual(BASE32_HEX.decodeBigInt("7VVVVVVVVVVVVVVVVVVVVVVVVV"), X);

assert.strictEqual(BASE36.encodeBigInt(X), "F5LXX1ZZ5PNORYNQGLHZMSP33");
assert.strictEqual(BASE36.decodeBigInt("F5LXX1ZZ5PNORYNQGLHZMSP33"), X);

assert.strictEqual(BASE58.encodeBigInt(X), "YcVfxkQb6JRzqk5kF2tNLv");
assert.strictEqual(BASE58.decodeBigInt("YcVfxkQb6JRzqk5kF2tNLv"), X);

assert.strictEqual(BASE64.encodeBigInt(X), "3/////////////////////");
assert.strictEqual(BASE64.decodeBigInt("3/////////////////////"), X);

assert.strictEqual(BASE85.encodeBigInt(X), "=r54lj&NUUO~Hi%c2ym0");
assert.strictEqual(BASE85.decodeBigInt("=r54lj&NUUO~Hi%c2ym0"), X);
check(BASE32_RFC4648, "H7777777777777777777777777", "32rfc");
check(BASE32_HEX, "7VVVVVVVVVVVVVVVVVVVVVVVVV", "32hex");
check(BASE36, "F5LXX1ZZ5PNORYNQGLHZMSP33");
check(BASE58, "YcVfxkQb6JRzqk5kF2tNLv");
check(BASE62, "7n42DGM5Tflk9n8mt7Fhc7");
check(BASE64, "3/////////////////////");
check(BASE85, "=r54lj&NUUO~Hi%c2ym0");
});
});

0 comments on commit d6205d7

Please sign in to comment.