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