From c2f669d1927546303560c4bb2036557a6b04cf58 Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Tue, 23 Dec 2025 21:45:51 +0800 Subject: [PATCH 1/3] buffer: add `Buffer.fromHex()` and `Buffer.fromBase64()` --- lib/buffer.js | 28 +++++++++++++++ .../test-buffer-fromhex-frombase64.js | 34 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 test/parallel/test-buffer-fromhex-frombase64.js diff --git a/lib/buffer.js b/lib/buffer.js index dc189712fda29f..7f0444e859fac8 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -336,6 +336,34 @@ Buffer.from = function from(value, encodingOrOffset, length) { ); }; +/** + * Same as Uint8Array.fromHex(hexstring), but returns instance of Buffer. + * Not same as Buffer.from(hexstring), as it performs different validations. + * @param {string} str + * @returns {Buffer} + */ +Buffer.fromHex = function fromHex(str) { + const buf = Uint8Array.fromHex(str); + return fromArrayBuffer( + TypedArrayPrototypeGetBuffer(buf), + TypedArrayPrototypeGetByteOffset(buf), + TypedArrayPrototypeGetByteLength(buf)); +}; + +/** + * Same as Uint8Array.fromBase64(base64string, options), but returns instance of Buffer. + * @param {string} str + * @param {object} [options] + * @returns {Buffer} + */ +Buffer.fromBase64 = function fromBase64(str, options) { + const buf = Uint8Array.fromBase64(str, options); + return fromArrayBuffer( + TypedArrayPrototypeGetBuffer(buf), + TypedArrayPrototypeGetByteOffset(buf), + TypedArrayPrototypeGetByteLength(buf)); +}; + /** * Creates the Buffer as a copy of the underlying ArrayBuffer of the view * rather than the contents of the view. diff --git a/test/parallel/test-buffer-fromhex-frombase64.js b/test/parallel/test-buffer-fromhex-frombase64.js new file mode 100644 index 00000000000000..54c6d3721e4a23 --- /dev/null +++ b/test/parallel/test-buffer-fromhex-frombase64.js @@ -0,0 +1,34 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Buffer } = require('buffer'); + +assert.deepStrictEqual(Buffer.fromHex('f00dcafe'), Buffer.from('f00dcafe', 'hex')); +assert.deepStrictEqual(Buffer.fromHex('F00DCAFE'), Buffer.from('f00dcafe', 'hex')); +assert.deepStrictEqual(Buffer.fromHex(''), Buffer.from('', 'hex')); + +assert.throws(() => Buffer.fromHex('0x'), { name: 'SyntaxError' }); +assert.throws(() => Buffer.fromHex('a'), { name: 'SyntaxError' }); +assert.throws(() => Buffer.fromHex(123), { name: 'TypeError' }); +assert.throws(() => Buffer.fromHex('abggcd00'), { name: 'SyntaxError' }); + +assert.deepStrictEqual(Buffer.fromBase64('SGVsbG8='), Buffer.from('SGVsbG8=', 'base64')); +assert.deepStrictEqual(Buffer.fromBase64('SGV sbG8='), Buffer.from('SGVsbG8=', 'base64')); + +assert.deepStrictEqual( + Buffer.fromBase64('PGJsZXA-PC9ibGVwPg', { alphabet: 'base64url' }), + Buffer.from('PGJsZXA+PC9ibGVwPg==', 'base64'), +); + +assert.deepStrictEqual(Buffer.fromBase64('SGVsbG8=', { lastChunkHandling: 'strict' }), Buffer.from('Hello')); +assert.throws(() => Buffer.fromBase64('SGVsbG8', { lastChunkHandling: 'strict' }), { name: 'SyntaxError' }); + +assert.deepStrictEqual( + Buffer.fromBase64('SGVsbG8', { lastChunkHandling: 'stop-before-partial' }), + Buffer.from('SGVs', 'base64'), +); + +assert.throws(() => Buffer.fromBase64('SGV$sbG8=', {}), { name: 'SyntaxError' }); +assert.throws(() => Buffer.fromBase64('S', {}), { name: 'SyntaxError' }); +assert.throws(() => Buffer.fromBase64(123), { name: 'TypeError' }); +assert.throws(() => Buffer.fromBase64('SGVsbG8=', { alphabet: 'unknown' }), { name: 'TypeError' }); From c78d989431182a9fa1ed6bbf99fabac1fc4b1f7d Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Sat, 27 Dec 2025 04:38:21 +0800 Subject: [PATCH 2/3] squash: make linter happy --- lib/buffer.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/buffer.js b/lib/buffer.js index 7f0444e859fac8..0d21c6b4f96f08 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -343,6 +343,8 @@ Buffer.from = function from(value, encodingOrOffset, length) { * @returns {Buffer} */ Buffer.fromHex = function fromHex(str) { + // TODO(LiviaMedeiros): replace with primordial once `--js-base-64` is not optional + // eslint-disable-next-line node-core/prefer-primordials const buf = Uint8Array.fromHex(str); return fromArrayBuffer( TypedArrayPrototypeGetBuffer(buf), @@ -357,6 +359,8 @@ Buffer.fromHex = function fromHex(str) { * @returns {Buffer} */ Buffer.fromBase64 = function fromBase64(str, options) { + // TODO(LiviaMedeiros): replace with primordial once `--js-base-64` is not optional + // eslint-disable-next-line node-core/prefer-primordials const buf = Uint8Array.fromBase64(str, options); return fromArrayBuffer( TypedArrayPrototypeGetBuffer(buf), From dfa2d2d004145fd4b75c8cc3d270240281a5f5ba Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Sat, 27 Dec 2025 04:45:57 +0800 Subject: [PATCH 3/3] squash: add docs --- doc/api/buffer.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 981c053ac40f0c..5f57bc10374d86 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -1419,6 +1419,39 @@ appropriate for `Buffer.from()` variants. [`Buffer.from(string)`][] may also use the internal `Buffer` pool like [`Buffer.allocUnsafe()`][] does. +### Static method: `Buffer.fromBase64(base64string[, options])` + + + +* `base64string` {string} A base64 string to decode. +* `options` {Object} + * `alphabet` {string} One of `'base64'` (default) or `'base64url'`. + * `lastChunkHandling` {string} One of `'loose'` (default), `'strict'`, or `'stop-before-partial'`. +* Returns: {Buffer} + +This static method is same as [`Uint8Array.fromBase64()`][], except it +returns `Buffer` rather than `Uint8Array`. + +This is not exactly the same as `Buffer.from(base64string, 'base64')`, +and this function will throw `SyntaxError` if input contains non-base64 symbols. + +### Static method: `Buffer.fromHex(string)` + + + +* `string` {string} A hexadecimal string to decode. +* Returns: {Buffer} + +This static method is same as [`Uint8Array.fromHex()`][], except it +returns `Buffer` rather than `Uint8Array`. + +This is not exactly the same as `Buffer.from(hexstring, 'hex')`, +and this function will throw `SyntaxError` if input contains non-hex symbols. + ### Static method: `Buffer.isBuffer(obj)`