summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/node/polyfills/internal/buffer.mjs35
-rw-r--r--ext/node/polyfills/internal_binding/_utils.ts86
-rw-r--r--ext/web/internal.d.ts2
3 files changed, 77 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;
}
diff --git a/ext/web/internal.d.ts b/ext/web/internal.d.ts
index ee20b6ead..a07f4b814 100644
--- a/ext/web/internal.d.ts
+++ b/ext/web/internal.d.ts
@@ -41,6 +41,8 @@ declare module "ext:deno_web/00_infra.js" {
};
function forgivingBase64Encode(data: Uint8Array): string;
function forgivingBase64Decode(data: string): Uint8Array;
+ function forgivingBase64UrlEncode(data: Uint8Array | string): string;
+ function forgivingBase64UrlDecode(data: string): Uint8Array;
function serializeJSValueToJSONString(value: unknown): string;
}