]> Lady’s Gitweb - Pisces/blob - binary.js
Add buffer getters and setters to binary.js
[Pisces] / binary.js
1 // ♓🌟 Piscēs ∷ binary.js
2 // ====================================================================
3 //
4 // Copyright © 2020–2023 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import { fill, map, reduce } from "./collection.js";
11 import { bind, call } from "./function.js";
12 import { ceil, floor } from "./numeric.js";
13 import { hasOwnProperty, objectCreate } from "./object.js";
14 import {
15 getCodeUnit,
16 rawString,
17 stringFromCodeUnits,
18 stringReplace,
19 } from "./string.js";
20 import { ITERATOR } from "./value.js";
21
22 const Buffer = ArrayBuffer;
23 const View = DataView;
24 const TypedArray = Object.getPrototypeOf(Uint8Array);
25 const { prototype: arrayPrototype } = Array;
26 const { prototype: bufferPrototype } = Buffer;
27 const { prototype: sharedBufferPrototype } = SharedArrayBuffer;
28 const { prototype: rePrototype } = RegExp;
29 const { prototype: typedArrayPrototype } = TypedArray;
30 const { prototype: viewPrototype } = View;
31
32 const { [ITERATOR]: arrayIterator } = arrayPrototype;
33 const {
34 next: arrayIteratorNext,
35 } = Object.getPrototypeOf([][ITERATOR]());
36 const argumentIterablePrototype = {
37 [ITERATOR]() {
38 return {
39 next: bind(
40 arrayIteratorNext,
41 call(arrayIterator, this.args, []),
42 [],
43 ),
44 };
45 },
46 };
47 const binaryCodeUnitIterablePrototype = {
48 [ITERATOR]() {
49 return {
50 next: bind(
51 arrayIteratorNext,
52 call(arrayIterator, this, []),
53 [],
54 ),
55 };
56 },
57 };
58
59 const { exec: reExec } = rePrototype;
60 const { slice: bufferSlice } = bufferPrototype;
61 const getBufferByteLength =
62 Object.getOwnPropertyDescriptor(bufferPrototype, "byteLength").get;
63 const { slice: sharedBufferSlice } = sharedBufferPrototype;
64 const getSharedBufferByteLength =
65 Object.getOwnPropertyDescriptor(sharedBufferPrototype, "byteLength")
66 .get;
67 const getTypedArrayBuffer =
68 Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer").get;
69 const getTypedArrayByteLength =
70 Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")
71 .get;
72 const getTypedArrayByteOffset =
73 Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteOffset")
74 .get;
75 const getViewBuffer =
76 Object.getOwnPropertyDescriptor(viewPrototype, "buffer").get;
77 const getViewByteLength =
78 Object.getOwnPropertyDescriptor(viewPrototype, "byteLength").get;
79 const getViewByteOffset =
80 Object.getOwnPropertyDescriptor(viewPrototype, "byteOffset").get;
81 const {
82 getBigInt64: viewGetInt64,
83 getBigUint64: viewGetUint64,
84 getFloat32: viewGetFloat32,
85 getFloat64: viewGetFloat64,
86 getInt8: viewGetInt8,
87 getInt16: viewGetInt16,
88 getInt32: viewGetInt32,
89 getUint8: viewGetUint8,
90 getUint16: viewGetUint16,
91 getUint32: viewGetUint32,
92 setFloat32: viewSetFloat32,
93 setFloat64: viewSetFloat64,
94 setUint8: viewSetUint8,
95 setUint16: viewSetUint16,
96 setUint32: viewSetUint32,
97 setBigUint64: viewSetUint64,
98 } = viewPrototype;
99
100 /**
101 * Returns an ArrayBuffer for encoding generated from the provided
102 * arguments.
103 */
104 const bufferFromArgs = ($, $s) => {
105 try {
106 // Try just getting the array buffer associated with the first
107 // argument and returning it if possible.
108 return toArrayBuffer($);
109 } catch {
110 // There is no array buffer associated with the first argument.
111 //
112 // Construct a string and convert it to an array buffer instead.
113 return ((string) =>
114 call(
115 getViewBuffer,
116 reduce(
117 string,
118 (result, ucsCharacter, index) => (
119 call(viewSetUint16, result, [
120 index * 2,
121 getCodeUnit(ucsCharacter, 0),
122 ]), result
123 ),
124 new View(new Buffer(string.length * 2)),
125 ),
126 [],
127 ))(
128 typeof $ === "string"
129 ? $
130 : hasOwnProperty($, "raw")
131 ? rawString(
132 $,
133 ...objectCreate(argumentIterablePrototype, {
134 args: { value: $s },
135 }),
136 )
137 : `${$}`,
138 );
139 }
140 };
141
142 /**
143 * Returns the result of decoding the provided base16 string into an
144 * ArrayBuffer.
145 *
146 * ※ This function is not exposed.
147 */
148 const decodeBase16 = (source) => {
149 const u4s = map(
150 source,
151 (ucsCharacter) => {
152 const code = getCodeUnit(ucsCharacter, 0);
153 const result = code >= 0x30 && code <= 0x39
154 ? code - 48
155 : code >= 0x41 && code <= 0x46
156 ? code - 55
157 : code >= 0x61 && code <= 0x66
158 ? code - 87
159 : -1;
160 if (result < 0) {
161 // The source contains an invalid character.
162 throw new RangeError(
163 `Piscēs: Invalid character in Base64: ${ucsCharacter}.`,
164 );
165 } else {
166 // The source contains a valid character with a recognized
167 // mapping.
168 return result;
169 }
170 },
171 );
172 const { length } = u4s;
173 if (length % 2 === 1) {
174 // The length is such that an entire letter would be dropped during
175 // a forgiving decode.
176 throw new RangeError(
177 `Piscēs: Base16 string has invalid length: ${source}.`,
178 );
179 } else {
180 // Every letter contributes at least some bits to the result.
181 const dataView = new View(new Buffer(floor(length / 2)));
182 for (let index = 0; index < length - 1;) {
183 // Iterate over the characters and assign their bits to the
184 // buffer.
185 call(viewSetUint8, dataView, [
186 floor(index / 2),
187 (u4s[index] << 4) | u4s[++index, index++],
188 ]);
189 }
190 return call(getViewBuffer, dataView, []);
191 }
192 };
193
194 /**
195 * Returns the result of decoding the provided base32 string into an
196 * ArrayBuffer.
197 *
198 * If the second argument is truthy, uses Crockford’s encoding rather
199 * than the RFC’s (see <https://www.crockford.com/base32.html>). This
200 * is more human‐friendly and tolerant. Check digits are not supported.
201 *
202 * ※ This function is not exposed.
203 */
204 const decodeBase32 = (source, wrmg) => {
205 const u5s = map(
206 wrmg
207 ? stringReplace(source, /-/gu, "")
208 : source.length % 8 === 0
209 ? stringReplace(source, /(?:=|={3,4}|={6})$/u, "")
210 : source,
211 (ucsCharacter) => {
212 const code = getCodeUnit(ucsCharacter, 0);
213 const result = wrmg
214 ? code >= 0x30 && code <= 0x39
215 ? code - 48
216 : code >= 0x41 && code <= 0x48
217 ? code - 55
218 : code === 0x49
219 ? 1 // I
220 : code >= 0x4A && code <= 0x4B
221 ? code - 56
222 : code === 0x4C
223 ? 1 // L
224 : code >= 0x4D && code <= 0x4E
225 ? code - 57
226 : code === 0x4F
227 ? 0 // O
228 : code >= 0x50 && code <= 0x54
229 ? code - 58
230 // U is skipped
231 : code >= 0x56 && code <= 0x5A
232 ? code - 59
233 : code >= 0x61 && code <= 0x68
234 ? code - 87
235 : code === 0x69
236 ? 1 // i
237 : code >= 0x6A && code <= 0x6B
238 ? code - 88
239 : code === 0x6C
240 ? 1 // l
241 : code >= 0x6D && code <= 0x6E
242 ? code - 89
243 : code === 0x6F
244 ? 0 // o
245 : code >= 0x70 && code <= 0x74
246 ? code - 90
247 // u is skipped
248 : code >= 0x76 && code <= 0x7A
249 ? code - 91
250 : -1
251 : code >= 0x41 && code <= 0x5A
252 ? code - 65
253 : code >= 0x61 && code <= 0x7A
254 ? code - 97 // same result as above; case insensitive
255 : code >= 0x32 && code <= 0x37
256 ? code - 24 // digits 2–7 map to 26–31
257 : -1;
258 if (result < 0) {
259 // The source contains an invalid character.
260 throw new RangeError(
261 `Piscēs: Invalid character in Base32: ${ucsCharacter}.`,
262 );
263 } else {
264 // The source contains a valid character with a recognized
265 // mapping.
266 return result;
267 }
268 },
269 );
270 const { length } = u5s;
271 const lengthMod8 = length % 8;
272 if (lengthMod8 === 1 || lengthMod8 === 3 || lengthMod8 === 6) {
273 // The length is such that an entire letter would be dropped during
274 // a forgiving decode.
275 throw new RangeError(
276 `Piscēs: Base32 string has invalid length: ${source}.`,
277 );
278 } else {
279 // Every letter contributes at least some bits to the result.
280 const dataView = new View(new Buffer(floor(length * 5 / 8)));
281 for (let index = 0; index < length - 1;) {
282 // Iterate over the characters and assign their bits to the
283 // buffer.
284 //
285 // The final index is not handled; if the string is not divisible
286 // by 8, some bits might be dropped. This matches the “forgiving
287 // decode” behaviour specified by WhatW·G for base64.
288 const dataIndex = ceil(index * 5 / 8);
289 const remainder = index % 8;
290 call(viewSetUint8, dataView, [
291 dataIndex,
292 remainder === 0
293 ? u5s[index] << 3 | u5s[++index] >> 2
294 : remainder === 1
295 ? u5s[index] << 6 | u5s[++index] << 1 | u5s[++index] >> 4
296 : remainder === 3
297 ? u5s[index] << 4 | u5s[++index] >> 1
298 : remainder === 4
299 ? u5s[index] << 7 | u5s[++index] << 2 | u5s[++index] >> 3
300 : u5s[index] << 5 | u5s[++index, index++], // remainder === 6
301 ]);
302 }
303 return call(getViewBuffer, dataView, []);
304 }
305 };
306
307 /**
308 * Returns the result of decoding the provided base64 string into an
309 * ArrayBuffer.
310 *
311 * ※ This function is not exposed.
312 */
313 const decodeBase64 = (source, safe = false) => {
314 const u6s = map(
315 source.length % 4 === 0
316 ? stringReplace(source, /={1,2}$/u, "")
317 : source,
318 (ucsCharacter) => {
319 const code = getCodeUnit(ucsCharacter, 0);
320 const result = code >= 0x41 && code <= 0x5A
321 ? code - 65
322 : code >= 0x61 && code <= 0x7A
323 ? code - 71
324 : code >= 0x30 && code <= 0x39
325 ? code + 4
326 : code === (safe ? 0x2D : 0x2B)
327 ? 62
328 : code === (safe ? 0x5F : 0x2F)
329 ? 63
330 : -1;
331 if (result < 0) {
332 // The source contains an invalid character.
333 throw new RangeError(
334 `Piscēs: Invalid character in Base64: ${ucsCharacter}.`,
335 );
336 } else {
337 // The source contains a valid character with a recognized
338 // mapping.
339 return result;
340 }
341 },
342 );
343 const { length } = u6s;
344 if (length % 4 === 1) {
345 // The length is such that an entire letter would be dropped during
346 // a forgiving decode.
347 throw new RangeError(
348 `Piscēs: Base64 string has invalid length: ${source}.`,
349 );
350 } else {
351 // Every letter contributes at least some bits to the result.
352 const dataView = new View(new Buffer(floor(length * 3 / 4)));
353 for (let index = 0; index < length - 1;) {
354 // Iterate over the characters and assign their bits to the
355 // buffer.
356 //
357 // The final index is not handled; if the string is not divisible
358 // by 4, some bits might be dropped. This matches the “forgiving
359 // decode” behaviour specified by WhatW·G for base64.
360 const dataIndex = ceil(index * 3 / 4);
361 const remainder = index % 4;
362 call(viewSetUint8, dataView, [
363 dataIndex,
364 remainder === 0
365 ? u6s[index] << 2 | u6s[++index] >> 4
366 : remainder === 1
367 ? u6s[index] << 4 | u6s[++index] >> 2
368 : u6s[index] << 6 | u6s[++index, index++], // remainder === 2
369 ]);
370 }
371 return call(getViewBuffer, dataView, []);
372 }
373 };
374
375 /**
376 * Returns the result of encoding the provided ArrayBuffer into a
377 * base16 string.
378 *
379 * ※ This function is not exposed.
380 */
381 const encodeBase16 = (buffer) => {
382 const dataView = new View(buffer);
383 const byteLength = call(getBufferByteLength, buffer, []);
384 const minimumLengthOfResults = byteLength * 2;
385 const resultingCodeUnits = fill(
386 objectCreate(
387 binaryCodeUnitIterablePrototype,
388 { length: { value: minimumLengthOfResults } },
389 ),
390 0x3D,
391 );
392 for (let index = 0; index < byteLength;) {
393 // Iterate over the bytes and generate code units for them.
394 const codeUnitIndex = index * 2;
395 const datum = call(viewGetUint8, dataView, [index++]);
396 const u4s = [datum >> 4, datum & 0xF];
397 for (let u4i = 0; u4i < 2; ++u4i) {
398 // Handle the high four bits, then the low four bits.
399 const u4 = u4s[u4i];
400 const result = u4 < 10 ? u4 + 48 : u4 < 16 ? u4 + 55 : -1;
401 if (result < 0) {
402 // No mapping exists for these four bits.
403 //
404 // ※ This shouldn’t be possible!
405 throw new RangeError(
406 `Piscēs: Unexpected Base16 value: ${u4}.`,
407 );
408 } else {
409 // A mapping exists for the bits.
410 resultingCodeUnits[codeUnitIndex + u4i] = result;
411 }
412 }
413 }
414 return stringFromCodeUnits(...resultingCodeUnits);
415 };
416
417 /**
418 * Returns the result of encoding the provided ArrayBuffer into a
419 * base32 string.
420 *
421 * ※ This function is not exposed.
422 */
423 const encodeBase32 = (buffer, wrmg = false) => {
424 const dataView = new View(buffer);
425 const byteLength = call(getBufferByteLength, buffer, []);
426 const minimumLengthOfResults = ceil(byteLength * 8 / 5);
427 const fillByte = wrmg ? 0x2D : 0x3D;
428 const resultingCodeUnits = fill(
429 objectCreate(
430 binaryCodeUnitIterablePrototype,
431 {
432 length: {
433 value: minimumLengthOfResults +
434 (8 - (minimumLengthOfResults % 8)) % 8,
435 },
436 },
437 ),
438 fillByte,
439 );
440 for (let index = 0; index < byteLength;) {
441 // Iterate over the bytes and generate code units for them.
442 const codeUnitIndex = ceil(index * 8 / 5);
443 const currentIndex = codeUnitIndex + +(
444 0b01011 & 1 << index % 5 &&
445 resultingCodeUnits[codeUnitIndex] != fillByte
446 ); // bytes 0, 1 & 3 handle two letters; this is for the second
447 const remainder = currentIndex % 8;
448 const currentByte = call(viewGetUint8, dataView, [index]);
449 const nextByte =
450 0b01011010 & 1 << remainder && ++index < byteLength
451 // digits 1, 3, 4 & 6 span multiple bytes
452 ? call(viewGetUint8, dataView, [index])
453 : 0;
454 const u5 = remainder === 0
455 ? currentByte >> 3
456 : remainder === 1
457 ? (currentByte & 0b00000111) << 2 | nextByte >> 6
458 : remainder === 2
459 ? (currentByte & 0b00111111) >> 1
460 : remainder === 3
461 ? (currentByte & 0b00000001) << 4 | nextByte >> 4
462 : remainder === 4
463 ? (currentByte & 0b00001111) << 1 | nextByte >> 7
464 : remainder === 5
465 ? (currentByte & 0b01111111) >> 2
466 : remainder === 6
467 ? (currentByte & 0b00000011) << 3 | nextByte >> 5
468 : (++index, currentByte & 0b00011111); // remainder === 7
469 const result = wrmg
470 ? u5 < 10 ? u5 + 48 : u5 < 18
471 ? u5 + 55
472 // skip I
473 : u5 < 20
474 ? u5 + 56
475 // skip L
476 : u5 < 22
477 ? u5 + 57
478 // skip O
479 : u5 < 27
480 ? u5 + 58
481 // skip U
482 : u5 < 32
483 ? u5 + 59
484 : -1
485 : u5 < 26
486 ? u5 + 65
487 : u5 < 32
488 ? u5 + 24
489 : -1;
490 if (result < 0) {
491 // No mapping exists for these five bits.
492 //
493 // ※ This shouldn’t be possible!
494 throw new RangeError(`Piscēs: Unexpected Base32 value: ${u5}.`);
495 } else {
496 // A mapping exists for the bits.
497 resultingCodeUnits[currentIndex] = result;
498 }
499 }
500 const answer = stringFromCodeUnits(...resultingCodeUnits);
501 return wrmg ? answer.replace(/-+$/u, "") : answer;
502 };
503
504 /**
505 * Returns the result of encoding the provided ArrayBuffer into a
506 * base64 string.
507 *
508 * ※ This function is not exposed.
509 */
510 const encodeBase64 = (buffer, safe = false) => {
511 const dataView = new View(buffer);
512 const byteLength = call(getBufferByteLength, buffer, []);
513 const minimumLengthOfResults = ceil(byteLength * 4 / 3);
514 const resultingCodeUnits = fill(
515 objectCreate(
516 binaryCodeUnitIterablePrototype,
517 {
518 length: {
519 value: minimumLengthOfResults +
520 (4 - (minimumLengthOfResults % 4)) % 4,
521 },
522 },
523 ),
524 0x3D,
525 );
526 for (let index = 0; index < byteLength;) {
527 // Iterate over the bytes and generate code units for them.
528 const codeUnitIndex = ceil(index * 4 / 3);
529 const currentIndex = codeUnitIndex + +(
530 index % 3 === 0 && resultingCodeUnits[codeUnitIndex] != 0x3D
531 ); // every third byte handles two letters; this is for the second
532 const remainder = currentIndex % 4;
533 const currentByte = call(viewGetUint8, dataView, [index]);
534 const nextByte = remainder % 3 && ++index < byteLength
535 // digits 1 & 2 span multiple bytes
536 ? call(viewGetUint8, dataView, [index])
537 : 0;
538 const u6 = remainder === 0
539 ? currentByte >> 2
540 : remainder === 1
541 ? (currentByte & 0b00000011) << 4 | nextByte >> 4
542 : remainder === 2
543 ? (currentByte & 0b00001111) << 2 | nextByte >> 6
544 : (++index, currentByte & 0b00111111); // remainder === 3
545 const result = u6 < 26
546 ? u6 + 65
547 : u6 < 52
548 ? u6 + 71
549 : u6 < 62
550 ? u6 - 4
551 : u6 < 63
552 ? (safe ? 0x2D : 0x2B)
553 : u6 < 64
554 ? (safe ? 0x5F : 0x2F)
555 : -1;
556 if (result < 0) {
557 // No mapping exists for these six bits.
558 //
559 // ※ This shouldn’t be possible!
560 throw new RangeError(`Piscēs: Unexpected Base64 value: ${u6}.`);
561 } else {
562 // A mapping exists for the bits.
563 resultingCodeUnits[currentIndex] = result;
564 }
565 }
566 return stringFromCodeUnits(...resultingCodeUnits);
567 };
568
569 /**
570 * Returns a source string generated from the arguments passed to a
571 * tag function.
572 *
573 * ※ This function is not exposed.
574 */
575 const sourceFromArgs = ($, $s) =>
576 stringReplace(
577 typeof $ === "string" ? $ : hasOwnProperty($, "raw")
578 ? rawString(
579 $,
580 ...objectCreate(argumentIterablePrototype, {
581 args: { value: $s },
582 }),
583 )
584 : `${$}`,
585 /[\t\n\f\r ]+/gu,
586 "",
587 );
588
589 /**
590 * Returns a slice of the provided value according to the algorithm of
591 * `ArrayBuffer::slice` (or `SharedArrayBuffer::slice`).
592 *
593 * ☡ This function throws if the provided value is not an array buffer.
594 */
595 export const arrayBufferSlice = ($, start, end, ...args) =>
596 call(
597 isSharedArrayBuffer($) ? sharedBufferSlice : bufferSlice,
598 $,
599 [
600 start,
601 end,
602 ...objectCreate(
603 argumentIterablePrototype,
604 { args: { value: args } },
605 ),
606 ],
607 );
608
609 /**
610 * Returns an ArrayBuffer generated from the provided base16 string.
611 *
612 * This function can also be used as a tag for a template literal. The
613 * literal will be interpreted akin to `String.raw`.
614 *
615 * ☡ This function throws if the provided string is not a valid base16
616 * string.
617 */
618 export const base16Binary = ($, ...$s) =>
619 decodeBase16(sourceFromArgs($, $s));
620
621 /**
622 * Returns a (big‐endian) base16 string created from the provided typed
623 * array, buffer, or (16‐bit) string.
624 *
625 * This function can also be used as a tag for a template literal. The
626 * literal will be interpreted akin to `String.raw`.
627 */
628 export const base16String = ($, ...$s) =>
629 encodeBase16(bufferFromArgs($, $s));
630
631 /**
632 * Returns an ArrayBuffer generated from the provided base32 string.
633 *
634 * This function can also be used as a tag for a template literal. The
635 * literal will be interpreted akin to `String.raw`.
636 *
637 * ☡ This function throws if the provided string is not a valid base32
638 * string.
639 */
640 export const base32Binary = ($, ...$s) =>
641 decodeBase32(sourceFromArgs($, $s));
642
643 /**
644 * Returns a (big‐endian) base32 string created from the provided typed
645 * array, buffer, or (16‐bit) string.
646 *
647 * This function can also be used as a tag for a template literal. The
648 * literal will be interpreted akin to `String.raw`.
649 */
650 export const base32String = ($, ...$s) =>
651 encodeBase32(bufferFromArgs($, $s));
652
653 /**
654 * Returns an ArrayBuffer generated from the provided base64 string.
655 *
656 * This function can also be used as a tag for a template literal. The
657 * literal will be interpreted akin to `String.raw`.
658 *
659 * ☡ This function throws if the provided string is not a valid base64
660 * string.
661 */
662 export const base64Binary = ($, ...$s) =>
663 decodeBase64(sourceFromArgs($, $s));
664
665 /**
666 * Returns a (big‐endian) base64 string created from the provided typed
667 * array, buffer, or (16‐bit) string.
668 *
669 * This function can also be used as a tag for a template literal. The
670 * literal will be interpreted akin to `String.raw`.
671 */
672 export const base64String = ($, ...$s) =>
673 encodeBase64(bufferFromArgs($, $s));
674
675 /**
676 * Returns an ArrayBuffer generated from the provided filename‐safe
677 * base64 string.
678 *
679 * This function can also be used as a tag for a template literal. The
680 * literal will be interpreted akin to `String.raw`.
681 *
682 * ☡ This function throws if the provided string is not a valid
683 * filename‐safe base64 string.
684 */
685 export const filenameSafeBase64Binary = ($, ...$s) =>
686 decodeBase64(sourceFromArgs($, $s), true);
687
688 /**
689 * Returns a (big‐endian) filename‐safe base64 string created from the
690 * provided typed array, buffer, or (16‐bit) string.
691 *
692 * This function can also be used as a tag for a template literal. The
693 * literal will be interpreted akin to `String.raw`.
694 */
695 export const filenameSafeBase64String = ($, ...$s) =>
696 encodeBase64(bufferFromArgs($, $s), true);
697
698 export const {
699 /**
700 * Returns the signed 8‐bit integral value in the provided array
701 * buffer or array buffer view at the provided byte offset.
702 *
703 * ※ The retrieved value will be big·endian unless a third argument
704 * is specified and truthy.
705 *
706 * ※ This is similar to `DataView::getInt8`, but works on all array
707 * buffers and array buffer views and returns a big·int.
708 *
709 * ☡ This function throws if the first argument is not an array
710 * buffer, data view, or typed array.
711 */
712 get8BitSignedIntegralItem,
713
714 /**
715 * Returns the unsigned 8‐bit integral value in the provided array
716 * buffer or array buffer view at the provided byte offset.
717 *
718 * ※ The retrieved value will be big·endian unless a third argument
719 * is specified and truthy.
720 *
721 * ※ This is similar to `DataView::getUint8`, but works on all array
722 * buffers and array buffer views and returns a big·int.
723 *
724 * ☡ This function throws if the first argument is not an array
725 * buffer, data view, or typed array.
726 */
727 get8BitUnsignedIntegralItem,
728
729 /**
730 * Returns the signed 16‐bit integral value in the provided array
731 * buffer or array buffer view at the provided byte offset.
732 *
733 * ※ The retrieved value will be big·endian unless a third argument
734 * is specified and truthy.
735 *
736 * ※ This is similar to `DataView::getInt16`, but works on all array
737 * buffers and array buffer views and returns a big·int.
738 *
739 * ☡ This function throws if the first argument is not an array
740 * buffer, data view, or typed array.
741 */
742 get16BitSignedIntegralItem,
743
744 /**
745 * Returns the unsigned 16‐bit integral value in the provided array
746 * buffer or array buffer view at the provided byte offset.
747 *
748 * ※ The retrieved value will be big·endian unless a third argument
749 * is specified and truthy.
750 *
751 * ※ This is similar to `DataView::getUint16`, but works on all
752 * array buffers and array buffer views and returns a big·int.
753 *
754 * ☡ This function throws if the first argument is not an array
755 * buffer, data view, or typed array.
756 */
757 get16BitUnsignedIntegralItem,
758
759 /**
760 * Returns the 32‐bit floating point value in the provided array
761 * buffer or array buffer view at the provided byte offset.
762 *
763 * ※ The retrieved value will be big·endian unless a third argument
764 * is specified and truthy.
765 *
766 * ※ This is similar to `DataView::getFloat32`, but works on all
767 * array buffers and array buffer views.
768 *
769 * ☡ This function throws if the first argument is not an array
770 * buffer, data view, or typed array.
771 */
772 get32BitFloatingPointItem,
773
774 /**
775 * Returns the signed 32‐bit integral value in the provided array
776 * buffer or array buffer view at the provided byte offset.
777 *
778 * ※ The retrieved value will be big·endian unless a third argument
779 * is specified and truthy.
780 *
781 * ※ This is similar to `DataView::getInt32`, but works on all array
782 * buffers and array buffer views and returns a big·int.
783 *
784 * ☡ This function throws if the first argument is not an array
785 * buffer, data view, or typed array.
786 */
787 get32BitSignedIntegralItem,
788
789 /**
790 * Returns the unsigned 32‐bit integral value in the provided array
791 * buffer or array buffer view at the provided byte offset.
792 *
793 * ※ The retrieved value will be big·endian unless a third argument
794 * is specified and truthy.
795 *
796 * ※ This is similar to `DataView::getUint32`, but works on all
797 * array buffers and array buffer views and returns a big·int.
798 *
799 * ☡ This function throws if the first argument is not an array
800 * buffer, data view, or typed array.
801 */
802 get32BitUnsignedIntegralItem,
803
804 /**
805 * Returns the 64‐bit floating point value in the provided array
806 * buffer or array buffer view at the provided byte offset.
807 *
808 * ※ The retrieved value will be big·endian unless a third argument
809 * is specified and truthy.
810 *
811 * ※ This is similar to `DataView::getFloat64`, but works on all
812 * array buffers and array buffer views.
813 *
814 * ☡ This function throws if the first argument is not an array
815 * buffer, data view, or typed array.
816 */
817 get64BitFloatingPointItem,
818
819 /**
820 * Returns the signed 64‐bit integral value in the provided array
821 * buffer or array buffer view at the provided byte offset.
822 *
823 * ※ The retrieved value will be big·endian unless a third argument
824 * is specified and truthy.
825 *
826 * ※ This is similar to `DataView::getBigInt64`, but works on all
827 * array buffers and array buffer views.
828 *
829 * ☡ This function throws if the first argument is not an array
830 * buffer, data view, or typed array.
831 */
832 get64BitSignedIntegralItem,
833
834 /**
835 * Returns the unsigned 64‐bit integral value in the provided array
836 * buffer or array buffer view at the provided byte offset.
837 *
838 * ※ The retrieved value will be big·endian unless a third argument
839 * is specified and truthy.
840 *
841 * ※ This is similar to `DataView::getBigUint64`, but works on all
842 * array buffers and array buffer views.
843 *
844 * ☡ This function throws if the first argument is not an array
845 * buffer, data view, or typed array.
846 */
847 get64BitUnsignedIntegralItem,
848
849 /**
850 * Sets the 8‐bit integral value in the provided array buffer or
851 * array buffer view at the provided byte offset to the provided
852 * value.
853 *
854 * ※ The value will be set as big·endian unless a fourth argument is
855 * specified and truthy.
856 *
857 * ※ This is similar to `DataView::setInt8`, but works on all array
858 * buffers and array buffer views and accepts both numeric and
859 * big·int values.
860 *
861 * ※ It doesn’t matter whether the provided value is signed or
862 * unsigned, as the algorithm will cast one to the other.
863 *
864 * ☡ This function throws if the first argument is not an array
865 * buffer, data view, or typed array.
866 */
867 set8BitIntegralItem,
868
869 /**
870 * Sets the 16‐bit integral value in the provided array buffer or
871 * array buffer view at the provided byte offset to the provided
872 * value.
873 *
874 * ※ The value will be set as big·endian unless a fourth argument is
875 * specified and truthy.
876 *
877 * ※ This is similar to `DataView::setInt16`, but works on all array
878 * buffers and array buffer views and accepts both numeric and
879 * big·int values.
880 *
881 * ※ It doesn’t matter whether the provided value is signed or
882 * unsigned, as the algorithm will cast one to the other.
883 *
884 * ☡ This function throws if the first argument is not an array
885 * buffer, data view, or typed array.
886 */
887 set16BitIntegralItem,
888
889 /**
890 * Sets the 32‐bit floating point value in the provided array buffer
891 * or array buffer view at the provided byte offset to the provided
892 * value.
893 *
894 * ※ The value will be set as big·endian unless a fourth argument is
895 * specified and truthy.
896 *
897 * ※ This is similar to `DataView::setFloat32`, but works on all
898 * array buffers and array buffer views.
899 *
900 * ☡ This function throws if the first argument is not an array
901 * buffer, data view, or typed array.
902 */
903 set32BitFloatingPointItem,
904
905 /**
906 * Sets the 32‐bit integral value in the provided array buffer or
907 * array buffer view at the provided byte offset to the provided
908 * value.
909 *
910 * ※ The value will be set as big·endian unless a fourth argument is
911 * specified and truthy.
912 *
913 * ※ This is similar to `DataView::setInt32`, but works on all array
914 * buffers and array buffer views and accepts both numeric and
915 * big·int values.
916 *
917 * ※ It doesn’t matter whether the provided value is signed or
918 * unsigned, as the algorithm will cast one to the other.
919 *
920 * ☡ This function throws if the first argument is not an array
921 * buffer, data view, or typed array.
922 */
923 set32BitIntegralItem,
924
925 /**
926 * Sets the 64‐bit floating point value in the provided array buffer
927 * or array buffer view at the provided byte offset to the provided
928 * value.
929 *
930 * ※ The value will be set as big·endian unless a fourth argument is
931 * specified and truthy.
932 *
933 * ※ This is similar to `DataView::setFloat64`, but works on all
934 * array buffers and array buffer views.
935 *
936 * ☡ This function throws if the first argument is not an array
937 * buffer, data view, or typed array.
938 */
939 set64BitFloatingPointItem,
940
941 /**
942 * Sets the 64‐bit integral value in the provided array buffer or
943 * array buffer view at the provided byte offset to the provided
944 * value.
945 *
946 * ※ The value will be set as big·endian unless a fourth argument is
947 * specified and truthy.
948 *
949 * ※ This is similar to `DataView::setInt32`, but works on all array
950 * buffers and array buffer views.
951 *
952 * ※ It doesn’t matter whether the provided value is signed or
953 * unsigned, as the algorithm will cast one to the other.
954 *
955 * ☡ This function throws if the first argument is not an array
956 * buffer, data view, or typed array, or if the third argument is not
957 * a big·int.
958 */
959 set64BitIntegralItem,
960 } = (() => {
961 const makeBigInt = BigInt;
962 const { asUintN } = BigInt;
963 const makeNumber = Number;
964
965 const viewMap = new WeakMap();
966 const view = ($) => {
967 const buffer = toArrayBuffer($);
968 if (viewMap.has(buffer)) {
969 // A view has already been allocated for this buffer; use it.
970 return viewMap.get(buffer);
971 } else {
972 // No view has been created for this buffer yet.
973 const result = new View(buffer);
974 viewMap.set(buffer, result);
975 return result;
976 }
977 };
978
979 return {
980 get8BitSignedIntegralItem: ($, byteOffset, ...args) =>
981 makeBigInt(
982 call(viewGetInt8, view($), [
983 getByteOffset($) + byteOffset,
984 ...objectCreate(
985 argumentIterablePrototype,
986 { args: { value: args } },
987 ),
988 ]),
989 ),
990 get8BitUnsignedIntegralItem: ($, byteOffset, ...args) =>
991 makeBigInt(
992 call(viewGetUint8, view($), [
993 getByteOffset($) + byteOffset,
994 ...objectCreate(
995 argumentIterablePrototype,
996 { args: { value: args } },
997 ),
998 ]),
999 ),
1000 get16BitSignedIntegralItem: ($, byteOffset, ...args) =>
1001 makeBigInt(
1002 call(viewGetInt16, view($), [
1003 getByteOffset($) + byteOffset,
1004 ...objectCreate(
1005 argumentIterablePrototype,
1006 { args: { value: args } },
1007 ),
1008 ]),
1009 ),
1010 get16BitUnsignedIntegralItem: ($, byteOffset, ...args) =>
1011 makeBigInt(
1012 call(viewGetUint16, view($), [
1013 getByteOffset($) + byteOffset,
1014 ...objectCreate(
1015 argumentIterablePrototype,
1016 { args: { value: args } },
1017 ),
1018 ]),
1019 ),
1020 get32BitFloatingPointItem: ($, byteOffset, ...args) =>
1021 call(viewGetFloat32, view($), [
1022 getByteOffset($) + byteOffset,
1023 ...objectCreate(
1024 argumentIterablePrototype,
1025 { args: { value: args } },
1026 ),
1027 ]),
1028 get32BitSignedIntegralItem: ($, byteOffset, ...args) =>
1029 makeBigInt(
1030 call(viewGetInt32, view($), [
1031 getByteOffset($) + byteOffset,
1032 ...objectCreate(
1033 argumentIterablePrototype,
1034 { args: { value: args } },
1035 ),
1036 ]),
1037 ),
1038 get32BitUnsignedIntegralItem: ($, byteOffset, ...args) =>
1039 makeBigInt(
1040 call(viewGetUint32, view($), [
1041 getByteOffset($) + byteOffset,
1042 ...objectCreate(
1043 argumentIterablePrototype,
1044 { args: { value: args } },
1045 ),
1046 ]),
1047 ),
1048 get64BitFloatingPointItem: ($, byteOffset, ...args) =>
1049 call(viewGetFloat64, view($), [
1050 getByteOffset($) + byteOffset,
1051 ...objectCreate(
1052 argumentIterablePrototype,
1053 { args: { value: args } },
1054 ),
1055 ]),
1056 get64BitSignedIntegralItem: ($, byteOffset, ...args) =>
1057 call(viewGetInt64, view($), [
1058 getByteOffset($) + byteOffset,
1059 ...objectCreate(
1060 argumentIterablePrototype,
1061 { args: { value: args } },
1062 ),
1063 ]),
1064 get64BitUnsignedIntegralItem: ($, byteOffset, ...args) =>
1065 call(viewGetUint64, view($), [
1066 getByteOffset($) + byteOffset,
1067 ...objectCreate(
1068 argumentIterablePrototype,
1069 { args: { value: args } },
1070 ),
1071 ]),
1072 set8BitIntegralItem: ($, byteOffset, value, ...args) =>
1073 call(viewSetUint8, view($), [
1074 getByteOffset($) + byteOffset,
1075 makeNumber(value),
1076 ...objectCreate(
1077 argumentIterablePrototype,
1078 { args: { value: args } },
1079 ),
1080 ]),
1081 set16BitIntegralItem: ($, byteOffset, value, ...args) =>
1082 call(viewSetUint16, view($), [
1083 getByteOffset($) + byteOffset,
1084 makeNumber(value),
1085 ...objectCreate(
1086 argumentIterablePrototype,
1087 { args: { value: args } },
1088 ),
1089 ]),
1090 set32BitFloatingPointItem: ($, byteOffset, value, ...args) =>
1091 call(viewSetFloat32, view($), [
1092 getByteOffset($) + byteOffset,
1093 value,
1094 ...objectCreate(
1095 argumentIterablePrototype,
1096 { args: { value: args } },
1097 ),
1098 ]),
1099 set32BitIntegralItem: ($, byteOffset, value, ...args) =>
1100 call(viewSetUint32, view($), [
1101 getByteOffset($) + byteOffset,
1102 makeNumber(value),
1103 ...objectCreate(
1104 argumentIterablePrototype,
1105 { args: { value: args } },
1106 ),
1107 ]),
1108 set64BitFloatingPointItem: ($, byteOffset, value, ...args) =>
1109 call(viewSetFloat64, view($), [
1110 getByteOffset($) + byteOffset,
1111 value,
1112 ...objectCreate(
1113 argumentIterablePrototype,
1114 { args: { value: args } },
1115 ),
1116 ]),
1117 set64BitIntegralItem: ($, byteOffset, value, ...args) =>
1118 call(viewSetUint64, view($), [
1119 getByteOffset($) + byteOffset,
1120 asUintN(64, value),
1121 ...objectCreate(
1122 argumentIterablePrototype,
1123 { args: { value: args } },
1124 ),
1125 ]),
1126 };
1127 })();
1128
1129 /**
1130 * Returns the byte length for the provided array buffer or array
1131 * buffer view.
1132 *
1133 * ☡ This function throws if the provided value is not an array buffer,
1134 * data view, or typed array.
1135 */
1136 export const getByteLength = ($) => {
1137 try {
1138 // Attempt to get the byte length from the provided value as an
1139 // `ArrayBuffer`.
1140 return call(getBufferByteLength, $, []);
1141 } catch {
1142 // The provided value is not an `ArrayBuffer`.
1143 /* do nothing */
1144 }
1145 try {
1146 // Attempt to get the byte length from the provided value as a
1147 // `SharedArrayBuffer`.
1148 return call(getSharedBufferByteLength, $, []);
1149 } catch {
1150 // The provided value is not a `SharedArrayBuffer`.
1151 /* do nothing */
1152 }
1153 try {
1154 // Attempt to get the byte length from the provided value as a
1155 // data view.
1156 return call(getViewByteLength, $, []);
1157 } catch {
1158 // The provided value is not a data view.
1159 /* do nothing */
1160 }
1161 try {
1162 // Attempt to get the byte length from the provided value as a
1163 // typed array.
1164 return call(getTypedArrayByteLength, $, []);
1165 } catch {
1166 // The provided value is not a typed array.
1167 /* do nothing */
1168 }
1169 throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
1170 };
1171
1172 /**
1173 * Returns the byte offset for the provided array buffer or array
1174 * buffer view.
1175 *
1176 * ※ This function always returns `0` for array buffers.
1177 *
1178 * ☡ This function throws if the provided value is not an array buffer,
1179 * data view, or typed array.
1180 */
1181 export const getByteOffset = ($) => {
1182 if (isArrayBuffer($)) {
1183 // The provided value is an array buffer.
1184 return 0;
1185 } else {
1186 try {
1187 // Attempt to get the byte offset from the provided value as a
1188 // data view.
1189 return call(getViewByteOffset, $, []);
1190 } catch {
1191 // The provided value is not a data view.
1192 /* do nothing */
1193 }
1194 try {
1195 // Attempt to get the byte offset from the provided value as a
1196 // typed array.
1197 return call(getTypedArrayByteOffset, $, []);
1198 } catch {
1199 // The provided value is not a typed array.
1200 /* do nothing */
1201 }
1202 throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
1203 }
1204 };
1205
1206 /**
1207 * Returns whether the provided value is a view on an underlying array
1208 * buffer.
1209 *
1210 * ※ This function returns true for typed arrays and data views.
1211 */
1212 export const { isView: isArrayBufferView } = Buffer;
1213
1214 /**
1215 * Returns whether the provided value is an array buffer.
1216 *
1217 * ※ This function returns true for both `ArrayBuffer`s and
1218 * `SharedArrayBuffer`s.
1219 */
1220 export const isArrayBuffer = ($) => {
1221 try {
1222 // Try to see if the provided argument has array buffer internal
1223 // slots and return true if so.
1224 return call(getBufferByteLength, $, []), true;
1225 } catch {
1226 // The provided argument does not have array buffer internal slots.
1227 /* do nothing */
1228 }
1229 try {
1230 // Try to see if the provided argument has array buffer internal
1231 // slots and return true if so.
1232 return call(getSharedBufferByteLength, $, []), true;
1233 } catch {
1234 // The provided argument does not have array buffer internal slots.
1235 /* do nothing */
1236 }
1237 return false;
1238 };
1239
1240 /**
1241 * Returns whether the provided value is a base16 string.
1242 *
1243 * ※ This function returns false if the provided value is not a string
1244 * primitive.
1245 */
1246 export const isBase16 = ($) => {
1247 if (typeof $ !== "string") {
1248 // The provided value is not a string.
1249 return false;
1250 } else {
1251 // The provided value is a string.
1252 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
1253 return source.length % 2 !== 1 &&
1254 call(reExec, /[^0-9A-F]/iu, [source]) === null;
1255 }
1256 };
1257
1258 /**
1259 * Returns whether the provided value is a base32 string.
1260 *
1261 * ※ This function returns false if the provided value is not a string
1262 * primitive.
1263 */
1264 export const isBase32 = ($) => {
1265 if (typeof $ !== "string") {
1266 // The provided value is not a string.
1267 return false;
1268 } else {
1269 // The provided value is a string.
1270 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
1271 const trimmed = source.length % 8 === 0
1272 ? stringReplace(source, /(?:=|={3,4}|={6})$/u, "")
1273 : source;
1274 return trimmed.length % 8 !== 1 &&
1275 call(reExec, /[^2-7A-Z/]/iu, [trimmed]) === null;
1276 }
1277 };
1278
1279 /**
1280 * Returns whether the provided value is a Base64 string.
1281 *
1282 * ※ This function returns false if the provided value is not a string
1283 * primitive.
1284 */
1285 export const isBase64 = ($) => {
1286 if (typeof $ !== "string") {
1287 // The provided value is not a string.
1288 return false;
1289 } else {
1290 // The provided value is a string.
1291 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
1292 const trimmed = source.length % 4 === 0
1293 ? stringReplace(source, /={1,2}$/u, "")
1294 : source;
1295 return trimmed.length % 4 !== 1 &&
1296 call(reExec, /[^0-9A-Za-z+\/]/u, [trimmed]) === null;
1297 }
1298 };
1299
1300 /**
1301 * Returns whether the provided value is a filename‐safe base64 string.
1302 *
1303 * ※ This function returns false if the provided value is not a string
1304 * primitive.
1305 */
1306 export const isFilenameSafeBase64 = ($) => {
1307 if (typeof $ !== "string") {
1308 // The provided value is not a string.
1309 return false;
1310 } else {
1311 // The provided value is a string.
1312 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
1313 const trimmed = source.length % 4 === 0
1314 ? stringReplace(source, /={1,2}$/u, "")
1315 : source;
1316 return trimmed.length % 4 !== 1 &&
1317 call(reExec, /[^0-9A-Za-z_-]/u, [trimmed]) === null;
1318 }
1319 };
1320
1321 /** Returns whether the provided value is a shared array buffer. */
1322 export const isSharedArrayBuffer = ($) => {
1323 try {
1324 // Try to see if the provided argument has shared array buffer
1325 // internal slots and return true if so.
1326 return call(getSharedBufferByteLength, $, []), true;
1327 } catch {
1328 // The provided argument does not have data view internal slots.
1329 return false;
1330 }
1331 };
1332
1333 /** Returns whether the provided value is a typed array. */
1334 export const isTypedArray = ($) => {
1335 try {
1336 // Try to see if the provided argument has typed array internal
1337 // slots and return true if so.
1338 return call(getTypedArrayBuffer, $, []), true;
1339 } catch {
1340 // The provided argument does not have typed array internal slots.
1341 return false;
1342 }
1343 };
1344
1345 /**
1346 * Returns whether the provided value is a W·R·M·G (Crockford) base32
1347 * string. Check digits are not supported.
1348 *
1349 * ※ This function returns false if the provided value is not a string
1350 * primitive.
1351 */
1352 export const isWRMGBase32 = ($) => {
1353 if (typeof $ !== "string") {
1354 // The provided value is not a string.
1355 return false;
1356 } else {
1357 // The provided value is a string.
1358 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
1359 const trimmed = stringReplace(source, /-/gu, "");
1360 return trimmed.length % 8 !== 1 &&
1361 call(reExec, /[^0-9A-TV-Z]/iu, [trimmed]) === null;
1362 }
1363 };
1364
1365 /**
1366 * Returns the array buffer associated with the provided object.
1367 *
1368 * ☡ This function throws if the provided object is not a data view or
1369 * typed array.
1370 */
1371 export const toArrayBuffer = ($) => {
1372 if (isArrayBuffer($)) {
1373 // The provided argument has array buffer internal slots.
1374 return $;
1375 } else {
1376 // The provided argument does not have array buffer internal slots.
1377 try {
1378 // The provided argument has typed array internal slots.
1379 return call(getTypedArrayBuffer, $, []);
1380 } catch {
1381 /* do nothing */
1382 }
1383 try {
1384 // The provided argument has data view internal slots.
1385 return call(getViewBuffer, $, []);
1386 } catch {
1387 /* do nothing */
1388 }
1389 throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
1390 }
1391 };
1392
1393 /**
1394 * Returns an ArrayBuffer generated from the provided W·R·M·G
1395 * (Crockford) base32 string.
1396 *
1397 * This function can also be used as a tag for a template literal. The
1398 * literal will be interpreted akin to `String.raw`.
1399 *
1400 * ☡ This function throws if the provided string is not a valid W·R·M·G
1401 * base32 string.
1402 */
1403 export const wrmgBase32Binary = ($, ...$s) =>
1404 decodeBase32(sourceFromArgs($, $s), true);
1405
1406 /**
1407 * Returns a (big‐endian) W·R·M·G (Crockford) base32 string created
1408 * from the provided typed array, buffer, or (16‐bit) string.
1409 *
1410 * This function can also be used as a tag for a template literal. The
1411 * literal will be interpreted akin to `String.raw`.
1412 */
1413 export const wrmgBase32String = ($, ...$s) =>
1414 encodeBase32(bufferFromArgs($, $s), true);
This page took 0.231864 seconds and 5 git commands to generate.