diff --git a/changelog.md b/changelog.md index 2e431b9..3732c12 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ ChangeLog ========= +2.0.0 (????-??-??) +------------------ + +* #66: We now convert from/to ArrayBuffer instead of a custom ByteSequence + object. This is a breaking change. + + 2.0.0-alpha.1 (2024-02-23) -------------------------- diff --git a/src/parser.ts b/src/parser.ts index f2c02b4..a28cf81 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -5,12 +5,11 @@ import { BareItem, Parameters, InnerList, - ByteSequence } from './types.js'; import { Token } from './token.js'; -import { isAscii } from './util.js'; +import { isAscii, base64ToArrayBuffer } from './util.js'; import { DisplayString } from './displaystring.js'; export function parseDictionary(input: string): Dictionary { @@ -367,7 +366,7 @@ export default class Parser { } - private parseByteSequence(): ByteSequence { + private parseByteSequence(): ArrayBuffer { this.expectChar(':'); this.pos++; @@ -382,7 +381,7 @@ export default class Parser { throw new ParseError(this.pos, 'ByteSequence does not contain a valid base64 string'); } - return new ByteSequence(b64Content); + return base64ToArrayBuffer(b64Content); } diff --git a/src/serializer.ts b/src/serializer.ts index 9d7c867..c1499fc 100644 --- a/src/serializer.ts +++ b/src/serializer.ts @@ -1,6 +1,5 @@ import { BareItem, - ByteSequence, Dictionary, DictionaryObject, InnerList, @@ -11,7 +10,7 @@ import { import { Token } from './token.js'; -import { isAscii, isInnerList, isValidKeyStr } from './util.js'; +import { isAscii, isInnerList, isValidKeyStr, arrayBufferToBase64 } from './util.js'; import { DisplayString } from './displaystring.js'; export class SerializeError extends Error {} @@ -100,7 +99,7 @@ export function serializeBareItem(input: BareItem): string { if (input instanceof Token) { return serializeToken(input); } - if (input instanceof ByteSequence) { + if (input instanceof ArrayBuffer) { return serializeByteSequence(input); } if (input instanceof DisplayString) { @@ -162,8 +161,8 @@ export function serializeBoolean(input: boolean): string { return input ? '?1' : '?0'; } -export function serializeByteSequence(input: ByteSequence): string { - return `:${input.toBase64()}:`; +export function serializeByteSequence(input: ArrayBuffer): string { + return `:${arrayBufferToBase64(input)}:`; } export function serializeToken(input: Token): string { diff --git a/src/types.ts b/src/types.ts index 2d0d1eb..3672fb3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,23 +39,6 @@ export type Dictionary = Map; */ export type DictionaryObject = Record; -export class ByteSequence { - - base64Value: string; - constructor(base64Value: string) { - - this.base64Value = base64Value; - - } - - toBase64(): string { - - return this.base64Value; - - } - -} - -export type BareItem = number | string | Token | ByteSequence | Date | boolean | DisplayString; +export type BareItem = number | string | Token | ArrayBuffer | Date | boolean | DisplayString; export type Item = [BareItem, Parameters]; diff --git a/src/util.ts b/src/util.ts index 3094df6..c885218 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,4 +1,4 @@ -import { Item, InnerList, BareItem, ByteSequence } from './types'; +import { Item, InnerList } from './types'; const asciiRe = /^[\x20-\x7E]*$/; const tokenRe = /^[a-zA-Z*][:/!#$%&'*+\-.^_`|~A-Za-z0-9]*$/; @@ -29,9 +29,36 @@ export function isInnerList(input: Item | InnerList): input is InnerList { } +export function arrayBufferToBase64(ab: ArrayBuffer): string { -export function isByteSequence(input: BareItem): input is ByteSequence { + // Create a Uint8Array to read the ArrayBuffer as bytes + const bytes = new Uint8Array(ab); + let binary = ''; - return typeof input === 'object' && 'base64Value' in input; + // Convert each byte to a character + for (const byte of bytes) { + binary += String.fromCharCode(byte); + } + + // Encode the binary string as Base64 + return btoa(binary); +} + +export function base64ToArrayBuffer(b64: string): ArrayBuffer { + + // Decode the base64 string into a binary string + const binaryString = atob(b64); + + // Create a new ArrayBuffer with the same length as the binary string + const len = binaryString.length; + const bytes = new Uint8Array(len); + + // Convert each character to its corresponding byte + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + // Return the ArrayBuffer + return bytes.buffer; } diff --git a/test/httpwg-tests.js b/test/httpwg-tests.js index 5c8e4c5..754b89c 100644 --- a/test/httpwg-tests.js +++ b/test/httpwg-tests.js @@ -12,7 +12,6 @@ import { ParseError, Token, - ByteSequence, DisplayString, } from '../dist/index.js'; @@ -304,10 +303,10 @@ function packTestValue(input) { value: input.toString() } } - if (input instanceof ByteSequence) { + if (input instanceof ArrayBuffer) { return { __type: 'binary', - value: base32Encode(Buffer.from(input.toBase64(), 'base64'), 'RFC4648') + value: base32Encode(input, 'RFC4648') } } if (input instanceof Date) { @@ -357,7 +356,7 @@ function unpackTestValue(input) { case 'token' : return new Token(input.value); case 'binary': - return new ByteSequence(Buffer.from(base32Decode(input.value, 'RFC4648')).toString('base64')); + return new base32Decode(input.value, 'RFC4648'); case 'date' : return new Date(input.value * 1000); case 'displaystring' : diff --git a/test/util.js b/test/util.js deleted file mode 100644 index 0c39c84..0000000 --- a/test/util.js +++ /dev/null @@ -1,27 +0,0 @@ -import { parseItem, isByteSequence } from "../dist/index.js"; -import { expect } from 'chai'; -import { describe, it } from 'node:test'; - -describe('.isByteSequence', () => { - - it('returns true for a valid Byte Sequence', () => { - const base64Value = `:${Buffer.from('TEST VALUE').toString('base64')}:` - const [item] = parseItem(base64Value) - expect(isByteSequence(item)).to.be.true - }) - - it('returns false for a number', () => { - const [item] = parseItem("98736459873465") - expect(isByteSequence(item)).to.be.false - }) - - it('returns false for a string', () => { - const [item] = parseItem('"TEST VALUE"') - expect(isByteSequence(item)).to.be.false - }) - - it('returns false for a JS object', () => { - expect(isByteSequence(/** @type {any} */({}))).to.be.false - }) - -});