2023-02-14 11:38:45 -05:00
|
|
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
|
|
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
|
|
|
// Copyright Feross Aboukhadijeh, and other contributors. All rights reserved. MIT license.
|
|
|
|
|
2023-06-27 02:18:22 -04:00
|
|
|
// TODO(petamoriken): enable prefer-primordials for node polyfills
|
|
|
|
// deno-lint-ignore-file prefer-primordials
|
|
|
|
|
2024-01-10 17:37:25 -05:00
|
|
|
import { core } from "ext:core/mod.js";
|
2024-05-21 18:50:59 -04:00
|
|
|
import { op_is_ascii, op_is_utf8 } from "ext:core/ops";
|
2024-01-10 17:37:25 -05:00
|
|
|
|
2023-03-08 06:44:54 -05:00
|
|
|
import { TextDecoder, TextEncoder } from "ext:deno_web/08_text_encoding.js";
|
|
|
|
import { codes } from "ext:deno_node/internal/error_codes.ts";
|
|
|
|
import { encodings } from "ext:deno_node/internal_binding/string_decoder.ts";
|
2023-07-08 14:34:08 -04:00
|
|
|
import {
|
|
|
|
indexOfBuffer,
|
|
|
|
indexOfNumber,
|
|
|
|
} from "ext:deno_node/internal_binding/buffer.ts";
|
2023-02-14 11:38:45 -05:00
|
|
|
import {
|
|
|
|
asciiToBytes,
|
|
|
|
base64ToBytes,
|
|
|
|
base64UrlToBytes,
|
|
|
|
bytesToAscii,
|
|
|
|
bytesToUtf16le,
|
|
|
|
hexToBytes,
|
|
|
|
utf16leToBytes,
|
2023-03-08 06:44:54 -05:00
|
|
|
} from "ext:deno_node/internal_binding/_utils.ts";
|
2023-07-08 14:34:08 -04:00
|
|
|
import {
|
|
|
|
isAnyArrayBuffer,
|
|
|
|
isArrayBufferView,
|
2024-05-21 18:50:59 -04:00
|
|
|
isTypedArray,
|
2023-07-08 14:34:08 -04:00
|
|
|
} from "ext:deno_node/internal/util/types.ts";
|
2023-03-08 06:44:54 -05:00
|
|
|
import { normalizeEncoding } from "ext:deno_node/internal/util.mjs";
|
|
|
|
import { validateBuffer } from "ext:deno_node/internal/validators.mjs";
|
|
|
|
import { isUint8Array } from "ext:deno_node/internal/util/types.ts";
|
2024-06-11 07:41:44 -04:00
|
|
|
import { ERR_INVALID_STATE, NodeError } from "ext:deno_node/internal/errors.ts";
|
2023-07-08 14:34:08 -04:00
|
|
|
import {
|
|
|
|
forgivingBase64Encode,
|
|
|
|
forgivingBase64UrlEncode,
|
|
|
|
} from "ext:deno_web/00_infra.js";
|
2023-03-08 06:44:54 -05:00
|
|
|
import { atob, btoa } from "ext:deno_web/05_base64.js";
|
|
|
|
import { Blob } from "ext:deno_web/09_file.js";
|
2023-02-17 09:58:52 -05:00
|
|
|
|
2023-07-08 14:34:08 -04:00
|
|
|
export { atob, Blob, btoa };
|
2023-02-14 11:38:45 -05:00
|
|
|
|
|
|
|
const utf8Encoder = new TextEncoder();
|
|
|
|
|
|
|
|
// Temporary buffers to convert numbers.
|
|
|
|
const float32Array = new Float32Array(1);
|
|
|
|
const uInt8Float32Array = new Uint8Array(float32Array.buffer);
|
|
|
|
const float64Array = new Float64Array(1);
|
|
|
|
const uInt8Float64Array = new Uint8Array(float64Array.buffer);
|
|
|
|
|
|
|
|
// Check endianness.
|
|
|
|
float32Array[0] = -1; // 0xBF800000
|
|
|
|
// Either it is [0, 0, 128, 191] or [191, 128, 0, 0]. It is not possible to
|
|
|
|
// check this with `os.endianness()` because that is determined at compile time.
|
|
|
|
export const bigEndian = uInt8Float32Array[3] === 0;
|
|
|
|
|
|
|
|
export const kMaxLength = 2147483647;
|
|
|
|
export const kStringMaxLength = 536870888;
|
|
|
|
const MAX_UINT32 = 2 ** 32;
|
|
|
|
|
|
|
|
const customInspectSymbol =
|
|
|
|
typeof Symbol === "function" && typeof Symbol["for"] === "function"
|
|
|
|
? Symbol["for"]("nodejs.util.inspect.custom")
|
|
|
|
: null;
|
|
|
|
|
|
|
|
const INSPECT_MAX_BYTES = 50;
|
|
|
|
|
|
|
|
export const constants = {
|
|
|
|
MAX_LENGTH: kMaxLength,
|
|
|
|
MAX_STRING_LENGTH: kStringMaxLength,
|
|
|
|
};
|
|
|
|
|
|
|
|
Object.defineProperty(Buffer.prototype, "parent", {
|
|
|
|
enumerable: true,
|
|
|
|
get: function () {
|
|
|
|
if (!Buffer.isBuffer(this)) {
|
|
|
|
return void 0;
|
|
|
|
}
|
|
|
|
return this.buffer;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.defineProperty(Buffer.prototype, "offset", {
|
|
|
|
enumerable: true,
|
|
|
|
get: function () {
|
|
|
|
if (!Buffer.isBuffer(this)) {
|
|
|
|
return void 0;
|
|
|
|
}
|
|
|
|
return this.byteOffset;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
function createBuffer(length) {
|
|
|
|
if (length > kMaxLength) {
|
|
|
|
throw new RangeError(
|
|
|
|
'The value "' + length + '" is invalid for option "size"',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const buf = new Uint8Array(length);
|
|
|
|
Object.setPrototypeOf(buf, Buffer.prototype);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function Buffer(arg, encodingOrOffset, length) {
|
|
|
|
if (typeof arg === "number") {
|
|
|
|
if (typeof encodingOrOffset === "string") {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"string",
|
|
|
|
"string",
|
|
|
|
arg,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return _allocUnsafe(arg);
|
|
|
|
}
|
|
|
|
return _from(arg, encodingOrOffset, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.poolSize = 8192;
|
|
|
|
|
|
|
|
function _from(value, encodingOrOffset, length) {
|
|
|
|
if (typeof value === "string") {
|
|
|
|
return fromString(value, encodingOrOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof value === "object" && value !== null) {
|
|
|
|
if (isAnyArrayBuffer(value)) {
|
|
|
|
return fromArrayBuffer(value, encodingOrOffset, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
const valueOf = value.valueOf && value.valueOf();
|
|
|
|
if (
|
|
|
|
valueOf != null &&
|
|
|
|
valueOf !== value &&
|
|
|
|
(typeof valueOf === "string" || typeof valueOf === "object")
|
|
|
|
) {
|
|
|
|
return _from(valueOf, encodingOrOffset, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
const b = fromObject(value);
|
|
|
|
if (b) {
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof value[Symbol.toPrimitive] === "function") {
|
|
|
|
const primitive = value[Symbol.toPrimitive]("string");
|
|
|
|
if (typeof primitive === "string") {
|
|
|
|
return fromString(primitive, encodingOrOffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"first argument",
|
|
|
|
["string", "Buffer", "ArrayBuffer", "Array", "Array-like Object"],
|
|
|
|
value,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.from = function from(value, encodingOrOffset, length) {
|
|
|
|
return _from(value, encodingOrOffset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype);
|
|
|
|
|
|
|
|
Object.setPrototypeOf(Buffer, Uint8Array);
|
|
|
|
|
|
|
|
function assertSize(size) {
|
2024-06-11 07:41:44 -04:00
|
|
|
validateNumber(size, "size", 0, kMaxLength);
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function _alloc(size, fill, encoding) {
|
|
|
|
assertSize(size);
|
|
|
|
|
|
|
|
const buffer = createBuffer(size);
|
|
|
|
if (fill !== undefined) {
|
|
|
|
if (encoding !== undefined && typeof encoding !== "string") {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"encoding",
|
|
|
|
"string",
|
|
|
|
encoding,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return buffer.fill(fill, encoding);
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.alloc = function alloc(size, fill, encoding) {
|
|
|
|
return _alloc(size, fill, encoding);
|
|
|
|
};
|
|
|
|
|
|
|
|
function _allocUnsafe(size) {
|
|
|
|
assertSize(size);
|
|
|
|
return createBuffer(size < 0 ? 0 : checked(size) | 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.allocUnsafe = function allocUnsafe(size) {
|
|
|
|
return _allocUnsafe(size);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) {
|
|
|
|
return _allocUnsafe(size);
|
|
|
|
};
|
|
|
|
|
|
|
|
function fromString(string, encoding) {
|
|
|
|
if (typeof encoding !== "string" || encoding === "") {
|
|
|
|
encoding = "utf8";
|
|
|
|
}
|
|
|
|
if (!Buffer.isEncoding(encoding)) {
|
|
|
|
throw new codes.ERR_UNKNOWN_ENCODING(encoding);
|
|
|
|
}
|
|
|
|
const length = byteLength(string, encoding) | 0;
|
|
|
|
let buf = createBuffer(length);
|
|
|
|
const actual = buf.write(string, encoding);
|
|
|
|
if (actual !== length) {
|
|
|
|
buf = buf.slice(0, actual);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
function fromArrayLike(array) {
|
|
|
|
const length = array.length < 0 ? 0 : checked(array.length) | 0;
|
|
|
|
const buf = createBuffer(length);
|
|
|
|
for (let i = 0; i < length; i += 1) {
|
|
|
|
buf[i] = array[i] & 255;
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
function fromObject(obj) {
|
|
|
|
if (obj.length !== undefined || isAnyArrayBuffer(obj.buffer)) {
|
|
|
|
if (typeof obj.length !== "number") {
|
|
|
|
return createBuffer(0);
|
|
|
|
}
|
|
|
|
return fromArrayLike(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj.type === "Buffer" && Array.isArray(obj.data)) {
|
|
|
|
return fromArrayLike(obj.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function checked(length) {
|
|
|
|
if (length >= kMaxLength) {
|
|
|
|
throw new RangeError(
|
|
|
|
"Attempt to allocate Buffer larger than maximum size: 0x" +
|
|
|
|
kMaxLength.toString(16) + " bytes",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return length | 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function SlowBuffer(length) {
|
|
|
|
assertSize(length);
|
|
|
|
return Buffer.alloc(+length);
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.setPrototypeOf(SlowBuffer.prototype, Uint8Array.prototype);
|
|
|
|
|
|
|
|
Object.setPrototypeOf(SlowBuffer, Uint8Array);
|
|
|
|
|
|
|
|
Buffer.isBuffer = function isBuffer(b) {
|
|
|
|
return b != null && b._isBuffer === true && b !== Buffer.prototype;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.compare = function compare(a, b) {
|
|
|
|
if (isInstance(a, Uint8Array)) {
|
|
|
|
a = Buffer.from(a, a.offset, a.byteLength);
|
|
|
|
}
|
|
|
|
if (isInstance(b, Uint8Array)) {
|
|
|
|
b = Buffer.from(b, b.offset, b.byteLength);
|
|
|
|
}
|
|
|
|
if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
|
|
|
|
throw new TypeError(
|
|
|
|
'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (a === b) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
let x = a.length;
|
|
|
|
let y = b.length;
|
|
|
|
for (let i = 0, len = Math.min(x, y); i < len; ++i) {
|
|
|
|
if (a[i] !== b[i]) {
|
|
|
|
x = a[i];
|
|
|
|
y = b[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (x < y) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (y < x) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.isEncoding = function isEncoding(encoding) {
|
|
|
|
return typeof encoding === "string" && encoding.length !== 0 &&
|
|
|
|
normalizeEncoding(encoding) !== undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.concat = function concat(list, length) {
|
|
|
|
if (!Array.isArray(list)) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE("list", "Array", list);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (list.length === 0) {
|
|
|
|
return Buffer.alloc(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length === undefined) {
|
|
|
|
length = 0;
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
if (list[i].length) {
|
|
|
|
length += list[i].length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
validateOffset(length, "length");
|
|
|
|
}
|
|
|
|
|
|
|
|
const buffer = Buffer.allocUnsafe(length);
|
|
|
|
let pos = 0;
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
const buf = list[i];
|
|
|
|
if (!isUint8Array(buf)) {
|
|
|
|
// TODO(BridgeAR): This should not be of type ERR_INVALID_ARG_TYPE.
|
|
|
|
// Instead, find the proper error code for this.
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
`list[${i}]`,
|
|
|
|
["Buffer", "Uint8Array"],
|
|
|
|
list[i],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
pos += _copyActual(buf, buffer, pos, 0, buf.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: `length` is always equal to `buffer.length` at this point
|
|
|
|
if (pos < length) {
|
|
|
|
// Zero-fill the remaining bytes if the specified `length` was more than
|
|
|
|
// the actual total length, i.e. if we have some remaining allocated bytes
|
|
|
|
// there were not initialized.
|
|
|
|
buffer.fill(0, pos, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
};
|
|
|
|
|
|
|
|
function byteLength(string, encoding) {
|
|
|
|
if (typeof string !== "string") {
|
|
|
|
if (isArrayBufferView(string) || isAnyArrayBuffer(string)) {
|
|
|
|
return string.byteLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"string",
|
|
|
|
["string", "Buffer", "ArrayBuffer"],
|
|
|
|
string,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const len = string.length;
|
|
|
|
const mustMatch = arguments.length > 2 && arguments[2] === true;
|
|
|
|
if (!mustMatch && len === 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!encoding) {
|
|
|
|
return (mustMatch ? -1 : byteLengthUtf8(string));
|
|
|
|
}
|
|
|
|
|
|
|
|
const ops = getEncodingOps(encoding);
|
|
|
|
if (ops === undefined) {
|
|
|
|
return (mustMatch ? -1 : byteLengthUtf8(string));
|
|
|
|
}
|
|
|
|
return ops.byteLength(string);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.byteLength = byteLength;
|
|
|
|
|
|
|
|
Buffer.prototype._isBuffer = true;
|
|
|
|
|
|
|
|
function swap(b, n, m) {
|
|
|
|
const i = b[n];
|
|
|
|
b[n] = b[m];
|
|
|
|
b[m] = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.prototype.swap16 = function swap16() {
|
|
|
|
const len = this.length;
|
|
|
|
if (len % 2 !== 0) {
|
|
|
|
throw new RangeError("Buffer size must be a multiple of 16-bits");
|
|
|
|
}
|
|
|
|
for (let i = 0; i < len; i += 2) {
|
|
|
|
swap(this, i, i + 1);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.swap32 = function swap32() {
|
|
|
|
const len = this.length;
|
|
|
|
if (len % 4 !== 0) {
|
|
|
|
throw new RangeError("Buffer size must be a multiple of 32-bits");
|
|
|
|
}
|
|
|
|
for (let i = 0; i < len; i += 4) {
|
|
|
|
swap(this, i, i + 3);
|
|
|
|
swap(this, i + 1, i + 2);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.swap64 = function swap64() {
|
|
|
|
const len = this.length;
|
|
|
|
if (len % 8 !== 0) {
|
|
|
|
throw new RangeError("Buffer size must be a multiple of 64-bits");
|
|
|
|
}
|
|
|
|
for (let i = 0; i < len; i += 8) {
|
|
|
|
swap(this, i, i + 7);
|
|
|
|
swap(this, i + 1, i + 6);
|
|
|
|
swap(this, i + 2, i + 5);
|
|
|
|
swap(this, i + 3, i + 4);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.toString = function toString(encoding, start, end) {
|
|
|
|
if (arguments.length === 0) {
|
|
|
|
return this.utf8Slice(0, this.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
const len = this.length;
|
|
|
|
|
|
|
|
if (start <= 0) {
|
|
|
|
start = 0;
|
|
|
|
} else if (start >= len) {
|
|
|
|
return "";
|
|
|
|
} else {
|
|
|
|
start |= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end === undefined || end > len) {
|
|
|
|
end = len;
|
|
|
|
} else {
|
|
|
|
end |= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end <= start) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encoding === undefined) {
|
|
|
|
return this.utf8Slice(start, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ops = getEncodingOps(encoding);
|
|
|
|
if (ops === undefined) {
|
|
|
|
throw new codes.ERR_UNKNOWN_ENCODING(encoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ops.slice(this, start, end);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.toLocaleString = Buffer.prototype.toString;
|
|
|
|
|
|
|
|
Buffer.prototype.equals = function equals(b) {
|
|
|
|
if (!isUint8Array(b)) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"otherBuffer",
|
|
|
|
["Buffer", "Uint8Array"],
|
|
|
|
b,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (this === b) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return Buffer.compare(this, b) === 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.inspect = function inspect() {
|
|
|
|
let str = "";
|
|
|
|
const max = INSPECT_MAX_BYTES;
|
|
|
|
str = this.toString("hex", 0, max).replace(/(.{2})/g, "$1 ").trim();
|
|
|
|
if (this.length > max) {
|
|
|
|
str += " ... ";
|
|
|
|
}
|
|
|
|
return "<Buffer " + str + ">";
|
|
|
|
};
|
|
|
|
|
|
|
|
if (customInspectSymbol) {
|
|
|
|
Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.prototype.compare = function compare(
|
|
|
|
target,
|
|
|
|
start,
|
|
|
|
end,
|
|
|
|
thisStart,
|
|
|
|
thisEnd,
|
|
|
|
) {
|
|
|
|
if (isInstance(target, Uint8Array)) {
|
|
|
|
target = Buffer.from(target, target.offset, target.byteLength);
|
|
|
|
}
|
|
|
|
if (!Buffer.isBuffer(target)) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"target",
|
|
|
|
["Buffer", "Uint8Array"],
|
|
|
|
target,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start === undefined) {
|
|
|
|
start = 0;
|
|
|
|
} else {
|
|
|
|
validateOffset(start, "targetStart", 0, kMaxLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end === undefined) {
|
|
|
|
end = target.length;
|
|
|
|
} else {
|
|
|
|
validateOffset(end, "targetEnd", 0, target.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thisStart === undefined) {
|
|
|
|
thisStart = 0;
|
|
|
|
} else {
|
|
|
|
validateOffset(start, "sourceStart", 0, kMaxLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thisEnd === undefined) {
|
|
|
|
thisEnd = this.length;
|
|
|
|
} else {
|
|
|
|
validateOffset(end, "sourceEnd", 0, this.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
start < 0 || end > target.length || thisStart < 0 ||
|
|
|
|
thisEnd > this.length
|
|
|
|
) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE("out of range index", "range");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thisStart >= thisEnd && start >= end) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (thisStart >= thisEnd) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (start >= end) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
start >>>= 0;
|
|
|
|
end >>>= 0;
|
|
|
|
thisStart >>>= 0;
|
|
|
|
thisEnd >>>= 0;
|
|
|
|
if (this === target) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
let x = thisEnd - thisStart;
|
|
|
|
let y = end - start;
|
|
|
|
const len = Math.min(x, y);
|
|
|
|
const thisCopy = this.slice(thisStart, thisEnd);
|
|
|
|
const targetCopy = target.slice(start, end);
|
|
|
|
for (let i = 0; i < len; ++i) {
|
|
|
|
if (thisCopy[i] !== targetCopy[i]) {
|
|
|
|
x = thisCopy[i];
|
|
|
|
y = targetCopy[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (x < y) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (y < x) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) {
|
|
|
|
validateBuffer(buffer);
|
|
|
|
|
|
|
|
if (typeof byteOffset === "string") {
|
|
|
|
encoding = byteOffset;
|
|
|
|
byteOffset = undefined;
|
|
|
|
} else if (byteOffset > 0x7fffffff) {
|
|
|
|
byteOffset = 0x7fffffff;
|
|
|
|
} else if (byteOffset < -0x80000000) {
|
|
|
|
byteOffset = -0x80000000;
|
|
|
|
}
|
|
|
|
byteOffset = +byteOffset;
|
|
|
|
if (Number.isNaN(byteOffset)) {
|
|
|
|
byteOffset = dir ? 0 : (buffer.length || buffer.byteLength);
|
|
|
|
}
|
|
|
|
dir = !!dir;
|
|
|
|
|
|
|
|
if (typeof val === "number") {
|
|
|
|
return indexOfNumber(buffer, val >>> 0, byteOffset, dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
let ops;
|
|
|
|
if (encoding === undefined) {
|
|
|
|
ops = encodingOps.utf8;
|
|
|
|
} else {
|
|
|
|
ops = getEncodingOps(encoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof val === "string") {
|
|
|
|
if (ops === undefined) {
|
|
|
|
throw new codes.ERR_UNKNOWN_ENCODING(encoding);
|
|
|
|
}
|
|
|
|
return ops.indexOf(buffer, val, byteOffset, dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isUint8Array(val)) {
|
|
|
|
const encodingVal = ops === undefined ? encodingsMap.utf8 : ops.encodingVal;
|
|
|
|
return indexOfBuffer(buffer, val, byteOffset, encodingVal, dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"value",
|
|
|
|
["number", "string", "Buffer", "Uint8Array"],
|
|
|
|
val,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.prototype.includes = function includes(val, byteOffset, encoding) {
|
|
|
|
return this.indexOf(val, byteOffset, encoding) !== -1;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.indexOf = function indexOf(val, byteOffset, encoding) {
|
|
|
|
return bidirectionalIndexOf(this, val, byteOffset, encoding, true);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.lastIndexOf = function lastIndexOf(
|
|
|
|
val,
|
|
|
|
byteOffset,
|
|
|
|
encoding,
|
|
|
|
) {
|
|
|
|
return bidirectionalIndexOf(this, val, byteOffset, encoding, false);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.asciiSlice = function asciiSlice(offset, length) {
|
|
|
|
if (offset === 0 && length === this.length) {
|
|
|
|
return bytesToAscii(this);
|
|
|
|
} else {
|
|
|
|
return bytesToAscii(this.slice(offset, length));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.asciiWrite = function asciiWrite(string, offset, length) {
|
|
|
|
return blitBuffer(asciiToBytes(string), this, offset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.base64Slice = function base64Slice(
|
|
|
|
offset,
|
|
|
|
length,
|
|
|
|
) {
|
|
|
|
if (offset === 0 && length === this.length) {
|
|
|
|
return forgivingBase64Encode(this);
|
|
|
|
} else {
|
|
|
|
return forgivingBase64Encode(this.slice(offset, length));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.base64Write = function base64Write(
|
|
|
|
string,
|
|
|
|
offset,
|
|
|
|
length,
|
|
|
|
) {
|
|
|
|
return blitBuffer(base64ToBytes(string), this, offset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.base64urlSlice = function base64urlSlice(
|
|
|
|
offset,
|
|
|
|
length,
|
|
|
|
) {
|
|
|
|
if (offset === 0 && length === this.length) {
|
|
|
|
return forgivingBase64UrlEncode(this);
|
|
|
|
} else {
|
|
|
|
return forgivingBase64UrlEncode(this.slice(offset, length));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.base64urlWrite = function base64urlWrite(
|
|
|
|
string,
|
|
|
|
offset,
|
|
|
|
length,
|
|
|
|
) {
|
|
|
|
return blitBuffer(base64UrlToBytes(string), this, offset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.hexWrite = function hexWrite(string, offset, length) {
|
|
|
|
return blitBuffer(
|
perf(ext/node): Optimise Buffer string operations (#20158)
Extracted from https://github.com/denoland/deno/pull/17815
Optimise Buffer's string operations, most significantly when dealing
with ASCII and UTF-16. Base64 and HEX encodings are affected to much
lesser degrees.
## Performance
### String length 15
With very small strings we're at break-even or sometimes even lose a tad
bit of performance from creating a `DataView` that ends up not paying
for itself.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.15 µs/iter 871,388.6 (728.78 ns … 1.56 µs) 1.23 µs 1.56 µs 1.56 µs
Buffer.from base64 string 1.63 µs/iter 612,790.9 (1.31 µs … 1.96 µs) 1.77 µs 1.96 µs 1.96 µs
Buffer.from utf16 string 1.41 µs/iter 707,396.3 (915.24 ns … 1.93 µs) 1.61 µs 1.93 µs 1.93 µs
Buffer.from hex string 1.87 µs/iter 535,357.9 (1.56 µs … 2.19 µs) 2 µs 2.19 µs 2.19 µs
Buffer.toString ascii string 154.58 ns/iter 6,469,162.8 (149.69 ns … 198 ns) 154.51 ns 182.89 ns 191.91 ns
Buffer.toString base64 string 161.65 ns/iter 6,186,189.6 (150.91 ns … 181.15 ns) 165.18 ns 171.87 ns 174.94 ns
Buffer.toString utf16 string 292.74 ns/iter 3,415,959.8 (285.43 ns … 312.47 ns) 295.25 ns 310.47 ns 312.47 ns
Buffer.toString hex string 89.61 ns/iter 11,159,315.6 (81.09 ns … 123.77 ns) 91.09 ns 113.62 ns 119.28 ns
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.26 µs/iter 794,875.8 (1.07 µs … 1.46 µs) 1.31 µs 1.46 µs 1.46 µs
Buffer.from base64 string 1.65 µs/iter 607,853.3 (1.38 µs … 2.01 µs) 1.69 µs 2.01 µs 2.01 µs
Buffer.from utf16 string 1.34 µs/iter 744,894.6 (1.09 µs … 1.55 µs) 1.45 µs 1.55 µs 1.55 µs
Buffer.from hex string 2.01 µs/iter 496,345.8 (1.54 µs … 2.6 µs) 2.26 µs 2.6 µs 2.6 µs
Buffer.toString ascii string 150.16 ns/iter 6,659,630.5 (144.99 ns … 166.68 ns) 152.4 ns 157.26 ns 159.14 ns
Buffer.toString base64 string 164.73 ns/iter 6,070,692.0 (158.77 ns … 185.63 ns) 168.48 ns 175.74 ns 176.68 ns
Buffer.toString utf16 string 150.61 ns/iter 6,639,864.0 (148.2 ns … 168.29 ns) 150.93 ns 157.21 ns 168.15 ns
Buffer.toString hex string 94.21 ns/iter 10,614,972.9 (86.21 ns … 98.75 ns) 95.43 ns 97.99 ns 98.21 ns
```
### String length 1500
With moderate lengths we already see great upsides for `Buffer.from()`
with ASCII and UTF-16.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 5.79 µs/iter 172,562.6 (4.72 µs … 4.71 ms) 5.04 µs 10.3 µs 11.67 µs
Buffer.from base64 string 5.08 µs/iter 196,678.9 (4.97 µs … 5.76 µs) 5.08 µs 5.76 µs 5.76 µs
Buffer.from utf16 string 9.68 µs/iter 103,316.5 (7.14 µs … 3.44 ms) 10.32 µs 13.42 µs 15.21 µs
Buffer.from hex string 53.7 µs/iter 18,620.2 (49.37 µs … 2.2 ms) 54.74 µs 72.2 µs 81.07 µs
Buffer.toString ascii string 6.63 µs/iter 150,761.3 (5.59 µs … 1.11 ms) 6.08 µs 15.68 µs 24.77 µs
Buffer.toString base64 string 460.57 ns/iter 2,171,224.4 (448.33 ns … 511.73 ns) 465.05 ns 495.54 ns 511.73 ns
Buffer.toString utf16 string 6.52 µs/iter 153,287.0 (6.47 µs … 6.66 µs) 6.53 µs 6.66 µs 6.66 µs
Buffer.toString hex string 3.68 µs/iter 271,965.4 (3.64 µs … 3.82 µs) 3.68 µs 3.82 µs 3.82 µs
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 11.46 µs/iter 87,298.1 (8.53 µs … 834.1 µs) 9.61 µs 83.31 µs 87.3 µs
Buffer.from base64 string 5.4 µs/iter 185,027.8 (5.07 µs … 7.49 µs) 5.44 µs 7.49 µs 7.49 µs
Buffer.from utf16 string 20.3 µs/iter 49,270.8 (13.55 µs … 649.11 µs) 18.8 µs 113.93 µs 125.17 µs
Buffer.from hex string 52.03 µs/iter 19,218.9 (48.74 µs … 2.59 ms) 52.84 µs 67.05 µs 73.56 µs
Buffer.toString ascii string 6.46 µs/iter 154,822.5 (6.32 µs … 6.69 µs) 6.52 µs 6.69 µs 6.69 µs
Buffer.toString base64 string 440.19 ns/iter 2,271,764.6 (427 ns … 490.77 ns) 444.74 ns 484.64 ns 490.77 ns
Buffer.toString utf16 string 6.89 µs/iter 145,106.7 (6.81 µs … 7.24 µs) 6.91 µs 7.24 µs 7.24 µs
Buffer.toString hex string 3.66 µs/iter 273,456.5 (3.6 µs … 4.02 µs) 3.64 µs 4.02 µs 4.02 µs
```
### String length 2^20
With massive lengths we the difference in ASCII and UTF-16 parsing
performance is enormous.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 4.1 ms/iter 243.7 (2.64 ms … 6.74 ms) 4.43 ms 6.26 ms 6.74 ms
Buffer.from base64 string 3.74 ms/iter 267.6 (2.91 ms … 4.92 ms) 3.96 ms 4.31 ms 4.92 ms
Buffer.from utf16 string 7.72 ms/iter 129.5 (5.91 ms … 11.03 ms) 7.97 ms 11.03 ms 11.03 ms
Buffer.from hex string 35.72 ms/iter 28.0 (34.71 ms … 38.42 ms) 35.93 ms 38.42 ms 38.42 ms
Buffer.toString ascii string 78.92 ms/iter 12.7 (42.72 ms … 94.13 ms) 91.64 ms 94.13 ms 94.13 ms
Buffer.toString base64 string 833.62 µs/iter 1,199.6 (638.05 µs … 5.97 ms) 826.86 µs 2.45 ms 2.48 ms
Buffer.toString utf16 string 79.35 ms/iter 12.6 (69.72 ms … 88.9 ms) 86.66 ms 88.9 ms 88.9 ms
Buffer.toString hex string 31.04 ms/iter 32.2 (4.3 ms … 46.9 ms) 37.21 ms 46.9 ms 46.9 ms
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 18.66 ms/iter 53.6 (15.61 ms … 23.26 ms) 20.62 ms 23.26 ms 23.26 ms
Buffer.from base64 string 4.7 ms/iter 212.9 (2.94 ms … 9.07 ms) 4.65 ms 9.06 ms 9.07 ms
Buffer.from utf16 string 33.49 ms/iter 29.9 (31.24 ms … 35.67 ms) 34.08 ms 35.67 ms 35.67 ms
Buffer.from hex string 39.38 ms/iter 25.4 (38.66 ms … 42.36 ms) 39.58 ms 42.36 ms 42.36 ms
Buffer.toString ascii string 77.68 ms/iter 12.9 (67.46 ms … 95.68 ms) 84.71 ms 95.68 ms 95.68 ms
Buffer.toString base64 string 825.53 µs/iter 1,211.3 (655.38 µs … 6.69 ms) 816.62 µs 3.07 ms 3.13 ms
Buffer.toString utf16 string 76.54 ms/iter 13.1 (66.9 ms … 85.26 ms) 83.63 ms 85.26 ms 85.26 ms
Buffer.toString hex string 38.56 ms/iter 25.9 (33.83 ms … 46.56 ms) 45.33 ms 46.56 ms 46.56 ms
```
2023-09-07 16:41:16 -04:00
|
|
|
hexToBytes(string),
|
2023-02-14 11:38:45 -05:00
|
|
|
this,
|
|
|
|
offset,
|
|
|
|
length,
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.hexSlice = function hexSlice(string, offset, length) {
|
|
|
|
return _hexSlice(this, string, offset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.latin1Slice = function latin1Slice(
|
|
|
|
string,
|
|
|
|
offset,
|
|
|
|
length,
|
|
|
|
) {
|
|
|
|
return _latin1Slice(this, string, offset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.latin1Write = function latin1Write(
|
|
|
|
string,
|
|
|
|
offset,
|
|
|
|
length,
|
|
|
|
) {
|
|
|
|
return blitBuffer(asciiToBytes(string), this, offset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.ucs2Slice = function ucs2Slice(offset, length) {
|
|
|
|
if (offset === 0 && length === this.length) {
|
|
|
|
return bytesToUtf16le(this);
|
|
|
|
} else {
|
|
|
|
return bytesToUtf16le(this.slice(offset, length));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.ucs2Write = function ucs2Write(string, offset, length) {
|
|
|
|
return blitBuffer(
|
|
|
|
utf16leToBytes(string, this.length - offset),
|
|
|
|
this,
|
|
|
|
offset,
|
|
|
|
length,
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.utf8Slice = function utf8Slice(string, offset, length) {
|
|
|
|
return _utf8Slice(this, string, offset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.utf8Write = function utf8Write(string, offset, length) {
|
2023-10-07 22:09:50 -04:00
|
|
|
offset = offset || 0;
|
|
|
|
const maxLength = Math.min(length || Infinity, this.length - offset);
|
|
|
|
const buf = offset || maxLength < this.length
|
|
|
|
? this.subarray(offset, maxLength + offset)
|
|
|
|
: this;
|
|
|
|
return utf8Encoder.encodeInto(string, buf).written;
|
2023-02-14 11:38:45 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.write = function write(string, offset, length, encoding) {
|
perf(ext/node): Optimise Buffer string operations (#20158)
Extracted from https://github.com/denoland/deno/pull/17815
Optimise Buffer's string operations, most significantly when dealing
with ASCII and UTF-16. Base64 and HEX encodings are affected to much
lesser degrees.
## Performance
### String length 15
With very small strings we're at break-even or sometimes even lose a tad
bit of performance from creating a `DataView` that ends up not paying
for itself.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.15 µs/iter 871,388.6 (728.78 ns … 1.56 µs) 1.23 µs 1.56 µs 1.56 µs
Buffer.from base64 string 1.63 µs/iter 612,790.9 (1.31 µs … 1.96 µs) 1.77 µs 1.96 µs 1.96 µs
Buffer.from utf16 string 1.41 µs/iter 707,396.3 (915.24 ns … 1.93 µs) 1.61 µs 1.93 µs 1.93 µs
Buffer.from hex string 1.87 µs/iter 535,357.9 (1.56 µs … 2.19 µs) 2 µs 2.19 µs 2.19 µs
Buffer.toString ascii string 154.58 ns/iter 6,469,162.8 (149.69 ns … 198 ns) 154.51 ns 182.89 ns 191.91 ns
Buffer.toString base64 string 161.65 ns/iter 6,186,189.6 (150.91 ns … 181.15 ns) 165.18 ns 171.87 ns 174.94 ns
Buffer.toString utf16 string 292.74 ns/iter 3,415,959.8 (285.43 ns … 312.47 ns) 295.25 ns 310.47 ns 312.47 ns
Buffer.toString hex string 89.61 ns/iter 11,159,315.6 (81.09 ns … 123.77 ns) 91.09 ns 113.62 ns 119.28 ns
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.26 µs/iter 794,875.8 (1.07 µs … 1.46 µs) 1.31 µs 1.46 µs 1.46 µs
Buffer.from base64 string 1.65 µs/iter 607,853.3 (1.38 µs … 2.01 µs) 1.69 µs 2.01 µs 2.01 µs
Buffer.from utf16 string 1.34 µs/iter 744,894.6 (1.09 µs … 1.55 µs) 1.45 µs 1.55 µs 1.55 µs
Buffer.from hex string 2.01 µs/iter 496,345.8 (1.54 µs … 2.6 µs) 2.26 µs 2.6 µs 2.6 µs
Buffer.toString ascii string 150.16 ns/iter 6,659,630.5 (144.99 ns … 166.68 ns) 152.4 ns 157.26 ns 159.14 ns
Buffer.toString base64 string 164.73 ns/iter 6,070,692.0 (158.77 ns … 185.63 ns) 168.48 ns 175.74 ns 176.68 ns
Buffer.toString utf16 string 150.61 ns/iter 6,639,864.0 (148.2 ns … 168.29 ns) 150.93 ns 157.21 ns 168.15 ns
Buffer.toString hex string 94.21 ns/iter 10,614,972.9 (86.21 ns … 98.75 ns) 95.43 ns 97.99 ns 98.21 ns
```
### String length 1500
With moderate lengths we already see great upsides for `Buffer.from()`
with ASCII and UTF-16.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 5.79 µs/iter 172,562.6 (4.72 µs … 4.71 ms) 5.04 µs 10.3 µs 11.67 µs
Buffer.from base64 string 5.08 µs/iter 196,678.9 (4.97 µs … 5.76 µs) 5.08 µs 5.76 µs 5.76 µs
Buffer.from utf16 string 9.68 µs/iter 103,316.5 (7.14 µs … 3.44 ms) 10.32 µs 13.42 µs 15.21 µs
Buffer.from hex string 53.7 µs/iter 18,620.2 (49.37 µs … 2.2 ms) 54.74 µs 72.2 µs 81.07 µs
Buffer.toString ascii string 6.63 µs/iter 150,761.3 (5.59 µs … 1.11 ms) 6.08 µs 15.68 µs 24.77 µs
Buffer.toString base64 string 460.57 ns/iter 2,171,224.4 (448.33 ns … 511.73 ns) 465.05 ns 495.54 ns 511.73 ns
Buffer.toString utf16 string 6.52 µs/iter 153,287.0 (6.47 µs … 6.66 µs) 6.53 µs 6.66 µs 6.66 µs
Buffer.toString hex string 3.68 µs/iter 271,965.4 (3.64 µs … 3.82 µs) 3.68 µs 3.82 µs 3.82 µs
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 11.46 µs/iter 87,298.1 (8.53 µs … 834.1 µs) 9.61 µs 83.31 µs 87.3 µs
Buffer.from base64 string 5.4 µs/iter 185,027.8 (5.07 µs … 7.49 µs) 5.44 µs 7.49 µs 7.49 µs
Buffer.from utf16 string 20.3 µs/iter 49,270.8 (13.55 µs … 649.11 µs) 18.8 µs 113.93 µs 125.17 µs
Buffer.from hex string 52.03 µs/iter 19,218.9 (48.74 µs … 2.59 ms) 52.84 µs 67.05 µs 73.56 µs
Buffer.toString ascii string 6.46 µs/iter 154,822.5 (6.32 µs … 6.69 µs) 6.52 µs 6.69 µs 6.69 µs
Buffer.toString base64 string 440.19 ns/iter 2,271,764.6 (427 ns … 490.77 ns) 444.74 ns 484.64 ns 490.77 ns
Buffer.toString utf16 string 6.89 µs/iter 145,106.7 (6.81 µs … 7.24 µs) 6.91 µs 7.24 µs 7.24 µs
Buffer.toString hex string 3.66 µs/iter 273,456.5 (3.6 µs … 4.02 µs) 3.64 µs 4.02 µs 4.02 µs
```
### String length 2^20
With massive lengths we the difference in ASCII and UTF-16 parsing
performance is enormous.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 4.1 ms/iter 243.7 (2.64 ms … 6.74 ms) 4.43 ms 6.26 ms 6.74 ms
Buffer.from base64 string 3.74 ms/iter 267.6 (2.91 ms … 4.92 ms) 3.96 ms 4.31 ms 4.92 ms
Buffer.from utf16 string 7.72 ms/iter 129.5 (5.91 ms … 11.03 ms) 7.97 ms 11.03 ms 11.03 ms
Buffer.from hex string 35.72 ms/iter 28.0 (34.71 ms … 38.42 ms) 35.93 ms 38.42 ms 38.42 ms
Buffer.toString ascii string 78.92 ms/iter 12.7 (42.72 ms … 94.13 ms) 91.64 ms 94.13 ms 94.13 ms
Buffer.toString base64 string 833.62 µs/iter 1,199.6 (638.05 µs … 5.97 ms) 826.86 µs 2.45 ms 2.48 ms
Buffer.toString utf16 string 79.35 ms/iter 12.6 (69.72 ms … 88.9 ms) 86.66 ms 88.9 ms 88.9 ms
Buffer.toString hex string 31.04 ms/iter 32.2 (4.3 ms … 46.9 ms) 37.21 ms 46.9 ms 46.9 ms
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 18.66 ms/iter 53.6 (15.61 ms … 23.26 ms) 20.62 ms 23.26 ms 23.26 ms
Buffer.from base64 string 4.7 ms/iter 212.9 (2.94 ms … 9.07 ms) 4.65 ms 9.06 ms 9.07 ms
Buffer.from utf16 string 33.49 ms/iter 29.9 (31.24 ms … 35.67 ms) 34.08 ms 35.67 ms 35.67 ms
Buffer.from hex string 39.38 ms/iter 25.4 (38.66 ms … 42.36 ms) 39.58 ms 42.36 ms 42.36 ms
Buffer.toString ascii string 77.68 ms/iter 12.9 (67.46 ms … 95.68 ms) 84.71 ms 95.68 ms 95.68 ms
Buffer.toString base64 string 825.53 µs/iter 1,211.3 (655.38 µs … 6.69 ms) 816.62 µs 3.07 ms 3.13 ms
Buffer.toString utf16 string 76.54 ms/iter 13.1 (66.9 ms … 85.26 ms) 83.63 ms 85.26 ms 85.26 ms
Buffer.toString hex string 38.56 ms/iter 25.9 (33.83 ms … 46.56 ms) 45.33 ms 46.56 ms 46.56 ms
```
2023-09-07 16:41:16 -04:00
|
|
|
if (typeof string !== "string") {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE("argument", "string");
|
|
|
|
}
|
2023-02-14 11:38:45 -05:00
|
|
|
// Buffer#write(string);
|
|
|
|
if (offset === undefined) {
|
|
|
|
return this.utf8Write(string, 0, this.length);
|
|
|
|
}
|
|
|
|
// Buffer#write(string, encoding)
|
|
|
|
if (length === undefined && typeof offset === "string") {
|
|
|
|
encoding = offset;
|
|
|
|
length = this.length;
|
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
// Buffer#write(string, offset[, length][, encoding])
|
|
|
|
} else {
|
|
|
|
validateOffset(offset, "offset", 0, this.length);
|
|
|
|
|
|
|
|
const remaining = this.length - offset;
|
|
|
|
|
|
|
|
if (length === undefined) {
|
|
|
|
length = remaining;
|
|
|
|
} else if (typeof length === "string") {
|
|
|
|
encoding = length;
|
|
|
|
length = remaining;
|
|
|
|
} else {
|
|
|
|
validateOffset(length, "length", 0, this.length);
|
|
|
|
if (length > remaining) {
|
|
|
|
length = remaining;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!encoding) {
|
|
|
|
return this.utf8Write(string, offset, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ops = getEncodingOps(encoding);
|
|
|
|
if (ops === undefined) {
|
|
|
|
throw new codes.ERR_UNKNOWN_ENCODING(encoding);
|
|
|
|
}
|
|
|
|
return ops.write(this, string, offset, length);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.toJSON = function toJSON() {
|
|
|
|
return {
|
|
|
|
type: "Buffer",
|
|
|
|
data: Array.prototype.slice.call(this._arr || this, 0),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
function fromArrayBuffer(obj, byteOffset, length) {
|
|
|
|
// Convert byteOffset to integer
|
|
|
|
if (byteOffset === undefined) {
|
|
|
|
byteOffset = 0;
|
|
|
|
} else {
|
|
|
|
byteOffset = +byteOffset;
|
|
|
|
if (Number.isNaN(byteOffset)) {
|
|
|
|
byteOffset = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const maxLength = obj.byteLength - byteOffset;
|
|
|
|
|
|
|
|
if (maxLength < 0) {
|
|
|
|
throw new codes.ERR_BUFFER_OUT_OF_BOUNDS("offset");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length === undefined) {
|
|
|
|
length = maxLength;
|
|
|
|
} else {
|
|
|
|
// Convert length to non-negative integer.
|
|
|
|
length = +length;
|
|
|
|
if (length > 0) {
|
|
|
|
if (length > maxLength) {
|
|
|
|
throw new codes.ERR_BUFFER_OUT_OF_BOUNDS("length");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
length = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const buffer = new Uint8Array(obj, byteOffset, length);
|
|
|
|
Object.setPrototypeOf(buffer, Buffer.prototype);
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _base64Slice(buf, start, end) {
|
|
|
|
if (start === 0 && end === buf.length) {
|
|
|
|
return forgivingBase64Encode(buf);
|
|
|
|
} else {
|
|
|
|
return forgivingBase64Encode(buf.slice(start, end));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
|
|
|
|
function _utf8Slice(buf, start, end) {
|
2024-06-11 07:41:44 -04:00
|
|
|
try {
|
|
|
|
return decoder.decode(buf.slice(start, end));
|
|
|
|
} catch (err) {
|
|
|
|
if (err instanceof TypeError) {
|
|
|
|
throw new NodeError("ERR_STRING_TOO_LONG", "String too long");
|
|
|
|
}
|
|
|
|
throw err;
|
|
|
|
}
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function _latin1Slice(buf, start, end) {
|
|
|
|
let ret = "";
|
|
|
|
end = Math.min(buf.length, end);
|
|
|
|
for (let i = start; i < end; ++i) {
|
|
|
|
ret += String.fromCharCode(buf[i]);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _hexSlice(buf, start, end) {
|
|
|
|
const len = buf.length;
|
|
|
|
if (!start || start < 0) {
|
|
|
|
start = 0;
|
|
|
|
}
|
|
|
|
if (!end || end < 0 || end > len) {
|
|
|
|
end = len;
|
|
|
|
}
|
|
|
|
let out = "";
|
|
|
|
for (let i = start; i < end; ++i) {
|
|
|
|
out += hexSliceLookupTable[buf[i]];
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.prototype.slice = function slice(start, end) {
|
2023-06-13 15:26:28 -04:00
|
|
|
return this.subarray(start, end);
|
2023-02-14 11:38:45 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readUintLE = Buffer.prototype.readUIntLE = function readUIntLE(
|
|
|
|
offset,
|
|
|
|
byteLength,
|
|
|
|
) {
|
|
|
|
if (offset === undefined) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE("offset", "number", offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 6) {
|
|
|
|
return readUInt48LE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 5) {
|
|
|
|
return readUInt40LE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 3) {
|
|
|
|
return readUInt24LE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 4) {
|
|
|
|
return this.readUInt32LE(offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 2) {
|
|
|
|
return this.readUInt16LE(offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 1) {
|
|
|
|
return this.readUInt8(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundsError(byteLength, 6, "byteLength");
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readUintBE = Buffer.prototype.readUIntBE = function readUIntBE(
|
|
|
|
offset,
|
|
|
|
byteLength,
|
|
|
|
) {
|
|
|
|
if (offset === undefined) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE("offset", "number", offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 6) {
|
|
|
|
return readUInt48BE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 5) {
|
|
|
|
return readUInt40BE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 3) {
|
|
|
|
return readUInt24BE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 4) {
|
|
|
|
return this.readUInt32BE(offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 2) {
|
|
|
|
return this.readUInt16BE(offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 1) {
|
|
|
|
return this.readUInt8(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundsError(byteLength, 6, "byteLength");
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readUint8 = Buffer.prototype.readUInt8 = function readUInt8(
|
|
|
|
offset = 0,
|
|
|
|
) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const val = this[offset];
|
|
|
|
if (val === undefined) {
|
|
|
|
boundsError(offset, this.length - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readUint16BE = Buffer.prototype.readUInt16BE = readUInt16BE;
|
|
|
|
|
|
|
|
Buffer.prototype.readUint16LE =
|
|
|
|
Buffer.prototype.readUInt16LE =
|
|
|
|
function readUInt16LE(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 1];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, this.length - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first + last * 2 ** 8;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readUint32LE =
|
|
|
|
Buffer.prototype.readUInt32LE =
|
|
|
|
function readUInt32LE(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 3];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, this.length - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first +
|
|
|
|
this[++offset] * 2 ** 8 +
|
|
|
|
this[++offset] * 2 ** 16 +
|
|
|
|
last * 2 ** 24;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readUint32BE = Buffer.prototype.readUInt32BE = readUInt32BE;
|
|
|
|
|
|
|
|
Buffer.prototype.readBigUint64LE =
|
|
|
|
Buffer.prototype.readBigUInt64LE =
|
|
|
|
defineBigIntMethod(
|
|
|
|
function readBigUInt64LE(offset) {
|
|
|
|
offset = offset >>> 0;
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 7];
|
|
|
|
if (first === void 0 || last === void 0) {
|
|
|
|
boundsError(offset, this.length - 8);
|
|
|
|
}
|
|
|
|
const lo = first + this[++offset] * 2 ** 8 +
|
|
|
|
this[++offset] * 2 ** 16 +
|
|
|
|
this[++offset] * 2 ** 24;
|
|
|
|
const hi = this[++offset] + this[++offset] * 2 ** 8 +
|
|
|
|
this[++offset] * 2 ** 16 + last * 2 ** 24;
|
|
|
|
return BigInt(lo) + (BigInt(hi) << BigInt(32));
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Buffer.prototype.readBigUint64BE =
|
|
|
|
Buffer.prototype.readBigUInt64BE =
|
|
|
|
defineBigIntMethod(
|
|
|
|
function readBigUInt64BE(offset) {
|
|
|
|
offset = offset >>> 0;
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 7];
|
|
|
|
if (first === void 0 || last === void 0) {
|
|
|
|
boundsError(offset, this.length - 8);
|
|
|
|
}
|
|
|
|
const hi = first * 2 ** 24 + this[++offset] * 2 ** 16 +
|
|
|
|
this[++offset] * 2 ** 8 + this[++offset];
|
|
|
|
const lo = this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 +
|
|
|
|
this[++offset] * 2 ** 8 + last;
|
|
|
|
return (BigInt(hi) << BigInt(32)) + BigInt(lo);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Buffer.prototype.readIntLE = function readIntLE(
|
|
|
|
offset,
|
|
|
|
byteLength,
|
|
|
|
) {
|
|
|
|
if (offset === undefined) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE("offset", "number", offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 6) {
|
|
|
|
return readInt48LE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 5) {
|
|
|
|
return readInt40LE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 3) {
|
|
|
|
return readInt24LE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 4) {
|
|
|
|
return this.readInt32LE(offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 2) {
|
|
|
|
return this.readInt16LE(offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 1) {
|
|
|
|
return this.readInt8(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundsError(byteLength, 6, "byteLength");
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readIntBE = function readIntBE(offset, byteLength) {
|
|
|
|
if (offset === undefined) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE("offset", "number", offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 6) {
|
|
|
|
return readInt48BE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 5) {
|
|
|
|
return readInt40BE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 3) {
|
|
|
|
return readInt24BE(this, offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 4) {
|
|
|
|
return this.readInt32BE(offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 2) {
|
|
|
|
return this.readInt16BE(offset);
|
|
|
|
}
|
|
|
|
if (byteLength === 1) {
|
|
|
|
return this.readInt8(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundsError(byteLength, 6, "byteLength");
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readInt8 = function readInt8(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const val = this[offset];
|
|
|
|
if (val === undefined) {
|
|
|
|
boundsError(offset, this.length - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return val | (val & 2 ** 7) * 0x1fffffe;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readInt16LE = function readInt16LE(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 1];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, this.length - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
const val = first + last * 2 ** 8;
|
|
|
|
return val | (val & 2 ** 15) * 0x1fffe;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readInt16BE = function readInt16BE(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 1];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, this.length - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
const val = first * 2 ** 8 + last;
|
|
|
|
return val | (val & 2 ** 15) * 0x1fffe;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readInt32LE = function readInt32LE(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 3];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, this.length - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first +
|
|
|
|
this[++offset] * 2 ** 8 +
|
|
|
|
this[++offset] * 2 ** 16 +
|
|
|
|
(last << 24); // Overflow
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readInt32BE = function readInt32BE(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 3];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, this.length - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (first << 24) + // Overflow
|
|
|
|
this[++offset] * 2 ** 16 +
|
|
|
|
this[++offset] * 2 ** 8 +
|
|
|
|
last;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readBigInt64LE = defineBigIntMethod(
|
|
|
|
function readBigInt64LE(offset) {
|
|
|
|
offset = offset >>> 0;
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 7];
|
|
|
|
if (first === void 0 || last === void 0) {
|
|
|
|
boundsError(offset, this.length - 8);
|
|
|
|
}
|
|
|
|
const val = this[offset + 4] + this[offset + 5] * 2 ** 8 +
|
|
|
|
this[offset + 6] * 2 ** 16 + (last << 24);
|
|
|
|
return (BigInt(val) << BigInt(32)) +
|
|
|
|
BigInt(
|
|
|
|
first + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 +
|
|
|
|
this[++offset] * 2 ** 24,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Buffer.prototype.readBigInt64BE = defineBigIntMethod(
|
|
|
|
function readBigInt64BE(offset) {
|
|
|
|
offset = offset >>> 0;
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 7];
|
|
|
|
if (first === void 0 || last === void 0) {
|
|
|
|
boundsError(offset, this.length - 8);
|
|
|
|
}
|
|
|
|
const val = (first << 24) + this[++offset] * 2 ** 16 +
|
|
|
|
this[++offset] * 2 ** 8 + this[++offset];
|
|
|
|
return (BigInt(val) << BigInt(32)) +
|
|
|
|
BigInt(
|
|
|
|
this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 +
|
|
|
|
this[++offset] * 2 ** 8 + last,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Buffer.prototype.readFloatLE = function readFloatLE(offset) {
|
|
|
|
return bigEndian
|
|
|
|
? readFloatBackwards(this, offset)
|
|
|
|
: readFloatForwards(this, offset);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readFloatBE = function readFloatBE(offset) {
|
|
|
|
return bigEndian
|
|
|
|
? readFloatForwards(this, offset)
|
|
|
|
: readFloatBackwards(this, offset);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readDoubleLE = function readDoubleLE(offset) {
|
|
|
|
return bigEndian
|
|
|
|
? readDoubleBackwards(this, offset)
|
|
|
|
: readDoubleForwards(this, offset);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.readDoubleBE = function readDoubleBE(offset) {
|
|
|
|
return bigEndian
|
|
|
|
? readDoubleForwards(this, offset)
|
|
|
|
: readDoubleBackwards(this, offset);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeUintLE =
|
|
|
|
Buffer.prototype.writeUIntLE =
|
|
|
|
function writeUIntLE(value, offset, byteLength) {
|
|
|
|
if (byteLength === 6) {
|
|
|
|
return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 5) {
|
|
|
|
return writeU_Int40LE(this, value, offset, 0, 0xffffffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 3) {
|
|
|
|
return writeU_Int24LE(this, value, offset, 0, 0xffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 4) {
|
|
|
|
return writeU_Int32LE(this, value, offset, 0, 0xffffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 2) {
|
|
|
|
return writeU_Int16LE(this, value, offset, 0, 0xffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 1) {
|
|
|
|
return writeU_Int8(this, value, offset, 0, 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundsError(byteLength, 6, "byteLength");
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeUintBE =
|
|
|
|
Buffer.prototype.writeUIntBE =
|
|
|
|
function writeUIntBE(value, offset, byteLength) {
|
|
|
|
if (byteLength === 6) {
|
|
|
|
return writeU_Int48BE(this, value, offset, 0, 0xffffffffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 5) {
|
|
|
|
return writeU_Int40BE(this, value, offset, 0, 0xffffffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 3) {
|
|
|
|
return writeU_Int24BE(this, value, offset, 0, 0xffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 4) {
|
|
|
|
return writeU_Int32BE(this, value, offset, 0, 0xffffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 2) {
|
|
|
|
return writeU_Int16BE(this, value, offset, 0, 0xffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 1) {
|
|
|
|
return writeU_Int8(this, value, offset, 0, 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundsError(byteLength, 6, "byteLength");
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeUint8 = Buffer.prototype.writeUInt8 = function writeUInt8(
|
|
|
|
value,
|
|
|
|
offset = 0,
|
|
|
|
) {
|
|
|
|
return writeU_Int8(this, value, offset, 0, 0xff);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeUint16LE =
|
|
|
|
Buffer.prototype.writeUInt16LE =
|
|
|
|
function writeUInt16LE(value, offset = 0) {
|
|
|
|
return writeU_Int16LE(this, value, offset, 0, 0xffff);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeUint16BE =
|
|
|
|
Buffer.prototype.writeUInt16BE =
|
|
|
|
function writeUInt16BE(value, offset = 0) {
|
|
|
|
return writeU_Int16BE(this, value, offset, 0, 0xffff);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeUint32LE =
|
|
|
|
Buffer.prototype.writeUInt32LE =
|
|
|
|
function writeUInt32LE(value, offset = 0) {
|
|
|
|
return _writeUInt32LE(this, value, offset, 0, 0xffffffff);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeUint32BE =
|
|
|
|
Buffer.prototype.writeUInt32BE =
|
|
|
|
function writeUInt32BE(value, offset = 0) {
|
|
|
|
return _writeUInt32BE(this, value, offset, 0, 0xffffffff);
|
|
|
|
};
|
|
|
|
|
|
|
|
function wrtBigUInt64LE(buf, value, offset, min, max) {
|
|
|
|
checkIntBI(value, min, max, buf, offset, 7);
|
|
|
|
let lo = Number(value & BigInt(4294967295));
|
|
|
|
buf[offset++] = lo;
|
|
|
|
lo = lo >> 8;
|
|
|
|
buf[offset++] = lo;
|
|
|
|
lo = lo >> 8;
|
|
|
|
buf[offset++] = lo;
|
|
|
|
lo = lo >> 8;
|
|
|
|
buf[offset++] = lo;
|
|
|
|
let hi = Number(value >> BigInt(32) & BigInt(4294967295));
|
|
|
|
buf[offset++] = hi;
|
|
|
|
hi = hi >> 8;
|
|
|
|
buf[offset++] = hi;
|
|
|
|
hi = hi >> 8;
|
|
|
|
buf[offset++] = hi;
|
|
|
|
hi = hi >> 8;
|
|
|
|
buf[offset++] = hi;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
function wrtBigUInt64BE(buf, value, offset, min, max) {
|
|
|
|
checkIntBI(value, min, max, buf, offset, 7);
|
|
|
|
let lo = Number(value & BigInt(4294967295));
|
|
|
|
buf[offset + 7] = lo;
|
|
|
|
lo = lo >> 8;
|
|
|
|
buf[offset + 6] = lo;
|
|
|
|
lo = lo >> 8;
|
|
|
|
buf[offset + 5] = lo;
|
|
|
|
lo = lo >> 8;
|
|
|
|
buf[offset + 4] = lo;
|
|
|
|
let hi = Number(value >> BigInt(32) & BigInt(4294967295));
|
|
|
|
buf[offset + 3] = hi;
|
|
|
|
hi = hi >> 8;
|
|
|
|
buf[offset + 2] = hi;
|
|
|
|
hi = hi >> 8;
|
|
|
|
buf[offset + 1] = hi;
|
|
|
|
hi = hi >> 8;
|
|
|
|
buf[offset] = hi;
|
|
|
|
return offset + 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer.prototype.writeBigUint64LE =
|
|
|
|
Buffer.prototype.writeBigUInt64LE =
|
|
|
|
defineBigIntMethod(
|
|
|
|
function writeBigUInt64LE(value, offset = 0) {
|
|
|
|
return wrtBigUInt64LE(
|
|
|
|
this,
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
BigInt(0),
|
|
|
|
BigInt("0xffffffffffffffff"),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Buffer.prototype.writeBigUint64BE =
|
|
|
|
Buffer.prototype.writeBigUInt64BE =
|
|
|
|
defineBigIntMethod(
|
|
|
|
function writeBigUInt64BE(value, offset = 0) {
|
|
|
|
return wrtBigUInt64BE(
|
|
|
|
this,
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
BigInt(0),
|
|
|
|
BigInt("0xffffffffffffffff"),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Buffer.prototype.writeIntLE = function writeIntLE(
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
byteLength,
|
|
|
|
) {
|
|
|
|
if (byteLength === 6) {
|
|
|
|
return writeU_Int48LE(
|
|
|
|
this,
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
-0x800000000000,
|
|
|
|
0x7fffffffffff,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (byteLength === 5) {
|
|
|
|
return writeU_Int40LE(this, value, offset, -0x8000000000, 0x7fffffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 3) {
|
|
|
|
return writeU_Int24LE(this, value, offset, -0x800000, 0x7fffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 4) {
|
|
|
|
return writeU_Int32LE(this, value, offset, -0x80000000, 0x7fffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 2) {
|
|
|
|
return writeU_Int16LE(this, value, offset, -0x8000, 0x7fff);
|
|
|
|
}
|
|
|
|
if (byteLength === 1) {
|
|
|
|
return writeU_Int8(this, value, offset, -0x80, 0x7f);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundsError(byteLength, 6, "byteLength");
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeIntBE = function writeIntBE(
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
byteLength,
|
|
|
|
) {
|
|
|
|
if (byteLength === 6) {
|
|
|
|
return writeU_Int48BE(
|
|
|
|
this,
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
-0x800000000000,
|
|
|
|
0x7fffffffffff,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (byteLength === 5) {
|
|
|
|
return writeU_Int40BE(this, value, offset, -0x8000000000, 0x7fffffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 3) {
|
|
|
|
return writeU_Int24BE(this, value, offset, -0x800000, 0x7fffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 4) {
|
|
|
|
return writeU_Int32BE(this, value, offset, -0x80000000, 0x7fffffff);
|
|
|
|
}
|
|
|
|
if (byteLength === 2) {
|
|
|
|
return writeU_Int16BE(this, value, offset, -0x8000, 0x7fff);
|
|
|
|
}
|
|
|
|
if (byteLength === 1) {
|
|
|
|
return writeU_Int8(this, value, offset, -0x80, 0x7f);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundsError(byteLength, 6, "byteLength");
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeInt8 = function writeInt8(value, offset = 0) {
|
|
|
|
return writeU_Int8(this, value, offset, -0x80, 0x7f);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeInt16LE = function writeInt16LE(value, offset = 0) {
|
|
|
|
return writeU_Int16LE(this, value, offset, -0x8000, 0x7fff);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeInt16BE = function writeInt16BE(
|
|
|
|
value,
|
|
|
|
offset = 0,
|
|
|
|
) {
|
|
|
|
return writeU_Int16BE(this, value, offset, -0x8000, 0x7fff);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeInt32LE = function writeInt32LE(value, offset = 0) {
|
|
|
|
return writeU_Int32LE(this, value, offset, -0x80000000, 0x7fffffff);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeInt32BE = function writeInt32BE(value, offset = 0) {
|
|
|
|
return writeU_Int32BE(this, value, offset, -0x80000000, 0x7fffffff);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeBigInt64LE = defineBigIntMethod(
|
|
|
|
function writeBigInt64LE(value, offset = 0) {
|
|
|
|
return wrtBigUInt64LE(
|
|
|
|
this,
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
-BigInt("0x8000000000000000"),
|
|
|
|
BigInt("0x7fffffffffffffff"),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Buffer.prototype.writeBigInt64BE = defineBigIntMethod(
|
|
|
|
function writeBigInt64BE(value, offset = 0) {
|
|
|
|
return wrtBigUInt64BE(
|
|
|
|
this,
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
-BigInt("0x8000000000000000"),
|
|
|
|
BigInt("0x7fffffffffffffff"),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Buffer.prototype.writeFloatLE = function writeFloatLE(
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
) {
|
|
|
|
return bigEndian
|
|
|
|
? writeFloatBackwards(this, value, offset)
|
|
|
|
: writeFloatForwards(this, value, offset);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeFloatBE = function writeFloatBE(
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
) {
|
|
|
|
return bigEndian
|
|
|
|
? writeFloatForwards(this, value, offset)
|
|
|
|
: writeFloatBackwards(this, value, offset);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeDoubleLE = function writeDoubleLE(
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
) {
|
|
|
|
return bigEndian
|
|
|
|
? writeDoubleBackwards(this, value, offset)
|
|
|
|
: writeDoubleForwards(this, value, offset);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.writeDoubleBE = function writeDoubleBE(
|
|
|
|
value,
|
|
|
|
offset,
|
|
|
|
) {
|
|
|
|
return bigEndian
|
|
|
|
? writeDoubleForwards(this, value, offset)
|
|
|
|
: writeDoubleBackwards(this, value, offset);
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.copy = function copy(
|
|
|
|
target,
|
|
|
|
targetStart,
|
|
|
|
sourceStart,
|
|
|
|
sourceEnd,
|
|
|
|
) {
|
|
|
|
if (!isUint8Array(this)) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"source",
|
|
|
|
["Buffer", "Uint8Array"],
|
|
|
|
this,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isUint8Array(target)) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(
|
|
|
|
"target",
|
|
|
|
["Buffer", "Uint8Array"],
|
|
|
|
target,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (targetStart === undefined) {
|
|
|
|
targetStart = 0;
|
|
|
|
} else {
|
|
|
|
targetStart = toInteger(targetStart, 0);
|
|
|
|
if (targetStart < 0) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE("targetStart", ">= 0", targetStart);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sourceStart === undefined) {
|
|
|
|
sourceStart = 0;
|
|
|
|
} else {
|
|
|
|
sourceStart = toInteger(sourceStart, 0);
|
2023-11-28 08:28:07 -05:00
|
|
|
if (sourceStart < 0 || sourceStart > this.length) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE(
|
|
|
|
"sourceStart",
|
|
|
|
`>= 0 && <= ${this.length}`,
|
|
|
|
sourceStart,
|
|
|
|
);
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
|
|
|
if (sourceStart >= MAX_UINT32) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE(
|
|
|
|
"sourceStart",
|
|
|
|
`< ${MAX_UINT32}`,
|
|
|
|
sourceStart,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sourceEnd === undefined) {
|
|
|
|
sourceEnd = this.length;
|
|
|
|
} else {
|
|
|
|
sourceEnd = toInteger(sourceEnd, 0);
|
|
|
|
if (sourceEnd < 0) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE("sourceEnd", ">= 0", sourceEnd);
|
|
|
|
}
|
|
|
|
if (sourceEnd >= MAX_UINT32) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE(
|
|
|
|
"sourceEnd",
|
|
|
|
`< ${MAX_UINT32}`,
|
|
|
|
sourceEnd,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (targetStart >= target.length) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sourceEnd > 0 && sourceEnd < sourceStart) {
|
|
|
|
sourceEnd = sourceStart;
|
|
|
|
}
|
|
|
|
if (sourceEnd === sourceStart) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (target.length === 0 || this.length === 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sourceEnd > this.length) {
|
|
|
|
sourceEnd = this.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target.length - targetStart < sourceEnd - sourceStart) {
|
|
|
|
sourceEnd = target.length - targetStart + sourceStart;
|
|
|
|
}
|
|
|
|
|
|
|
|
const len = sourceEnd - sourceStart;
|
|
|
|
if (
|
|
|
|
this === target && typeof Uint8Array.prototype.copyWithin === "function"
|
|
|
|
) {
|
|
|
|
this.copyWithin(targetStart, sourceStart, sourceEnd);
|
|
|
|
} else {
|
|
|
|
Uint8Array.prototype.set.call(
|
|
|
|
target,
|
|
|
|
this.subarray(sourceStart, sourceEnd),
|
|
|
|
targetStart,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
};
|
|
|
|
|
|
|
|
Buffer.prototype.fill = function fill(val, start, end, encoding) {
|
|
|
|
if (typeof val === "string") {
|
|
|
|
if (typeof start === "string") {
|
|
|
|
encoding = start;
|
|
|
|
start = 0;
|
|
|
|
end = this.length;
|
|
|
|
} else if (typeof end === "string") {
|
|
|
|
encoding = end;
|
|
|
|
end = this.length;
|
|
|
|
}
|
|
|
|
if (encoding !== void 0 && typeof encoding !== "string") {
|
|
|
|
throw new TypeError("encoding must be a string");
|
|
|
|
}
|
|
|
|
if (typeof encoding === "string" && !Buffer.isEncoding(encoding)) {
|
|
|
|
throw new TypeError("Unknown encoding: " + encoding);
|
|
|
|
}
|
|
|
|
if (val.length === 1) {
|
|
|
|
const code = val.charCodeAt(0);
|
|
|
|
if (encoding === "utf8" && code < 128 || encoding === "latin1") {
|
|
|
|
val = code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (typeof val === "number") {
|
|
|
|
val = val & 255;
|
|
|
|
} else if (typeof val === "boolean") {
|
|
|
|
val = Number(val);
|
|
|
|
}
|
|
|
|
if (start < 0 || this.length < start || this.length < end) {
|
|
|
|
throw new RangeError("Out of range index");
|
|
|
|
}
|
|
|
|
if (end <= start) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
start = start >>> 0;
|
|
|
|
end = end === void 0 ? this.length : end >>> 0;
|
|
|
|
if (!val) {
|
|
|
|
val = 0;
|
|
|
|
}
|
|
|
|
let i;
|
|
|
|
if (typeof val === "number") {
|
|
|
|
for (i = start; i < end; ++i) {
|
|
|
|
this[i] = val;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const bytes = Buffer.isBuffer(val) ? val : Buffer.from(val, encoding);
|
|
|
|
const len = bytes.length;
|
|
|
|
if (len === 0) {
|
|
|
|
throw new codes.ERR_INVALID_ARG_VALUE(
|
|
|
|
"value",
|
|
|
|
val,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
for (i = 0; i < end - start; ++i) {
|
|
|
|
this[i + start] = bytes[i % len];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
function checkBounds(buf, offset, byteLength2) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
if (buf[offset] === void 0 || buf[offset + byteLength2] === void 0) {
|
|
|
|
boundsError(offset, buf.length - (byteLength2 + 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkIntBI(value, min, max, buf, offset, byteLength2) {
|
|
|
|
if (value > max || value < min) {
|
|
|
|
const n = typeof min === "bigint" ? "n" : "";
|
|
|
|
let range;
|
|
|
|
if (byteLength2 > 3) {
|
|
|
|
if (min === 0 || min === BigInt(0)) {
|
|
|
|
range = `>= 0${n} and < 2${n} ** ${(byteLength2 + 1) * 8}${n}`;
|
|
|
|
} else {
|
|
|
|
range = `>= -(2${n} ** ${(byteLength2 + 1) * 8 - 1}${n}) and < 2 ** ${
|
|
|
|
(byteLength2 + 1) * 8 - 1
|
|
|
|
}${n}`;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
range = `>= ${min}${n} and <= ${max}${n}`;
|
|
|
|
}
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE("value", range, value);
|
|
|
|
}
|
|
|
|
checkBounds(buf, offset, byteLength2);
|
|
|
|
}
|
|
|
|
|
2023-10-07 22:09:50 -04:00
|
|
|
/**
|
|
|
|
* @param {Uint8Array} src Source buffer to read from
|
|
|
|
* @param {Buffer} dst Destination buffer to write to
|
|
|
|
* @param {number} [offset] Byte offset to write at in the destination buffer
|
|
|
|
* @param {number} [byteLength] Optional number of bytes to, at most, write into destination buffer.
|
|
|
|
* @returns {number} Number of bytes written to destination buffer
|
|
|
|
*/
|
perf(ext/node): Optimise Buffer string operations (#20158)
Extracted from https://github.com/denoland/deno/pull/17815
Optimise Buffer's string operations, most significantly when dealing
with ASCII and UTF-16. Base64 and HEX encodings are affected to much
lesser degrees.
## Performance
### String length 15
With very small strings we're at break-even or sometimes even lose a tad
bit of performance from creating a `DataView` that ends up not paying
for itself.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.15 µs/iter 871,388.6 (728.78 ns … 1.56 µs) 1.23 µs 1.56 µs 1.56 µs
Buffer.from base64 string 1.63 µs/iter 612,790.9 (1.31 µs … 1.96 µs) 1.77 µs 1.96 µs 1.96 µs
Buffer.from utf16 string 1.41 µs/iter 707,396.3 (915.24 ns … 1.93 µs) 1.61 µs 1.93 µs 1.93 µs
Buffer.from hex string 1.87 µs/iter 535,357.9 (1.56 µs … 2.19 µs) 2 µs 2.19 µs 2.19 µs
Buffer.toString ascii string 154.58 ns/iter 6,469,162.8 (149.69 ns … 198 ns) 154.51 ns 182.89 ns 191.91 ns
Buffer.toString base64 string 161.65 ns/iter 6,186,189.6 (150.91 ns … 181.15 ns) 165.18 ns 171.87 ns 174.94 ns
Buffer.toString utf16 string 292.74 ns/iter 3,415,959.8 (285.43 ns … 312.47 ns) 295.25 ns 310.47 ns 312.47 ns
Buffer.toString hex string 89.61 ns/iter 11,159,315.6 (81.09 ns … 123.77 ns) 91.09 ns 113.62 ns 119.28 ns
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.26 µs/iter 794,875.8 (1.07 µs … 1.46 µs) 1.31 µs 1.46 µs 1.46 µs
Buffer.from base64 string 1.65 µs/iter 607,853.3 (1.38 µs … 2.01 µs) 1.69 µs 2.01 µs 2.01 µs
Buffer.from utf16 string 1.34 µs/iter 744,894.6 (1.09 µs … 1.55 µs) 1.45 µs 1.55 µs 1.55 µs
Buffer.from hex string 2.01 µs/iter 496,345.8 (1.54 µs … 2.6 µs) 2.26 µs 2.6 µs 2.6 µs
Buffer.toString ascii string 150.16 ns/iter 6,659,630.5 (144.99 ns … 166.68 ns) 152.4 ns 157.26 ns 159.14 ns
Buffer.toString base64 string 164.73 ns/iter 6,070,692.0 (158.77 ns … 185.63 ns) 168.48 ns 175.74 ns 176.68 ns
Buffer.toString utf16 string 150.61 ns/iter 6,639,864.0 (148.2 ns … 168.29 ns) 150.93 ns 157.21 ns 168.15 ns
Buffer.toString hex string 94.21 ns/iter 10,614,972.9 (86.21 ns … 98.75 ns) 95.43 ns 97.99 ns 98.21 ns
```
### String length 1500
With moderate lengths we already see great upsides for `Buffer.from()`
with ASCII and UTF-16.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 5.79 µs/iter 172,562.6 (4.72 µs … 4.71 ms) 5.04 µs 10.3 µs 11.67 µs
Buffer.from base64 string 5.08 µs/iter 196,678.9 (4.97 µs … 5.76 µs) 5.08 µs 5.76 µs 5.76 µs
Buffer.from utf16 string 9.68 µs/iter 103,316.5 (7.14 µs … 3.44 ms) 10.32 µs 13.42 µs 15.21 µs
Buffer.from hex string 53.7 µs/iter 18,620.2 (49.37 µs … 2.2 ms) 54.74 µs 72.2 µs 81.07 µs
Buffer.toString ascii string 6.63 µs/iter 150,761.3 (5.59 µs … 1.11 ms) 6.08 µs 15.68 µs 24.77 µs
Buffer.toString base64 string 460.57 ns/iter 2,171,224.4 (448.33 ns … 511.73 ns) 465.05 ns 495.54 ns 511.73 ns
Buffer.toString utf16 string 6.52 µs/iter 153,287.0 (6.47 µs … 6.66 µs) 6.53 µs 6.66 µs 6.66 µs
Buffer.toString hex string 3.68 µs/iter 271,965.4 (3.64 µs … 3.82 µs) 3.68 µs 3.82 µs 3.82 µs
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 11.46 µs/iter 87,298.1 (8.53 µs … 834.1 µs) 9.61 µs 83.31 µs 87.3 µs
Buffer.from base64 string 5.4 µs/iter 185,027.8 (5.07 µs … 7.49 µs) 5.44 µs 7.49 µs 7.49 µs
Buffer.from utf16 string 20.3 µs/iter 49,270.8 (13.55 µs … 649.11 µs) 18.8 µs 113.93 µs 125.17 µs
Buffer.from hex string 52.03 µs/iter 19,218.9 (48.74 µs … 2.59 ms) 52.84 µs 67.05 µs 73.56 µs
Buffer.toString ascii string 6.46 µs/iter 154,822.5 (6.32 µs … 6.69 µs) 6.52 µs 6.69 µs 6.69 µs
Buffer.toString base64 string 440.19 ns/iter 2,271,764.6 (427 ns … 490.77 ns) 444.74 ns 484.64 ns 490.77 ns
Buffer.toString utf16 string 6.89 µs/iter 145,106.7 (6.81 µs … 7.24 µs) 6.91 µs 7.24 µs 7.24 µs
Buffer.toString hex string 3.66 µs/iter 273,456.5 (3.6 µs … 4.02 µs) 3.64 µs 4.02 µs 4.02 µs
```
### String length 2^20
With massive lengths we the difference in ASCII and UTF-16 parsing
performance is enormous.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 4.1 ms/iter 243.7 (2.64 ms … 6.74 ms) 4.43 ms 6.26 ms 6.74 ms
Buffer.from base64 string 3.74 ms/iter 267.6 (2.91 ms … 4.92 ms) 3.96 ms 4.31 ms 4.92 ms
Buffer.from utf16 string 7.72 ms/iter 129.5 (5.91 ms … 11.03 ms) 7.97 ms 11.03 ms 11.03 ms
Buffer.from hex string 35.72 ms/iter 28.0 (34.71 ms … 38.42 ms) 35.93 ms 38.42 ms 38.42 ms
Buffer.toString ascii string 78.92 ms/iter 12.7 (42.72 ms … 94.13 ms) 91.64 ms 94.13 ms 94.13 ms
Buffer.toString base64 string 833.62 µs/iter 1,199.6 (638.05 µs … 5.97 ms) 826.86 µs 2.45 ms 2.48 ms
Buffer.toString utf16 string 79.35 ms/iter 12.6 (69.72 ms … 88.9 ms) 86.66 ms 88.9 ms 88.9 ms
Buffer.toString hex string 31.04 ms/iter 32.2 (4.3 ms … 46.9 ms) 37.21 ms 46.9 ms 46.9 ms
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 18.66 ms/iter 53.6 (15.61 ms … 23.26 ms) 20.62 ms 23.26 ms 23.26 ms
Buffer.from base64 string 4.7 ms/iter 212.9 (2.94 ms … 9.07 ms) 4.65 ms 9.06 ms 9.07 ms
Buffer.from utf16 string 33.49 ms/iter 29.9 (31.24 ms … 35.67 ms) 34.08 ms 35.67 ms 35.67 ms
Buffer.from hex string 39.38 ms/iter 25.4 (38.66 ms … 42.36 ms) 39.58 ms 42.36 ms 42.36 ms
Buffer.toString ascii string 77.68 ms/iter 12.9 (67.46 ms … 95.68 ms) 84.71 ms 95.68 ms 95.68 ms
Buffer.toString base64 string 825.53 µs/iter 1,211.3 (655.38 µs … 6.69 ms) 816.62 µs 3.07 ms 3.13 ms
Buffer.toString utf16 string 76.54 ms/iter 13.1 (66.9 ms … 85.26 ms) 83.63 ms 85.26 ms 85.26 ms
Buffer.toString hex string 38.56 ms/iter 25.9 (33.83 ms … 46.56 ms) 45.33 ms 46.56 ms 46.56 ms
```
2023-09-07 16:41:16 -04:00
|
|
|
function blitBuffer(src, dst, offset, byteLength = Infinity) {
|
2023-09-24 06:48:23 -04:00
|
|
|
const srcLength = src.length;
|
perf(ext/node): Optimise Buffer string operations (#20158)
Extracted from https://github.com/denoland/deno/pull/17815
Optimise Buffer's string operations, most significantly when dealing
with ASCII and UTF-16. Base64 and HEX encodings are affected to much
lesser degrees.
## Performance
### String length 15
With very small strings we're at break-even or sometimes even lose a tad
bit of performance from creating a `DataView` that ends up not paying
for itself.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.15 µs/iter 871,388.6 (728.78 ns … 1.56 µs) 1.23 µs 1.56 µs 1.56 µs
Buffer.from base64 string 1.63 µs/iter 612,790.9 (1.31 µs … 1.96 µs) 1.77 µs 1.96 µs 1.96 µs
Buffer.from utf16 string 1.41 µs/iter 707,396.3 (915.24 ns … 1.93 µs) 1.61 µs 1.93 µs 1.93 µs
Buffer.from hex string 1.87 µs/iter 535,357.9 (1.56 µs … 2.19 µs) 2 µs 2.19 µs 2.19 µs
Buffer.toString ascii string 154.58 ns/iter 6,469,162.8 (149.69 ns … 198 ns) 154.51 ns 182.89 ns 191.91 ns
Buffer.toString base64 string 161.65 ns/iter 6,186,189.6 (150.91 ns … 181.15 ns) 165.18 ns 171.87 ns 174.94 ns
Buffer.toString utf16 string 292.74 ns/iter 3,415,959.8 (285.43 ns … 312.47 ns) 295.25 ns 310.47 ns 312.47 ns
Buffer.toString hex string 89.61 ns/iter 11,159,315.6 (81.09 ns … 123.77 ns) 91.09 ns 113.62 ns 119.28 ns
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.26 µs/iter 794,875.8 (1.07 µs … 1.46 µs) 1.31 µs 1.46 µs 1.46 µs
Buffer.from base64 string 1.65 µs/iter 607,853.3 (1.38 µs … 2.01 µs) 1.69 µs 2.01 µs 2.01 µs
Buffer.from utf16 string 1.34 µs/iter 744,894.6 (1.09 µs … 1.55 µs) 1.45 µs 1.55 µs 1.55 µs
Buffer.from hex string 2.01 µs/iter 496,345.8 (1.54 µs … 2.6 µs) 2.26 µs 2.6 µs 2.6 µs
Buffer.toString ascii string 150.16 ns/iter 6,659,630.5 (144.99 ns … 166.68 ns) 152.4 ns 157.26 ns 159.14 ns
Buffer.toString base64 string 164.73 ns/iter 6,070,692.0 (158.77 ns … 185.63 ns) 168.48 ns 175.74 ns 176.68 ns
Buffer.toString utf16 string 150.61 ns/iter 6,639,864.0 (148.2 ns … 168.29 ns) 150.93 ns 157.21 ns 168.15 ns
Buffer.toString hex string 94.21 ns/iter 10,614,972.9 (86.21 ns … 98.75 ns) 95.43 ns 97.99 ns 98.21 ns
```
### String length 1500
With moderate lengths we already see great upsides for `Buffer.from()`
with ASCII and UTF-16.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 5.79 µs/iter 172,562.6 (4.72 µs … 4.71 ms) 5.04 µs 10.3 µs 11.67 µs
Buffer.from base64 string 5.08 µs/iter 196,678.9 (4.97 µs … 5.76 µs) 5.08 µs 5.76 µs 5.76 µs
Buffer.from utf16 string 9.68 µs/iter 103,316.5 (7.14 µs … 3.44 ms) 10.32 µs 13.42 µs 15.21 µs
Buffer.from hex string 53.7 µs/iter 18,620.2 (49.37 µs … 2.2 ms) 54.74 µs 72.2 µs 81.07 µs
Buffer.toString ascii string 6.63 µs/iter 150,761.3 (5.59 µs … 1.11 ms) 6.08 µs 15.68 µs 24.77 µs
Buffer.toString base64 string 460.57 ns/iter 2,171,224.4 (448.33 ns … 511.73 ns) 465.05 ns 495.54 ns 511.73 ns
Buffer.toString utf16 string 6.52 µs/iter 153,287.0 (6.47 µs … 6.66 µs) 6.53 µs 6.66 µs 6.66 µs
Buffer.toString hex string 3.68 µs/iter 271,965.4 (3.64 µs … 3.82 µs) 3.68 µs 3.82 µs 3.82 µs
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 11.46 µs/iter 87,298.1 (8.53 µs … 834.1 µs) 9.61 µs 83.31 µs 87.3 µs
Buffer.from base64 string 5.4 µs/iter 185,027.8 (5.07 µs … 7.49 µs) 5.44 µs 7.49 µs 7.49 µs
Buffer.from utf16 string 20.3 µs/iter 49,270.8 (13.55 µs … 649.11 µs) 18.8 µs 113.93 µs 125.17 µs
Buffer.from hex string 52.03 µs/iter 19,218.9 (48.74 µs … 2.59 ms) 52.84 µs 67.05 µs 73.56 µs
Buffer.toString ascii string 6.46 µs/iter 154,822.5 (6.32 µs … 6.69 µs) 6.52 µs 6.69 µs 6.69 µs
Buffer.toString base64 string 440.19 ns/iter 2,271,764.6 (427 ns … 490.77 ns) 444.74 ns 484.64 ns 490.77 ns
Buffer.toString utf16 string 6.89 µs/iter 145,106.7 (6.81 µs … 7.24 µs) 6.91 µs 7.24 µs 7.24 µs
Buffer.toString hex string 3.66 µs/iter 273,456.5 (3.6 µs … 4.02 µs) 3.64 µs 4.02 µs 4.02 µs
```
### String length 2^20
With massive lengths we the difference in ASCII and UTF-16 parsing
performance is enormous.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 4.1 ms/iter 243.7 (2.64 ms … 6.74 ms) 4.43 ms 6.26 ms 6.74 ms
Buffer.from base64 string 3.74 ms/iter 267.6 (2.91 ms … 4.92 ms) 3.96 ms 4.31 ms 4.92 ms
Buffer.from utf16 string 7.72 ms/iter 129.5 (5.91 ms … 11.03 ms) 7.97 ms 11.03 ms 11.03 ms
Buffer.from hex string 35.72 ms/iter 28.0 (34.71 ms … 38.42 ms) 35.93 ms 38.42 ms 38.42 ms
Buffer.toString ascii string 78.92 ms/iter 12.7 (42.72 ms … 94.13 ms) 91.64 ms 94.13 ms 94.13 ms
Buffer.toString base64 string 833.62 µs/iter 1,199.6 (638.05 µs … 5.97 ms) 826.86 µs 2.45 ms 2.48 ms
Buffer.toString utf16 string 79.35 ms/iter 12.6 (69.72 ms … 88.9 ms) 86.66 ms 88.9 ms 88.9 ms
Buffer.toString hex string 31.04 ms/iter 32.2 (4.3 ms … 46.9 ms) 37.21 ms 46.9 ms 46.9 ms
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 18.66 ms/iter 53.6 (15.61 ms … 23.26 ms) 20.62 ms 23.26 ms 23.26 ms
Buffer.from base64 string 4.7 ms/iter 212.9 (2.94 ms … 9.07 ms) 4.65 ms 9.06 ms 9.07 ms
Buffer.from utf16 string 33.49 ms/iter 29.9 (31.24 ms … 35.67 ms) 34.08 ms 35.67 ms 35.67 ms
Buffer.from hex string 39.38 ms/iter 25.4 (38.66 ms … 42.36 ms) 39.58 ms 42.36 ms 42.36 ms
Buffer.toString ascii string 77.68 ms/iter 12.9 (67.46 ms … 95.68 ms) 84.71 ms 95.68 ms 95.68 ms
Buffer.toString base64 string 825.53 µs/iter 1,211.3 (655.38 µs … 6.69 ms) 816.62 µs 3.07 ms 3.13 ms
Buffer.toString utf16 string 76.54 ms/iter 13.1 (66.9 ms … 85.26 ms) 83.63 ms 85.26 ms 85.26 ms
Buffer.toString hex string 38.56 ms/iter 25.9 (33.83 ms … 46.56 ms) 45.33 ms 46.56 ms 46.56 ms
```
2023-09-07 16:41:16 -04:00
|
|
|
// Establish the number of bytes to be written
|
|
|
|
const bytesToWrite = Math.min(
|
|
|
|
// If byte length is defined in the call, then it sets an upper bound,
|
|
|
|
// otherwise it is Infinity and is never chosen.
|
|
|
|
byteLength,
|
|
|
|
// The length of the source sets an upper bound being the source of data.
|
2023-09-24 06:48:23 -04:00
|
|
|
srcLength,
|
perf(ext/node): Optimise Buffer string operations (#20158)
Extracted from https://github.com/denoland/deno/pull/17815
Optimise Buffer's string operations, most significantly when dealing
with ASCII and UTF-16. Base64 and HEX encodings are affected to much
lesser degrees.
## Performance
### String length 15
With very small strings we're at break-even or sometimes even lose a tad
bit of performance from creating a `DataView` that ends up not paying
for itself.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.15 µs/iter 871,388.6 (728.78 ns … 1.56 µs) 1.23 µs 1.56 µs 1.56 µs
Buffer.from base64 string 1.63 µs/iter 612,790.9 (1.31 µs … 1.96 µs) 1.77 µs 1.96 µs 1.96 µs
Buffer.from utf16 string 1.41 µs/iter 707,396.3 (915.24 ns … 1.93 µs) 1.61 µs 1.93 µs 1.93 µs
Buffer.from hex string 1.87 µs/iter 535,357.9 (1.56 µs … 2.19 µs) 2 µs 2.19 µs 2.19 µs
Buffer.toString ascii string 154.58 ns/iter 6,469,162.8 (149.69 ns … 198 ns) 154.51 ns 182.89 ns 191.91 ns
Buffer.toString base64 string 161.65 ns/iter 6,186,189.6 (150.91 ns … 181.15 ns) 165.18 ns 171.87 ns 174.94 ns
Buffer.toString utf16 string 292.74 ns/iter 3,415,959.8 (285.43 ns … 312.47 ns) 295.25 ns 310.47 ns 312.47 ns
Buffer.toString hex string 89.61 ns/iter 11,159,315.6 (81.09 ns … 123.77 ns) 91.09 ns 113.62 ns 119.28 ns
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.26 µs/iter 794,875.8 (1.07 µs … 1.46 µs) 1.31 µs 1.46 µs 1.46 µs
Buffer.from base64 string 1.65 µs/iter 607,853.3 (1.38 µs … 2.01 µs) 1.69 µs 2.01 µs 2.01 µs
Buffer.from utf16 string 1.34 µs/iter 744,894.6 (1.09 µs … 1.55 µs) 1.45 µs 1.55 µs 1.55 µs
Buffer.from hex string 2.01 µs/iter 496,345.8 (1.54 µs … 2.6 µs) 2.26 µs 2.6 µs 2.6 µs
Buffer.toString ascii string 150.16 ns/iter 6,659,630.5 (144.99 ns … 166.68 ns) 152.4 ns 157.26 ns 159.14 ns
Buffer.toString base64 string 164.73 ns/iter 6,070,692.0 (158.77 ns … 185.63 ns) 168.48 ns 175.74 ns 176.68 ns
Buffer.toString utf16 string 150.61 ns/iter 6,639,864.0 (148.2 ns … 168.29 ns) 150.93 ns 157.21 ns 168.15 ns
Buffer.toString hex string 94.21 ns/iter 10,614,972.9 (86.21 ns … 98.75 ns) 95.43 ns 97.99 ns 98.21 ns
```
### String length 1500
With moderate lengths we already see great upsides for `Buffer.from()`
with ASCII and UTF-16.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 5.79 µs/iter 172,562.6 (4.72 µs … 4.71 ms) 5.04 µs 10.3 µs 11.67 µs
Buffer.from base64 string 5.08 µs/iter 196,678.9 (4.97 µs … 5.76 µs) 5.08 µs 5.76 µs 5.76 µs
Buffer.from utf16 string 9.68 µs/iter 103,316.5 (7.14 µs … 3.44 ms) 10.32 µs 13.42 µs 15.21 µs
Buffer.from hex string 53.7 µs/iter 18,620.2 (49.37 µs … 2.2 ms) 54.74 µs 72.2 µs 81.07 µs
Buffer.toString ascii string 6.63 µs/iter 150,761.3 (5.59 µs … 1.11 ms) 6.08 µs 15.68 µs 24.77 µs
Buffer.toString base64 string 460.57 ns/iter 2,171,224.4 (448.33 ns … 511.73 ns) 465.05 ns 495.54 ns 511.73 ns
Buffer.toString utf16 string 6.52 µs/iter 153,287.0 (6.47 µs … 6.66 µs) 6.53 µs 6.66 µs 6.66 µs
Buffer.toString hex string 3.68 µs/iter 271,965.4 (3.64 µs … 3.82 µs) 3.68 µs 3.82 µs 3.82 µs
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 11.46 µs/iter 87,298.1 (8.53 µs … 834.1 µs) 9.61 µs 83.31 µs 87.3 µs
Buffer.from base64 string 5.4 µs/iter 185,027.8 (5.07 µs … 7.49 µs) 5.44 µs 7.49 µs 7.49 µs
Buffer.from utf16 string 20.3 µs/iter 49,270.8 (13.55 µs … 649.11 µs) 18.8 µs 113.93 µs 125.17 µs
Buffer.from hex string 52.03 µs/iter 19,218.9 (48.74 µs … 2.59 ms) 52.84 µs 67.05 µs 73.56 µs
Buffer.toString ascii string 6.46 µs/iter 154,822.5 (6.32 µs … 6.69 µs) 6.52 µs 6.69 µs 6.69 µs
Buffer.toString base64 string 440.19 ns/iter 2,271,764.6 (427 ns … 490.77 ns) 444.74 ns 484.64 ns 490.77 ns
Buffer.toString utf16 string 6.89 µs/iter 145,106.7 (6.81 µs … 7.24 µs) 6.91 µs 7.24 µs 7.24 µs
Buffer.toString hex string 3.66 µs/iter 273,456.5 (3.6 µs … 4.02 µs) 3.64 µs 4.02 µs 4.02 µs
```
### String length 2^20
With massive lengths we the difference in ASCII and UTF-16 parsing
performance is enormous.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 4.1 ms/iter 243.7 (2.64 ms … 6.74 ms) 4.43 ms 6.26 ms 6.74 ms
Buffer.from base64 string 3.74 ms/iter 267.6 (2.91 ms … 4.92 ms) 3.96 ms 4.31 ms 4.92 ms
Buffer.from utf16 string 7.72 ms/iter 129.5 (5.91 ms … 11.03 ms) 7.97 ms 11.03 ms 11.03 ms
Buffer.from hex string 35.72 ms/iter 28.0 (34.71 ms … 38.42 ms) 35.93 ms 38.42 ms 38.42 ms
Buffer.toString ascii string 78.92 ms/iter 12.7 (42.72 ms … 94.13 ms) 91.64 ms 94.13 ms 94.13 ms
Buffer.toString base64 string 833.62 µs/iter 1,199.6 (638.05 µs … 5.97 ms) 826.86 µs 2.45 ms 2.48 ms
Buffer.toString utf16 string 79.35 ms/iter 12.6 (69.72 ms … 88.9 ms) 86.66 ms 88.9 ms 88.9 ms
Buffer.toString hex string 31.04 ms/iter 32.2 (4.3 ms … 46.9 ms) 37.21 ms 46.9 ms 46.9 ms
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 18.66 ms/iter 53.6 (15.61 ms … 23.26 ms) 20.62 ms 23.26 ms 23.26 ms
Buffer.from base64 string 4.7 ms/iter 212.9 (2.94 ms … 9.07 ms) 4.65 ms 9.06 ms 9.07 ms
Buffer.from utf16 string 33.49 ms/iter 29.9 (31.24 ms … 35.67 ms) 34.08 ms 35.67 ms 35.67 ms
Buffer.from hex string 39.38 ms/iter 25.4 (38.66 ms … 42.36 ms) 39.58 ms 42.36 ms 42.36 ms
Buffer.toString ascii string 77.68 ms/iter 12.9 (67.46 ms … 95.68 ms) 84.71 ms 95.68 ms 95.68 ms
Buffer.toString base64 string 825.53 µs/iter 1,211.3 (655.38 µs … 6.69 ms) 816.62 µs 3.07 ms 3.13 ms
Buffer.toString utf16 string 76.54 ms/iter 13.1 (66.9 ms … 85.26 ms) 83.63 ms 85.26 ms 85.26 ms
Buffer.toString hex string 38.56 ms/iter 25.9 (33.83 ms … 46.56 ms) 45.33 ms 46.56 ms 46.56 ms
```
2023-09-07 16:41:16 -04:00
|
|
|
// The length of the destination minus any offset into it sets an upper bound.
|
2023-10-07 22:09:50 -04:00
|
|
|
dst.length - (offset || 0),
|
perf(ext/node): Optimise Buffer string operations (#20158)
Extracted from https://github.com/denoland/deno/pull/17815
Optimise Buffer's string operations, most significantly when dealing
with ASCII and UTF-16. Base64 and HEX encodings are affected to much
lesser degrees.
## Performance
### String length 15
With very small strings we're at break-even or sometimes even lose a tad
bit of performance from creating a `DataView` that ends up not paying
for itself.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.15 µs/iter 871,388.6 (728.78 ns … 1.56 µs) 1.23 µs 1.56 µs 1.56 µs
Buffer.from base64 string 1.63 µs/iter 612,790.9 (1.31 µs … 1.96 µs) 1.77 µs 1.96 µs 1.96 µs
Buffer.from utf16 string 1.41 µs/iter 707,396.3 (915.24 ns … 1.93 µs) 1.61 µs 1.93 µs 1.93 µs
Buffer.from hex string 1.87 µs/iter 535,357.9 (1.56 µs … 2.19 µs) 2 µs 2.19 µs 2.19 µs
Buffer.toString ascii string 154.58 ns/iter 6,469,162.8 (149.69 ns … 198 ns) 154.51 ns 182.89 ns 191.91 ns
Buffer.toString base64 string 161.65 ns/iter 6,186,189.6 (150.91 ns … 181.15 ns) 165.18 ns 171.87 ns 174.94 ns
Buffer.toString utf16 string 292.74 ns/iter 3,415,959.8 (285.43 ns … 312.47 ns) 295.25 ns 310.47 ns 312.47 ns
Buffer.toString hex string 89.61 ns/iter 11,159,315.6 (81.09 ns … 123.77 ns) 91.09 ns 113.62 ns 119.28 ns
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.26 µs/iter 794,875.8 (1.07 µs … 1.46 µs) 1.31 µs 1.46 µs 1.46 µs
Buffer.from base64 string 1.65 µs/iter 607,853.3 (1.38 µs … 2.01 µs) 1.69 µs 2.01 µs 2.01 µs
Buffer.from utf16 string 1.34 µs/iter 744,894.6 (1.09 µs … 1.55 µs) 1.45 µs 1.55 µs 1.55 µs
Buffer.from hex string 2.01 µs/iter 496,345.8 (1.54 µs … 2.6 µs) 2.26 µs 2.6 µs 2.6 µs
Buffer.toString ascii string 150.16 ns/iter 6,659,630.5 (144.99 ns … 166.68 ns) 152.4 ns 157.26 ns 159.14 ns
Buffer.toString base64 string 164.73 ns/iter 6,070,692.0 (158.77 ns … 185.63 ns) 168.48 ns 175.74 ns 176.68 ns
Buffer.toString utf16 string 150.61 ns/iter 6,639,864.0 (148.2 ns … 168.29 ns) 150.93 ns 157.21 ns 168.15 ns
Buffer.toString hex string 94.21 ns/iter 10,614,972.9 (86.21 ns … 98.75 ns) 95.43 ns 97.99 ns 98.21 ns
```
### String length 1500
With moderate lengths we already see great upsides for `Buffer.from()`
with ASCII and UTF-16.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 5.79 µs/iter 172,562.6 (4.72 µs … 4.71 ms) 5.04 µs 10.3 µs 11.67 µs
Buffer.from base64 string 5.08 µs/iter 196,678.9 (4.97 µs … 5.76 µs) 5.08 µs 5.76 µs 5.76 µs
Buffer.from utf16 string 9.68 µs/iter 103,316.5 (7.14 µs … 3.44 ms) 10.32 µs 13.42 µs 15.21 µs
Buffer.from hex string 53.7 µs/iter 18,620.2 (49.37 µs … 2.2 ms) 54.74 µs 72.2 µs 81.07 µs
Buffer.toString ascii string 6.63 µs/iter 150,761.3 (5.59 µs … 1.11 ms) 6.08 µs 15.68 µs 24.77 µs
Buffer.toString base64 string 460.57 ns/iter 2,171,224.4 (448.33 ns … 511.73 ns) 465.05 ns 495.54 ns 511.73 ns
Buffer.toString utf16 string 6.52 µs/iter 153,287.0 (6.47 µs … 6.66 µs) 6.53 µs 6.66 µs 6.66 µs
Buffer.toString hex string 3.68 µs/iter 271,965.4 (3.64 µs … 3.82 µs) 3.68 µs 3.82 µs 3.82 µs
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 11.46 µs/iter 87,298.1 (8.53 µs … 834.1 µs) 9.61 µs 83.31 µs 87.3 µs
Buffer.from base64 string 5.4 µs/iter 185,027.8 (5.07 µs … 7.49 µs) 5.44 µs 7.49 µs 7.49 µs
Buffer.from utf16 string 20.3 µs/iter 49,270.8 (13.55 µs … 649.11 µs) 18.8 µs 113.93 µs 125.17 µs
Buffer.from hex string 52.03 µs/iter 19,218.9 (48.74 µs … 2.59 ms) 52.84 µs 67.05 µs 73.56 µs
Buffer.toString ascii string 6.46 µs/iter 154,822.5 (6.32 µs … 6.69 µs) 6.52 µs 6.69 µs 6.69 µs
Buffer.toString base64 string 440.19 ns/iter 2,271,764.6 (427 ns … 490.77 ns) 444.74 ns 484.64 ns 490.77 ns
Buffer.toString utf16 string 6.89 µs/iter 145,106.7 (6.81 µs … 7.24 µs) 6.91 µs 7.24 µs 7.24 µs
Buffer.toString hex string 3.66 µs/iter 273,456.5 (3.6 µs … 4.02 µs) 3.64 µs 4.02 µs 4.02 µs
```
### String length 2^20
With massive lengths we the difference in ASCII and UTF-16 parsing
performance is enormous.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 4.1 ms/iter 243.7 (2.64 ms … 6.74 ms) 4.43 ms 6.26 ms 6.74 ms
Buffer.from base64 string 3.74 ms/iter 267.6 (2.91 ms … 4.92 ms) 3.96 ms 4.31 ms 4.92 ms
Buffer.from utf16 string 7.72 ms/iter 129.5 (5.91 ms … 11.03 ms) 7.97 ms 11.03 ms 11.03 ms
Buffer.from hex string 35.72 ms/iter 28.0 (34.71 ms … 38.42 ms) 35.93 ms 38.42 ms 38.42 ms
Buffer.toString ascii string 78.92 ms/iter 12.7 (42.72 ms … 94.13 ms) 91.64 ms 94.13 ms 94.13 ms
Buffer.toString base64 string 833.62 µs/iter 1,199.6 (638.05 µs … 5.97 ms) 826.86 µs 2.45 ms 2.48 ms
Buffer.toString utf16 string 79.35 ms/iter 12.6 (69.72 ms … 88.9 ms) 86.66 ms 88.9 ms 88.9 ms
Buffer.toString hex string 31.04 ms/iter 32.2 (4.3 ms … 46.9 ms) 37.21 ms 46.9 ms 46.9 ms
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 18.66 ms/iter 53.6 (15.61 ms … 23.26 ms) 20.62 ms 23.26 ms 23.26 ms
Buffer.from base64 string 4.7 ms/iter 212.9 (2.94 ms … 9.07 ms) 4.65 ms 9.06 ms 9.07 ms
Buffer.from utf16 string 33.49 ms/iter 29.9 (31.24 ms … 35.67 ms) 34.08 ms 35.67 ms 35.67 ms
Buffer.from hex string 39.38 ms/iter 25.4 (38.66 ms … 42.36 ms) 39.58 ms 42.36 ms 42.36 ms
Buffer.toString ascii string 77.68 ms/iter 12.9 (67.46 ms … 95.68 ms) 84.71 ms 95.68 ms 95.68 ms
Buffer.toString base64 string 825.53 µs/iter 1,211.3 (655.38 µs … 6.69 ms) 816.62 µs 3.07 ms 3.13 ms
Buffer.toString utf16 string 76.54 ms/iter 13.1 (66.9 ms … 85.26 ms) 83.63 ms 85.26 ms 85.26 ms
Buffer.toString hex string 38.56 ms/iter 25.9 (33.83 ms … 46.56 ms) 45.33 ms 46.56 ms 46.56 ms
```
2023-09-07 16:41:16 -04:00
|
|
|
);
|
2023-09-24 06:48:23 -04:00
|
|
|
if (bytesToWrite < srcLength) {
|
perf(ext/node): Optimise Buffer string operations (#20158)
Extracted from https://github.com/denoland/deno/pull/17815
Optimise Buffer's string operations, most significantly when dealing
with ASCII and UTF-16. Base64 and HEX encodings are affected to much
lesser degrees.
## Performance
### String length 15
With very small strings we're at break-even or sometimes even lose a tad
bit of performance from creating a `DataView` that ends up not paying
for itself.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.15 µs/iter 871,388.6 (728.78 ns … 1.56 µs) 1.23 µs 1.56 µs 1.56 µs
Buffer.from base64 string 1.63 µs/iter 612,790.9 (1.31 µs … 1.96 µs) 1.77 µs 1.96 µs 1.96 µs
Buffer.from utf16 string 1.41 µs/iter 707,396.3 (915.24 ns … 1.93 µs) 1.61 µs 1.93 µs 1.93 µs
Buffer.from hex string 1.87 µs/iter 535,357.9 (1.56 µs … 2.19 µs) 2 µs 2.19 µs 2.19 µs
Buffer.toString ascii string 154.58 ns/iter 6,469,162.8 (149.69 ns … 198 ns) 154.51 ns 182.89 ns 191.91 ns
Buffer.toString base64 string 161.65 ns/iter 6,186,189.6 (150.91 ns … 181.15 ns) 165.18 ns 171.87 ns 174.94 ns
Buffer.toString utf16 string 292.74 ns/iter 3,415,959.8 (285.43 ns … 312.47 ns) 295.25 ns 310.47 ns 312.47 ns
Buffer.toString hex string 89.61 ns/iter 11,159,315.6 (81.09 ns … 123.77 ns) 91.09 ns 113.62 ns 119.28 ns
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.26 µs/iter 794,875.8 (1.07 µs … 1.46 µs) 1.31 µs 1.46 µs 1.46 µs
Buffer.from base64 string 1.65 µs/iter 607,853.3 (1.38 µs … 2.01 µs) 1.69 µs 2.01 µs 2.01 µs
Buffer.from utf16 string 1.34 µs/iter 744,894.6 (1.09 µs … 1.55 µs) 1.45 µs 1.55 µs 1.55 µs
Buffer.from hex string 2.01 µs/iter 496,345.8 (1.54 µs … 2.6 µs) 2.26 µs 2.6 µs 2.6 µs
Buffer.toString ascii string 150.16 ns/iter 6,659,630.5 (144.99 ns … 166.68 ns) 152.4 ns 157.26 ns 159.14 ns
Buffer.toString base64 string 164.73 ns/iter 6,070,692.0 (158.77 ns … 185.63 ns) 168.48 ns 175.74 ns 176.68 ns
Buffer.toString utf16 string 150.61 ns/iter 6,639,864.0 (148.2 ns … 168.29 ns) 150.93 ns 157.21 ns 168.15 ns
Buffer.toString hex string 94.21 ns/iter 10,614,972.9 (86.21 ns … 98.75 ns) 95.43 ns 97.99 ns 98.21 ns
```
### String length 1500
With moderate lengths we already see great upsides for `Buffer.from()`
with ASCII and UTF-16.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 5.79 µs/iter 172,562.6 (4.72 µs … 4.71 ms) 5.04 µs 10.3 µs 11.67 µs
Buffer.from base64 string 5.08 µs/iter 196,678.9 (4.97 µs … 5.76 µs) 5.08 µs 5.76 µs 5.76 µs
Buffer.from utf16 string 9.68 µs/iter 103,316.5 (7.14 µs … 3.44 ms) 10.32 µs 13.42 µs 15.21 µs
Buffer.from hex string 53.7 µs/iter 18,620.2 (49.37 µs … 2.2 ms) 54.74 µs 72.2 µs 81.07 µs
Buffer.toString ascii string 6.63 µs/iter 150,761.3 (5.59 µs … 1.11 ms) 6.08 µs 15.68 µs 24.77 µs
Buffer.toString base64 string 460.57 ns/iter 2,171,224.4 (448.33 ns … 511.73 ns) 465.05 ns 495.54 ns 511.73 ns
Buffer.toString utf16 string 6.52 µs/iter 153,287.0 (6.47 µs … 6.66 µs) 6.53 µs 6.66 µs 6.66 µs
Buffer.toString hex string 3.68 µs/iter 271,965.4 (3.64 µs … 3.82 µs) 3.68 µs 3.82 µs 3.82 µs
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 11.46 µs/iter 87,298.1 (8.53 µs … 834.1 µs) 9.61 µs 83.31 µs 87.3 µs
Buffer.from base64 string 5.4 µs/iter 185,027.8 (5.07 µs … 7.49 µs) 5.44 µs 7.49 µs 7.49 µs
Buffer.from utf16 string 20.3 µs/iter 49,270.8 (13.55 µs … 649.11 µs) 18.8 µs 113.93 µs 125.17 µs
Buffer.from hex string 52.03 µs/iter 19,218.9 (48.74 µs … 2.59 ms) 52.84 µs 67.05 µs 73.56 µs
Buffer.toString ascii string 6.46 µs/iter 154,822.5 (6.32 µs … 6.69 µs) 6.52 µs 6.69 µs 6.69 µs
Buffer.toString base64 string 440.19 ns/iter 2,271,764.6 (427 ns … 490.77 ns) 444.74 ns 484.64 ns 490.77 ns
Buffer.toString utf16 string 6.89 µs/iter 145,106.7 (6.81 µs … 7.24 µs) 6.91 µs 7.24 µs 7.24 µs
Buffer.toString hex string 3.66 µs/iter 273,456.5 (3.6 µs … 4.02 µs) 3.64 µs 4.02 µs 4.02 µs
```
### String length 2^20
With massive lengths we the difference in ASCII and UTF-16 parsing
performance is enormous.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 4.1 ms/iter 243.7 (2.64 ms … 6.74 ms) 4.43 ms 6.26 ms 6.74 ms
Buffer.from base64 string 3.74 ms/iter 267.6 (2.91 ms … 4.92 ms) 3.96 ms 4.31 ms 4.92 ms
Buffer.from utf16 string 7.72 ms/iter 129.5 (5.91 ms … 11.03 ms) 7.97 ms 11.03 ms 11.03 ms
Buffer.from hex string 35.72 ms/iter 28.0 (34.71 ms … 38.42 ms) 35.93 ms 38.42 ms 38.42 ms
Buffer.toString ascii string 78.92 ms/iter 12.7 (42.72 ms … 94.13 ms) 91.64 ms 94.13 ms 94.13 ms
Buffer.toString base64 string 833.62 µs/iter 1,199.6 (638.05 µs … 5.97 ms) 826.86 µs 2.45 ms 2.48 ms
Buffer.toString utf16 string 79.35 ms/iter 12.6 (69.72 ms … 88.9 ms) 86.66 ms 88.9 ms 88.9 ms
Buffer.toString hex string 31.04 ms/iter 32.2 (4.3 ms … 46.9 ms) 37.21 ms 46.9 ms 46.9 ms
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 18.66 ms/iter 53.6 (15.61 ms … 23.26 ms) 20.62 ms 23.26 ms 23.26 ms
Buffer.from base64 string 4.7 ms/iter 212.9 (2.94 ms … 9.07 ms) 4.65 ms 9.06 ms 9.07 ms
Buffer.from utf16 string 33.49 ms/iter 29.9 (31.24 ms … 35.67 ms) 34.08 ms 35.67 ms 35.67 ms
Buffer.from hex string 39.38 ms/iter 25.4 (38.66 ms … 42.36 ms) 39.58 ms 42.36 ms 42.36 ms
Buffer.toString ascii string 77.68 ms/iter 12.9 (67.46 ms … 95.68 ms) 84.71 ms 95.68 ms 95.68 ms
Buffer.toString base64 string 825.53 µs/iter 1,211.3 (655.38 µs … 6.69 ms) 816.62 µs 3.07 ms 3.13 ms
Buffer.toString utf16 string 76.54 ms/iter 13.1 (66.9 ms … 85.26 ms) 83.63 ms 85.26 ms 85.26 ms
Buffer.toString hex string 38.56 ms/iter 25.9 (33.83 ms … 46.56 ms) 45.33 ms 46.56 ms 46.56 ms
```
2023-09-07 16:41:16 -04:00
|
|
|
// Resize the source buffer to the number of bytes we're about to write.
|
|
|
|
// This both makes sure that we're actually only writing what we're told to
|
|
|
|
// write but also prevents `Uint8Array#set` from throwing an error if the
|
|
|
|
// source is longer than the target.
|
2023-09-24 06:48:23 -04:00
|
|
|
src = src.subarray(0, bytesToWrite);
|
perf(ext/node): Optimise Buffer string operations (#20158)
Extracted from https://github.com/denoland/deno/pull/17815
Optimise Buffer's string operations, most significantly when dealing
with ASCII and UTF-16. Base64 and HEX encodings are affected to much
lesser degrees.
## Performance
### String length 15
With very small strings we're at break-even or sometimes even lose a tad
bit of performance from creating a `DataView` that ends up not paying
for itself.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.15 µs/iter 871,388.6 (728.78 ns … 1.56 µs) 1.23 µs 1.56 µs 1.56 µs
Buffer.from base64 string 1.63 µs/iter 612,790.9 (1.31 µs … 1.96 µs) 1.77 µs 1.96 µs 1.96 µs
Buffer.from utf16 string 1.41 µs/iter 707,396.3 (915.24 ns … 1.93 µs) 1.61 µs 1.93 µs 1.93 µs
Buffer.from hex string 1.87 µs/iter 535,357.9 (1.56 µs … 2.19 µs) 2 µs 2.19 µs 2.19 µs
Buffer.toString ascii string 154.58 ns/iter 6,469,162.8 (149.69 ns … 198 ns) 154.51 ns 182.89 ns 191.91 ns
Buffer.toString base64 string 161.65 ns/iter 6,186,189.6 (150.91 ns … 181.15 ns) 165.18 ns 171.87 ns 174.94 ns
Buffer.toString utf16 string 292.74 ns/iter 3,415,959.8 (285.43 ns … 312.47 ns) 295.25 ns 310.47 ns 312.47 ns
Buffer.toString hex string 89.61 ns/iter 11,159,315.6 (81.09 ns … 123.77 ns) 91.09 ns 113.62 ns 119.28 ns
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 1.26 µs/iter 794,875.8 (1.07 µs … 1.46 µs) 1.31 µs 1.46 µs 1.46 µs
Buffer.from base64 string 1.65 µs/iter 607,853.3 (1.38 µs … 2.01 µs) 1.69 µs 2.01 µs 2.01 µs
Buffer.from utf16 string 1.34 µs/iter 744,894.6 (1.09 µs … 1.55 µs) 1.45 µs 1.55 µs 1.55 µs
Buffer.from hex string 2.01 µs/iter 496,345.8 (1.54 µs … 2.6 µs) 2.26 µs 2.6 µs 2.6 µs
Buffer.toString ascii string 150.16 ns/iter 6,659,630.5 (144.99 ns … 166.68 ns) 152.4 ns 157.26 ns 159.14 ns
Buffer.toString base64 string 164.73 ns/iter 6,070,692.0 (158.77 ns … 185.63 ns) 168.48 ns 175.74 ns 176.68 ns
Buffer.toString utf16 string 150.61 ns/iter 6,639,864.0 (148.2 ns … 168.29 ns) 150.93 ns 157.21 ns 168.15 ns
Buffer.toString hex string 94.21 ns/iter 10,614,972.9 (86.21 ns … 98.75 ns) 95.43 ns 97.99 ns 98.21 ns
```
### String length 1500
With moderate lengths we already see great upsides for `Buffer.from()`
with ASCII and UTF-16.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 5.79 µs/iter 172,562.6 (4.72 µs … 4.71 ms) 5.04 µs 10.3 µs 11.67 µs
Buffer.from base64 string 5.08 µs/iter 196,678.9 (4.97 µs … 5.76 µs) 5.08 µs 5.76 µs 5.76 µs
Buffer.from utf16 string 9.68 µs/iter 103,316.5 (7.14 µs … 3.44 ms) 10.32 µs 13.42 µs 15.21 µs
Buffer.from hex string 53.7 µs/iter 18,620.2 (49.37 µs … 2.2 ms) 54.74 µs 72.2 µs 81.07 µs
Buffer.toString ascii string 6.63 µs/iter 150,761.3 (5.59 µs … 1.11 ms) 6.08 µs 15.68 µs 24.77 µs
Buffer.toString base64 string 460.57 ns/iter 2,171,224.4 (448.33 ns … 511.73 ns) 465.05 ns 495.54 ns 511.73 ns
Buffer.toString utf16 string 6.52 µs/iter 153,287.0 (6.47 µs … 6.66 µs) 6.53 µs 6.66 µs 6.66 µs
Buffer.toString hex string 3.68 µs/iter 271,965.4 (3.64 µs … 3.82 µs) 3.68 µs 3.82 µs 3.82 µs
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 11.46 µs/iter 87,298.1 (8.53 µs … 834.1 µs) 9.61 µs 83.31 µs 87.3 µs
Buffer.from base64 string 5.4 µs/iter 185,027.8 (5.07 µs … 7.49 µs) 5.44 µs 7.49 µs 7.49 µs
Buffer.from utf16 string 20.3 µs/iter 49,270.8 (13.55 µs … 649.11 µs) 18.8 µs 113.93 µs 125.17 µs
Buffer.from hex string 52.03 µs/iter 19,218.9 (48.74 µs … 2.59 ms) 52.84 µs 67.05 µs 73.56 µs
Buffer.toString ascii string 6.46 µs/iter 154,822.5 (6.32 µs … 6.69 µs) 6.52 µs 6.69 µs 6.69 µs
Buffer.toString base64 string 440.19 ns/iter 2,271,764.6 (427 ns … 490.77 ns) 444.74 ns 484.64 ns 490.77 ns
Buffer.toString utf16 string 6.89 µs/iter 145,106.7 (6.81 µs … 7.24 µs) 6.91 µs 7.24 µs 7.24 µs
Buffer.toString hex string 3.66 µs/iter 273,456.5 (3.6 µs … 4.02 µs) 3.64 µs 4.02 µs 4.02 µs
```
### String length 2^20
With massive lengths we the difference in ASCII and UTF-16 parsing
performance is enormous.
**This PR:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 4.1 ms/iter 243.7 (2.64 ms … 6.74 ms) 4.43 ms 6.26 ms 6.74 ms
Buffer.from base64 string 3.74 ms/iter 267.6 (2.91 ms … 4.92 ms) 3.96 ms 4.31 ms 4.92 ms
Buffer.from utf16 string 7.72 ms/iter 129.5 (5.91 ms … 11.03 ms) 7.97 ms 11.03 ms 11.03 ms
Buffer.from hex string 35.72 ms/iter 28.0 (34.71 ms … 38.42 ms) 35.93 ms 38.42 ms 38.42 ms
Buffer.toString ascii string 78.92 ms/iter 12.7 (42.72 ms … 94.13 ms) 91.64 ms 94.13 ms 94.13 ms
Buffer.toString base64 string 833.62 µs/iter 1,199.6 (638.05 µs … 5.97 ms) 826.86 µs 2.45 ms 2.48 ms
Buffer.toString utf16 string 79.35 ms/iter 12.6 (69.72 ms … 88.9 ms) 86.66 ms 88.9 ms 88.9 ms
Buffer.toString hex string 31.04 ms/iter 32.2 (4.3 ms … 46.9 ms) 37.21 ms 46.9 ms 46.9 ms
```
**Main:**
```
benchmark time (avg) iter/s (min … max) p75 p99 p995
-------------------------------------------------------------------------------------------------------------------- -----------------------------
Buffer.from ascii string 18.66 ms/iter 53.6 (15.61 ms … 23.26 ms) 20.62 ms 23.26 ms 23.26 ms
Buffer.from base64 string 4.7 ms/iter 212.9 (2.94 ms … 9.07 ms) 4.65 ms 9.06 ms 9.07 ms
Buffer.from utf16 string 33.49 ms/iter 29.9 (31.24 ms … 35.67 ms) 34.08 ms 35.67 ms 35.67 ms
Buffer.from hex string 39.38 ms/iter 25.4 (38.66 ms … 42.36 ms) 39.58 ms 42.36 ms 42.36 ms
Buffer.toString ascii string 77.68 ms/iter 12.9 (67.46 ms … 95.68 ms) 84.71 ms 95.68 ms 95.68 ms
Buffer.toString base64 string 825.53 µs/iter 1,211.3 (655.38 µs … 6.69 ms) 816.62 µs 3.07 ms 3.13 ms
Buffer.toString utf16 string 76.54 ms/iter 13.1 (66.9 ms … 85.26 ms) 83.63 ms 85.26 ms 85.26 ms
Buffer.toString hex string 38.56 ms/iter 25.9 (33.83 ms … 46.56 ms) 45.33 ms 46.56 ms 46.56 ms
```
2023-09-07 16:41:16 -04:00
|
|
|
}
|
|
|
|
dst.set(src, offset);
|
|
|
|
return bytesToWrite;
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function isInstance(obj, type) {
|
|
|
|
return obj instanceof type ||
|
|
|
|
obj != null && obj.constructor != null &&
|
|
|
|
obj.constructor.name != null && obj.constructor.name === type.name;
|
|
|
|
}
|
|
|
|
|
|
|
|
const hexSliceLookupTable = function () {
|
|
|
|
const alphabet = "0123456789abcdef";
|
|
|
|
const table = new Array(256);
|
|
|
|
for (let i = 0; i < 16; ++i) {
|
|
|
|
const i16 = i * 16;
|
|
|
|
for (let j = 0; j < 16; ++j) {
|
|
|
|
table[i16 + j] = alphabet[i] + alphabet[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return table;
|
|
|
|
}();
|
|
|
|
|
|
|
|
function defineBigIntMethod(fn) {
|
|
|
|
return typeof BigInt === "undefined" ? BufferBigIntNotDefined : fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
function BufferBigIntNotDefined() {
|
|
|
|
throw new Error("BigInt not supported");
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readUInt48LE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 5];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first +
|
|
|
|
buf[++offset] * 2 ** 8 +
|
|
|
|
buf[++offset] * 2 ** 16 +
|
|
|
|
buf[++offset] * 2 ** 24 +
|
|
|
|
(buf[++offset] + last * 2 ** 8) * 2 ** 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readUInt40LE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 4];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first +
|
|
|
|
buf[++offset] * 2 ** 8 +
|
|
|
|
buf[++offset] * 2 ** 16 +
|
|
|
|
buf[++offset] * 2 ** 24 +
|
|
|
|
last * 2 ** 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readUInt24LE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 2];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first + buf[++offset] * 2 ** 8 + last * 2 ** 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readUInt48BE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 5];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (first * 2 ** 8 + buf[++offset]) * 2 ** 32 +
|
|
|
|
buf[++offset] * 2 ** 24 +
|
|
|
|
buf[++offset] * 2 ** 16 +
|
|
|
|
buf[++offset] * 2 ** 8 +
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readUInt40BE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 4];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first * 2 ** 32 +
|
|
|
|
buf[++offset] * 2 ** 24 +
|
|
|
|
buf[++offset] * 2 ** 16 +
|
|
|
|
buf[++offset] * 2 ** 8 +
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readUInt24BE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 2];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first * 2 ** 16 + buf[++offset] * 2 ** 8 + last;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readUInt16BE(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 1];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, this.length - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first * 2 ** 8 + last;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readUInt32BE(offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = this[offset];
|
|
|
|
const last = this[offset + 3];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, this.length - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
return first * 2 ** 24 +
|
|
|
|
this[++offset] * 2 ** 16 +
|
|
|
|
this[++offset] * 2 ** 8 +
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readDoubleBackwards(buffer, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buffer[offset];
|
|
|
|
const last = buffer[offset + 7];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buffer.length - 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
uInt8Float64Array[7] = first;
|
|
|
|
uInt8Float64Array[6] = buffer[++offset];
|
|
|
|
uInt8Float64Array[5] = buffer[++offset];
|
|
|
|
uInt8Float64Array[4] = buffer[++offset];
|
|
|
|
uInt8Float64Array[3] = buffer[++offset];
|
|
|
|
uInt8Float64Array[2] = buffer[++offset];
|
|
|
|
uInt8Float64Array[1] = buffer[++offset];
|
|
|
|
uInt8Float64Array[0] = last;
|
|
|
|
return float64Array[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readDoubleForwards(buffer, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buffer[offset];
|
|
|
|
const last = buffer[offset + 7];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buffer.length - 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
uInt8Float64Array[0] = first;
|
|
|
|
uInt8Float64Array[1] = buffer[++offset];
|
|
|
|
uInt8Float64Array[2] = buffer[++offset];
|
|
|
|
uInt8Float64Array[3] = buffer[++offset];
|
|
|
|
uInt8Float64Array[4] = buffer[++offset];
|
|
|
|
uInt8Float64Array[5] = buffer[++offset];
|
|
|
|
uInt8Float64Array[6] = buffer[++offset];
|
|
|
|
uInt8Float64Array[7] = last;
|
|
|
|
return float64Array[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
export function writeDoubleForwards(buffer, val, offset = 0) {
|
|
|
|
val = +val;
|
|
|
|
checkBounds(buffer, offset, 7);
|
|
|
|
|
|
|
|
float64Array[0] = val;
|
|
|
|
buffer[offset++] = uInt8Float64Array[0];
|
|
|
|
buffer[offset++] = uInt8Float64Array[1];
|
|
|
|
buffer[offset++] = uInt8Float64Array[2];
|
|
|
|
buffer[offset++] = uInt8Float64Array[3];
|
|
|
|
buffer[offset++] = uInt8Float64Array[4];
|
|
|
|
buffer[offset++] = uInt8Float64Array[5];
|
|
|
|
buffer[offset++] = uInt8Float64Array[6];
|
|
|
|
buffer[offset++] = uInt8Float64Array[7];
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function writeDoubleBackwards(buffer, val, offset = 0) {
|
|
|
|
val = +val;
|
|
|
|
checkBounds(buffer, offset, 7);
|
|
|
|
|
|
|
|
float64Array[0] = val;
|
|
|
|
buffer[offset++] = uInt8Float64Array[7];
|
|
|
|
buffer[offset++] = uInt8Float64Array[6];
|
|
|
|
buffer[offset++] = uInt8Float64Array[5];
|
|
|
|
buffer[offset++] = uInt8Float64Array[4];
|
|
|
|
buffer[offset++] = uInt8Float64Array[3];
|
|
|
|
buffer[offset++] = uInt8Float64Array[2];
|
|
|
|
buffer[offset++] = uInt8Float64Array[1];
|
|
|
|
buffer[offset++] = uInt8Float64Array[0];
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readFloatBackwards(buffer, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buffer[offset];
|
|
|
|
const last = buffer[offset + 3];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buffer.length - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
uInt8Float32Array[3] = first;
|
|
|
|
uInt8Float32Array[2] = buffer[++offset];
|
|
|
|
uInt8Float32Array[1] = buffer[++offset];
|
|
|
|
uInt8Float32Array[0] = last;
|
|
|
|
return float32Array[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readFloatForwards(buffer, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buffer[offset];
|
|
|
|
const last = buffer[offset + 3];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buffer.length - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
uInt8Float32Array[0] = first;
|
|
|
|
uInt8Float32Array[1] = buffer[++offset];
|
|
|
|
uInt8Float32Array[2] = buffer[++offset];
|
|
|
|
uInt8Float32Array[3] = last;
|
|
|
|
return float32Array[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
export function writeFloatForwards(buffer, val, offset = 0) {
|
|
|
|
val = +val;
|
|
|
|
checkBounds(buffer, offset, 3);
|
|
|
|
|
|
|
|
float32Array[0] = val;
|
|
|
|
buffer[offset++] = uInt8Float32Array[0];
|
|
|
|
buffer[offset++] = uInt8Float32Array[1];
|
|
|
|
buffer[offset++] = uInt8Float32Array[2];
|
|
|
|
buffer[offset++] = uInt8Float32Array[3];
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function writeFloatBackwards(buffer, val, offset = 0) {
|
|
|
|
val = +val;
|
|
|
|
checkBounds(buffer, offset, 3);
|
|
|
|
|
|
|
|
float32Array[0] = val;
|
|
|
|
buffer[offset++] = uInt8Float32Array[3];
|
|
|
|
buffer[offset++] = uInt8Float32Array[2];
|
|
|
|
buffer[offset++] = uInt8Float32Array[1];
|
|
|
|
buffer[offset++] = uInt8Float32Array[0];
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readInt24LE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 2];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
const val = first + buf[++offset] * 2 ** 8 + last * 2 ** 16;
|
|
|
|
return val | (val & 2 ** 23) * 0x1fe;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readInt40LE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 4];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 +
|
|
|
|
first +
|
|
|
|
buf[++offset] * 2 ** 8 +
|
|
|
|
buf[++offset] * 2 ** 16 +
|
|
|
|
buf[++offset] * 2 ** 24;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readInt48LE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 5];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
const val = buf[offset + 4] + last * 2 ** 8;
|
|
|
|
return (val | (val & 2 ** 15) * 0x1fffe) * 2 ** 32 +
|
|
|
|
first +
|
|
|
|
buf[++offset] * 2 ** 8 +
|
|
|
|
buf[++offset] * 2 ** 16 +
|
|
|
|
buf[++offset] * 2 ** 24;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readInt24BE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 2];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
const val = first * 2 ** 16 + buf[++offset] * 2 ** 8 + last;
|
|
|
|
return val | (val & 2 ** 23) * 0x1fe;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readInt48BE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 5];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
const val = buf[++offset] + first * 2 ** 8;
|
|
|
|
return (val | (val & 2 ** 15) * 0x1fffe) * 2 ** 32 +
|
|
|
|
buf[++offset] * 2 ** 24 +
|
|
|
|
buf[++offset] * 2 ** 16 +
|
|
|
|
buf[++offset] * 2 ** 8 +
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readInt40BE(buf, offset = 0) {
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
const first = buf[offset];
|
|
|
|
const last = buf[offset + 4];
|
|
|
|
if (first === undefined || last === undefined) {
|
|
|
|
boundsError(offset, buf.length - 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (first | (first & 2 ** 7) * 0x1fffffe) * 2 ** 32 +
|
|
|
|
buf[++offset] * 2 ** 24 +
|
|
|
|
buf[++offset] * 2 ** 16 +
|
|
|
|
buf[++offset] * 2 ** 8 +
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function byteLengthUtf8(str) {
|
2023-09-30 10:34:40 -04:00
|
|
|
return core.byteLength(str);
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function base64ByteLength(str, bytes) {
|
|
|
|
// Handle padding
|
|
|
|
if (str.charCodeAt(bytes - 1) === 0x3D) {
|
|
|
|
bytes--;
|
|
|
|
}
|
|
|
|
if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3D) {
|
|
|
|
bytes--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Base64 ratio: 3/4
|
|
|
|
return (bytes * 3) >>> 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const encodingsMap = Object.create(null);
|
|
|
|
for (let i = 0; i < encodings.length; ++i) {
|
|
|
|
encodingsMap[encodings[i]] = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const encodingOps = {
|
|
|
|
ascii: {
|
|
|
|
byteLength: (string) => string.length,
|
|
|
|
encoding: "ascii",
|
|
|
|
encodingVal: encodingsMap.ascii,
|
|
|
|
indexOf: (buf, val, byteOffset, dir) =>
|
|
|
|
indexOfBuffer(
|
|
|
|
buf,
|
|
|
|
asciiToBytes(val),
|
|
|
|
byteOffset,
|
|
|
|
encodingsMap.ascii,
|
|
|
|
dir,
|
|
|
|
),
|
|
|
|
slice: (buf, start, end) => buf.asciiSlice(start, end),
|
|
|
|
write: (buf, string, offset, len) => buf.asciiWrite(string, offset, len),
|
|
|
|
},
|
|
|
|
base64: {
|
|
|
|
byteLength: (string) => base64ByteLength(string, string.length),
|
|
|
|
encoding: "base64",
|
|
|
|
encodingVal: encodingsMap.base64,
|
|
|
|
indexOf: (buf, val, byteOffset, dir) =>
|
|
|
|
indexOfBuffer(
|
|
|
|
buf,
|
|
|
|
base64ToBytes(val),
|
|
|
|
byteOffset,
|
|
|
|
encodingsMap.base64,
|
|
|
|
dir,
|
|
|
|
),
|
|
|
|
slice: (buf, start, end) => buf.base64Slice(start, end),
|
|
|
|
write: (buf, string, offset, len) => buf.base64Write(string, offset, len),
|
|
|
|
},
|
|
|
|
base64url: {
|
|
|
|
byteLength: (string) => base64ByteLength(string, string.length),
|
|
|
|
encoding: "base64url",
|
|
|
|
encodingVal: encodingsMap.base64url,
|
|
|
|
indexOf: (buf, val, byteOffset, dir) =>
|
|
|
|
indexOfBuffer(
|
|
|
|
buf,
|
|
|
|
base64UrlToBytes(val),
|
|
|
|
byteOffset,
|
|
|
|
encodingsMap.base64url,
|
|
|
|
dir,
|
|
|
|
),
|
|
|
|
slice: (buf, start, end) => buf.base64urlSlice(start, end),
|
|
|
|
write: (buf, string, offset, len) =>
|
|
|
|
buf.base64urlWrite(string, offset, len),
|
|
|
|
},
|
|
|
|
hex: {
|
|
|
|
byteLength: (string) => string.length >>> 1,
|
|
|
|
encoding: "hex",
|
|
|
|
encodingVal: encodingsMap.hex,
|
|
|
|
indexOf: (buf, val, byteOffset, dir) =>
|
|
|
|
indexOfBuffer(
|
|
|
|
buf,
|
|
|
|
hexToBytes(val),
|
|
|
|
byteOffset,
|
|
|
|
encodingsMap.hex,
|
|
|
|
dir,
|
|
|
|
),
|
|
|
|
slice: (buf, start, end) => buf.hexSlice(start, end),
|
|
|
|
write: (buf, string, offset, len) => buf.hexWrite(string, offset, len),
|
|
|
|
},
|
|
|
|
latin1: {
|
|
|
|
byteLength: (string) => string.length,
|
|
|
|
encoding: "latin1",
|
|
|
|
encodingVal: encodingsMap.latin1,
|
|
|
|
indexOf: (buf, val, byteOffset, dir) =>
|
|
|
|
indexOfBuffer(
|
|
|
|
buf,
|
|
|
|
asciiToBytes(val),
|
|
|
|
byteOffset,
|
|
|
|
encodingsMap.latin1,
|
|
|
|
dir,
|
|
|
|
),
|
|
|
|
slice: (buf, start, end) => buf.latin1Slice(start, end),
|
|
|
|
write: (buf, string, offset, len) => buf.latin1Write(string, offset, len),
|
|
|
|
},
|
|
|
|
ucs2: {
|
|
|
|
byteLength: (string) => string.length * 2,
|
|
|
|
encoding: "ucs2",
|
|
|
|
encodingVal: encodingsMap.utf16le,
|
|
|
|
indexOf: (buf, val, byteOffset, dir) =>
|
|
|
|
indexOfBuffer(
|
|
|
|
buf,
|
|
|
|
utf16leToBytes(val),
|
|
|
|
byteOffset,
|
|
|
|
encodingsMap.utf16le,
|
|
|
|
dir,
|
|
|
|
),
|
|
|
|
slice: (buf, start, end) => buf.ucs2Slice(start, end),
|
|
|
|
write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len),
|
|
|
|
},
|
|
|
|
utf8: {
|
|
|
|
byteLength: byteLengthUtf8,
|
|
|
|
encoding: "utf8",
|
|
|
|
encodingVal: encodingsMap.utf8,
|
|
|
|
indexOf: (buf, val, byteOffset, dir) =>
|
|
|
|
indexOfBuffer(
|
|
|
|
buf,
|
|
|
|
utf8Encoder.encode(val),
|
|
|
|
byteOffset,
|
|
|
|
encodingsMap.utf8,
|
|
|
|
dir,
|
|
|
|
),
|
|
|
|
slice: (buf, start, end) => buf.utf8Slice(start, end),
|
|
|
|
write: (buf, string, offset, len) => buf.utf8Write(string, offset, len),
|
|
|
|
},
|
|
|
|
utf16le: {
|
|
|
|
byteLength: (string) => string.length * 2,
|
|
|
|
encoding: "utf16le",
|
|
|
|
encodingVal: encodingsMap.utf16le,
|
|
|
|
indexOf: (buf, val, byteOffset, dir) =>
|
|
|
|
indexOfBuffer(
|
|
|
|
buf,
|
|
|
|
utf16leToBytes(val),
|
|
|
|
byteOffset,
|
|
|
|
encodingsMap.utf16le,
|
|
|
|
dir,
|
|
|
|
),
|
|
|
|
slice: (buf, start, end) => buf.ucs2Slice(start, end),
|
|
|
|
write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export function getEncodingOps(encoding) {
|
|
|
|
encoding = String(encoding).toLowerCase();
|
|
|
|
switch (encoding.length) {
|
|
|
|
case 4:
|
|
|
|
if (encoding === "utf8") return encodingOps.utf8;
|
|
|
|
if (encoding === "ucs2") return encodingOps.ucs2;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
if (encoding === "utf-8") return encodingOps.utf8;
|
|
|
|
if (encoding === "ascii") return encodingOps.ascii;
|
|
|
|
if (encoding === "ucs-2") return encodingOps.ucs2;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
if (encoding === "utf16le") {
|
|
|
|
return encodingOps.utf16le;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (encoding === "utf-16le") {
|
|
|
|
return encodingOps.utf16le;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
// deno-lint-ignore no-fallthrough
|
|
|
|
case 6:
|
|
|
|
if (encoding === "latin1" || encoding === "binary") {
|
|
|
|
return encodingOps.latin1;
|
|
|
|
}
|
|
|
|
if (encoding === "base64") return encodingOps.base64;
|
|
|
|
case 3:
|
|
|
|
if (encoding === "hex") {
|
|
|
|
return encodingOps.hex;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
if (encoding === "base64url") {
|
|
|
|
return encodingOps.base64url;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function _copyActual(
|
|
|
|
source,
|
|
|
|
target,
|
|
|
|
targetStart,
|
|
|
|
sourceStart,
|
|
|
|
sourceEnd,
|
|
|
|
) {
|
|
|
|
if (sourceEnd - sourceStart > target.length - targetStart) {
|
|
|
|
sourceEnd = sourceStart + target.length - targetStart;
|
|
|
|
}
|
|
|
|
|
|
|
|
let nb = sourceEnd - sourceStart;
|
|
|
|
const sourceLen = source.length - sourceStart;
|
|
|
|
if (nb > sourceLen) {
|
|
|
|
nb = sourceLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sourceStart !== 0 || sourceEnd < source.length) {
|
|
|
|
source = new Uint8Array(source.buffer, source.byteOffset + sourceStart, nb);
|
|
|
|
}
|
|
|
|
|
|
|
|
target.set(source, targetStart);
|
|
|
|
|
|
|
|
return nb;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function boundsError(value, length, type) {
|
|
|
|
if (Math.floor(value) !== value) {
|
|
|
|
validateNumber(value, type);
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE(type || "offset", "an integer", value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length < 0) {
|
|
|
|
throw new codes.ERR_BUFFER_OUT_OF_BOUNDS();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE(
|
|
|
|
type || "offset",
|
|
|
|
`>= ${type ? 1 : 0} and <= ${length}`,
|
|
|
|
value,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-06-11 07:41:44 -04:00
|
|
|
export function validateNumber(value, name, min = undefined, max) {
|
2023-02-14 11:38:45 -05:00
|
|
|
if (typeof value !== "number") {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value);
|
|
|
|
}
|
2024-06-11 07:41:44 -04:00
|
|
|
|
|
|
|
if (
|
|
|
|
(min != null && value < min) || (max != null && value > max) ||
|
|
|
|
((min != null || max != null) && Number.isNaN(value))
|
|
|
|
) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE(
|
|
|
|
name,
|
|
|
|
`${min != null ? `>= ${min}` : ""}${
|
|
|
|
min != null && max != null ? " && " : ""
|
|
|
|
}${max != null ? `<= ${max}` : ""}`,
|
|
|
|
value,
|
|
|
|
);
|
|
|
|
}
|
2023-02-14 11:38:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function checkInt(value, min, max, buf, offset, byteLength) {
|
|
|
|
if (value > max || value < min) {
|
|
|
|
const n = typeof min === "bigint" ? "n" : "";
|
|
|
|
let range;
|
|
|
|
if (byteLength > 3) {
|
|
|
|
if (min === 0 || min === 0n) {
|
|
|
|
range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`;
|
|
|
|
} else {
|
|
|
|
range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and ` +
|
|
|
|
`< 2${n} ** ${(byteLength + 1) * 8 - 1}${n}`;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
range = `>= ${min}${n} and <= ${max}${n}`;
|
|
|
|
}
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE("value", range, value);
|
|
|
|
}
|
|
|
|
checkBounds(buf, offset, byteLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function toInteger(n, defaultVal) {
|
|
|
|
n = +n;
|
|
|
|
if (
|
|
|
|
!Number.isNaN(n) &&
|
|
|
|
n >= Number.MIN_SAFE_INTEGER &&
|
|
|
|
n <= Number.MAX_SAFE_INTEGER
|
|
|
|
) {
|
|
|
|
return ((n % 1) === 0 ? n : Math.floor(n));
|
|
|
|
}
|
|
|
|
return defaultVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int8(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
validateNumber(offset, "offset");
|
|
|
|
if (value > max || value < min) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE("value", `>= ${min} and <= ${max}`, value);
|
|
|
|
}
|
|
|
|
if (buf[offset] === undefined) {
|
|
|
|
boundsError(offset, buf.length - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[offset] = value;
|
|
|
|
return offset + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int16BE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 1);
|
|
|
|
|
|
|
|
buf[offset++] = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function _writeUInt32LE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 3);
|
|
|
|
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int16LE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 1);
|
|
|
|
|
|
|
|
buf[offset++] = value;
|
|
|
|
buf[offset++] = value >>> 8;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function _writeUInt32BE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 3);
|
|
|
|
|
|
|
|
buf[offset + 3] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 2] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 1] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset] = value;
|
|
|
|
return offset + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int48BE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 5);
|
|
|
|
|
|
|
|
const newVal = Math.floor(value * 2 ** -32);
|
|
|
|
buf[offset++] = newVal >>> 8;
|
|
|
|
buf[offset++] = newVal;
|
|
|
|
buf[offset + 3] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 2] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 1] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset] = value;
|
|
|
|
return offset + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int40BE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 4);
|
|
|
|
|
|
|
|
buf[offset++] = Math.floor(value * 2 ** -32);
|
|
|
|
buf[offset + 3] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 2] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 1] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset] = value;
|
|
|
|
return offset + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int32BE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 3);
|
|
|
|
|
|
|
|
buf[offset + 3] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 2] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 1] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset] = value;
|
|
|
|
return offset + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int24BE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 2);
|
|
|
|
|
|
|
|
buf[offset + 2] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset + 1] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset] = value;
|
|
|
|
return offset + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function validateOffset(
|
|
|
|
value,
|
|
|
|
name,
|
|
|
|
min = 0,
|
|
|
|
max = Number.MAX_SAFE_INTEGER,
|
|
|
|
) {
|
|
|
|
if (typeof value !== "number") {
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value);
|
|
|
|
}
|
|
|
|
if (!Number.isInteger(value)) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE(name, "an integer", value);
|
|
|
|
}
|
|
|
|
if (value < min || value > max) {
|
|
|
|
throw new codes.ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int48LE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 5);
|
|
|
|
|
|
|
|
const newVal = Math.floor(value * 2 ** -32);
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
buf[offset++] = newVal;
|
|
|
|
buf[offset++] = newVal >>> 8;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int40LE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 4);
|
|
|
|
|
|
|
|
const newVal = value;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
buf[offset++] = Math.floor(newVal * 2 ** -32);
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int32LE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 3);
|
|
|
|
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
// deno-lint-ignore camelcase
|
|
|
|
export function writeU_Int24LE(buf, value, offset, min, max) {
|
|
|
|
value = +value;
|
|
|
|
checkInt(value, min, max, buf, offset, 2);
|
|
|
|
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
value = value >>> 8;
|
|
|
|
buf[offset++] = value;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
2024-05-21 18:50:59 -04:00
|
|
|
export function isUtf8(input) {
|
|
|
|
if (isTypedArray(input)) {
|
|
|
|
if (input.buffer.detached) {
|
|
|
|
throw new ERR_INVALID_STATE("Cannot validate on a detached buffer");
|
|
|
|
}
|
|
|
|
return op_is_utf8(input);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isAnyArrayBuffer(input)) {
|
|
|
|
if (input.detached) {
|
|
|
|
throw new ERR_INVALID_STATE("Cannot validate on a detached buffer");
|
|
|
|
}
|
|
|
|
return op_is_utf8(new Uint8Array(input));
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE("input", [
|
|
|
|
"ArrayBuffer",
|
|
|
|
"Buffer",
|
|
|
|
"TypedArray",
|
|
|
|
], input);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isAscii(input) {
|
|
|
|
if (isTypedArray(input)) {
|
|
|
|
if (input.buffer.detached) {
|
|
|
|
throw new ERR_INVALID_STATE("Cannot validate on a detached buffer");
|
|
|
|
}
|
|
|
|
return op_is_ascii(input);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isAnyArrayBuffer(input)) {
|
|
|
|
if (input.detached) {
|
|
|
|
throw new ERR_INVALID_STATE("Cannot validate on a detached buffer");
|
|
|
|
}
|
|
|
|
return op_is_ascii(new Uint8Array(input));
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new codes.ERR_INVALID_ARG_TYPE("input", [
|
|
|
|
"ArrayBuffer",
|
|
|
|
"Buffer",
|
|
|
|
"TypedArray",
|
|
|
|
], input);
|
|
|
|
}
|
|
|
|
|
2023-02-14 11:38:45 -05:00
|
|
|
export default {
|
|
|
|
atob,
|
|
|
|
btoa,
|
|
|
|
Blob,
|
|
|
|
Buffer,
|
|
|
|
constants,
|
2024-05-21 18:50:59 -04:00
|
|
|
isAscii,
|
|
|
|
isUtf8,
|
2023-02-14 11:38:45 -05:00
|
|
|
kMaxLength,
|
|
|
|
kStringMaxLength,
|
|
|
|
SlowBuffer,
|
|
|
|
};
|