]> Lady’s Gitweb - Pisces/blob - binary.js
Support SharedArrayBuffer; drop isDataView
[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 getBufferByteLength =
61 Object.getOwnPropertyDescriptor(bufferPrototype, "byteLength").get;
62 const getSharedBufferByteLength =
63 Object.getOwnPropertyDescriptor(sharedBufferPrototype, "byteLength")
64 .get;
65 const getTypedArrayBuffer =
66 Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer").get;
67 const getTypedArrayByteLength =
68 Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")
69 .get;
70 const getTypedArrayByteOffset =
71 Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteOffset")
72 .get;
73 const getViewBuffer =
74 Object.getOwnPropertyDescriptor(viewPrototype, "buffer").get;
75 const getViewByteLength =
76 Object.getOwnPropertyDescriptor(viewPrototype, "byteLength").get;
77 const getViewByteOffset =
78 Object.getOwnPropertyDescriptor(viewPrototype, "byteOffset").get;
79 const {
80 getUint8: viewGetUint8,
81 setUint8: viewSetUint8,
82 setUint16: viewSetUint16,
83 } = viewPrototype;
84
85 /**
86 * Returns an ArrayBuffer for encoding generated from the provided
87 * arguments.
88 */
89 const bufferFromArgs = ($, $s) => {
90 try {
91 // Try just getting the array buffer associated with the first
92 // argument and returning it if possible.
93 return toArrayBuffer($);
94 } catch {
95 // There is no array buffer associated with the first argument.
96 //
97 // Construct a string and convert it to an array buffer instead.
98 return ((string) =>
99 call(
100 getViewBuffer,
101 reduce(
102 string,
103 (result, ucsCharacter, index) => (
104 call(viewSetUint16, result, [
105 index * 2,
106 getCodeUnit(ucsCharacter, 0),
107 ]), result
108 ),
109 new View(new Buffer(string.length * 2)),
110 ),
111 [],
112 ))(
113 typeof $ === "string"
114 ? $
115 : hasOwnProperty($, "raw")
116 ? rawString(
117 $,
118 ...objectCreate(argumentIterablePrototype, {
119 args: { value: $s },
120 }),
121 )
122 : `${$}`,
123 );
124 }
125 };
126
127 /**
128 * Returns the result of decoding the provided base16 string into an
129 * ArrayBuffer.
130 *
131 * ※ This function is not exposed.
132 */
133 const decodeBase16 = (source) => {
134 const u4s = map(
135 source,
136 (ucsCharacter) => {
137 const code = getCodeUnit(ucsCharacter, 0);
138 const result = code >= 0x30 && code <= 0x39
139 ? code - 48
140 : code >= 0x41 && code <= 0x46
141 ? code - 55
142 : code >= 0x61 && code <= 0x66
143 ? code - 87
144 : -1;
145 if (result < 0) {
146 // The source contains an invalid character.
147 throw new RangeError(
148 `Piscēs: Invalid character in Base64: ${ucsCharacter}.`,
149 );
150 } else {
151 // The source contains a valid character with a recognized
152 // mapping.
153 return result;
154 }
155 },
156 );
157 const { length } = u4s;
158 if (length % 2 === 1) {
159 // The length is such that an entire letter would be dropped during
160 // a forgiving decode.
161 throw new RangeError(
162 `Piscēs: Base16 string has invalid length: ${source}.`,
163 );
164 } else {
165 // Every letter contributes at least some bits to the result.
166 const dataView = new View(new Buffer(floor(length / 2)));
167 for (let index = 0; index < length - 1;) {
168 // Iterate over the characters and assign their bits to the
169 // buffer.
170 call(viewSetUint8, dataView, [
171 floor(index / 2),
172 (u4s[index] << 4) | u4s[++index, index++],
173 ]);
174 }
175 return call(getViewBuffer, dataView, []);
176 }
177 };
178
179 /**
180 * Returns the result of decoding the provided base32 string into an
181 * ArrayBuffer.
182 *
183 * If the second argument is truthy, uses Crockford’s encoding rather
184 * than the RFC’s (see <https://www.crockford.com/base32.html>). This
185 * is more human‐friendly and tolerant. Check digits are not supported.
186 *
187 * ※ This function is not exposed.
188 */
189 const decodeBase32 = (source, wrmg) => {
190 const u5s = map(
191 wrmg
192 ? stringReplace(source, /-/gu, "")
193 : source.length % 8 === 0
194 ? stringReplace(source, /(?:=|={3,4}|={6})$/u, "")
195 : source,
196 (ucsCharacter) => {
197 const code = getCodeUnit(ucsCharacter, 0);
198 const result = wrmg
199 ? code >= 0x30 && code <= 0x39
200 ? code - 48
201 : code >= 0x41 && code <= 0x48
202 ? code - 55
203 : code === 0x49
204 ? 1 // I
205 : code >= 0x4A && code <= 0x4B
206 ? code - 56
207 : code === 0x4C
208 ? 1 // L
209 : code >= 0x4D && code <= 0x4E
210 ? code - 57
211 : code === 0x4F
212 ? 0 // O
213 : code >= 0x50 && code <= 0x54
214 ? code - 58
215 // U is skipped
216 : code >= 0x56 && code <= 0x5A
217 ? code - 59
218 : code >= 0x61 && code <= 0x68
219 ? code - 87
220 : code === 0x69
221 ? 1 // i
222 : code >= 0x6A && code <= 0x6B
223 ? code - 88
224 : code === 0x6C
225 ? 1 // l
226 : code >= 0x6D && code <= 0x6E
227 ? code - 89
228 : code === 0x6F
229 ? 0 // o
230 : code >= 0x70 && code <= 0x74
231 ? code - 90
232 // u is skipped
233 : code >= 0x76 && code <= 0x7A
234 ? code - 91
235 : -1
236 : code >= 0x41 && code <= 0x5A
237 ? code - 65
238 : code >= 0x61 && code <= 0x7A
239 ? code - 97 // same result as above; case insensitive
240 : code >= 0x32 && code <= 0x37
241 ? code - 24 // digits 2–7 map to 26–31
242 : -1;
243 if (result < 0) {
244 // The source contains an invalid character.
245 throw new RangeError(
246 `Piscēs: Invalid character in Base32: ${ucsCharacter}.`,
247 );
248 } else {
249 // The source contains a valid character with a recognized
250 // mapping.
251 return result;
252 }
253 },
254 );
255 const { length } = u5s;
256 const lengthMod8 = length % 8;
257 if (lengthMod8 === 1 || lengthMod8 === 3 || lengthMod8 === 6) {
258 // The length is such that an entire letter would be dropped during
259 // a forgiving decode.
260 throw new RangeError(
261 `Piscēs: Base32 string has invalid length: ${source}.`,
262 );
263 } else {
264 // Every letter contributes at least some bits to the result.
265 const dataView = new View(new Buffer(floor(length * 5 / 8)));
266 for (let index = 0; index < length - 1;) {
267 // Iterate over the characters and assign their bits to the
268 // buffer.
269 //
270 // The final index is not handled; if the string is not divisible
271 // by 8, some bits might be dropped. This matches the “forgiving
272 // decode” behaviour specified by WhatW·G for base64.
273 const dataIndex = ceil(index * 5 / 8);
274 const remainder = index % 8;
275 call(viewSetUint8, dataView, [
276 dataIndex,
277 remainder === 0
278 ? u5s[index] << 3 | u5s[++index] >> 2
279 : remainder === 1
280 ? u5s[index] << 6 | u5s[++index] << 1 | u5s[++index] >> 4
281 : remainder === 3
282 ? u5s[index] << 4 | u5s[++index] >> 1
283 : remainder === 4
284 ? u5s[index] << 7 | u5s[++index] << 2 | u5s[++index] >> 3
285 : u5s[index] << 5 | u5s[++index, index++], // remainder === 6
286 ]);
287 }
288 return call(getViewBuffer, dataView, []);
289 }
290 };
291
292 /**
293 * Returns the result of decoding the provided base64 string into an
294 * ArrayBuffer.
295 *
296 * ※ This function is not exposed.
297 */
298 const decodeBase64 = (source, safe = false) => {
299 const u6s = map(
300 source.length % 4 === 0
301 ? stringReplace(source, /={1,2}$/u, "")
302 : source,
303 (ucsCharacter) => {
304 const code = getCodeUnit(ucsCharacter, 0);
305 const result = code >= 0x41 && code <= 0x5A
306 ? code - 65
307 : code >= 0x61 && code <= 0x7A
308 ? code - 71
309 : code >= 0x30 && code <= 0x39
310 ? code + 4
311 : code === (safe ? 0x2D : 0x2B)
312 ? 62
313 : code === (safe ? 0x5F : 0x2F)
314 ? 63
315 : -1;
316 if (result < 0) {
317 // The source contains an invalid character.
318 throw new RangeError(
319 `Piscēs: Invalid character in Base64: ${ucsCharacter}.`,
320 );
321 } else {
322 // The source contains a valid character with a recognized
323 // mapping.
324 return result;
325 }
326 },
327 );
328 const { length } = u6s;
329 if (length % 4 === 1) {
330 // The length is such that an entire letter would be dropped during
331 // a forgiving decode.
332 throw new RangeError(
333 `Piscēs: Base64 string has invalid length: ${source}.`,
334 );
335 } else {
336 // Every letter contributes at least some bits to the result.
337 const dataView = new View(new Buffer(floor(length * 3 / 4)));
338 for (let index = 0; index < length - 1;) {
339 // Iterate over the characters and assign their bits to the
340 // buffer.
341 //
342 // The final index is not handled; if the string is not divisible
343 // by 4, some bits might be dropped. This matches the “forgiving
344 // decode” behaviour specified by WhatW·G for base64.
345 const dataIndex = ceil(index * 3 / 4);
346 const remainder = index % 4;
347 call(viewSetUint8, dataView, [
348 dataIndex,
349 remainder === 0
350 ? u6s[index] << 2 | u6s[++index] >> 4
351 : remainder === 1
352 ? u6s[index] << 4 | u6s[++index] >> 2
353 : u6s[index] << 6 | u6s[++index, index++], // remainder === 2
354 ]);
355 }
356 return call(getViewBuffer, dataView, []);
357 }
358 };
359
360 /**
361 * Returns the result of encoding the provided ArrayBuffer into a
362 * base16 string.
363 *
364 * ※ This function is not exposed.
365 */
366 const encodeBase16 = (buffer) => {
367 const dataView = new View(buffer);
368 const byteLength = call(getBufferByteLength, buffer, []);
369 const minimumLengthOfResults = byteLength * 2;
370 const resultingCodeUnits = fill(
371 objectCreate(
372 binaryCodeUnitIterablePrototype,
373 { length: { value: minimumLengthOfResults } },
374 ),
375 0x3D,
376 );
377 for (let index = 0; index < byteLength;) {
378 // Iterate over the bytes and generate code units for them.
379 const codeUnitIndex = index * 2;
380 const datum = call(viewGetUint8, dataView, [index++]);
381 const u4s = [datum >> 4, datum & 0xF];
382 for (let u4i = 0; u4i < 2; ++u4i) {
383 // Handle the high four bits, then the low four bits.
384 const u4 = u4s[u4i];
385 const result = u4 < 10 ? u4 + 48 : u4 < 16 ? u4 + 55 : -1;
386 if (result < 0) {
387 // No mapping exists for these four bits.
388 //
389 // ※ This shouldn’t be possible!
390 throw new RangeError(
391 `Piscēs: Unexpected Base16 value: ${u4}.`,
392 );
393 } else {
394 // A mapping exists for the bits.
395 resultingCodeUnits[codeUnitIndex + u4i] = result;
396 }
397 }
398 }
399 return stringFromCodeUnits(...resultingCodeUnits);
400 };
401
402 /**
403 * Returns the result of encoding the provided ArrayBuffer into a
404 * base32 string.
405 *
406 * ※ This function is not exposed.
407 */
408 const encodeBase32 = (buffer, wrmg = false) => {
409 const dataView = new View(buffer);
410 const byteLength = call(getBufferByteLength, buffer, []);
411 const minimumLengthOfResults = ceil(byteLength * 8 / 5);
412 const fillByte = wrmg ? 0x2D : 0x3D;
413 const resultingCodeUnits = fill(
414 objectCreate(
415 binaryCodeUnitIterablePrototype,
416 {
417 length: {
418 value: minimumLengthOfResults +
419 (8 - (minimumLengthOfResults % 8)) % 8,
420 },
421 },
422 ),
423 fillByte,
424 );
425 for (let index = 0; index < byteLength;) {
426 // Iterate over the bytes and generate code units for them.
427 const codeUnitIndex = ceil(index * 8 / 5);
428 const currentIndex = codeUnitIndex + +(
429 0b01011 & 1 << index % 5 &&
430 resultingCodeUnits[codeUnitIndex] != fillByte
431 ); // bytes 0, 1 & 3 handle two letters; this is for the second
432 const remainder = currentIndex % 8;
433 const currentByte = call(viewGetUint8, dataView, [index]);
434 const nextByte =
435 0b01011010 & 1 << remainder && ++index < byteLength
436 // digits 1, 3, 4 & 6 span multiple bytes
437 ? call(viewGetUint8, dataView, [index])
438 : 0;
439 const u5 = remainder === 0
440 ? currentByte >> 3
441 : remainder === 1
442 ? (currentByte & 0b00000111) << 2 | nextByte >> 6
443 : remainder === 2
444 ? (currentByte & 0b00111111) >> 1
445 : remainder === 3
446 ? (currentByte & 0b00000001) << 4 | nextByte >> 4
447 : remainder === 4
448 ? (currentByte & 0b00001111) << 1 | nextByte >> 7
449 : remainder === 5
450 ? (currentByte & 0b01111111) >> 2
451 : remainder === 6
452 ? (currentByte & 0b00000011) << 3 | nextByte >> 5
453 : (++index, currentByte & 0b00011111); // remainder === 7
454 const result = wrmg
455 ? u5 < 10 ? u5 + 48 : u5 < 18
456 ? u5 + 55
457 // skip I
458 : u5 < 20
459 ? u5 + 56
460 // skip L
461 : u5 < 22
462 ? u5 + 57
463 // skip O
464 : u5 < 27
465 ? u5 + 58
466 // skip U
467 : u5 < 32
468 ? u5 + 59
469 : -1
470 : u5 < 26
471 ? u5 + 65
472 : u5 < 32
473 ? u5 + 24
474 : -1;
475 if (result < 0) {
476 // No mapping exists for these five bits.
477 //
478 // ※ This shouldn’t be possible!
479 throw new RangeError(`Piscēs: Unexpected Base32 value: ${u5}.`);
480 } else {
481 // A mapping exists for the bits.
482 resultingCodeUnits[currentIndex] = result;
483 }
484 }
485 const answer = stringFromCodeUnits(...resultingCodeUnits);
486 return wrmg ? answer.replace(/-+$/u, "") : answer;
487 };
488
489 /**
490 * Returns the result of encoding the provided ArrayBuffer into a
491 * base64 string.
492 *
493 * ※ This function is not exposed.
494 */
495 const encodeBase64 = (buffer, safe = false) => {
496 const dataView = new View(buffer);
497 const byteLength = call(getBufferByteLength, buffer, []);
498 const minimumLengthOfResults = ceil(byteLength * 4 / 3);
499 const resultingCodeUnits = fill(
500 objectCreate(
501 binaryCodeUnitIterablePrototype,
502 {
503 length: {
504 value: minimumLengthOfResults +
505 (4 - (minimumLengthOfResults % 4)) % 4,
506 },
507 },
508 ),
509 0x3D,
510 );
511 for (let index = 0; index < byteLength;) {
512 // Iterate over the bytes and generate code units for them.
513 const codeUnitIndex = ceil(index * 4 / 3);
514 const currentIndex = codeUnitIndex + +(
515 index % 3 === 0 && resultingCodeUnits[codeUnitIndex] != 0x3D
516 ); // every third byte handles two letters; this is for the second
517 const remainder = currentIndex % 4;
518 const currentByte = call(viewGetUint8, dataView, [index]);
519 const nextByte = remainder % 3 && ++index < byteLength
520 // digits 1 & 2 span multiple bytes
521 ? call(viewGetUint8, dataView, [index])
522 : 0;
523 const u6 = remainder === 0
524 ? currentByte >> 2
525 : remainder === 1
526 ? (currentByte & 0b00000011) << 4 | nextByte >> 4
527 : remainder === 2
528 ? (currentByte & 0b00001111) << 2 | nextByte >> 6
529 : (++index, currentByte & 0b00111111); // remainder === 3
530 const result = u6 < 26
531 ? u6 + 65
532 : u6 < 52
533 ? u6 + 71
534 : u6 < 62
535 ? u6 - 4
536 : u6 < 63
537 ? (safe ? 0x2D : 0x2B)
538 : u6 < 64
539 ? (safe ? 0x5F : 0x2F)
540 : -1;
541 if (result < 0) {
542 // No mapping exists for these six bits.
543 //
544 // ※ This shouldn’t be possible!
545 throw new RangeError(`Piscēs: Unexpected Base64 value: ${u6}.`);
546 } else {
547 // A mapping exists for the bits.
548 resultingCodeUnits[currentIndex] = result;
549 }
550 }
551 return stringFromCodeUnits(...resultingCodeUnits);
552 };
553
554 /**
555 * Returns a source string generated from the arguments passed to a
556 * tag function.
557 *
558 * ※ This function is not exposed.
559 */
560 const sourceFromArgs = ($, $s) =>
561 stringReplace(
562 typeof $ === "string" ? $ : hasOwnProperty($, "raw")
563 ? rawString(
564 $,
565 ...objectCreate(argumentIterablePrototype, {
566 args: { value: $s },
567 }),
568 )
569 : `${$}`,
570 /[\t\n\f\r ]+/gu,
571 "",
572 );
573
574 /**
575 * Returns an ArrayBuffer generated from the provided base16 string.
576 *
577 * This function can also be used as a tag for a template literal. The
578 * literal will be interpreted akin to `String.raw`.
579 *
580 * ☡ This function throws if the provided string is not a valid base16
581 * string.
582 */
583 export const base16Binary = ($, ...$s) =>
584 decodeBase16(sourceFromArgs($, $s));
585
586 /**
587 * Returns a (big‐endian) base16 string created from the provided typed
588 * array, buffer, or (16‐bit) string.
589 *
590 * This function can also be used as a tag for a template literal. The
591 * literal will be interpreted akin to `String.raw`.
592 */
593 export const base16String = ($, ...$s) =>
594 encodeBase16(bufferFromArgs($, $s));
595
596 /**
597 * Returns an ArrayBuffer generated from the provided base32 string.
598 *
599 * This function can also be used as a tag for a template literal. The
600 * literal will be interpreted akin to `String.raw`.
601 *
602 * ☡ This function throws if the provided string is not a valid base32
603 * string.
604 */
605 export const base32Binary = ($, ...$s) =>
606 decodeBase32(sourceFromArgs($, $s));
607
608 /**
609 * Returns a (big‐endian) base32 string created from the provided typed
610 * array, buffer, or (16‐bit) 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 export const base32String = ($, ...$s) =>
616 encodeBase32(bufferFromArgs($, $s));
617
618 /**
619 * Returns an ArrayBuffer generated from the provided base64 string.
620 *
621 * This function can also be used as a tag for a template literal. The
622 * literal will be interpreted akin to `String.raw`.
623 *
624 * ☡ This function throws if the provided string is not a valid base64
625 * string.
626 */
627 export const base64Binary = ($, ...$s) =>
628 decodeBase64(sourceFromArgs($, $s));
629
630 /**
631 * Returns a (big‐endian) base64 string created from the provided typed
632 * array, buffer, or (16‐bit) 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 export const base64String = ($, ...$s) =>
638 encodeBase64(bufferFromArgs($, $s));
639
640 /**
641 * Returns an ArrayBuffer generated from the provided filename‐safe
642 * base64 string.
643 *
644 * This function can also be used as a tag for a template literal. The
645 * literal will be interpreted akin to `String.raw`.
646 *
647 * ☡ This function throws if the provided string is not a valid
648 * filename‐safe base64 string.
649 */
650 export const filenameSafeBase64Binary = ($, ...$s) =>
651 decodeBase64(sourceFromArgs($, $s), true);
652
653 /**
654 * Returns a (big‐endian) filename‐safe base64 string created from the
655 * provided typed array, buffer, or (16‐bit) string.
656 *
657 * This function can also be used as a tag for a template literal. The
658 * literal will be interpreted akin to `String.raw`.
659 */
660 export const filenameSafeBase64String = ($, ...$s) =>
661 encodeBase64(bufferFromArgs($, $s), true);
662
663 /**
664 * Returns the byte length for the provided array buffer or array
665 * buffer view.
666 *
667 * ☡ This function throws if the provided value is not an array buffer,
668 * data view, or typed array.
669 */
670 export const getByteLength = ($) => {
671 try {
672 // Attempt to get the byte length from the provided value as an
673 // `ArrayBuffer`.
674 return call(getBufferByteLength, $, []);
675 } catch {
676 // The provided value is not an `ArrayBuffer`.
677 /* do nothing */
678 }
679 try {
680 // Attempt to get the byte length from the provided value as a
681 // `SharedArrayBuffer`.
682 return call(getSharedBufferByteLength, $, []);
683 } catch {
684 // The provided value is not a `SharedArrayBuffer`.
685 /* do nothing */
686 }
687 try {
688 // Attempt to get the byte length from the provided value as a
689 // data view.
690 return call(getViewByteLength, $, []);
691 } catch {
692 // The provided value is not a data view.
693 /* do nothing */
694 }
695 try {
696 // Attempt to get the byte length from the provided value as a
697 // typed array.
698 return call(getTypedArrayByteLength, $, []);
699 } catch {
700 // The provided value is not a typed array.
701 /* do nothing */
702 }
703 throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
704 };
705
706 /**
707 * Returns the byte offset for the provided array buffer or array
708 * buffer view.
709 *
710 * ※ This function always returns `0` for array buffers.
711 *
712 * ☡ This function throws if the provided value is not an array buffer,
713 * data view, or typed array.
714 */
715 export const getByteOffset = ($) => {
716 if (isArrayBuffer($)) {
717 // The provided value is an array buffer.
718 return 0;
719 } else {
720 try {
721 // Attempt to get the byte offset from the provided value as a
722 // data view.
723 return call(getViewByteOffset, $, []);
724 } catch {
725 // The provided value is not a data view.
726 /* do nothing */
727 }
728 try {
729 // Attempt to get the byte offset from the provided value as a
730 // typed array.
731 return call(getTypedArrayByteOffset, $, []);
732 } catch {
733 // The provided value is not a typed array.
734 /* do nothing */
735 }
736 throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
737 }
738 };
739
740 /**
741 * Returns whether the provided value is a view on an underlying array
742 * buffer.
743 *
744 * ※ This function returns true for typed arrays and data views.
745 */
746 export const { isView: isArrayBufferView } = Buffer;
747
748 /**
749 * Returns whether the provided value is an array buffer.
750 *
751 * ※ This function returns true for both `ArrayBuffer`s and
752 * `SharedArrayBuffer`s.
753 */
754 export const isArrayBuffer = ($) => {
755 try {
756 // Try to see if the provided argument has array buffer internal
757 // slots and return true if so.
758 return call(getBufferByteLength, $, []), true;
759 } catch {
760 // The provided argument does not have array buffer internal slots.
761 /* do nothing */
762 }
763 try {
764 // Try to see if the provided argument has array buffer internal
765 // slots and return true if so.
766 return call(getSharedBufferByteLength, $, []), true;
767 } catch {
768 // The provided argument does not have array buffer internal slots.
769 /* do nothing */
770 }
771 return false;
772 };
773
774 /**
775 * Returns whether the provided value is a base16 string.
776 *
777 * ※ This function returns false if the provided value is not a string
778 * primitive.
779 */
780 export const isBase16 = ($) => {
781 if (typeof $ !== "string") {
782 // The provided value is not a string.
783 return false;
784 } else {
785 // The provided value is a string.
786 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
787 return source.length % 2 !== 1 &&
788 call(reExec, /[^0-9A-F]/iu, [source]) === null;
789 }
790 };
791
792 /**
793 * Returns whether the provided value is a base32 string.
794 *
795 * ※ This function returns false if the provided value is not a string
796 * primitive.
797 */
798 export const isBase32 = ($) => {
799 if (typeof $ !== "string") {
800 // The provided value is not a string.
801 return false;
802 } else {
803 // The provided value is a string.
804 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
805 const trimmed = source.length % 8 === 0
806 ? stringReplace(source, /(?:=|={3,4}|={6})$/u, "")
807 : source;
808 return trimmed.length % 8 !== 1 &&
809 call(reExec, /[^2-7A-Z/]/iu, [trimmed]) === null;
810 }
811 };
812
813 /**
814 * Returns whether the provided value is a Base64 string.
815 *
816 * ※ This function returns false if the provided value is not a string
817 * primitive.
818 */
819 export const isBase64 = ($) => {
820 if (typeof $ !== "string") {
821 // The provided value is not a string.
822 return false;
823 } else {
824 // The provided value is a string.
825 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
826 const trimmed = source.length % 4 === 0
827 ? stringReplace(source, /={1,2}$/u, "")
828 : source;
829 return trimmed.length % 4 !== 1 &&
830 call(reExec, /[^0-9A-Za-z+\/]/u, [trimmed]) === null;
831 }
832 };
833
834 /**
835 * Returns whether the provided value is a filename‐safe base64 string.
836 *
837 * ※ This function returns false if the provided value is not a string
838 * primitive.
839 */
840 export const isFilenameSafeBase64 = ($) => {
841 if (typeof $ !== "string") {
842 // The provided value is not a string.
843 return false;
844 } else {
845 // The provided value is a string.
846 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
847 const trimmed = source.length % 4 === 0
848 ? stringReplace(source, /={1,2}$/u, "")
849 : source;
850 return trimmed.length % 4 !== 1 &&
851 call(reExec, /[^0-9A-Za-z_-]/u, [trimmed]) === null;
852 }
853 };
854
855 /** Returns whether the provided value is a shared array buffer. */
856 export const isSharedArrayBuffer = ($) => {
857 try {
858 // Try to see if the provided argument has shared array buffer
859 // internal slots and return true if so.
860 return call(getSharedBufferByteLength, $, []), true;
861 } catch {
862 // The provided argument does not have data view internal slots.
863 return false;
864 }
865 };
866
867 /** Returns whether the provided value is a typed array. */
868 export const isTypedArray = ($) => {
869 try {
870 // Try to see if the provided argument has typed array internal
871 // slots and return true if so.
872 return call(getTypedArrayBuffer, $, []), true;
873 } catch {
874 // The provided argument does not have typed array internal slots.
875 return false;
876 }
877 };
878
879 /**
880 * Returns whether the provided value is a W·R·M·G (Crockford) base32
881 * string. Check digits are not supported.
882 *
883 * ※ This function returns false if the provided value is not a string
884 * primitive.
885 */
886 export const isWRMGBase32 = ($) => {
887 if (typeof $ !== "string") {
888 // The provided value is not a string.
889 return false;
890 } else {
891 // The provided value is a string.
892 const source = stringReplace($, /[\t\n\f\r ]+/gu, "");
893 const trimmed = stringReplace(source, /-/gu, "");
894 return trimmed.length % 8 !== 1 &&
895 call(reExec, /[^0-9A-TV-Z]/iu, [trimmed]) === null;
896 }
897 };
898
899 /**
900 * Returns the array buffer associated with the provided object.
901 *
902 * ☡ This function throws if the provided object is not a data view or
903 * typed array.
904 */
905 export const toArrayBuffer = ($) => {
906 if (isArrayBuffer($)) {
907 // The provided argument has array buffer internal slots.
908 return $;
909 } else {
910 // The provided argument does not have array buffer internal slots.
911 try {
912 // The provided argument has typed array internal slots.
913 return call(getTypedArrayBuffer, $, []);
914 } catch {
915 /* do nothing */
916 }
917 try {
918 // The provided argument has data view internal slots.
919 return call(getViewBuffer, $, []);
920 } catch {
921 /* do nothing */
922 }
923 throw new TypeError(`Piscēs: Not an array buffer or view: ${$}.`);
924 }
925 };
926
927 /**
928 * Returns an ArrayBuffer generated from the provided W·R·M·G
929 * (Crockford) base32 string.
930 *
931 * This function can also be used as a tag for a template literal. The
932 * literal will be interpreted akin to `String.raw`.
933 *
934 * ☡ This function throws if the provided string is not a valid W·R·M·G
935 * base32 string.
936 */
937 export const wrmgBase32Binary = ($, ...$s) =>
938 decodeBase32(sourceFromArgs($, $s), true);
939
940 /**
941 * Returns a (big‐endian) W·R·M·G (Crockford) base32 string created
942 * from the provided typed array, buffer, or (16‐bit) string.
943 *
944 * This function can also be used as a tag for a template literal. The
945 * literal will be interpreted akin to `String.raw`.
946 */
947 export const wrmgBase32String = ($, ...$s) =>
948 encodeBase32(bufferFromArgs($, $s), true);
This page took 0.388625 seconds and 5 git commands to generate.