Skip to content

Commit

Permalink
feat(encoding/unstable): Crockford base32 unstable support (#6238)
Browse files Browse the repository at this point in the history
  • Loading branch information
wesleyakio authored Dec 9, 2024
1 parent 31b3cd7 commit b2b7f99
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 0 deletions.
1 change: 1 addition & 0 deletions encoding/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"./ascii85": "./ascii85.ts",
"./base32": "./base32.ts",
"./unstable-base32-stream": "./unstable_base32_stream.ts",
"./unstable-base32crockford": "./unstable_base32crockford.ts",
"./unstable-base32hex": "./unstable_base32hex.ts",
"./unstable-base32hex-stream": "./unstable_base32hex_stream.ts",
"./base58": "./base58.ts",
Expand Down
75 changes: 75 additions & 0 deletions encoding/unstable_base32crockford.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// Copyright (c) 2014 Jameson Little. MIT License.
// This module is browser compatible.

/**
* Utilities for
* {@link https://www.crockford.com/base32.html | Crockford base32}
* encoding and decoding.
*
* Modified from @std/encoding/base32.
*
* ```ts
* import { encodeBase32Crockford, decodeBase32Crockford } from "@std/encoding/unstable-base32crockford";
* import { assertEquals } from "@std/assert";
*
* assertEquals(encodeBase32Crockford("foobar"), "CSQPYRK1E8======");
*
* assertEquals(
* decodeBase32Crockford("CSQPYRK1E8======"),
* new TextEncoder().encode("foobar")
* );
* ```
*
* @module
*/
import { decode, encode } from "./_base32_common.ts";

const lookup: string[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".split("");
const revLookup: number[] = [];
lookup.forEach((c, i) => (revLookup[c.charCodeAt(0)] = i));

/**
* Decodes a Crockford base32-encoded string.
*
* @see {@link https://www.crockford.com/base32.html}
*
* @param b32 The Crockford Base32-encoded string to decode.
* @returns The decoded data.
*
* @example Usage
* ```ts
* import { decodeBase32Crockford } from "@std/encoding/unstable-base32crockford";
* import { assertEquals } from "@std/assert";
*
* assertEquals(
* decodeBase32Crockford("CSQPYRK1E8======"),
* new TextEncoder().encode("foobar")
* );
* ```
*/
export function decodeBase32Crockford(b32: string): Uint8Array {
return decode(b32, lookup);
}

/**
* Converts data into a Crockford base32-encoded string.
*
* @see {@link https://www.crockford.com/base32.html}
*
* @param data The data to encode.
* @returns The Crockford base32-encoded string.
*
* @example Usage
* ```ts
* import { encodeBase32Crockford } from "@std/encoding/unstable-base32crockford";
* import { assertEquals } from "@std/assert";
*
* assertEquals(encodeBase32Crockford("foobar"), "CSQPYRK1E8======");
* ```
*/
export function encodeBase32Crockford(
data: ArrayBuffer | Uint8Array | string,
): string {
return encode(data, lookup);
}
67 changes: 67 additions & 0 deletions encoding/unstable_base32crockford_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Test cases copied from https://github.com/LinusU/base32-encode/blob/master/test.js
// Copyright (c) 2016-2017 Linus Unnebäck. MIT license.
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertExists, assertThrows } from "@std/assert";
import {
decodeBase32Crockford,
encodeBase32Crockford,
} from "./unstable_base32crockford.ts";

// Test vectors from https://www.rfc-editor.org/rfc/rfc4648.html#section-10
const testCases = [
["", ""],
["f", "CR======"],
["fo", "CSQG===="],
["foo", "CSQPY==="],
["foob", "CSQPYRG="],
["fooba", "CSQPYRK1"],
["foobar", "CSQPYRK1E8======"],
] as const;

Deno.test({
name: "encodeBase32()",
fn() {
for (const [bin, b32] of testCases) {
assertEquals(encodeBase32Crockford(bin), b32);
}
},
});

Deno.test({
name: "decodeBase32()",
fn() {
for (const [bin, b32] of testCases) {
assertEquals(decodeBase32Crockford(b32), new TextEncoder().encode(bin));
}
},
});

Deno.test({
name: "decodeBase32() throws on bad length",
fn() {
assertThrows(
() => decodeBase32Crockford("OOOO=="),
Error,
"Cannot decode base32 string as the length must be a multiple of 8: received length 6",
);
},
});

Deno.test({
name: "decodeBase32() throws on bad padding",
fn() {
assertThrows(
() => decodeBase32Crockford("5HXR334AQYAAAA=="),
Error,
"Invalid pad length",
);
},
});

Deno.test({
name: "encodeBase32() encodes very long text",
fn() {
const data = "a".repeat(16400);
assertExists(encodeBase32Crockford(data));
},
});

0 comments on commit b2b7f99

Please sign in to comment.