From 826e0092f438156749f82352602e72a03839e18c Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Wed, 7 Feb 2024 15:44:25 -0800 Subject: [PATCH] Normative: move 'into' methods onto prototype and rename (#45) --- README.md | 6 ++--- playground/index-raw.html | 8 +++---- playground/polyfill-install.mjs | 41 ++++++++++++++++++--------------- spec.html | 14 ++++++----- stream.mjs | 2 +- test-polyfill.mjs | 28 +++++++++++----------- 6 files changed, 52 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index ff1dcac..1d44c68 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,11 @@ The hex methods do not take any options. ## Writing to an existing Uint8Array -The `Uint8Array.fromBase64Into` method allows writing to an existing Uint8Array. Like the [TextEncoder `encodeInto` method](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encodeInto), it returns a `{ read, written }` pair. +The `Uint8Array.prototype.setFromBase64` method allows writing to an existing Uint8Array. Like the [TextEncoder `encodeInto` method](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encodeInto), it returns a `{ read, written }` pair. ```js let target = new Uint8Array(8); -let { read, written } = Uint8Array.fromBase64Into('Zm9vYmFy', target); +let { read, written } = target.setFromBase64('Zm9vYmFy'); assert.deepStrictEqual([...target], [102, 111, 111, 98, 97, 114, 0, 0]); assert.deepStrictEqual({ read, written }, { read: 8, written: 6 }); ``` @@ -55,7 +55,7 @@ This method takes an optional final options bag with the same options as above. As with `encodeInto`, there is not explicit support for writing to specified offset of the target, but you can accomplish that by creating a subarray. -`Uint8Array.fromHexInto` is the same except for hex. +`Uint8Array.prototype.setFromHex` is the same except for hex. ## Streaming diff --git a/playground/index-raw.html b/playground/index-raw.html index b165318..f8dfa3e 100644 --- a/playground/index-raw.html +++ b/playground/index-raw.html @@ -112,22 +112,22 @@

Options

Writing to an existing Uint8Array

-

The Uint8Array.fromBase64Into method allows writing to an existing Uint8Array. Like the TextEncoder encodeInto method, it returns a { read, written } pair.

+

The Uint8Array.prototype.setFromBase64 method allows writing to an existing Uint8Array. Like the TextEncoder encodeInto method, it returns a { read, written } pair.

This method takes an optional final options bag with the same options as above.


 let target = new Uint8Array(7);
-let { read, written } = Uint8Array.fromBase64Into('Zm9vYmFy', target);
+let { read, written } = target.setFromBase64('Zm9vYmFy');
 console.log({ target, read, written });
 // { target: Uint8Array([102, 111, 111, 98, 97, 114, 0]), read: 8, written: 6 }
 
-

Uint8Array.fromHexInto is the same except for hex.

+

Uint8Array.prototype.setFromHex is the same except for hex.


 let target = new Uint8Array(6);
-let { read, written } = Uint8Array.fromHexInto('deadbeef', target);
+let { read, written } = target.setFromHex('deadbeef');
 console.log({ target, read, written });
 // { target: Uint8Array([222, 173, 190, 239, 0, 0]), read: 8, written: 4 }
 
diff --git a/playground/polyfill-install.mjs b/playground/polyfill-install.mjs index f7300bf..ad356ca 100644 --- a/playground/polyfill-install.mjs +++ b/playground/polyfill-install.mjs @@ -19,17 +19,19 @@ Object.defineProperty(Uint8Array, 'fromBase64', { enumerable: false }); Object.defineProperty(Uint8Array.fromBase64, 'length', { value: 1 }); Object.defineProperty(Uint8Array.fromBase64, 'name', { value: 'fromBase64' }); -Uint8Array.fromBase64Into = (string, into, options) => { - if (typeof string !== 'string') { - throw new TypeError('expected input to be a string'); +// method shenanigans to make a non-constructor which can refer to "this" +Uint8Array.prototype.setFromBase64 = { + setFromBase64(string, options) { + checkUint8Array(this); + if (typeof string !== 'string') { + throw new TypeError('expected input to be a string'); + } + let { read, bytes } = base64ToUint8Array(string, options, this); + return { read, written: bytes.length }; } - checkUint8Array(into); - let { read, bytes } = base64ToUint8Array(string, options, into); - return { read, written: bytes.length }; -}; -Object.defineProperty(Uint8Array, 'fromBase64Into', { enumerable: false }); -Object.defineProperty(Uint8Array.fromBase64Into, 'length', { value: 2 }); -Object.defineProperty(Uint8Array.fromBase64Into, 'name', { value: 'fromBase64Into' }); +}.setFromBase64; +Object.defineProperty(Uint8Array.prototype, 'setFromBase64', { enumerable: false }); +Object.defineProperty(Uint8Array.prototype.setFromBase64, 'length', { value: 1 }); Uint8Array.prototype.toHex = { toHex() { @@ -47,13 +49,14 @@ Uint8Array.fromHex = (string) => { Object.defineProperty(Uint8Array, 'fromHex', { enumerable: false }); Object.defineProperty(Uint8Array.fromHex, 'name', { value: 'fromHex' }); -Uint8Array.fromHexInto = (string, into) => { - if (typeof string !== 'string') { - throw new TypeError('expected input to be a string'); +Uint8Array.prototype.setFromHex = { + setFromHex(string) { + checkUint8Array(this); + if (typeof string !== 'string') { + throw new TypeError('expected input to be a string'); + } + let { read, bytes } = hexToUint8Array(string, this); + return { read, written: bytes.length }; } - checkUint8Array(into); - let { read, bytes } = hexToUint8Array(string, into); - return { read, written: bytes.length }; -}; -Object.defineProperty(Uint8Array, 'fromHexInto', { enumerable: false }); -Object.defineProperty(Uint8Array.fromHexInto, 'name', { value: 'fromHexInto' }); +}.setFromHex; +Object.defineProperty(Uint8Array.prototype, 'setFromHex', { enumerable: false }); diff --git a/spec.html b/spec.html index 7d22698..a4c176e 100644 --- a/spec.html +++ b/spec.html @@ -69,11 +69,12 @@

Uint8Array.fromBase64 ( _string_ [ , _options_ ] )

- -

Uint8Array.fromBase64Into ( _string_, _into_ [ , _options_ ] )

+ +

Uint8Array.prototype.setFromBase64 ( _string_ [ , _options_ ] )

- 1. If _string_ is not a String, throw a *TypeError* exception. + 1. Let _into_ be the *this* value. 1. Perform ? ValidateUint8Array(_into_). + 1. If _string_ is not a String, throw a *TypeError* exception. 1. Let _opts_ be ? GetOptionsObject(_options_). 1. Let _alphabet_ be ? Get(_opts_, *"alphabet"*). 1. If _alphabet_ is *undefined*, set _alphabet_ to *"base64"*. @@ -111,11 +112,12 @@

Uint8Array.fromHex ( _string_ )

- -

Uint8Array.fromHexInto ( _string_, _into_ )

+ +

Uint8Array.prototype.setFromHex ( _string_ )

- 1. If _string_ is not a String, throw a *TypeError* exception. + 1. Let _into_ be the *this* value. 1. Perform ? ValidateUint8Array(_into_). + 1. If _string_ is not a String, throw a *TypeError* exception. 1. Let _taRecord_ be MakeTypedArrayWithBufferWitnessRecord(_into_, ~seq-cst~). 1. If IsTypedArrayOutOfBounds(_taRecord_) is *true*, throw a *TypeError* exception. 1. Let _byteLength_ be TypedArrayByteLength(_taRecord_). diff --git a/stream.mjs b/stream.mjs index 47cbf08..af1c8c5 100644 --- a/stream.mjs +++ b/stream.mjs @@ -18,7 +18,7 @@ class Base64Decoder { // but may be too much if there is whitespace // if you're really concerned about memory, a TextDecoder style API is a bad choice let buffer = new Uint8Array(Math.ceil(chunk.length * 3 / 4)); - let { read, written } = Uint8Array.fromBase64Into(chunk, buffer, opts); + let { read, written } = buffer.setFromBase64(chunk, opts); buffer = buffer.subarray(0, written); this.#extra = chunk.slice(read); return buffer; diff --git a/test-polyfill.mjs b/test-polyfill.mjs index d509797..82f5ed6 100644 --- a/test-polyfill.mjs +++ b/test-polyfill.mjs @@ -96,70 +96,70 @@ test('writing to an existing buffer', async t => { await t.test('buffer exact', () => { let output = new Uint8Array(6); - let { read, written } = Uint8Array.fromBase64Into(foobarInput, output); + let { read, written } = output.setFromBase64(foobarInput); assert.deepStrictEqual([...output], foobarOutput); assert.deepStrictEqual({ read, written }, { read: 8, written: 6 }); }); await t.test('buffer too large', () => { let output = new Uint8Array(8); - let { read, written } = Uint8Array.fromBase64Into(foobarInput, output); + let { read, written } = output.setFromBase64(foobarInput); assert.deepStrictEqual([...output], [...foobarOutput, 0, 0]); assert.deepStrictEqual({ read, written }, { read: 8, written: 6 }); }); await t.test('buffer too small', () => { let output = new Uint8Array(5); - let { read, written } = Uint8Array.fromBase64Into(foobarInput, output); + let { read, written } = output.setFromBase64(foobarInput); assert.deepStrictEqual([...output], [...foobarOutput.slice(0, 3), 0, 0]); assert.deepStrictEqual({ read, written }, { read: 4, written: 3 }); }); await t.test('buffer exact, padded', () => { let output = new Uint8Array(5); - let { read, written } = Uint8Array.fromBase64Into(foobaInput + '=', output); + let { read, written } = output.setFromBase64(foobaInput + '='); assert.deepStrictEqual([...output], foobarOutput.slice(0, 5)); assert.deepStrictEqual({ read, written }, { read: 8, written: 5 }); }); await t.test('buffer exact, not padded', () => { let output = new Uint8Array(5); - let { read, written } = Uint8Array.fromBase64Into(foobaInput, output); + let { read, written } = output.setFromBase64(foobaInput); assert.deepStrictEqual([...output], foobarOutput.slice(0, 5)); assert.deepStrictEqual({ read, written }, { read: 7, written: 5 }); }); await t.test('buffer exact, padded, stop-before-partial', () => { let output = new Uint8Array(5); - let { read, written } = Uint8Array.fromBase64Into(foobaInput + '=', output, { lastChunkHandling: 'stop-before-partial' }); + let { read, written } = output.setFromBase64(foobaInput + '=', { lastChunkHandling: 'stop-before-partial' }); assert.deepStrictEqual([...output], foobarOutput.slice(0, 5)); assert.deepStrictEqual({ read, written }, { read: 8, written: 5 }); }); await t.test('buffer exact, not padded, stop-before-partial', () => { let output = new Uint8Array(5); - let { read, written } = Uint8Array.fromBase64Into(foobaInput, output, { lastChunkHandling: 'stop-before-partial' }); + let { read, written } = output.setFromBase64(foobaInput, { lastChunkHandling: 'stop-before-partial' }); assert.deepStrictEqual([...output], [...foobarOutput.slice(0, 3), 0, 0]); assert.deepStrictEqual({ read, written }, { read: 4, written: 3 }); }); await t.test('buffer too small, padded', () => { let output = new Uint8Array(4); - let { read, written } = Uint8Array.fromBase64Into(foobaInput + '=', output); + let { read, written } = output.setFromBase64(foobaInput + '='); assert.deepStrictEqual([...output], [...foobarOutput.slice(0, 3), 0]); assert.deepStrictEqual({ read, written }, { read: 4, written: 3 }); }); await t.test('buffer too large, trailing whitespace', () => { let output = new Uint8Array(8); - let { read, written } = Uint8Array.fromBase64Into(foobarInput + ' '.repeat(10), output); + let { read, written } = output.setFromBase64(foobarInput + ' '.repeat(10)); assert.deepStrictEqual([...output], [...foobarOutput, 0, 0]); assert.deepStrictEqual({ read, written }, { read: 18, written: 6 }); }); await t.test('buffer too large, not padded, trailing whitespace', () => { let output = new Uint8Array(8); - let { read, written } = Uint8Array.fromBase64Into(foobaInput + ' '.repeat(10), output); + let { read, written } = output.setFromBase64(foobaInput + ' '.repeat(10)); assert.deepStrictEqual([...output], [...foobarOutput.slice(0, 5), 0, 0, 0]); assert.deepStrictEqual({ read, written }, { read: 17, written: 5 }); }); @@ -181,7 +181,7 @@ test('stop-before-partial', async t => { await t.test('no padding, trailing whitespace', () => { let output = new Uint8Array(8); - let { read, written } = Uint8Array.fromBase64Into(foobaInput + ' '.repeat(10), output, { lastChunkHandling: 'stop-before-partial' }); + let { read, written } = output.setFromBase64(foobaInput + ' '.repeat(10), { lastChunkHandling: 'stop-before-partial' }); assert.deepStrictEqual([...output], [...foobarOutput.slice(0, 3), 0, 0, 0, 0, 0]); assert.deepStrictEqual({ read, written }, { read: 4, written: 3 }); }); @@ -197,21 +197,21 @@ test('hex', async t => { await t.test('decode into, exact', () => { let output = new Uint8Array(4); - let { read, written } = Uint8Array.fromHexInto(encoded, output); + let { read, written } = output.setFromHex(encoded); assert.deepStrictEqual([...output], decoded); assert.deepStrictEqual({ read, written }, { read: 8, written: 4 }); }); await t.test('decode into, buffer too large', () => { let output = new Uint8Array(6); - let { read, written } = Uint8Array.fromHexInto(encoded, output); + let { read, written } = output.setFromHex(encoded); assert.deepStrictEqual([...output], [...decoded, 0, 0]); assert.deepStrictEqual({ read, written }, { read: 8, written: 4 }); }); await t.test('decode into, buffer too small', () => { let output = new Uint8Array(3); - let { read, written } = Uint8Array.fromHexInto(encoded, output); + let { read, written } = output.setFromHex(encoded); assert.deepStrictEqual([...output], decoded.slice(0, 3)); assert.deepStrictEqual({ read, written }, { read: 6, written: 3 }); });