forked from denoland/deno
-
Notifications
You must be signed in to change notification settings - Fork 1
/
_utils.ts
112 lines (102 loc) · 3.54 KB
/
_utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// 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;
}