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