Skip to content

Commit

Permalink
buffer: add endings option, remove Node.js specific encoding option
Browse files Browse the repository at this point in the history
Signed-off-by: James M Snell <jasnell@gmail.com>

PR-URL: #39708
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
  • Loading branch information
jasnell authored and danielleadams committed Aug 16, 2021
1 parent cd9b0bf commit 5bc31ea
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 24 deletions.
14 changes: 11 additions & 3 deletions doc/api/buffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,14 +459,20 @@ multiple worker threads.
### `new buffer.Blob([sources[, options]])`
<!-- YAML
added: v15.7.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/39708
description: Added the standard `endings` option to replace line-endings,
and removed the non-standard `encoding` option.
-->

* `sources` {string[]|ArrayBuffer[]|TypedArray[]|DataView[]|Blob[]} An array
of string, {ArrayBuffer}, {TypedArray}, {DataView}, or {Blob} objects, or
any mix of such objects, that will be stored within the `Blob`.
* `options` {Object}
* `encoding` {string} The character encoding to use for string sources.
**Default:** `'utf8'`.
* `endings` {string} One of either `'transparent'` or `'native'`. When set
to `'native'`, line endings in string source parts will be converted to
the platform native line-ending as specified by `require('os').EOL`.
* `type` {string} The Blob content-type. The intent is for `type` to convey
the MIME media type of the data, however no validation of the type format
is performed.
Expand All @@ -476,7 +482,9 @@ Creates a new `Blob` object containing a concatenation of the given sources.
{ArrayBuffer}, {TypedArray}, {DataView}, and {Buffer} sources are copied into
the 'Blob' and can therefore be safely modified after the 'Blob' is created.

String sources are also copied into the `Blob`.
String sources are encoded as UTF-8 byte sequences and copied into the Blob.
Unmatched surrogate pairs within each string part will be replaced by Unicode
U+FFFD replacement characters.

### `blob.arrayBuffer()`
<!-- YAML
Expand Down
52 changes: 40 additions & 12 deletions lib/internal/blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
PromiseReject,
SafePromisePrototypeFinally,
ReflectConstruct,
RegExpPrototypeSymbolReplace,
RegExpPrototypeTest,
StringPrototypeToLowerCase,
StringPrototypeSplit,
Expand All @@ -24,7 +25,10 @@ const {
getDataObject,
} = internalBinding('blob');

const { TextDecoder } = require('internal/encoding');
const {
TextDecoder,
TextEncoder,
} = require('internal/encoding');

const {
makeTransferable,
Expand All @@ -48,6 +52,7 @@ const {
AbortError,
codes: {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_THIS,
ERR_BUFFER_TOO_LARGE,
}
Expand All @@ -68,10 +73,11 @@ const kMaxChunkSize = 65536;

const disallowedTypeCharacters = /[^\u{0020}-\u{007E}]/u;

let Buffer;
let ReadableStream;
let URL;
let EOL;

const enc = new TextEncoder();

// Yes, lazy loading is annoying but because of circular
// references between the url, internal/blob, and buffer
Expand All @@ -82,29 +88,35 @@ function lazyURL(id) {
return new URL(id);
}

function lazyBuffer() {
Buffer ??= require('buffer').Buffer;
return Buffer;
}

function lazyReadableStream(options) {
ReadableStream ??=
require('internal/webstreams/readablestream').ReadableStream;
return new ReadableStream(options);
}

// TODO(@jasnell): This is annoying but this has to be lazy because
// requiring the 'os' module too early causes building Node.js to
// fail with an unknown reference failure.
function lazyEOL() {
EOL ??= require('os').EOL;
return EOL;
}

function isBlob(object) {
return object?.[kHandle] !== undefined;
}

function getSource(source, encoding) {
function getSource(source, endings) {
if (isBlob(source))
return [source.size, source[kHandle]];

if (isAnyArrayBuffer(source)) {
source = new Uint8Array(source);
} else if (!isArrayBufferView(source)) {
source = lazyBuffer().from(`${source}`, encoding);
source = `${source}`;
if (endings === 'native')
source = RegExpPrototypeSymbolReplace(/\n|\r\n/g, source, lazyEOL());
source = enc.encode(source);
}

// We copy into a new Uint8Array because the underlying
Expand All @@ -116,6 +128,16 @@ function getSource(source, encoding) {
}

class Blob {
/**
* @typedef {string|ArrayBuffer|ArrayBufferView|Blob} SourcePart
*
* @param {SourcePart[]} [sources]
* @param {{
* endings? : string,
* type? : string,
* }} [options]
* @returns
*/
constructor(sources = [], options = {}) {
emitExperimentalWarning('buffer.Blob');
if (sources === null ||
Expand All @@ -124,12 +146,18 @@ class Blob {
throw new ERR_INVALID_ARG_TYPE('sources', 'Iterable', sources);
}
validateObject(options, 'options');
const { encoding = 'utf8' } = options;
let { type = '' } = options;
let {
type = '',
endings = 'transparent',
} = options;

endings = `${endings}`;
if (endings !== 'transparent' && endings !== 'native')
throw new ERR_INVALID_ARG_VALUE('options.endings', endings);

let length = 0;
const sources_ = ArrayFrom(sources, (source) => {
const { 0: len, 1: src } = getSource(source, encoding);
const { 0: len, 1: src } = getSource(source, endings);
length += len;
return src;
});
Expand Down
22 changes: 13 additions & 9 deletions test/parallel/test-blob.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Flags: --no-warnings
'use strict';

const common = require('../common');
const assert = require('assert');
const { Blob } = require('buffer');
const { inspect } = require('util');
const { EOL } = require('os');

{
const b = new Blob();
Expand Down Expand Up @@ -45,15 +47,6 @@ assert.throws(() => new Blob({}), {
assert.strictEqual(new Blob([], { type: {} }).type, '[object object]');
}

{
const b = new Blob(['616263'], { encoding: 'hex', type: 'foo' });
assert.strictEqual(b.size, 3);
assert.strictEqual(b.type, 'foo');
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'abc');
}));
}

{
const b = new Blob([Buffer.from('abc')]);
assert.strictEqual(b.size, 3);
Expand Down Expand Up @@ -216,3 +209,14 @@ assert.throws(() => new Blob({}), {
res = await reader.read();
assert(res.done);
})().then(common.mustCall());

{
const b = new Blob(['hello\n'], { endings: 'native' });
assert.strictEqual(b.size, EOL.length + 5);

[1, {}, 'foo'].forEach((endings) => {
assert.throws(() => new Blob([], { endings }), {
code: 'ERR_INVALID_ARG_VALUE',
});
});
}

0 comments on commit 5bc31ea

Please sign in to comment.