diff --git a/benchmark/buffers/buffer-stringlength.js b/benchmark/buffers/buffer-stringlength.js new file mode 100644 index 000000000000..df2a8507b285 --- /dev/null +++ b/benchmark/buffers/buffer-stringlength.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common.js'); +const { stringLength } = require('node:buffer'); +const assert = require('node:assert'); + +const bench = common.createBenchmark(main, { + n: [1e6], + encoding: ['utf8', 'latin1', 'base64'], + len: [32, 4096, 1048576], + input: ['ascii', 'multibyte', 'invalid'], +}); + +function main({ n, encoding, len, input }) { + let buf; + if (input === 'ascii') { + buf = Buffer.alloc(len, 'a'); + } else { + buf = Buffer.alloc(len - (len % 3), '€'); + if (input === 'invalid') buf = Buffer.concat([buf, Buffer.from([0xE2, 0x82])]); + } + const expected = buf.toString(encoding).length; + bench.start(); + for (let i = 0; i < n; ++i) { + assert.strictEqual(stringLength(buf, encoding), expected); + } + bench.end(n); +} diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 7d4a70a37877..b04d085a5db1 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -5425,6 +5425,58 @@ changes: Resolves a `'blob:nodedata:...'` an associated {Blob} object registered using a prior call to `URL.createObjectURL()`. +### `buffer.stringLength(input[, encoding])` + + + +* `input` {Buffer | ArrayBuffer | TypedArray} The bytes that would be decoded. +* `encoding` {string} The character encoding `input` would be decoded with. + **Default:** `'utf8'`. +* Returns: {integer} + +Returns the length, in UTF-16 code units, of the string that +`buf.toString(encoding)` would produce for the same bytes, without decoding +them. This is the counterpart of [`Buffer.byteLength()`][]. + +For `'utf8'`, invalid byte sequences are counted as they would be decoded: +each maximal invalid subsequence becomes one `U+FFFD` replacement character. +For every other encoding the result is computed from `input.byteLength` alone. + +A detached `ArrayBuffer`, or a `TypedArray` backed by one, is treated as empty. + +The result is not capped: compare it with +[`buffer.constants.MAX_STRING_LENGTH`][] before decoding to know whether the +decode can succeed at all. A string of `n` code units occupies between `n` and +`2 * n` bytes of memory. + +```mjs +import { Buffer, stringLength, constants } from 'node:buffer'; + +const buf = Buffer.from('€ 100', 'utf8'); + +console.log(stringLength(buf)); +// Prints: 5 +console.log(stringLength(buf, 'hex')); +// Prints: 14 +console.log(stringLength(buf) <= constants.MAX_STRING_LENGTH); +// Prints: true +``` + +```cjs +const { Buffer, stringLength, constants } = require('node:buffer'); + +const buf = Buffer.from('€ 100', 'utf8'); + +console.log(stringLength(buf)); +// Prints: 5 +console.log(stringLength(buf, 'hex')); +// Prints: 14 +console.log(stringLength(buf) <= constants.MAX_STRING_LENGTH); +// Prints: true +``` + ### `buffer.transcode(source, fromEnc, toEnc)`