diff options
Diffstat (limited to 'ext/node')
-rw-r--r-- | ext/node/polyfills/internal/buffer.mjs | 35 | ||||
-rw-r--r-- | ext/node/polyfills/internal_binding/_utils.ts | 86 |
2 files changed, 75 insertions, 46 deletions
diff --git a/ext/node/polyfills/internal/buffer.mjs b/ext/node/polyfills/internal/buffer.mjs index 29ea2e84d..1a2dc31df 100644 --- a/ext/node/polyfills/internal/buffer.mjs +++ b/ext/node/polyfills/internal/buffer.mjs @@ -693,7 +693,7 @@ Buffer.prototype.base64urlWrite = function base64urlWrite( Buffer.prototype.hexWrite = function hexWrite(string, offset, length) { return blitBuffer( - hexToBytes(string, this.length - offset), + hexToBytes(string), this, offset, length, @@ -751,6 +751,9 @@ Buffer.prototype.utf8Write = function utf8Write(string, offset, length) { }; Buffer.prototype.write = function write(string, offset, length, encoding) { + if (typeof string !== "string") { + throw new codes.ERR_INVALID_ARG_TYPE("argument", "string"); + } // Buffer#write(string); if (offset === undefined) { return this.utf8Write(string, 0, this.length); @@ -1756,16 +1759,26 @@ function utf8ToBytes(string, units) { return bytes; } -function blitBuffer(src, dst, offset, byteLength) { - let i; - const length = byteLength === undefined ? src.length : byteLength; - for (i = 0; i < length; ++i) { - if (i + offset >= dst.length || i >= src.length) { - break; - } - dst[i + offset] = src[i]; - } - return i; +function blitBuffer(src, dst, offset, byteLength = Infinity) { + // 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. + src.length, + // The length of the destination minus any offset into it sets an upper bound. + dst.length - offset, + ); + if (bytesToWrite < src.length) { + // 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. + src = src.subarray(0, length); + } + dst.set(src, offset); + return bytesToWrite; } function isInstance(obj, type) { diff --git a/ext/node/polyfills/internal_binding/_utils.ts b/ext/node/polyfills/internal_binding/_utils.ts index d543fd372..ab174608b 100644 --- a/ext/node/polyfills/internal_binding/_utils.ts +++ b/ext/node/polyfills/internal_binding/_utils.ts @@ -9,11 +9,12 @@ import { } from "ext:deno_web/00_infra.js"; export function asciiToBytes(str: string) { - const byteArray = []; - for (let i = 0; i < str.length; ++i) { - byteArray.push(str.charCodeAt(i) & 255); + const length = str.length; + const byteArray = new Uint8Array(length); + for (let i = 0; i < length; ++i) { + byteArray[i] = str.charCodeAt(i) & 255; } - return new Uint8Array(byteArray); + return byteArray; } export function base64ToBytes(str: string) { @@ -25,16 +26,26 @@ export function base64ToBytes(str: string) { const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g; function base64clean(str: string) { // Node takes equal signs as end of the Base64 encoding - str = str.split("=")[0]; + const eqIndex = str.indexOf("="); + str = eqIndex !== -1 ? str.substring(0, eqIndex).trimStart() : str.trim(); // Node strips out invalid characters like \n and \t from the string, std/base64 does not - str = str.trim().replace(INVALID_BASE64_RE, ""); + str = str.replace(INVALID_BASE64_RE, ""); // Node converts strings with length < 2 to '' - if (str.length < 2) return ""; + const length = str.length; + if (length < 2) return ""; // Node allows for non-padded base64 strings (missing trailing ===), std/base64 does not - while (str.length % 4 !== 0) { - str = str + "="; + switch (length % 4) { + case 0: + return str; + case 1: + return `${str}===`; + case 2: + return `${str}==`; + case 3: + return `${str}=`; + default: + throw new Error("Unexpected NaN value for string length"); } - return str; } export function base64UrlToBytes(str: string) { @@ -44,9 +55,10 @@ export function base64UrlToBytes(str: string) { } export function hexToBytes(str: string) { - const byteArray = new Uint8Array(Math.floor((str || "").length / 2)); - let i; - for (i = 0; i < byteArray.length; i++) { + const length = str.length >>> 1; + const byteArray = new Uint8Array(length); + let i: number; + for (i = 0; i < length; i++) { const a = Number.parseInt(str[i * 2], 16); const b = Number.parseInt(str[i * 2 + 1], 16); if (Number.isNaN(a) && Number.isNaN(b)) { @@ -54,39 +66,43 @@ export function hexToBytes(str: string) { } byteArray[i] = (a << 4) | b; } - return new Uint8Array( - i === byteArray.length ? byteArray : byteArray.slice(0, i), - ); + // Returning a buffer subarray is okay: This API's return value + // is never exposed to users and is only ever used for its length + // and the data within the subarray. + return i === length ? byteArray : byteArray.subarray(0, i); } -export function utf16leToBytes(str: string, units: number) { - let c, hi, lo; - const byteArray = []; - for (let i = 0; i < str.length; ++i) { - if ((units -= 2) < 0) { - break; - } - c = str.charCodeAt(i); - hi = c >> 8; - lo = c % 256; - byteArray.push(lo); - byteArray.push(hi); +export function utf16leToBytes(str: string, units?: number) { + // If units is defined, round it to even values for 16 byte "steps" + // and use it as an upper bound value for our string byte array's length. + const length = Math.min(str.length * 2, units ? (units >>> 1) * 2 : Infinity); + const byteArray = new Uint8Array(length); + const view = new DataView(byteArray.buffer); + let i: number; + for (i = 0; i * 2 < length; i++) { + view.setUint16(i * 2, str.charCodeAt(i), true); } - return new Uint8Array(byteArray); + // Returning a buffer subarray is okay: This API's return value + // is never exposed to users and is only ever used for its length + // and the data within the subarray. + return i * 2 === length ? byteArray : byteArray.subarray(0, i * 2); } export function bytesToAscii(bytes: Uint8Array) { - let ret = ""; - for (let i = 0; i < bytes.length; ++i) { - ret += String.fromCharCode(bytes[i] & 127); + let res = ""; + const length = bytes.byteLength; + for (let i = 0; i < length; ++i) { + res = `${res}${String.fromCharCode(bytes[i] & 127)}`; } - return ret; + return res; } export function bytesToUtf16le(bytes: Uint8Array) { let res = ""; - for (let i = 0; i < bytes.length - 1; i += 2) { - res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256); + const length = bytes.byteLength; + const view = new DataView(bytes.buffer, bytes.byteOffset, length); + for (let i = 0; i < length - 1; i += 2) { + res = `${res}${String.fromCharCode(view.getUint16(i, true))}`; } return res; } |