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