/proc/self/cwd/internal/strings.cc
Line | Count | Source |
1 | | // Copyright 2021 Google LLC |
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 | | // https://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 | | |
15 | | #include "internal/strings.h" |
16 | | |
17 | | #include <cstddef> |
18 | | #include <string> |
19 | | |
20 | | #include "absl/base/attributes.h" |
21 | | #include "absl/status/status.h" |
22 | | #include "absl/strings/ascii.h" |
23 | | #include "absl/strings/cord.h" |
24 | | #include "absl/strings/match.h" |
25 | | #include "absl/strings/str_cat.h" |
26 | | #include "absl/strings/string_view.h" |
27 | | #include "internal/lexis.h" |
28 | | #include "internal/unicode.h" |
29 | | #include "internal/utf8.h" |
30 | | |
31 | | namespace cel::internal { |
32 | | |
33 | | namespace { |
34 | | |
35 | | constexpr char kHexTable[] = "0123456789abcdef"; |
36 | | |
37 | 29.9k | constexpr int HexDigitToInt(char x) { |
38 | 29.9k | if (x > '9') { |
39 | 14.9k | x += 9; |
40 | 14.9k | } |
41 | 29.9k | return x & 0xf; |
42 | 29.9k | } |
43 | | |
44 | 6.11k | constexpr bool IsOctalDigit(char x) { return x >= '0' && x <= '7'; } |
45 | | |
46 | | // Returns true when following conditions are met: |
47 | | // - <closing_str> is a suffix of <source>. |
48 | | // - No other unescaped occurrence of <closing_str> inside <source> (apart from |
49 | | // being a suffix). |
50 | | // Returns false otherwise. If <error> is non-NULL, returns an error message in |
51 | | // <error>. If <error_offset> is non-NULL, returns the offset in <source> that |
52 | | // corresponds to the location of the error. |
53 | | bool CheckForClosingString(absl::string_view source, |
54 | 230k | absl::string_view closing_str, std::string* error) { |
55 | 230k | if (closing_str.empty()) return true; |
56 | | |
57 | 230k | const char* p = source.data(); |
58 | 230k | const char* end = p + source.size(); |
59 | | |
60 | 230k | bool is_closed = false; |
61 | 4.86M | while (p + closing_str.length() <= end) { |
62 | 4.63M | if (*p != '\\') { |
63 | 4.59M | size_t cur_pos = p - source.data(); |
64 | 4.59M | bool is_closing = |
65 | 4.59M | absl::StartsWith(absl::ClippedSubstr(source, cur_pos), closing_str); |
66 | 4.59M | if (is_closing && p + closing_str.length() < end) { |
67 | 0 | if (error) { |
68 | 0 | *error = |
69 | 0 | absl::StrCat("String cannot contain unescaped ", closing_str); |
70 | 0 | } |
71 | 0 | return false; |
72 | 0 | } |
73 | 4.59M | is_closed = is_closing && (p + closing_str.length() == end); |
74 | 4.59M | } else { |
75 | 36.7k | p++; // Read past the escaped character. |
76 | 36.7k | } |
77 | 4.63M | p++; |
78 | 4.63M | } |
79 | | |
80 | 230k | if (!is_closed) { |
81 | 9.78k | if (error) { |
82 | 9.78k | *error = absl::StrCat("String must end with ", closing_str); |
83 | 9.78k | } |
84 | 9.78k | return false; |
85 | 9.78k | } |
86 | | |
87 | 220k | return true; |
88 | 230k | } |
89 | | |
90 | | // ---------------------------------------------------------------------- |
91 | | // CUnescapeInternal() |
92 | | // Unescapes C escape sequences and is the reverse of CEscape(). |
93 | | // |
94 | | // If 'source' is valid, stores the unescaped string and its size in |
95 | | // 'dest' and 'dest_len' respectively, and returns true. Otherwise |
96 | | // returns false and optionally stores the error description in |
97 | | // 'error' and the error offset in 'error_offset'. If 'error' is |
98 | | // nonempty on return, 'error_offset' is in range [0, str.size()]. |
99 | | // Set 'error' and 'error_offset' to NULL to disable error reporting. |
100 | | // |
101 | | // 'dest' must point to a buffer that is at least as big as 'source'. The |
102 | | // unescaped string cannot grow bigger than the source string since no |
103 | | // unescaped sequence is longer than the corresponding escape sequence. |
104 | | // 'source' and 'dest' must not be the same. |
105 | | // |
106 | | // If <closing_str> is non-empty, for <source> to be valid: |
107 | | // - It must end with <closing_str>. |
108 | | // - Should not contain any other unescaped occurrence of <closing_str>. |
109 | | // ---------------------------------------------------------------------- |
110 | | bool UnescapeInternal(absl::string_view source, absl::string_view closing_str, |
111 | | bool is_raw_literal, bool is_bytes_literal, |
112 | 230k | std::string* dest, std::string* error) { |
113 | 230k | if (!CheckForClosingString(source, closing_str, error)) { |
114 | 9.78k | return false; |
115 | 9.78k | } |
116 | | |
117 | 220k | if (ABSL_PREDICT_FALSE(source.empty())) { |
118 | 0 | *dest = std::string(); |
119 | 0 | return true; |
120 | 0 | } |
121 | | |
122 | | // Strip off the closing_str from the end before unescaping. |
123 | 220k | source = source.substr(0, source.size() - closing_str.size()); |
124 | 220k | if (!is_bytes_literal) { |
125 | 211k | if (!Utf8IsValid(source)) { |
126 | 0 | if (error) { |
127 | 0 | *error = absl::StrCat("Structurally invalid UTF8 string: ", |
128 | 0 | EscapeBytes(source)); |
129 | 0 | } |
130 | 0 | return false; |
131 | 0 | } |
132 | 211k | } |
133 | | |
134 | 220k | dest->reserve(source.size()); |
135 | | |
136 | 220k | const char* p = source.data(); |
137 | 220k | const char* end = p + source.size(); |
138 | 220k | const char* last_byte = end - 1; |
139 | | |
140 | 4.26M | while (p < end) { |
141 | 4.05M | if (*p != '\\') { |
142 | 4.02M | if (*p != '\r') { |
143 | 4.02M | dest->push_back(*p++); |
144 | 4.02M | } else { |
145 | | // All types of newlines in different platforms i.e. '\r', '\n', '\r\n' |
146 | | // are replaced with '\n'. |
147 | 1.85k | dest->push_back('\n'); |
148 | 1.85k | p++; |
149 | 1.85k | if (p < end && *p == '\n') { |
150 | 280 | p++; |
151 | 280 | } |
152 | 1.85k | } |
153 | 4.02M | } else { |
154 | 26.4k | if ((p + 1) > last_byte) { |
155 | 0 | if (error) { |
156 | 0 | *error = is_raw_literal |
157 | 0 | ? "Raw literals cannot end with odd number of \\" |
158 | 0 | : is_bytes_literal ? "Bytes literal cannot end with \\" |
159 | 0 | : "String literal cannot end with \\"; |
160 | 0 | } |
161 | 0 | return false; |
162 | 0 | } |
163 | 26.4k | if (is_raw_literal) { |
164 | | // For raw literals, all escapes are valid and those characters ('\\' |
165 | | // and the escaped character) come through literally in the string. |
166 | 12.2k | dest->push_back(*p++); |
167 | 12.2k | dest->push_back(*p++); |
168 | 12.2k | continue; |
169 | 12.2k | } |
170 | | // Any error that occurs in the escape is accounted to the start of |
171 | | // the escape. |
172 | 14.2k | p++; // Read past the escape character. |
173 | | |
174 | 14.2k | switch (*p) { |
175 | 391 | case 'a': |
176 | 391 | dest->push_back('\a'); |
177 | 391 | break; |
178 | 206 | case 'b': |
179 | 206 | dest->push_back('\b'); |
180 | 206 | break; |
181 | 338 | case 'f': |
182 | 338 | dest->push_back('\f'); |
183 | 338 | break; |
184 | 222 | case 'n': |
185 | 222 | dest->push_back('\n'); |
186 | 222 | break; |
187 | 233 | case 'r': |
188 | 233 | dest->push_back('\r'); |
189 | 233 | break; |
190 | 214 | case 't': |
191 | 214 | dest->push_back('\t'); |
192 | 214 | break; |
193 | 349 | case 'v': |
194 | 349 | dest->push_back('\v'); |
195 | 349 | break; |
196 | 451 | case '\\': |
197 | 451 | dest->push_back('\\'); |
198 | 451 | break; |
199 | 290 | case '?': |
200 | 290 | dest->push_back('\?'); |
201 | 290 | break; // \? Who knew? |
202 | 204 | case '\'': |
203 | 204 | dest->push_back('\''); |
204 | 204 | break; |
205 | 567 | case '"': |
206 | 567 | dest->push_back('\"'); |
207 | 567 | break; |
208 | 239 | case '`': |
209 | 239 | dest->push_back('`'); |
210 | 239 | break; |
211 | 446 | case '0': |
212 | 446 | ABSL_FALLTHROUGH_INTENDED; |
213 | 983 | case '1': |
214 | 983 | ABSL_FALLTHROUGH_INTENDED; |
215 | 1.77k | case '2': |
216 | 1.77k | ABSL_FALLTHROUGH_INTENDED; |
217 | 2.03k | case '3': { |
218 | | // Octal escape '\ddd': requires exactly 3 octal digits. Note that |
219 | | // the highest valid escape sequence is '\377'. |
220 | | // For string literals, octal and hex escape sequences are interpreted |
221 | | // as unicode code points, and the related UTF8-encoded character is |
222 | | // added to the destination. For bytes literals, octal and hex |
223 | | // escape sequences are interpreted as a single byte value. |
224 | 2.03k | const char* octal_start = p; |
225 | 2.03k | if (p + 2 >= end) { |
226 | 0 | if (error) { |
227 | 0 | *error = |
228 | 0 | "Illegal escape sequence: Octal escape must be followed by 3 " |
229 | 0 | "octal digits but saw: \\" + |
230 | 0 | std::string(octal_start, end - p); |
231 | 0 | } |
232 | | // Error offset was set to the start of the escape above the switch. |
233 | 0 | return false; |
234 | 0 | } |
235 | 2.03k | const char* octal_end = p + 2; |
236 | 2.03k | char32_t ch = 0; |
237 | 8.14k | for (; p <= octal_end; ++p) { |
238 | 6.11k | if (IsOctalDigit(*p)) { |
239 | 6.11k | ch = ch * 8 + *p - '0'; |
240 | 6.11k | } else { |
241 | 0 | if (error) { |
242 | 0 | *error = |
243 | 0 | "Illegal escape sequence: Octal escape must be followed by " |
244 | 0 | "3 octal digits but saw: \\" + |
245 | 0 | std::string(octal_start, 3); |
246 | 0 | } |
247 | | // Error offset was set to the start of the escape above the |
248 | | // switch. |
249 | 0 | return false; |
250 | 0 | } |
251 | 6.11k | } |
252 | 2.03k | p = octal_end; // p points at last digit. |
253 | 2.03k | if (is_bytes_literal) { |
254 | 992 | dest->push_back(static_cast<char>(ch)); |
255 | 1.04k | } else { |
256 | 1.04k | Utf8Encode(*dest, ch); |
257 | 1.04k | } |
258 | 2.03k | break; |
259 | 2.03k | } |
260 | 284 | case 'x': |
261 | 284 | ABSL_FALLTHROUGH_INTENDED; |
262 | 1.07k | case 'X': { |
263 | | // Hex escape '\xhh': requires exactly 2 hex digits. |
264 | | // For string literals, octal and hex escape sequences are |
265 | | // interpreted as unicode code points, and the related UTF8-encoded |
266 | | // character is added to the destination. For bytes literals, octal |
267 | | // and hex escape sequences are interpreted as a single byte value. |
268 | 1.07k | const char* hex_start = p; |
269 | 1.07k | if (p + 2 >= end) { |
270 | 0 | if (error) { |
271 | 0 | *error = |
272 | 0 | "Illegal escape sequence: Hex escape must be followed by 2 " |
273 | 0 | "hex digits but saw: \\" + |
274 | 0 | std::string(hex_start, end - p); |
275 | 0 | } |
276 | | // Error offset was set to the start of the escape above the switch. |
277 | 0 | return false; |
278 | 0 | } |
279 | 1.07k | char32_t ch = 0; |
280 | 1.07k | const char* hex_end = p + 2; |
281 | 3.21k | for (++p; p <= hex_end; ++p) { |
282 | 2.14k | if (absl::ascii_isxdigit(*p)) { |
283 | 2.14k | ch = (ch << 4) + HexDigitToInt(*p); |
284 | 2.14k | } else { |
285 | 0 | if (error) { |
286 | 0 | *error = |
287 | 0 | "Illegal escape sequence: Hex escape must be followed by 2 " |
288 | 0 | "hex digits but saw: \\" + |
289 | 0 | std::string(hex_start, 3); |
290 | 0 | } |
291 | | // Error offset was set to the start of the escape above the |
292 | | // switch. |
293 | 0 | return false; |
294 | 0 | } |
295 | 2.14k | } |
296 | 1.07k | p = hex_end; // p points at last digit. |
297 | 1.07k | if (is_bytes_literal) { |
298 | 372 | dest->push_back(static_cast<char>(ch)); |
299 | 701 | } else { |
300 | 701 | Utf8Encode(*dest, ch); |
301 | 701 | } |
302 | 1.07k | break; |
303 | 1.07k | } |
304 | 3.95k | case 'u': { |
305 | 3.95k | if (is_bytes_literal) { |
306 | 1.40k | if (error) { |
307 | 1.40k | *error = |
308 | 1.40k | std::string( |
309 | 1.40k | "Illegal escape sequence: Unicode escape sequence \\") + |
310 | 1.40k | *p + " cannot be used in bytes literals"; |
311 | 1.40k | } |
312 | | // Error offset was set to the start of the escape above the switch. |
313 | 1.40k | return false; |
314 | 1.40k | } |
315 | | // \uhhhh => Read 4 hex digits as a code point, |
316 | | // then write it as UTF-8 bytes. |
317 | 2.55k | char32_t cp = 0; |
318 | 2.55k | const char* hex_start = p; |
319 | 2.55k | if (p + 4 >= end) { |
320 | 0 | if (error) { |
321 | 0 | *error = |
322 | 0 | "Illegal escape sequence: \\u must be followed by 4 hex " |
323 | 0 | "digits but saw: \\" + |
324 | 0 | std::string(hex_start, end - p); |
325 | 0 | } |
326 | | // Error offset was set to the start of the escape above the switch. |
327 | 0 | return false; |
328 | 0 | } |
329 | 12.7k | for (int i = 0; i < 4; ++i) { |
330 | | // Look one char ahead. |
331 | 10.2k | if (absl::ascii_isxdigit(p[1])) { |
332 | 10.2k | cp = (cp << 4) + HexDigitToInt(*++p); // Advance p. |
333 | 10.2k | } else { |
334 | 0 | if (error) { |
335 | 0 | *error = |
336 | 0 | "Illegal escape sequence: \\u must be followed by 4 " |
337 | 0 | "hex digits but saw: \\" + |
338 | 0 | std::string(hex_start, 5); |
339 | 0 | } |
340 | | // Error offset was set to the start of the escape above the |
341 | | // switch. |
342 | 0 | return false; |
343 | 0 | } |
344 | 10.2k | } |
345 | 2.55k | if (!UnicodeIsValid(cp)) { |
346 | 979 | if (error) { |
347 | 979 | *error = "Illegal escape sequence: Unicode value \\" + |
348 | 979 | std::string(hex_start, 5) + " is invalid"; |
349 | 979 | } |
350 | | // Error offset was set to the start of the escape above the switch. |
351 | 979 | return false; |
352 | 979 | } |
353 | 1.57k | Utf8Encode(*dest, cp); |
354 | 1.57k | break; |
355 | 2.55k | } |
356 | 3.49k | case 'U': { |
357 | 3.49k | if (is_bytes_literal) { |
358 | 1.16k | if (error) { |
359 | 1.16k | *error = |
360 | 1.16k | std::string( |
361 | 1.16k | "Illegal escape sequence: Unicode escape sequence \\") + |
362 | 1.16k | *p + " cannot be used in bytes literals"; |
363 | 1.16k | } |
364 | 1.16k | return false; |
365 | 1.16k | } |
366 | | // \Uhhhhhhhh => convert 8 hex digits to UTF-8. Note that the |
367 | | // first two digits must be 00: The valid range is |
368 | | // '\U00000000' to '\U0010FFFF' (excluding surrogates). |
369 | 2.32k | char32_t cp = 0; |
370 | 2.32k | const char* hex_start = p; |
371 | 2.32k | if (p + 8 >= end) { |
372 | 0 | if (error) { |
373 | 0 | *error = |
374 | 0 | "Illegal escape sequence: \\U must be followed by 8 hex " |
375 | 0 | "digits but saw: \\" + |
376 | 0 | std::string(hex_start, end - p); |
377 | 0 | } |
378 | | // Error offset was set to the start of the escape above the switch. |
379 | 0 | return false; |
380 | 0 | } |
381 | 19.3k | for (int i = 0; i < 8; ++i) { |
382 | | // Look one char ahead. |
383 | 17.5k | if (absl::ascii_isxdigit(p[1])) { |
384 | 17.5k | cp = (cp << 4) + HexDigitToInt(*++p); |
385 | 17.5k | if (cp > 0x10FFFF) { |
386 | 537 | if (error) { |
387 | 537 | *error = "Illegal escape sequence: Value of \\" + |
388 | 537 | std::string(hex_start, 9) + |
389 | 537 | " exceeds Unicode limit (0x0010FFFF)"; |
390 | 537 | } |
391 | | // Error offset was set to the start of the escape above the |
392 | | // switch. |
393 | 537 | return false; |
394 | 537 | } |
395 | 17.5k | } else { |
396 | 0 | if (error) { |
397 | 0 | *error = |
398 | 0 | "Illegal escape sequence: \\U must be followed by 8 " |
399 | 0 | "hex digits but saw: \\" + |
400 | 0 | std::string(hex_start, 9); |
401 | 0 | } |
402 | | // Error offset was set to the start of the escape above the |
403 | | // switch. |
404 | 0 | return false; |
405 | 0 | } |
406 | 17.5k | } |
407 | 1.78k | if (!UnicodeIsValid(cp)) { |
408 | 556 | if (error) { |
409 | 556 | *error = "Illegal escape sequence: Unicode value \\" + |
410 | 556 | std::string(hex_start, 9) + " is invalid"; |
411 | 556 | } |
412 | | // Error offset was set to the start of the escape above the switch. |
413 | 556 | return false; |
414 | 556 | } |
415 | 1.23k | Utf8Encode(*dest, cp); |
416 | 1.23k | break; |
417 | 1.78k | } |
418 | 0 | case '\r': |
419 | 0 | ABSL_FALLTHROUGH_INTENDED; |
420 | 0 | case '\n': { |
421 | 0 | if (error) { |
422 | 0 | *error = "Illegal escaped newline"; |
423 | 0 | } |
424 | | // Error offset was set to the start of the escape above the switch. |
425 | 0 | return false; |
426 | 0 | } |
427 | 0 | default: { |
428 | 0 | if (error) { |
429 | 0 | *error = std::string("Illegal escape sequence: \\") + *p; |
430 | 0 | } |
431 | | // Error offset was set to the start of the escape above the switch. |
432 | 0 | return false; |
433 | 0 | } |
434 | 14.2k | } |
435 | 9.61k | p++; // read past letter we escaped |
436 | 9.61k | } |
437 | 4.05M | } |
438 | | |
439 | 215k | dest->shrink_to_fit(); |
440 | | |
441 | 215k | return true; |
442 | 220k | } |
443 | | |
444 | | [[nodiscard]] |
445 | | bool EscapeChar(std::string* dest, unsigned char c, bool escape_all_bytes, |
446 | 0 | char escape_quote_char, bool last_hex_escape) { |
447 | 0 | bool is_hex_escape = false; |
448 | 0 | switch (c) { |
449 | 0 | case '\n': |
450 | 0 | dest->append("\\n"); |
451 | 0 | break; |
452 | 0 | case '\r': |
453 | 0 | dest->append("\\r"); |
454 | 0 | break; |
455 | 0 | case '\t': |
456 | 0 | dest->append("\\t"); |
457 | 0 | break; |
458 | 0 | case '\\': |
459 | 0 | dest->append("\\\\"); |
460 | 0 | break; |
461 | 0 | case '\'': |
462 | 0 | ABSL_FALLTHROUGH_INTENDED; |
463 | 0 | case '\"': |
464 | 0 | ABSL_FALLTHROUGH_INTENDED; |
465 | 0 | case '`': |
466 | | // Escape only quote chars that match escape_quote_char. |
467 | 0 | if (escape_quote_char == 0 || c == escape_quote_char) { |
468 | 0 | dest->push_back('\\'); |
469 | 0 | } |
470 | 0 | dest->push_back(c); |
471 | 0 | break; |
472 | 0 | default: |
473 | | // Note that if we emit \xNN and the src character after that is a hex |
474 | | // digit then that digit must be escaped too to prevent it being |
475 | | // interpreted as part of the character code by C. |
476 | 0 | if ((!escape_all_bytes || c < 0x80) && |
477 | 0 | (!absl::ascii_isprint(c) || |
478 | 0 | (last_hex_escape && absl::ascii_isxdigit(c)))) { |
479 | 0 | dest->append("\\x"); |
480 | 0 | dest->push_back(kHexTable[c / 16]); |
481 | 0 | dest->push_back(kHexTable[c % 16]); |
482 | 0 | is_hex_escape = true; |
483 | 0 | } else { |
484 | 0 | dest->push_back(c); |
485 | 0 | break; |
486 | 0 | } |
487 | 0 | } |
488 | 0 | return is_hex_escape; |
489 | 0 | } |
490 | | |
491 | | template <typename Iter> |
492 | | void EscapeStringInternal(std::string* dest, Iter src_begin, const Iter src_end, |
493 | 0 | bool escape_all_bytes, char escape_quote_char) { |
494 | 0 | bool last_hex_escape = false; // true if last output char was \xNN. |
495 | 0 | for (; src_begin != src_end; ++src_begin) { |
496 | 0 | last_hex_escape = |
497 | 0 | EscapeChar(dest, static_cast<unsigned char>(*src_begin), |
498 | 0 | escape_all_bytes, escape_quote_char, last_hex_escape); |
499 | 0 | } |
500 | 0 | } Unexecuted instantiation: strings.cc:void cel::internal::(anonymous namespace)::EscapeStringInternal<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, char const*, bool, char) Unexecuted instantiation: strings.cc:void cel::internal::(anonymous namespace)::EscapeStringInternal<absl::lts_20260526::Cord::CharIterator>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, absl::lts_20260526::Cord::CharIterator, absl::lts_20260526::Cord::CharIterator, bool, char) |
501 | | |
502 | | void EscapeByte(std::string* dest, unsigned char b, bool escape_all_bytes, |
503 | 0 | char escape_quote_char) { |
504 | 0 | if (escape_all_bytes || !absl::ascii_isprint(b)) { |
505 | 0 | dest->append("\\x"); |
506 | 0 | dest->push_back(kHexTable[b / 16]); |
507 | 0 | dest->push_back(kHexTable[b % 16]); |
508 | 0 | } else { |
509 | 0 | switch (b) { |
510 | | // Note that we only handle printable escape characters here. All |
511 | | // unprintable (\n, \r, \t, etc.) are hex escaped above. |
512 | 0 | case '\\': |
513 | 0 | dest->append("\\\\"); |
514 | 0 | break; |
515 | 0 | case '\'': |
516 | 0 | case '"': |
517 | 0 | case '`': |
518 | | // Escape only quote chars that match escape_quote_char. |
519 | 0 | if (escape_quote_char == 0 || b == escape_quote_char) { |
520 | 0 | dest->push_back('\\'); |
521 | 0 | } |
522 | 0 | dest->push_back(static_cast<char>(b)); |
523 | 0 | break; |
524 | 0 | default: |
525 | 0 | dest->push_back(static_cast<char>(b)); |
526 | 0 | break; |
527 | 0 | } |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | | template <typename Iter> |
532 | | void EscapeBytesInternal(std::string* dest, Iter src_begin, const Iter src_end, |
533 | 0 | bool escape_all_bytes, char escape_quote_char) { |
534 | 0 | for (; src_begin != src_end; ++src_begin) { |
535 | 0 | EscapeByte(dest, static_cast<unsigned char>(*src_begin), escape_all_bytes, |
536 | 0 | escape_quote_char); |
537 | 0 | } |
538 | 0 | } Unexecuted instantiation: strings.cc:void cel::internal::(anonymous namespace)::EscapeBytesInternal<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, char const*, bool, char) Unexecuted instantiation: strings.cc:void cel::internal::(anonymous namespace)::EscapeBytesInternal<absl::lts_20260526::Cord::CharIterator>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, absl::lts_20260526::Cord::CharIterator, absl::lts_20260526::Cord::CharIterator, bool, char) |
539 | | |
540 | 230k | bool MayBeTripleQuotedString(absl::string_view str) { |
541 | 230k | return (str.size() >= 6 && |
542 | 10.2k | ((absl::StartsWith(str, "\"\"\"") && absl::EndsWith(str, "\"\"\"")) || |
543 | 8.90k | (absl::StartsWith(str, "'''") && absl::EndsWith(str, "'''")))); |
544 | 230k | } |
545 | | |
546 | 220k | bool MayBeStringLiteral(absl::string_view str) { |
547 | 220k | return (str.size() >= 2 && str[0] == str[str.size() - 1] && |
548 | 209k | (str[0] == '\'' || str[0] == '"')); |
549 | 220k | } |
550 | | |
551 | 9.70k | bool MayBeBytesLiteral(absl::string_view str) { |
552 | 9.70k | return (str.size() >= 3 && absl::StartsWithIgnoreCase(str, "b") && |
553 | 9.70k | str[1] == str[str.size() - 1] && (str[1] == '\'' || str[1] == '"')); |
554 | 9.70k | } |
555 | | |
556 | 220k | bool MayBeRawStringLiteral(absl::string_view str) { |
557 | 220k | return (str.size() >= 3 && absl::StartsWithIgnoreCase(str, "r") && |
558 | 10.9k | str[1] == str[str.size() - 1] && (str[1] == '\'' || str[1] == '"')); |
559 | 220k | } |
560 | | |
561 | 9.70k | bool MayBeRawBytesLiteral(absl::string_view str) { |
562 | 9.70k | return (str.size() >= 4 && |
563 | 6.36k | (absl::StartsWithIgnoreCase(str, "rb") || |
564 | 6.36k | absl::StartsWithIgnoreCase(str, "br")) && |
565 | 1.27k | (str[2] == str[str.size() - 1]) && (str[2] == '\'' || str[2] == '"')); |
566 | 9.70k | } |
567 | | |
568 | | } // namespace |
569 | | |
570 | 0 | absl::StatusOr<std::string> UnescapeString(absl::string_view str) { |
571 | 0 | std::string out; |
572 | 0 | std::string error; |
573 | 0 | if (!UnescapeInternal(str, "", false, false, &out, &error)) { |
574 | 0 | return absl::InvalidArgumentError( |
575 | 0 | absl::StrCat("Invalid escaped string: ", error)); |
576 | 0 | } |
577 | 0 | return out; |
578 | 0 | } |
579 | | |
580 | 0 | absl::StatusOr<std::string> UnescapeBytes(absl::string_view str) { |
581 | 0 | std::string out; |
582 | 0 | std::string error; |
583 | 0 | if (!UnescapeInternal(str, "", false, true, &out, &error)) { |
584 | 0 | return absl::InvalidArgumentError( |
585 | 0 | absl::StrCat("Invalid escaped bytes: ", error)); |
586 | 0 | } |
587 | 0 | return out; |
588 | 0 | } |
589 | | |
590 | 0 | std::string EscapeString(absl::string_view str) { |
591 | 0 | std::string dest; |
592 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
593 | | // byte. |
594 | 0 | dest.reserve(str.size() * 4); |
595 | 0 | EscapeStringInternal(&dest, str.begin(), str.end(), true, '\0'); |
596 | 0 | dest.shrink_to_fit(); |
597 | 0 | return dest; |
598 | 0 | } |
599 | | |
600 | | std::string EscapeBytes(absl::string_view str, bool escape_all_bytes, |
601 | 0 | char escape_quote_char) { |
602 | 0 | std::string dest; |
603 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
604 | | // byte. |
605 | 0 | dest.reserve(str.size() * 4); |
606 | 0 | EscapeBytesInternal(&dest, str.begin(), str.end(), escape_all_bytes, |
607 | 0 | escape_quote_char); |
608 | 0 | dest.shrink_to_fit(); |
609 | 0 | return dest; |
610 | 0 | } |
611 | | |
612 | 220k | absl::StatusOr<std::string> ParseStringLiteral(absl::string_view str) { |
613 | 220k | std::string out; |
614 | 220k | bool is_string_literal = MayBeStringLiteral(str); |
615 | 220k | bool is_raw_string_literal = MayBeRawStringLiteral(str); |
616 | 220k | if (!is_string_literal && !is_raw_string_literal) { |
617 | 0 | return absl::InvalidArgumentError("Invalid string literal"); |
618 | 0 | } |
619 | | |
620 | 220k | absl::string_view copy_str = str; |
621 | 220k | if (is_raw_string_literal) { |
622 | | // Strip off the prefix 'r' from the raw string content before parsing. |
623 | 10.9k | copy_str = absl::ClippedSubstr(copy_str, 1); |
624 | 10.9k | } |
625 | | |
626 | 220k | bool is_triple_quoted = MayBeTripleQuotedString(copy_str); |
627 | | // Starts after the opening quotes {""", '''} or {", '}. |
628 | 220k | int quotes_length = is_triple_quoted ? 3 : 1; |
629 | 220k | absl::string_view quotes = copy_str.substr(0, quotes_length); |
630 | 220k | copy_str = absl::ClippedSubstr(copy_str, quotes_length); |
631 | 220k | std::string error; |
632 | 220k | if (!UnescapeInternal(copy_str, quotes, is_raw_string_literal, false, &out, |
633 | 220k | &error)) { |
634 | 10.9k | return absl::InvalidArgumentError( |
635 | 10.9k | absl::StrCat("Invalid string literal: ", error)); |
636 | 10.9k | } |
637 | 209k | return out; |
638 | 220k | } |
639 | | |
640 | 9.70k | absl::StatusOr<std::string> ParseBytesLiteral(absl::string_view str) { |
641 | 9.70k | std::string out; |
642 | 9.70k | bool is_bytes_literal = MayBeBytesLiteral(str); |
643 | 9.70k | bool is_raw_bytes_literal = MayBeRawBytesLiteral(str); |
644 | 9.70k | if (!is_bytes_literal && !is_raw_bytes_literal) { |
645 | 0 | return absl::InvalidArgumentError("Invalid bytes literal"); |
646 | 0 | } |
647 | | |
648 | 9.70k | absl::string_view copy_str = str; |
649 | 9.70k | if (is_raw_bytes_literal) { |
650 | | // Strip off the prefix {"rb", "br"} from the raw bytes content before |
651 | 1.27k | copy_str = absl::ClippedSubstr(copy_str, 2); |
652 | 8.43k | } else { |
653 | | // Strip off the prefix 'b' from the bytes content before parsing. |
654 | 8.43k | copy_str = absl::ClippedSubstr(copy_str, 1); |
655 | 8.43k | } |
656 | | |
657 | 9.70k | bool is_triple_quoted = MayBeTripleQuotedString(copy_str); |
658 | | // Starts after the opening quotes {""", '''} or {", '}. |
659 | 9.70k | int quotes_length = is_triple_quoted ? 3 : 1; |
660 | 9.70k | absl::string_view quotes = copy_str.substr(0, quotes_length); |
661 | | // Includes the closing quotes. |
662 | 9.70k | copy_str = absl::ClippedSubstr(copy_str, quotes_length); |
663 | 9.70k | std::string error; |
664 | 9.70k | if (!UnescapeInternal(copy_str, quotes, is_raw_bytes_literal, true, &out, |
665 | 9.70k | &error)) { |
666 | 3.48k | return absl::InvalidArgumentError( |
667 | 3.48k | absl::StrCat("Invalid bytes literal: ", error)); |
668 | 3.48k | } |
669 | 6.22k | return out; |
670 | 9.70k | } |
671 | | |
672 | 0 | std::string FormatStringLiteral(absl::string_view str) { |
673 | 0 | std::string dest; |
674 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
675 | | // byte. |
676 | 0 | dest.reserve(str.size() * 4 + 2); |
677 | 0 | FormatStringLiteralTo(str, &dest); |
678 | 0 | dest.shrink_to_fit(); |
679 | 0 | return dest; |
680 | 0 | } |
681 | | |
682 | 0 | std::string FormatStringLiteral(const absl::Cord& str) { |
683 | 0 | std::string dest; |
684 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
685 | | // byte. |
686 | 0 | dest.reserve(str.size() * 4 + 2); |
687 | 0 | FormatStringLiteralTo(str, &dest); |
688 | 0 | dest.shrink_to_fit(); |
689 | 0 | return dest; |
690 | 0 | } |
691 | | |
692 | 0 | void FormatStringLiteralTo(absl::string_view str, std::string* dest) { |
693 | 0 | const char quote = |
694 | 0 | absl::StrContains(str, '"') && !absl::StrContains(str, '\'') ? '\'' : '"'; |
695 | 0 | dest->push_back(quote); |
696 | 0 | EscapeStringInternal(dest, str.begin(), str.end(), true, quote); |
697 | 0 | dest->push_back(quote); |
698 | 0 | } |
699 | | |
700 | 0 | void FormatStringLiteralTo(const absl::Cord& str, std::string* dest) { |
701 | 0 | const char quote = (str.Contains("\"") && !str.Contains("'")) ? '\'' : '"'; |
702 | 0 | dest->push_back(quote); |
703 | 0 | EscapeStringInternal(dest, str.char_begin(), str.char_end(), true, quote); |
704 | 0 | dest->push_back(quote); |
705 | 0 | } |
706 | | |
707 | 0 | std::string FormatSingleQuotedStringLiteral(absl::string_view str) { |
708 | 0 | std::string dest; |
709 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
710 | | // byte. |
711 | 0 | dest.reserve(str.size() * 4 + 2); |
712 | 0 | FormatSingleQuotedStringLiteralTo(str, &dest); |
713 | 0 | dest.shrink_to_fit(); |
714 | 0 | return dest; |
715 | 0 | } |
716 | | |
717 | | void FormatSingleQuotedStringLiteralTo(absl::string_view str, |
718 | 0 | std::string* dest) { |
719 | 0 | dest->push_back('\''); |
720 | 0 | EscapeStringInternal(dest, str.begin(), str.end(), true, '\''); |
721 | 0 | dest->push_back('\''); |
722 | 0 | } |
723 | | |
724 | 0 | std::string FormatDoubleQuotedStringLiteral(absl::string_view str) { |
725 | 0 | std::string dest; |
726 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
727 | | // byte. |
728 | 0 | dest.reserve(str.size() * 4 + 2); |
729 | 0 | FormatDoubleQuotedStringLiteralTo(str, &dest); |
730 | 0 | dest.shrink_to_fit(); |
731 | 0 | return dest; |
732 | 0 | } |
733 | | |
734 | | void FormatDoubleQuotedStringLiteralTo(absl::string_view str, |
735 | 0 | std::string* dest) { |
736 | 0 | dest->push_back('"'); |
737 | 0 | EscapeStringInternal(dest, str.begin(), str.end(), true, '"'); |
738 | 0 | dest->push_back('"'); |
739 | 0 | } |
740 | | |
741 | 0 | std::string FormatBytesLiteral(absl::string_view str) { |
742 | 0 | std::string dest; |
743 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
744 | | // byte. |
745 | 0 | dest.reserve(str.size() * 4 + 3); |
746 | 0 | FormatBytesLiteralTo(str, &dest); |
747 | 0 | dest.shrink_to_fit(); |
748 | 0 | return dest; |
749 | 0 | } |
750 | | |
751 | 0 | std::string FormatBytesLiteral(const absl::Cord& str) { |
752 | 0 | std::string dest; |
753 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
754 | | // byte. |
755 | 0 | dest.reserve(str.size() * 4 + 3); |
756 | 0 | FormatBytesLiteralTo(str, &dest); |
757 | 0 | dest.shrink_to_fit(); |
758 | 0 | return dest; |
759 | 0 | } |
760 | | |
761 | 0 | void FormatBytesLiteralTo(absl::string_view str, std::string* dest) { |
762 | 0 | const char quote = |
763 | 0 | absl::StrContains(str, '"') && !absl::StrContains(str, '\'') ? '\'' : '"'; |
764 | 0 | dest->push_back('b'); |
765 | 0 | dest->push_back(quote); |
766 | 0 | EscapeBytesInternal(dest, str.begin(), str.end(), false, quote); |
767 | 0 | dest->push_back(quote); |
768 | 0 | } |
769 | | |
770 | 0 | void FormatBytesLiteralTo(const absl::Cord& str, std::string* dest) { |
771 | 0 | const char quote = (str.Contains("\"") && !str.Contains("'")) ? '\'' : '"'; |
772 | 0 | dest->push_back('b'); |
773 | 0 | dest->push_back(quote); |
774 | 0 | EscapeBytesInternal(dest, str.char_begin(), str.char_end(), false, quote); |
775 | 0 | dest->push_back(quote); |
776 | 0 | } |
777 | | |
778 | 0 | std::string FormatSingleQuotedBytesLiteral(absl::string_view str) { |
779 | 0 | std::string dest; |
780 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
781 | | // byte. |
782 | 0 | dest.reserve(str.size() * 4 + 3); |
783 | 0 | FormatSingleQuotedBytesLiteralTo(str, &dest); |
784 | 0 | dest.shrink_to_fit(); |
785 | 0 | return dest; |
786 | 0 | } |
787 | | |
788 | | void FormatSingleQuotedBytesLiteralTo(absl::string_view str, |
789 | 0 | std::string* dest) { |
790 | 0 | dest->append("b'"); |
791 | 0 | EscapeBytesInternal(dest, str.begin(), str.end(), false, '\''); |
792 | 0 | dest->push_back('\''); |
793 | 0 | } |
794 | | |
795 | 0 | std::string FormatDoubleQuotedBytesLiteral(absl::string_view str) { |
796 | 0 | std::string dest; |
797 | | // Worst case size is every byte has to be hex escaped, so 4 char for every |
798 | | // byte. |
799 | 0 | dest.reserve(str.size() * 4 + 3); |
800 | 0 | FormatDoubleQuotedBytesLiteralTo(str, &dest); |
801 | 0 | dest.shrink_to_fit(); |
802 | 0 | return dest; |
803 | 0 | } |
804 | | |
805 | | void FormatDoubleQuotedBytesLiteralTo(absl::string_view str, |
806 | 0 | std::string* dest) { |
807 | 0 | dest->append("b\""); |
808 | 0 | EscapeBytesInternal(dest, str.begin(), str.end(), false, '"'); |
809 | 0 | dest->push_back('"'); |
810 | 0 | } |
811 | | |
812 | 0 | absl::StatusOr<std::string> ParseIdentifier(absl::string_view str) { |
813 | 0 | if (!LexisIsIdentifier(str)) { |
814 | 0 | return absl::InvalidArgumentError("Invalid identifier"); |
815 | 0 | } |
816 | 0 | return std::string(str); |
817 | 0 | } |
818 | | |
819 | | } // namespace cel::internal |