Skip to content

Commit

Permalink
copilot is a lifesaver
Browse files Browse the repository at this point in the history
  • Loading branch information
hiimjasmine00 committed Apr 11, 2023
1 parent e0772a8 commit 4e8d0ea
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 14 deletions.
3 changes: 2 additions & 1 deletion src/base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { NumberSystem } from ".";
import * as util from "./util";

/** Creates a new number system object with the given radix/base. */
export function base(radix: number): NumberSystem {
const rdx = Math.floor(Math.min(36, Math.max(2, radix)));
const max = (255).toString(rdx).length;
Expand All @@ -17,7 +18,7 @@ export function base(radix: number): NumberSystem {
const charMatch = data.toString().toLowerCase().match(this.regex);
if (charMatch == null) return "";

const decoded = Buffer.from(util.padStart(charMatch, max).map(x => parseInt(x, rdx)));
const decoded = Buffer.from(util.padStart((data.toString().toLowerCase().match(this.regex) ?? []).join(""), max).map(x => parseInt(x, rdx)));
return encoding ? decoded.toString(encoding) : decoded;
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/base32.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ export const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
export const radix = 32;
export const regex = /[A-Z2-7]/gi;

const bits = util.quickMap(5, x => 2 ** (x * 8)).reverse();
const chars = util.quickMap(8, x => 2 ** (x * 5)).reverse();
const pads = util.quickMap(5, x => (8 - Math.ceil(x * 8 / 5)) % 8);
const bits = util.quickMap(5, x => 2 ** (x * 8), true); // 4294967296, 16777216, 65536, 256, 1
const chars = util.quickMap(8, x => 2 ** (x * 5), true); // 34359738368, 1073741824, 33554432, 1048576, 32768, 1024, 32, 1
const pads = util.quickMap(5, x => (8 - Math.ceil(x * 8 / 5)) % 8, false); // 0, 6, 4, 3, 1

// Encodes the given data into a Base 32 encoded string.
export function encode(data: string | Buffer, encoding: BufferEncoding = "utf8") {
let padding = 0;
return Buffer.from(data.toString(encoding), encoding).reduce((a, x, i, r) => {
Expand All @@ -22,8 +23,9 @@ export function encode(data: string | Buffer, encoding: BufferEncoding = "utf8")
charset[Math.floor(x / y) % 32]).join("")).join("") + "=".repeat(padding);
}

// Decodes the given Base 32 encoded string into a buffer, or a string if a character encoding is provided.
export function decode(data: string, encoding?: BufferEncoding) {
const decoded = Buffer.from(util.padEnd(data.toString().toUpperCase().match(regex) ?? [], 8)
const decoded = Buffer.from(util.padEnd((data.toString().toUpperCase().match(regex) ?? []).join(""), 8)
.flatMap(x => {
const value = util.stringToNumber(x, charset);
return bits.slice(0, pads.findIndex(y => y == (x.match(/=/g) ?? []).length) || 5)
Expand Down
10 changes: 6 additions & 4 deletions src/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ export const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123
export const radix = 64;
export const regex = /[A-Za-z0-9+/]/gi;

const bits = util.quickMap(3, x => 2 ** (x * 8)).reverse();
const chars = util.quickMap(4, x => 2 ** (x * 6)).reverse();
const pads = util.quickMap(3, x => (4 - Math.ceil(x * 4 / 3)) % 4);
const bits = util.quickMap(3, x => 2 ** (x * 8), true); // 65536, 256, 1
const chars = util.quickMap(4, x => 2 ** (x * 6), true); // 262144, 4096, 64, 1
const pads = util.quickMap(3, x => (4 - Math.ceil(x * 4 / 3)) % 4, false); // 0, 2, 1

// Encodes the given data into a Base 64 encoded string.
export function encode(data: string | Buffer, encoding: BufferEncoding = "utf8") {
let padding = 0;
return Buffer.from(data.toString(encoding), encoding).reduce((a, x, i, r) => {
Expand All @@ -22,8 +23,9 @@ export function encode(data: string | Buffer, encoding: BufferEncoding = "utf8")
charset[Math.floor(x / y) % 64]).join("")).join("") + "=".repeat(padding);
}

// Decodes the given Base 64 encoded string into a buffer, or a string if a character encoding is provided.
export function decode(data: string, encoding?: BufferEncoding) {
const decoded = Buffer.from(util.padEnd(data.toString().replaceAll("-", "+").replaceAll("_", "/").match(regex) ?? [], 4)
const decoded = Buffer.from(util.padEnd((data.toString().replaceAll("-", "+").replaceAll("_", "/").match(regex) ?? []).join(""), 4)
.flatMap(x => {
const value = util.stringToNumber(x, charset);
return bits.slice(0, pads.findIndex(y => y == (x.match(/=/g) ?? []).length) || 3)
Expand Down
5 changes: 4 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ type Options = {
out: string;
}

// Get the version from the package.json file
const { version } = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
// The main CLI function
export = function (argv: string[], mode: "encode" | "decode") {
if (!["encode", "decode"].includes(mode)) return process.exit(1);
const isEncode = mode.startsWith("e");

const descStart = `${mode[0].toUpperCase() + mode.slice(1)}s the specified input data ${isEncode ? "with" : "from"}`;
Expand Down Expand Up @@ -61,6 +62,7 @@ export = function (argv: string[], mode: "encode" | "decode") {
.alias("radix")
.action(actionHandler);

// Create a command for each base/radix in the dencodeme object
for (const [command, base] of Object.entries(dencodeme)
.filter((x): x is [string, NumberSystem] => typeof x[1] !== "function")
.map((x): [string, number] => [x[0], x[1].radix])) {
Expand All @@ -76,5 +78,6 @@ export = function (argv: string[], mode: "encode" | "decode") {
.action(actionHandler.bind(null, command as Exclude<keyof typeof dencodeme, "base">));
}

// Parse the given arguments and run the program
program.parse(argv, { from: "user" });
}
13 changes: 13 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,33 @@ import { base } from "./base";
import * as b32 from "./base32";
import * as b64 from "./base64";

/** The number system object interface. */
export interface NumberSystem {
/** The character set of the number system. */
readonly charset: string;
/** The radix/base of the number system. */
readonly radix: number;
/** The regex used to match characters in the number system. */
readonly regex: RegExp;
/** Encodes the given data into an encoded string. */
encode(data: string | Buffer, encoding?: BufferEncoding): string;
/** Decodes the given encoded string into a buffer, or a string if a character encoding is provided. */
decode(data: string, encoding?: BufferEncoding): string | Buffer;
}

export * from "./base";

/** The binary (Base 2) number system object. */
export const binary = base(2);
/** The octal (Base 8) number system object. */
export const octal = base(8);
/** The decimal (Base 10) number system object. */
export const decimal = base(10);
/** The hexadecimal (Base 16) number system object. */
export const hexadecimal = base(16);
/** The Base 32 number system object. */
export const base32: NumberSystem = b32;
/** The Base 36 number system object. */
export const base36 = base(36);
/** The Base 64 number system object. */
export const base64: NumberSystem = b64;
13 changes: 9 additions & 4 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Returns a mapper function to split a string into an array of strings of the given length.
function getMapper(length: number) {
return (a: string[], x: string, i: number) => {
if (i % length == 0) a.push(x);
Expand All @@ -6,18 +7,22 @@ function getMapper(length: number) {
}
}

export function padStart(data: string[], length: number): string[] {
// Pads the given string with zeros to the left, and splits it into an array of strings of the given length.
export function padStart(data: string, length: number): string[] {
return [ ...Array((length - data.length % length) % length).fill("0"), ...data ].reduce(getMapper(length), []);
}

export function padEnd(data: string[], length: number): string[] {
// Pads the given string with equals signs to the right, and splits it into an array of strings of the given length.
export function padEnd(data: string, length: number): string[] {
return [ ...data, ...Array((length - data.length % length) % length).fill("=") ].reduce(getMapper(length), []);
}

export function quickMap(length: number, multiplier: (x: number) => number) {
return Array(length).fill(0).map((_, x) => multiplier(x));
// Creates an array of numbers from 0 to length - 1, and multiplies each number by the given multiplier.
export function quickMap(length: number, multiplier: (x: number) => number, reverse: boolean = false) {
return Array(length).fill(0).map((_, x) => multiplier(reverse ? -x + length - 1 : x));
}

// Converts the given string to a number using the given character set.
export function stringToNumber(str: string, charset: string) {
return str.split("").reverse().map(x => charset.indexOf(x)).reduce((a, x, i) => a + (x >= 0 ? x * charset.length ** i : 0), 0);
}

0 comments on commit 4e8d0ea

Please sign in to comment.