1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00
denoland-deno/ext/node/polyfills/internal_binding/_utils.ts

112 lines
3.5 KiB
TypeScript

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials
import {
forgivingBase64Decode,
forgivingBase64UrlDecode,
} from "ext:deno_web/00_infra.js";
export function asciiToBytes(str: string) {
const length = str.length;
const byteArray = new Uint8Array(length);
for (let i = 0; i < length; ++i) {
byteArray[i] = str.charCodeAt(i) & 255;
}
return byteArray;
}
export function base64ToBytes(str: string) {
try {
return forgivingBase64Decode(str);
} catch {
str = base64clean(str);
str = str.replaceAll("-", "+").replaceAll("_", "/");
return forgivingBase64Decode(str);
}
}
const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g;
function base64clean(str: string) {
// Node takes equal signs as end of the Base64 encoding
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.replace(INVALID_BASE64_RE, "");
// Node converts strings with length < 2 to ''
const length = str.length;
if (length < 2) return "";
// Node allows for non-padded base64 strings (missing trailing ===), std/base64 does not
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");
}
}
export function base64UrlToBytes(str: string) {
str = base64clean(str);
str = str.replaceAll("+", "-").replaceAll("/", "_");
return forgivingBase64UrlDecode(str);
}
export function hexToBytes(str: string) {
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)) {
break;
}
byteArray[i] = (a << 4) | b;
}
// 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) {
// 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);
}
// 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 res = "";
const length = bytes.byteLength;
for (let i = 0; i < length; ++i) {
res = `${res}${String.fromCharCode(bytes[i] & 127)}`;
}
return res;
}
export function bytesToUtf16le(bytes: Uint8Array) {
let res = "";
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;
}