Skip to content

Commit

Permalink
Merge pull request #37 from swansontec/william/one-file
Browse files Browse the repository at this point in the history
Combine everything into one file
  • Loading branch information
swansontec authored Dec 6, 2024
2 parents 30deebb + 9bccfa8 commit 136a7cf
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 125 deletions.
118 changes: 0 additions & 118 deletions src/codec.ts

This file was deleted.

131 changes: 124 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */

import {
Encoding,
parse,
ParseOptions,
stringify,
StringifyOptions
} from './codec'
export interface Encoding {
bits: number
chars: string
codes?: { [char: string]: number }
}

export interface ParseOptions {
loose?: boolean
out?: new (size: number) => { [index: number]: number }
}

export interface StringifyOptions {
pad?: boolean
}

// ---------------------------------------------------------------------------
// Specific encodings
// ---------------------------------------------------------------------------

const base16Encoding: Encoding = {
chars: '0123456789ABCDEF',
Expand Down Expand Up @@ -93,4 +104,110 @@ export const base64url = {
}
}

// ---------------------------------------------------------------------------
// Codec implementation
// ---------------------------------------------------------------------------

export const codec = { parse, stringify }

function parse(
string: string,
encoding: Encoding,
opts: ParseOptions = {}
): Uint8Array {
// Build the character lookup table:
if (!encoding.codes) {
encoding.codes = {}
for (let i = 0; i < encoding.chars.length; ++i) {
encoding.codes[encoding.chars[i]] = i
}
}

// The string must have a whole number of bytes:
if (!opts.loose && (string.length * encoding.bits) & 7) {
throw new SyntaxError('Invalid padding')
}

// Count the padding bytes:
let end = string.length
while (string[end - 1] === '=') {
--end

// If we get a whole number of bytes, there is too much padding:
if (!opts.loose && !(((string.length - end) * encoding.bits) & 7)) {
throw new SyntaxError('Invalid padding')
}
}

// Allocate the output:
const out = new (opts.out ?? Uint8Array)(
((end * encoding.bits) / 8) | 0
) as Uint8Array

// Parse the data:
let bits = 0 // Number of bits currently in the buffer
let buffer = 0 // Bits waiting to be written out, MSB first
let written = 0 // Next byte to write
for (let i = 0; i < end; ++i) {
// Read one character from the string:
const value = encoding.codes[string[i]]
if (value === undefined) {
throw new SyntaxError('Invalid character ' + string[i])
}

// Append the bits to the buffer:
buffer = (buffer << encoding.bits) | value
bits += encoding.bits

// Write out some bits if the buffer has a byte's worth:
if (bits >= 8) {
bits -= 8
out[written++] = 0xff & (buffer >> bits)
}
}

// Verify that we have received just enough bits:
if (bits >= encoding.bits || 0xff & (buffer << (8 - bits))) {
throw new SyntaxError('Unexpected end of data')
}

return out
}

function stringify(
data: ArrayLike<number>,
encoding: Encoding,
opts: StringifyOptions = {}
): string {
const { pad = true } = opts
const mask = (1 << encoding.bits) - 1
let out = ''

let bits = 0 // Number of bits currently in the buffer
let buffer = 0 // Bits waiting to be written out, MSB first
for (let i = 0; i < data.length; ++i) {
// Slurp data into the buffer:
buffer = (buffer << 8) | (0xff & data[i])
bits += 8

// Write out as much as we can:
while (bits > encoding.bits) {
bits -= encoding.bits
out += encoding.chars[mask & (buffer >> bits)]
}
}

// Partial character:
if (bits) {
out += encoding.chars[mask & (buffer << (encoding.bits - bits))]
}

// Add padding characters until we hit a byte boundary:
if (pad) {
while ((out.length * encoding.bits) & 7) {
out += '='
}
}

return out
}

0 comments on commit 136a7cf

Please sign in to comment.