xref: /aosp_15_r20/external/perfetto/ui/src/base/string_utils.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2019 The Android Open Source Project
2*6dbdd20aSAndroid Build Coastguard Worker//
3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*6dbdd20aSAndroid Build Coastguard Worker//
7*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*6dbdd20aSAndroid Build Coastguard Worker//
9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard Workerimport {
16*6dbdd20aSAndroid Build Coastguard Worker  decode as b64Decode,
17*6dbdd20aSAndroid Build Coastguard Worker  encode as b64Encode,
18*6dbdd20aSAndroid Build Coastguard Worker  length as b64Len,
19*6dbdd20aSAndroid Build Coastguard Worker} from '@protobufjs/base64';
20*6dbdd20aSAndroid Build Coastguard Workerimport {assertTrue} from './logging';
21*6dbdd20aSAndroid Build Coastguard Worker
22*6dbdd20aSAndroid Build Coastguard Worker// Lazy initialize at first use.
23*6dbdd20aSAndroid Build Coastguard Workerlet textDecoder: TextDecoder | undefined = undefined;
24*6dbdd20aSAndroid Build Coastguard Workerlet textEncoder: TextEncoder | undefined = undefined;
25*6dbdd20aSAndroid Build Coastguard Worker
26*6dbdd20aSAndroid Build Coastguard Workerexport function base64Encode(buffer: Uint8Array): string {
27*6dbdd20aSAndroid Build Coastguard Worker  return b64Encode(buffer, 0, buffer.length);
28*6dbdd20aSAndroid Build Coastguard Worker}
29*6dbdd20aSAndroid Build Coastguard Worker
30*6dbdd20aSAndroid Build Coastguard Workerexport function base64Decode(str: string): Uint8Array {
31*6dbdd20aSAndroid Build Coastguard Worker  // if the string is in base64url format, convert to base64
32*6dbdd20aSAndroid Build Coastguard Worker  const b64 = str.replaceAll('-', '+').replaceAll('_', '/');
33*6dbdd20aSAndroid Build Coastguard Worker  const arr = new Uint8Array(b64Len(b64));
34*6dbdd20aSAndroid Build Coastguard Worker  const written = b64Decode(b64, arr, 0);
35*6dbdd20aSAndroid Build Coastguard Worker  assertTrue(written === arr.length);
36*6dbdd20aSAndroid Build Coastguard Worker  return arr;
37*6dbdd20aSAndroid Build Coastguard Worker}
38*6dbdd20aSAndroid Build Coastguard Worker
39*6dbdd20aSAndroid Build Coastguard Worker// encode binary array to hex string
40*6dbdd20aSAndroid Build Coastguard Workerexport function hexEncode(bytes: Uint8Array): string {
41*6dbdd20aSAndroid Build Coastguard Worker  return bytes.reduce(
42*6dbdd20aSAndroid Build Coastguard Worker    (prev, cur) => prev + ('0' + cur.toString(16)).slice(-2),
43*6dbdd20aSAndroid Build Coastguard Worker    '',
44*6dbdd20aSAndroid Build Coastguard Worker  );
45*6dbdd20aSAndroid Build Coastguard Worker}
46*6dbdd20aSAndroid Build Coastguard Worker
47*6dbdd20aSAndroid Build Coastguard Workerexport function utf8Encode(str: string): Uint8Array {
48*6dbdd20aSAndroid Build Coastguard Worker  textEncoder = textEncoder ?? new TextEncoder();
49*6dbdd20aSAndroid Build Coastguard Worker  return textEncoder.encode(str);
50*6dbdd20aSAndroid Build Coastguard Worker}
51*6dbdd20aSAndroid Build Coastguard Worker
52*6dbdd20aSAndroid Build Coastguard Worker// Note: not all byte sequences can be converted to<>from UTF8. This can be
53*6dbdd20aSAndroid Build Coastguard Worker// used only with valid unicode strings, not arbitrary byte buffers.
54*6dbdd20aSAndroid Build Coastguard Workerexport function utf8Decode(buffer: Uint8Array | ArrayBuffer): string {
55*6dbdd20aSAndroid Build Coastguard Worker  textDecoder = textDecoder ?? new TextDecoder();
56*6dbdd20aSAndroid Build Coastguard Worker  return textDecoder.decode(buffer);
57*6dbdd20aSAndroid Build Coastguard Worker}
58*6dbdd20aSAndroid Build Coastguard Worker
59*6dbdd20aSAndroid Build Coastguard Worker// The binaryEncode/Decode functions below allow to encode an arbitrary binary
60*6dbdd20aSAndroid Build Coastguard Worker// buffer into a string that can be JSON-encoded. binaryEncode() applies
61*6dbdd20aSAndroid Build Coastguard Worker// UTF-16 encoding to each byte individually.
62*6dbdd20aSAndroid Build Coastguard Worker// Unlike utf8Encode/Decode, any arbitrary byte sequence can be converted into a
63*6dbdd20aSAndroid Build Coastguard Worker// valid string, and viceversa.
64*6dbdd20aSAndroid Build Coastguard Worker// This should be only used when a byte array needs to be transmitted over an
65*6dbdd20aSAndroid Build Coastguard Worker// interface that supports only JSON serialization (e.g., postmessage to a
66*6dbdd20aSAndroid Build Coastguard Worker// chrome extension).
67*6dbdd20aSAndroid Build Coastguard Worker
68*6dbdd20aSAndroid Build Coastguard Workerexport function binaryEncode(buf: Uint8Array): string {
69*6dbdd20aSAndroid Build Coastguard Worker  let str = '';
70*6dbdd20aSAndroid Build Coastguard Worker  for (let i = 0; i < buf.length; i++) {
71*6dbdd20aSAndroid Build Coastguard Worker    str += String.fromCharCode(buf[i]);
72*6dbdd20aSAndroid Build Coastguard Worker  }
73*6dbdd20aSAndroid Build Coastguard Worker  return str;
74*6dbdd20aSAndroid Build Coastguard Worker}
75*6dbdd20aSAndroid Build Coastguard Worker
76*6dbdd20aSAndroid Build Coastguard Workerexport function binaryDecode(str: string): Uint8Array {
77*6dbdd20aSAndroid Build Coastguard Worker  const buf = new Uint8Array(str.length);
78*6dbdd20aSAndroid Build Coastguard Worker  const strLen = str.length;
79*6dbdd20aSAndroid Build Coastguard Worker  for (let i = 0; i < strLen; i++) {
80*6dbdd20aSAndroid Build Coastguard Worker    buf[i] = str.charCodeAt(i);
81*6dbdd20aSAndroid Build Coastguard Worker  }
82*6dbdd20aSAndroid Build Coastguard Worker  return buf;
83*6dbdd20aSAndroid Build Coastguard Worker}
84*6dbdd20aSAndroid Build Coastguard Worker
85*6dbdd20aSAndroid Build Coastguard Worker// A function used to interpolate strings into SQL query. The only replacement
86*6dbdd20aSAndroid Build Coastguard Worker// is done is that single quote replaced with two single quotes, according to
87*6dbdd20aSAndroid Build Coastguard Worker// SQLite documentation:
88*6dbdd20aSAndroid Build Coastguard Worker// https://www.sqlite.org/lang_expr.html#literal_values_constants_
89*6dbdd20aSAndroid Build Coastguard Worker//
90*6dbdd20aSAndroid Build Coastguard Worker// The purpose of this function is to use in simple comparisons, to escape
91*6dbdd20aSAndroid Build Coastguard Worker// strings used in GLOB clauses see escapeQuery function.
92*6dbdd20aSAndroid Build Coastguard Workerexport function sqliteString(str: string): string {
93*6dbdd20aSAndroid Build Coastguard Worker  return `'${str.replaceAll("'", "''")}'`;
94*6dbdd20aSAndroid Build Coastguard Worker}
95*6dbdd20aSAndroid Build Coastguard Worker
96*6dbdd20aSAndroid Build Coastguard Worker// Makes a string safe to be used as a SQL table/view/function name.
97*6dbdd20aSAndroid Build Coastguard Workerexport function sqlNameSafe(str: string): string {
98*6dbdd20aSAndroid Build Coastguard Worker  return str.replace(/[^a-zA-Z0-9_]+/g, '_');
99*6dbdd20aSAndroid Build Coastguard Worker}
100*6dbdd20aSAndroid Build Coastguard Worker
101*6dbdd20aSAndroid Build Coastguard Worker// Chat apps (including G Chat) sometimes replace ASCII characters with similar
102*6dbdd20aSAndroid Build Coastguard Worker// looking unicode characters that break code snippets.
103*6dbdd20aSAndroid Build Coastguard Worker// This function attempts to undo these replacements.
104*6dbdd20aSAndroid Build Coastguard Workerexport function undoCommonChatAppReplacements(str: string): string {
105*6dbdd20aSAndroid Build Coastguard Worker  // Replace non-breaking spaces with normal spaces.
106*6dbdd20aSAndroid Build Coastguard Worker  return str.replaceAll('\u00A0', ' ');
107*6dbdd20aSAndroid Build Coastguard Worker}
108*6dbdd20aSAndroid Build Coastguard Worker
109*6dbdd20aSAndroid Build Coastguard Workerexport function cropText(str: string, charWidth: number, rectWidth: number) {
110*6dbdd20aSAndroid Build Coastguard Worker  let displayText = '';
111*6dbdd20aSAndroid Build Coastguard Worker  const maxLength = Math.floor(rectWidth / charWidth) - 1;
112*6dbdd20aSAndroid Build Coastguard Worker  if (str.length <= maxLength) {
113*6dbdd20aSAndroid Build Coastguard Worker    displayText = str;
114*6dbdd20aSAndroid Build Coastguard Worker  } else {
115*6dbdd20aSAndroid Build Coastguard Worker    let limit = maxLength;
116*6dbdd20aSAndroid Build Coastguard Worker    let maybeTripleDot = '';
117*6dbdd20aSAndroid Build Coastguard Worker    if (maxLength > 1) {
118*6dbdd20aSAndroid Build Coastguard Worker      limit = maxLength - 1;
119*6dbdd20aSAndroid Build Coastguard Worker      maybeTripleDot = '\u2026';
120*6dbdd20aSAndroid Build Coastguard Worker    }
121*6dbdd20aSAndroid Build Coastguard Worker    // Javascript strings are UTF-16. |limit| could point in the middle of a
122*6dbdd20aSAndroid Build Coastguard Worker    // 32-bit double-wchar codepoint (e.g., an emoji). Here we detect if the
123*6dbdd20aSAndroid Build Coastguard Worker    // |limit|-th wchar is a leading surrogate and attach the trailing one.
124*6dbdd20aSAndroid Build Coastguard Worker    const lastCharCode = str.charCodeAt(limit - 1);
125*6dbdd20aSAndroid Build Coastguard Worker    limit += lastCharCode >= 55296 && lastCharCode < 56320 ? 1 : 0;
126*6dbdd20aSAndroid Build Coastguard Worker    displayText = str.substring(0, limit) + maybeTripleDot;
127*6dbdd20aSAndroid Build Coastguard Worker  }
128*6dbdd20aSAndroid Build Coastguard Worker  return displayText;
129*6dbdd20aSAndroid Build Coastguard Worker}
130