1 // -*- mode: javascript; tab-width: 4; indent-tabs-mode: nil; -*-
2 //------------------------------------------------------------------------------
3 // This is free and unencumbered software released into the public domain.
5 // Anyone is free to copy, modify, publish, use, compile, sell, or
6 // distribute this software, either in source code form or as a compiled
7 // binary, for any purpose, commercial or non-commercial, and by any
10 // In jurisdictions that recognize copyright laws, the author or authors
11 // of this software dedicate any and all copyright interest in the
12 // software to the public domain. We make this dedication for the benefit
13 // of the public at large and to the detriment of our heirs and
14 // successors. We intend this dedication to be an overt act of
15 // relinquishment in perpetuity of all present and future rights to this
16 // software under copyright law.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 // OTHER DEALINGS IN THE SOFTWARE.
26 // For more information, please refer to <https://unlicense.org/>
27 //------------------------------------------------------------------------------
29 const BIN2HEX_LUT = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
31 export function binToHex(uint8Array) {
33 for (const b of uint8Array) {
34 result += BIN2HEX_LUT[(b >> 4) & 15];
35 result += BIN2HEX_LUT[b & 15];
40 export function hexToBin(hexStr) {
41 function fromHexChar(b) {
43 return b - 48; // "0" - "9"
45 return b - 55; // "A" - "F"
47 return b - 87; // "a" - "f"
51 let result = new Uint8Array(hexStr.length / 2);
52 for (let i = 0; i < result.length; ++i) {
53 const c1 = fromHexChar(hexStr.charCodeAt(i * 2));
54 const c2 = fromHexChar(hexStr.charCodeAt(i * 2 + 1));
55 result[i] = (c1 << 4) | c2;
60 // Convert to base64url (RFC 4648).
61 export function binToB64(uint8Array) {
62 function codeToB64(c) {
64 return String.fromCharCode(c + 65);
65 } else if (c <= 51) { // a-z
66 return String.fromCharCode(c + 71);
67 } else if (c <= 61) { // 0-9
68 return String.fromCharCode(c - 4);
77 const triplets = Math.ceil(uint8Array.length / 3);
78 for (let i = 0; i < triplets; ++i) {
79 const b1 = uint8Array[i * 3 + 0] | 0;
80 const b2 = uint8Array[i * 3 + 1] | 0;
81 const b3 = uint8Array[i * 3 + 2] | 0;
82 result += codeToB64(b1 >> 2);
83 result += codeToB64(((b1 & 3) << 4) | (b2 >> 4));
84 result += codeToB64(((b2 & 15) << 2) | (b3 >> 6));
85 result += codeToB64(b3 & 63);
88 const actualLength = Math.ceil((uint8Array.length * 4) / 3);
89 return result.slice(0, actualLength);
92 // Convert from base64url (RFC 4648).
93 export function b64ToBin(str) {
94 function b64ToCode(c) {
97 } else if (c == 95) { // _
99 } else if (c <= 57) { // 0-9
101 } else if (c <= 90) { // A-Z
108 const quads = Math.ceil(str.length / 4);
109 let result = new Uint8Array(quads * 4);
110 for (let i = 0; i < quads; ++i) {
111 const c1 = b64ToCode(str.charCodeAt(i * 4 + 0) | 0);
112 const c2 = b64ToCode(str.charCodeAt(i * 4 + 1) | 0);
113 const c3 = b64ToCode(str.charCodeAt(i * 4 + 2) | 0);
114 const c4 = b64ToCode(str.charCodeAt(i * 4 + 3) | 0);
115 result[i * 3 + 0] = (c1 << 2) | (c2 >> 4);
116 result[i * 3 + 1] = (c2 << 4) | (c3 >> 2);
117 result[i * 3 + 2] = (c3 << 6) | c4;
120 const actualLength = Math.floor((str.length * 3) / 4);
121 return result.subarray(0, actualLength);
124 export function binToDataUri(bin, mimeType) {
125 let CHUNK_SIZE = 0x8000;
127 let length = bin.length;
130 while (index < length) {
131 slice = bin.subarray(index, Math.min(index + CHUNK_SIZE, length));
132 result += String.fromCharCode.apply(null, slice);
135 return `data:${mimeType};base64,${btoa(result)}`;
138 export function strToBin(str) {
139 const textEncoder = new TextEncoder();
140 return textEncoder.encode(str);
143 export function binToStr(bin) {
144 const textDecoder = new TextDecoder();
145 return textDecoder.decode(bin);
148 export function int64ToBin(x) {
150 let shift = BigInt(8);
151 let mask = BigInt(0xff);
152 let uint8Array = new Uint8Array(8);
153 for (let i = 0; i < uint8Array.length; ++i) {
154 uint8Array[i] = Number(x & mask);
160 export function binToInt64(uint8Array) {
162 let shift = BigInt(8);
163 for (let i = uint8Array.length - 1; i >= 0; --i) {
164 x = (x << shift) + BigInt(uint8Array[i]);
169 export function senderNameToBin(senderName) {
170 // The sender ID is encoded as UTF-8, maximum 16 bytes (without zero termination).
171 const MAX_NUM_BYTES = 16;
172 let bin = new Uint8Array(MAX_NUM_BYTES);
173 const textEncoder = new TextEncoder();
174 textEncoder.encodeInto(senderName, bin);
178 export function binToSenderName(bin) {
179 const textDecoder = new TextDecoder();
180 return textDecoder.decode(bin).replace(/\0.*$/g,'');
183 const MIME_TYPE_TO_ENUM_LUT = {
184 "application/octet-stream": 1,
185 "application/gzip": 2,
186 "application/zip": 3,
187 "application/vnd.chachachat.geolocation": 4,
191 "text/markdown": 103,
197 "image/svg+xml": 205,
207 export function isSupportedMessageFileType(mimeType) {
208 return MIME_TYPE_TO_ENUM_LUT[mimeType] ? true : false;
211 export function mimeTypeToBin(mimeType) {
212 // Convert the mime type string to an integer.
213 let t = MIME_TYPE_TO_ENUM_LUT[mimeType];
215 t = MIME_TYPE_TO_ENUM_LUT["application/octet-stream"];
218 // Encode the integer as a 32-bit integer (little endian).
219 let bin = new Uint8Array(4);
221 bin[1] = (t >> 8) & 255;
222 bin[2] = (t >> 16) & 255;
223 bin[3] = (t >> 24) & 255;
227 export function binToMimeType(bin) {
228 // Get the type integer (32 bit, little endian).
229 const t = bin[0] | (bin[1] << 8) | (bin[2] << 16) | (bin[3] << 24);
231 // Look up the mime type string.
232 // TODO: Use an O(1) reverse dict instead of this O(n) loop.
233 for (const [mimeType, value] of Object.entries(MIME_TYPE_TO_ENUM_LUT)) {
238 return "application/octet-stream";
241 export function u8ArraysEqual(a, b)
243 if (a.length != b.length) {
246 for (let i = 0 ; i != a.length ; ++i)