1// Copyright (C) 2019 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import { 16 decode as b64Decode, 17 encode as b64Encode, 18 length as b64Len, 19} from '@protobufjs/base64'; 20import { 21 length as utf8Len, 22 read as utf8Read, 23 write as utf8Write, 24} from '@protobufjs/utf8'; 25import {assertTrue} from './logging'; 26 27// TextDecoder/Decoder requires the full DOM and isn't available in all types 28// of tests. Use fallback implementation from protbufjs. 29let Utf8Decoder: {decode: (buf: Uint8Array) => string;}; 30let Utf8Encoder: {encode: (str: string) => Uint8Array;}; 31try { 32 Utf8Decoder = new TextDecoder('utf-8'); 33 Utf8Encoder = new TextEncoder(); 34} catch (_) { 35 if (typeof process === 'undefined') { 36 // Silence the warning when we know we are running under NodeJS. 37 console.warn( 38 'Using fallback UTF8 Encoder/Decoder, This should happen only in ' + 39 'tests and NodeJS-based environments, not in browsers.'); 40 } 41 Utf8Decoder = {decode: (buf: Uint8Array) => utf8Read(buf, 0, buf.length)}; 42 Utf8Encoder = { 43 encode: (str: string) => { 44 const arr = new Uint8Array(utf8Len(str)); 45 const written = utf8Write(str, arr, 0); 46 assertTrue(written === arr.length); 47 return arr; 48 }, 49 }; 50} 51 52export function base64Encode(buffer: Uint8Array): string { 53 return b64Encode(buffer, 0, buffer.length); 54} 55 56export function base64Decode(str: string): Uint8Array { 57 // if the string is in base64url format, convert to base64 58 const b64 = str.replace(/-/g, '+').replace(/_/g, '/'); 59 const arr = new Uint8Array(b64Len(b64)); 60 const written = b64Decode(b64, arr, 0); 61 assertTrue(written === arr.length); 62 return arr; 63} 64 65// encode binary array to hex string 66export function hexEncode(bytes: Uint8Array): string { 67 return bytes.reduce( 68 (prev, cur) => prev + ('0' + cur.toString(16)).slice(-2), ''); 69} 70 71export function utf8Encode(str: string): Uint8Array { 72 return Utf8Encoder.encode(str); 73} 74 75// Note: not all byte sequences can be converted to<>from UTF8. This can be 76// used only with valid unicode strings, not arbitrary byte buffers. 77export function utf8Decode(buffer: Uint8Array): string { 78 return Utf8Decoder.decode(buffer); 79} 80 81// The binaryEncode/Decode functions below allow to encode an arbitrary binary 82// buffer into a string that can be JSON-encoded. binaryEncode() applies 83// UTF-16 encoding to each byte individually. 84// Unlike utf8Encode/Decode, any arbitrary byte sequence can be converted into a 85// valid string, and viceversa. 86// This should be only used when a byte array needs to be transmitted over an 87// interface that supports only JSON serialization (e.g., postmessage to a 88// chrome extension). 89 90export function binaryEncode(buf: Uint8Array): string { 91 let str = ''; 92 for (let i = 0; i < buf.length; i++) { 93 str += String.fromCharCode(buf[i]); 94 } 95 return str; 96} 97 98export function binaryDecode(str: string): Uint8Array { 99 const buf = new Uint8Array(str.length); 100 const strLen = str.length; 101 for (let i = 0; i < strLen; i++) { 102 buf[i] = str.charCodeAt(i); 103 } 104 return buf; 105} 106 107// A function used to interpolate strings into SQL query. The only replacement 108// is done is that single quote replaced with two single quotes, according to 109// SQLite documentation: 110// https://www.sqlite.org/lang_expr.html#literal_values_constants_ 111// 112// The purpose of this function is to use in simple comparisons, to escape 113// strings used in GLOB clauses see escapeQuery function. 114export function sqliteString(str: string): string { 115 return `'${str.replace(/'/g, '\'\'')}'`; 116} 117