@wener/utils 1.1.53 → 1.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/arrays/arrayFromAsync.js +11 -3
- package/lib/asyncs/AsyncInterval.js +11 -3
- package/lib/asyncs/Promises.js +6 -5
- package/lib/asyncs/createAsyncIterator.js +11 -3
- package/lib/asyncs/createLazyPromise.test.js +11 -3
- package/lib/asyncs/generatorOfStream.js +11 -3
- package/lib/browsers/download.js +11 -3
- package/lib/browsers/loaders.js +11 -3
- package/lib/crypto/hashing.js +11 -3
- package/lib/crypto/hashing.test.js +11 -3
- package/lib/crypto/pem/pem.js +1 -1
- package/lib/errors/Errors.js +201 -2
- package/lib/fetch/createFetchWith.js +11 -3
- package/lib/fetch/dumpRequest.js +12 -4
- package/lib/fetch/dumpRequest.test.js +11 -3
- package/lib/fetch/dumpResponse.js +11 -3
- package/lib/fetch/dumpResponse.test.js +11 -3
- package/lib/index.js +9 -8
- package/lib/io/ArrayBuffers.js +559 -2
- package/lib/io/ByteBuffer.test.js +11 -3
- package/lib/io/parseDataUri.js +11 -3
- package/lib/io/parseDataUri.test.js +31 -11
- package/lib/langs/AsyncCloser.js +11 -3
- package/lib/langs/deepFreeze.js +5 -5
- package/lib/langs/mixin.js +6 -12
- package/lib/langs/mixin.test.js +53 -5
- package/lib/langs/mixin2.js +26 -0
- package/lib/langs/parseBoolean.js +3 -2
- package/lib/langs/parseDate.js +20 -0
- package/lib/langs/shallowEqual.js +5 -5
- package/lib/libs/ms.js +1 -1
- package/lib/maths/random.js +12 -4
- package/lib/objects/merge/isMergeableObject.js +1 -1
- package/lib/objects/merge/merge.js +1 -1
- package/lib/objects/merge/merge.test.js +11 -3
- package/lib/objects/set.js +10 -2
- package/lib/objects/set.test.js +2 -2
- package/lib/scripts/getGenerateContext.js +13 -5
- package/lib/server/fetch/createFetchWithProxyByNodeFetch.js +11 -3
- package/lib/server/fetch/createFetchWithProxyByUndici.js +11 -3
- package/lib/server/polyfill/polyfillBrowser.js +11 -3
- package/lib/server/polyfill/polyfillBrowser.test.js +11 -3
- package/lib/server/polyfill/polyfillCrypto.js +11 -3
- package/lib/server/polyfill/polyfillJsDom.js +31 -11
- package/lib/strings/renderTemplate.test.js +2 -2
- package/lib/web/getRandomValues.js +1 -1
- package/lib/web/structuredClone.js +2 -2
- package/package.json +8 -4
- package/src/asyncs/Promises.ts +2 -1
- package/src/asyncs/timeout.ts +1 -1
- package/src/crypto/hashing.ts +7 -6
- package/src/crypto/pem/pem.ts +3 -2
- package/src/errors/Errors.ts +106 -1
- package/src/fetch/dumpRequest.ts +1 -1
- package/src/index.ts +10 -8
- package/src/io/ArrayBuffers.ts +676 -1
- package/src/langs/mixin.test.ts +35 -5
- package/src/langs/mixin.ts +46 -65
- package/src/langs/mixin2.ts +80 -0
- package/src/langs/parseBoolean.ts +9 -1
- package/src/langs/parseDate.ts +13 -0
- package/src/objects/set.ts +10 -1
- package/src/types.d.ts +1 -1
- package/tsconfig.json +8 -14
- package/lib/errors/Errors.mod.js +0 -206
- package/lib/io/ArrayBuffers.mod.js +0 -531
- package/src/errors/Errors.mod.ts +0 -104
- package/src/io/ArrayBuffers.mod.ts +0 -670
package/src/io/ArrayBuffers.ts
CHANGED
|
@@ -1 +1,676 @@
|
|
|
1
|
-
|
|
1
|
+
import { classOf } from '../langs/classOf';
|
|
2
|
+
import { getGlobalThis } from '../web/getGlobalThis';
|
|
3
|
+
import { decodeBase64ToUint8Array, encodeArrayBufferToBase64 } from './base64';
|
|
4
|
+
import { isBuffer } from './isBuffer';
|
|
5
|
+
import type { Bytes, TypedArray } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Various utils to work with {@link ArrayBuffer}
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/tc39/proposal-resizablearraybuffer
|
|
11
|
+
*/
|
|
12
|
+
/*
|
|
13
|
+
Uint8Array to/from base64 and hex
|
|
14
|
+
Stage 3
|
|
15
|
+
Uint8Array.fromBase64, Uint8Array.prototype.toBase64
|
|
16
|
+
Uint8Array.fromHex, Uint8Array.prototype.toHex
|
|
17
|
+
https://github.com/tc39/proposal-arraybuffer-base64
|
|
18
|
+
|
|
19
|
+
Unicode routines (UTF8, UTF16, UTF32) and Base64
|
|
20
|
+
used by Node.js, WebKit/Safari, Ladybird, Cloudflare Workers, Bun
|
|
21
|
+
https://github.com/simdutf/simdutf
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
In-Place Resizable and Growable ArrayBuffers
|
|
26
|
+
Stage 4
|
|
27
|
+
Chrome 111, Nodejs 20, Safari 16.4
|
|
28
|
+
|
|
29
|
+
SharedArrayBuffer & ArrayBuffer
|
|
30
|
+
constructor(byteLength, {maxByteLength})
|
|
31
|
+
prototype.resize(newByteLength)
|
|
32
|
+
prototype.slice(start, end)
|
|
33
|
+
prototype.{resizable,maxByteLength}
|
|
34
|
+
https://github.com/tc39/proposal-resizablearraybuffer
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
export type BinaryStringEncoding =
|
|
38
|
+
| 'ascii'
|
|
39
|
+
| 'utf16le'
|
|
40
|
+
// | 'utf-16le'
|
|
41
|
+
| 'ucs2'
|
|
42
|
+
| 'ucs-2'
|
|
43
|
+
| 'base64'
|
|
44
|
+
| 'base64url'
|
|
45
|
+
| 'latin1'
|
|
46
|
+
| 'binary'
|
|
47
|
+
| 'utf8'
|
|
48
|
+
| 'utf-8'
|
|
49
|
+
| 'hex';
|
|
50
|
+
type BufferSource = ArrayBufferView<ArrayBuffer> | ArrayBuffer;
|
|
51
|
+
|
|
52
|
+
export namespace ArrayBuffers {
|
|
53
|
+
let nativeBufferAllowed: boolean = true;
|
|
54
|
+
let isBufferAvailable: undefined | boolean;
|
|
55
|
+
let textEncoder: TextEncoder;
|
|
56
|
+
let textDecoder: TextDecoder;
|
|
57
|
+
|
|
58
|
+
function decode(v: AllowSharedBufferSource): string {
|
|
59
|
+
// need icu full data
|
|
60
|
+
// if (encoding) return new TextDecoder(encoding).decode(v);
|
|
61
|
+
return (textDecoder ||= new TextDecoder()).decode(v);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function encode(v: string): Bytes;
|
|
65
|
+
function encode<T>(v: string | T): T | Bytes;
|
|
66
|
+
function encode<T>(v: string | T): T | Bytes {
|
|
67
|
+
if (typeof v === 'string') {
|
|
68
|
+
return (textEncoder ||= new TextEncoder()).encode(v);
|
|
69
|
+
}
|
|
70
|
+
return v;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* isNativeBufferAvailable check if the native {@link Buffer} is available
|
|
75
|
+
*/
|
|
76
|
+
export function isNativeBufferAvailable(): boolean {
|
|
77
|
+
// eslint-disable-next-line no-return-assign
|
|
78
|
+
return (isBufferAvailable ??= !(getGlobalThis().Buffer as any)?.isPollyfill?.());
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function isNativeBufferAllowed(): boolean {
|
|
82
|
+
return Boolean(nativeBufferAllowed && isBufferAvailable);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function setNativeBufferAllowed(v: boolean): void {
|
|
86
|
+
nativeBufferAllowed = v;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function isArrayBuffer(v: unknown): v is ArrayBuffer {
|
|
90
|
+
return v instanceof ArrayBuffer;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* slice the given view with the given offset and length, will handle the {@link Buffer} as well
|
|
95
|
+
*
|
|
96
|
+
* @see {@link https://nodejs.org/api/buffer.html#bufslicestart-end Buffer.slice}
|
|
97
|
+
*/
|
|
98
|
+
export function slice<T extends TypedArray>(o: T, start?: number, end?: number): T {
|
|
99
|
+
// NodeJS Buffer slice is not the same as UInt8Array slice
|
|
100
|
+
// https://nodejs.org/api/buffer.html#bufslicestart-end
|
|
101
|
+
if (isBuffer(o)) {
|
|
102
|
+
return Uint8Array.prototype.slice.call(o, start, end) as T;
|
|
103
|
+
}
|
|
104
|
+
return o.slice(start, end) as T;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* asView convert the given value to given {@link TypedArray} view
|
|
109
|
+
*
|
|
110
|
+
* TypedArray can be {@link Buffer}, will avoid copy
|
|
111
|
+
*/
|
|
112
|
+
export function asView<C extends ArrayBufferViewConstructor<unknown>>(
|
|
113
|
+
TypedArray: C,
|
|
114
|
+
v: BufferSource | TypedArray,
|
|
115
|
+
byteOffset?: number,
|
|
116
|
+
byteLength?: number,
|
|
117
|
+
): InstanceType<C> {
|
|
118
|
+
if (v instanceof TypedArray && (byteOffset ?? 0) === 0 && byteLength === undefined) {
|
|
119
|
+
return v as InstanceType<C>;
|
|
120
|
+
}
|
|
121
|
+
if (ArrayBuffer.isView(v) || isBuffer(v)) {
|
|
122
|
+
if (isNativeBufferAllowed() && (TypedArray as any) === Buffer) {
|
|
123
|
+
// new Buffer() is deprecated
|
|
124
|
+
return Buffer.from(v.buffer, byteOffset, byteLength) as InstanceType<C>;
|
|
125
|
+
}
|
|
126
|
+
return new TypedArray(v.buffer, v.byteOffset + (byteOffset ?? 0), byteLength ?? v.byteLength) as InstanceType<C>;
|
|
127
|
+
}
|
|
128
|
+
return new TypedArray(v, byteOffset, byteLength) as InstanceType<C>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* toString convert the given {@link BufferSource} to string
|
|
133
|
+
*/
|
|
134
|
+
export function toString(
|
|
135
|
+
source: BufferSource | TypedArray | string,
|
|
136
|
+
encoding: BinaryStringEncoding = 'utf8',
|
|
137
|
+
): string {
|
|
138
|
+
if (typeof source === 'string') {
|
|
139
|
+
switch (encoding) {
|
|
140
|
+
case 'base64':
|
|
141
|
+
return btoa(source);
|
|
142
|
+
case 'utf-8':
|
|
143
|
+
case 'utf8':
|
|
144
|
+
return source;
|
|
145
|
+
default:
|
|
146
|
+
throw new Error(`[ArrayBuffers.toString] Unsupported encoding for string: ${encoding}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const u8 = asView(Uint8Array, source);
|
|
151
|
+
if (isNativeBufferAllowed()) {
|
|
152
|
+
return Buffer.from(u8).toString(encoding);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// reference: https://github.com/feross/buffer/blob/master/index.js
|
|
156
|
+
switch (encoding) {
|
|
157
|
+
case 'hex': {
|
|
158
|
+
return toHexString(u8);
|
|
159
|
+
}
|
|
160
|
+
case 'base64': {
|
|
161
|
+
return toBase64(u8);
|
|
162
|
+
}
|
|
163
|
+
case 'utf8':
|
|
164
|
+
case 'utf-8':
|
|
165
|
+
return decode(source);
|
|
166
|
+
case 'ascii': {
|
|
167
|
+
return toAsciiString(u8);
|
|
168
|
+
}
|
|
169
|
+
case 'latin1':
|
|
170
|
+
case 'binary': {
|
|
171
|
+
return toLatin1String(u8);
|
|
172
|
+
}
|
|
173
|
+
case 'ucs2':
|
|
174
|
+
case 'ucs-2':
|
|
175
|
+
case 'utf16le': {
|
|
176
|
+
return toUtf16LeString(u8);
|
|
177
|
+
}
|
|
178
|
+
default:
|
|
179
|
+
throw new Error(`[ArrayBuffers.toString] Unknown encoding: ${encoding}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Normalize encoding string to standard form
|
|
185
|
+
* @param encoding - The encoding string to normalize
|
|
186
|
+
* @returns Normalized encoding or undefined if invalid
|
|
187
|
+
*/
|
|
188
|
+
function normalizeEncoding(encoding: string | undefined): BinaryStringEncoding | undefined {
|
|
189
|
+
switch (encoding?.toLowerCase()) {
|
|
190
|
+
case 'utf-8':
|
|
191
|
+
case 'utf8':
|
|
192
|
+
return 'utf8';
|
|
193
|
+
case 'utf-16le':
|
|
194
|
+
case 'ucs2':
|
|
195
|
+
case 'ucs-2':
|
|
196
|
+
return 'utf16le';
|
|
197
|
+
case 'hex':
|
|
198
|
+
case 'ascii':
|
|
199
|
+
case 'latin1':
|
|
200
|
+
case 'binary':
|
|
201
|
+
case 'base64':
|
|
202
|
+
case 'utf16le':
|
|
203
|
+
return encoding as BinaryStringEncoding;
|
|
204
|
+
default:
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Check if the given string is a supported character encoding
|
|
211
|
+
* @param v - The string to check
|
|
212
|
+
* @returns True if the encoding is supported, false otherwise
|
|
213
|
+
*/
|
|
214
|
+
export function isEncoding(v?: string): v is BinaryStringEncoding {
|
|
215
|
+
return normalizeEncoding(v) !== undefined;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function toJSON<T = any>(v: BufferSource | string, reviver?: (this: any, key: string, value: any) => any): T {
|
|
219
|
+
return JSON.parse(toString(v), reviver);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* from convert the given value to {@link ArrayBuffer} like
|
|
224
|
+
*/
|
|
225
|
+
export function from(
|
|
226
|
+
src: string | BufferSource | ArrayLike<number> | Iterable<number>,
|
|
227
|
+
encoding?: BinaryStringEncoding,
|
|
228
|
+
): ArrayBuffer | TypedArray;
|
|
229
|
+
/**
|
|
230
|
+
* from convert the given value to {@link TypedArray}
|
|
231
|
+
*/
|
|
232
|
+
export function from<C extends ArrayBufferViewConstructor<unknown>>(
|
|
233
|
+
src: string | BufferSource | ArrayLike<number> | Iterable<number>,
|
|
234
|
+
encoding: BinaryStringEncoding,
|
|
235
|
+
TypedArray: C,
|
|
236
|
+
): InstanceType<C>;
|
|
237
|
+
export function from(
|
|
238
|
+
src: string | BufferSource | ArrayLike<number> | Iterable<number>,
|
|
239
|
+
encoding: BinaryStringEncoding = 'utf8',
|
|
240
|
+
view?: any,
|
|
241
|
+
): any {
|
|
242
|
+
if (!src) {
|
|
243
|
+
return new (view || ArrayBuffer)(0);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (isBufferSource(src)) {
|
|
247
|
+
return view ? asView(view, src) : src;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Array<number> | Iterable<number>
|
|
251
|
+
if ((typeof src !== 'string' && isIterable(src)) || Array.isArray(src)) {
|
|
252
|
+
return (view || Uint8Array).from(src as ArrayLike<number>);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (view) {
|
|
256
|
+
return asView(view, from(src, encoding));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (typeof src === 'string') {
|
|
260
|
+
if (isNativeBufferAllowed()) {
|
|
261
|
+
return Buffer.from(src, encoding);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
switch (encoding) {
|
|
265
|
+
case 'utf-8':
|
|
266
|
+
case 'utf8':
|
|
267
|
+
return encode(src).buffer;
|
|
268
|
+
case 'base64':
|
|
269
|
+
return fromBase64(src);
|
|
270
|
+
case 'hex':
|
|
271
|
+
return fromHex(src);
|
|
272
|
+
default:
|
|
273
|
+
throw new Error(`ArrayBuffers.from unsupported encoding: ${encoding}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const type = classOf(src);
|
|
278
|
+
throw new TypeError(`ArrayBuffers.from unsupported type ${type}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* concat the given {@link BufferSource} to a new {@link ArrayBuffer}
|
|
283
|
+
*/
|
|
284
|
+
export function concat(buffers: Array<BufferSource>, result?: ArrayBuffer, offset = 0): ArrayBuffer {
|
|
285
|
+
if (!Array.isArray(buffers) || buffers.length === 0) {
|
|
286
|
+
return new ArrayBuffer(0);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const length = buffers.reduce((a, b) => a + (b?.byteLength ?? 0), 0);
|
|
290
|
+
const r = result ? new Uint8Array(result) : new Uint8Array(length);
|
|
291
|
+
|
|
292
|
+
for (const buffer of buffers) {
|
|
293
|
+
if (!buffer?.byteLength) continue;
|
|
294
|
+
|
|
295
|
+
let n: Uint8Array;
|
|
296
|
+
if (buffer instanceof ArrayBuffer) {
|
|
297
|
+
n = new Uint8Array(buffer);
|
|
298
|
+
} else if (ArrayBuffer.isView(buffer)) {
|
|
299
|
+
n = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
300
|
+
} else {
|
|
301
|
+
throw new Error(`ArrayBuffers.concat unsupported type ${classOf(buffer)}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
r.set(n, offset);
|
|
305
|
+
offset += buffer.byteLength;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return r.buffer;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function fromBase64(v: string, encoding?: undefined): Bytes;
|
|
312
|
+
export function fromBase64(v: string, encoding: BinaryStringEncoding): string;
|
|
313
|
+
export function fromBase64(v: string, encoding?: BinaryStringEncoding): Bytes | string {
|
|
314
|
+
if (encoding) {
|
|
315
|
+
return toString(fromBase64(v), encoding);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if ('fromBase64' in Uint8Array && typeof Uint8Array.fromBase64 === 'function') {
|
|
319
|
+
return Uint8Array.fromBase64(v);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (isNativeBufferAllowed()) {
|
|
323
|
+
return Buffer.from(v, 'base64');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Clean the base64 string by removing invalid characters
|
|
327
|
+
return decodeBase64ToUint8Array(v.replace(/[^0-9a-zA-Z=+/_]/g, ''));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function fromHex(v: string, encoding?: undefined): Bytes;
|
|
331
|
+
export function fromHex(v: string, encoding: BinaryStringEncoding): string;
|
|
332
|
+
export function fromHex(v: string, encoding?: BinaryStringEncoding): Uint8Array | string {
|
|
333
|
+
if (encoding) {
|
|
334
|
+
return toString(fromHex(v), encoding);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if ('fromHex' in Uint8Array && typeof Uint8Array.fromHex === 'function') {
|
|
338
|
+
return Uint8Array.fromHex(v);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (isNativeBufferAllowed()) {
|
|
342
|
+
return Buffer.from(v, 'hex');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Handle odd-length hex strings by padding with leading zero
|
|
346
|
+
const cleanHex = v.length % 2 === 1 ? '0' + v : v;
|
|
347
|
+
const matches = cleanHex.match(/.{1,2}/g);
|
|
348
|
+
if (!matches) {
|
|
349
|
+
throw new Error('Invalid hex string');
|
|
350
|
+
}
|
|
351
|
+
const num = matches.map((byte) => parseInt(byte, 16));
|
|
352
|
+
return new Uint8Array(num);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* toBase64 convert the given {@link BufferSource} to base64 string
|
|
357
|
+
* @param source if string, will be encoded as utf8
|
|
358
|
+
*/
|
|
359
|
+
export function toBase64(source: BufferSource | string): string {
|
|
360
|
+
source = encode(source);
|
|
361
|
+
|
|
362
|
+
if ('toBase64' in Uint8Array.prototype) {
|
|
363
|
+
return (toUint8Array(source) as Uint8Array2).toBase64();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (isNativeBufferAllowed()) {
|
|
367
|
+
return Buffer.from(asView(Uint8Array, source)).toString('base64');
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return encodeArrayBufferToBase64(toArrayBuffer(source));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function toHex(v: BufferSource | string): string {
|
|
374
|
+
v = encode(v);
|
|
375
|
+
|
|
376
|
+
if ('toHex' in Uint8Array.prototype) {
|
|
377
|
+
return (toUint8Array(v) as Uint8Array2).toHex();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (isNativeBufferAllowed()) {
|
|
381
|
+
return Buffer.from(asView(Uint8Array, v)).toString('hex');
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return toString(v, 'hex');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function resize(v: ArrayBuffer, newByteLength?: number, maxByteLength?: number): ArrayBuffer {
|
|
388
|
+
if (newByteLength === undefined || newByteLength === null) {
|
|
389
|
+
return v;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Chrome 111, Nodejs 20 - use native resize if available
|
|
393
|
+
if ('resize' in v && typeof v.resize === 'function') {
|
|
394
|
+
if ('resizable' in v && v.resizable) {
|
|
395
|
+
if ('maxByteLength' in v && typeof v.maxByteLength === 'number' && v.maxByteLength >= newByteLength) {
|
|
396
|
+
v.resize(newByteLength);
|
|
397
|
+
return v as ArrayBuffer;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Fallback: create new buffer and copy data
|
|
403
|
+
const old = v;
|
|
404
|
+
const newBuf = new (ArrayBuffer as ArrayBuffer2Constructor)(newByteLength, { maxByteLength: maxByteLength });
|
|
405
|
+
const oldView = new Uint8Array(old);
|
|
406
|
+
const newView = new Uint8Array(newBuf);
|
|
407
|
+
newView.set(oldView);
|
|
408
|
+
return newBuf;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export function toArrayBuffer(v: BufferSource): ArrayBuffer {
|
|
412
|
+
if (v instanceof ArrayBuffer) {
|
|
413
|
+
return v;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (ArrayBuffer.isView(v)) {
|
|
417
|
+
if (v.byteOffset > 0) {
|
|
418
|
+
throw new Error('ArrayBuffers.toArrayBuffer does not support view with offset');
|
|
419
|
+
}
|
|
420
|
+
return v.buffer;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
throw new Error(`ArrayBuffers.toArrayBuffer unsupported type ${classOf(v)}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export function toUint8Array(v: BufferSource): Bytes {
|
|
427
|
+
return asView(Uint8Array, v);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Allocate a new ArrayBuffer or Uint8Array with optional fill value
|
|
432
|
+
* @param size - The size in bytes to allocate
|
|
433
|
+
* @param fill - Optional fill value (number or string)
|
|
434
|
+
* @param encoding - Encoding for string fill value (default: 'utf8')
|
|
435
|
+
* @returns ArrayBuffer or Uint8Array
|
|
436
|
+
*/
|
|
437
|
+
export function alloc(size: number, fill?: string | number, encoding?: BinaryStringEncoding): ArrayBuffer | Bytes {
|
|
438
|
+
if (fill !== undefined) {
|
|
439
|
+
if (typeof fill === 'number') {
|
|
440
|
+
return new Uint8Array(size).fill(fill);
|
|
441
|
+
}
|
|
442
|
+
// Convert string to buffer and slice to size
|
|
443
|
+
// https://stackoverflow.com/questions/73994091
|
|
444
|
+
return asView(Uint8Array, from(fill, encoding)).slice(0, size);
|
|
445
|
+
}
|
|
446
|
+
return new ArrayBuffer(size);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
type ArrayBufferViewConstructor<T> = new (buffer: ArrayBufferLike, byteOffset?: number, byteLength?: number) => T;
|
|
450
|
+
|
|
451
|
+
// Helper functions for string conversion
|
|
452
|
+
/**
|
|
453
|
+
* Convert Uint8Array to hex string efficiently
|
|
454
|
+
* @param u8 - The Uint8Array to convert
|
|
455
|
+
* @returns Hex string representation
|
|
456
|
+
*/
|
|
457
|
+
function toHexString(u8: Uint8Array): string {
|
|
458
|
+
let result = '';
|
|
459
|
+
for (let i = 0; i < u8.length; i++) {
|
|
460
|
+
result += hexLookupTable[u8[i]];
|
|
461
|
+
}
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Convert Uint8Array to ASCII string
|
|
467
|
+
* @param u8 - The Uint8Array to convert
|
|
468
|
+
* @returns ASCII string representation
|
|
469
|
+
*/
|
|
470
|
+
function toAsciiString(u8: Uint8Array): string {
|
|
471
|
+
let result = '';
|
|
472
|
+
for (let i = 0; i < u8.length; i++) {
|
|
473
|
+
result += String.fromCharCode(u8[i] & 0x7f);
|
|
474
|
+
}
|
|
475
|
+
return result;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Convert Uint8Array to Latin1 string
|
|
480
|
+
* @param u8 - The Uint8Array to convert
|
|
481
|
+
* @returns Latin1 string representation
|
|
482
|
+
*/
|
|
483
|
+
function toLatin1String(u8: Uint8Array): string {
|
|
484
|
+
let result = '';
|
|
485
|
+
for (let i = 0; i < u8.length; i++) {
|
|
486
|
+
result += String.fromCharCode(u8[i]);
|
|
487
|
+
}
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Convert Uint8Array to UTF-16LE string
|
|
493
|
+
* @param u8 - The Uint8Array to convert
|
|
494
|
+
* @returns UTF-16LE string representation
|
|
495
|
+
*/
|
|
496
|
+
function toUtf16LeString(u8: Uint8Array): string {
|
|
497
|
+
let result = '';
|
|
498
|
+
// If length is odd, the last 8 bits must be ignored (same as node.js)
|
|
499
|
+
for (let i = 0; i < u8.length - 1; i += 2) {
|
|
500
|
+
result += String.fromCharCode(u8[i] + u8[i + 1] * 256);
|
|
501
|
+
}
|
|
502
|
+
return result;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// base16 lookup table for efficient hex conversion
|
|
506
|
+
const hexLookupTable = (function () {
|
|
507
|
+
const alphabet = '0123456789abcdef';
|
|
508
|
+
const table = new Array(256);
|
|
509
|
+
for (let i = 0; i < 16; ++i) {
|
|
510
|
+
const i16 = i * 16;
|
|
511
|
+
for (let j = 0; j < 16; ++j) {
|
|
512
|
+
table[i16 + j] = alphabet[i] + alphabet[j];
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return table;
|
|
516
|
+
})();
|
|
517
|
+
|
|
518
|
+
// avoid declare global
|
|
519
|
+
|
|
520
|
+
interface Uint8Array2 extends Uint8Array {
|
|
521
|
+
toBase64(): string;
|
|
522
|
+
|
|
523
|
+
toHex(): string;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
type IArrayBuffer = (ArrayBuffer | SharedArrayBuffer) & {
|
|
527
|
+
resize(newByteLength: number): void;
|
|
528
|
+
resizable: boolean;
|
|
529
|
+
maxByteLength: number;
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
interface ArrayBuffer2Constructor {
|
|
533
|
+
new (byteLength: number, opts?: { maxByteLength?: number }): ArrayBuffer;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Helper functions for internal use
|
|
537
|
+
function isIterable<T>(obj: unknown): obj is Iterable<T> {
|
|
538
|
+
return obj != null && typeof (obj as any)?.[Symbol.iterator] === 'function';
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function isBufferSource(value: unknown): value is BufferSource {
|
|
542
|
+
return ArrayBuffer.isView(value) || value instanceof ArrayBuffer;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Check if two BufferSources are equal
|
|
547
|
+
* @param a - First buffer source
|
|
548
|
+
* @param b - Second buffer source
|
|
549
|
+
* @returns True if buffers are equal, false otherwise
|
|
550
|
+
*/
|
|
551
|
+
export function equals(a: BufferSource, b: BufferSource): boolean {
|
|
552
|
+
if (a === b) return true;
|
|
553
|
+
const aView = asView(Uint8Array, a);
|
|
554
|
+
const bView = asView(Uint8Array, b);
|
|
555
|
+
|
|
556
|
+
if (aView.length !== bView.length) {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
for (let i = 0; i < aView.length; i++) {
|
|
561
|
+
if (aView[i] !== bView[i]) {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Compare two BufferSources lexicographically
|
|
571
|
+
* @param a - First buffer source
|
|
572
|
+
* @param b - Second buffer source
|
|
573
|
+
* @returns -1 if a < b, 0 if a === b, 1 if a > b
|
|
574
|
+
*/
|
|
575
|
+
export function compare(a: BufferSource, b: BufferSource): number {
|
|
576
|
+
if (a === b) return 0;
|
|
577
|
+
const aView = asView(Uint8Array, a);
|
|
578
|
+
const bView = asView(Uint8Array, b);
|
|
579
|
+
|
|
580
|
+
const minLength = Math.min(aView.length, bView.length);
|
|
581
|
+
|
|
582
|
+
for (let i = 0; i < minLength; i++) {
|
|
583
|
+
if (aView[i] < bView[i]) return -1;
|
|
584
|
+
if (aView[i] > bView[i]) return 1;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return aView.length - bView.length;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Check if a BufferSource starts with another BufferSource
|
|
592
|
+
* @param buffer - The buffer to check
|
|
593
|
+
* @param prefix - The prefix to check for
|
|
594
|
+
* @returns True if buffer starts with prefix, false otherwise
|
|
595
|
+
*/
|
|
596
|
+
export function startsWith(buffer: BufferSource, prefix: BufferSource): boolean {
|
|
597
|
+
const bufferView = asView(Uint8Array, buffer);
|
|
598
|
+
const prefixView = asView(Uint8Array, prefix);
|
|
599
|
+
|
|
600
|
+
if (prefixView.length > bufferView.length) {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
for (let i = 0; i < prefixView.length; i++) {
|
|
605
|
+
if (bufferView[i] !== prefixView[i]) {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return true;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Check if a BufferSource ends with another BufferSource
|
|
615
|
+
* @param buffer - The buffer to check
|
|
616
|
+
* @param suffix - The suffix to check for
|
|
617
|
+
* @returns True if buffer ends with suffix, false otherwise
|
|
618
|
+
*/
|
|
619
|
+
export function endsWith(buffer: BufferSource, suffix: BufferSource): boolean {
|
|
620
|
+
const bufferView = asView(Uint8Array, buffer);
|
|
621
|
+
const suffixView = asView(Uint8Array, suffix);
|
|
622
|
+
|
|
623
|
+
if (suffixView.length > bufferView.length) {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const offset = bufferView.length - suffixView.length;
|
|
628
|
+
for (let i = 0; i < suffixView.length; i++) {
|
|
629
|
+
if (bufferView[offset + i] !== suffixView[i]) {
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Find the index of a sub-buffer within a buffer
|
|
639
|
+
* @param buffer - The buffer to search in
|
|
640
|
+
* @param search - The sub-buffer to search for
|
|
641
|
+
* @param startIndex - Starting index for search (default: 0)
|
|
642
|
+
* @returns Index of first occurrence, or -1 if not found
|
|
643
|
+
*/
|
|
644
|
+
export function indexOf(buffer: BufferSource, search: BufferSource, startIndex = 0): number {
|
|
645
|
+
const bufferView = asView(Uint8Array, buffer);
|
|
646
|
+
const searchView = asView(Uint8Array, search);
|
|
647
|
+
|
|
648
|
+
if (searchView.length === 0) return startIndex;
|
|
649
|
+
if (searchView.length > bufferView.length) return -1;
|
|
650
|
+
|
|
651
|
+
for (let i = startIndex; i <= bufferView.length - searchView.length; i++) {
|
|
652
|
+
let found = true;
|
|
653
|
+
for (let j = 0; j < searchView.length; j++) {
|
|
654
|
+
if (bufferView[i + j] !== searchView[j]) {
|
|
655
|
+
found = false;
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (found) return i;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return -1;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Get a sub-buffer from a buffer
|
|
667
|
+
* @param buffer - The source buffer
|
|
668
|
+
* @param start - Start index (inclusive)
|
|
669
|
+
* @param end - End index (exclusive, optional)
|
|
670
|
+
* @returns New Uint8Array containing the sub-buffer
|
|
671
|
+
*/
|
|
672
|
+
export function subarray(buffer: BufferSource, start: number, end?: number): Uint8Array {
|
|
673
|
+
const view = asView(Uint8Array, buffer);
|
|
674
|
+
return view.subarray(start, end);
|
|
675
|
+
}
|
|
676
|
+
}
|