Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/third_party/abseil-cpp/absl/strings/strings_gn/./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc
Warning:line 603, column 7
Value stored to 'szdest' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_strings_strings_gn0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/third_party/abseil-cpp/absl/strings/strings_gn -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/third_party/abseil-cpp/absl/strings/strings_gn -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D ABSL_ALLOCATOR_NOTHROW=1 -D DYNAMIC_ANNOTATIONS_ENABLED=1 -D USE_AURA=1 -D USE_GLIB=1 -D USE_OZONE=1 -D USE_UDEV -D _FILE_OFFSET_BITS=64 -D _LARGEFILE64_SOURCE -D _LARGEFILE_SOURCE -D _DEBUG -D _GNU_SOURCE -I /root/firefox-clang/third_party/abseil-cpp/absl/strings/strings_gn -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/third_party/abseil-cpp/absl/strings/strings_gn -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/third_party/abseil-cpp/gen -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/tools/profiler/public -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-comma -Wno-tautological-type-limit-compare -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_strings_strings_gn0.cpp
1// Copyright 2017 The Abseil Authors.
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 "absl/strings/escaping.h"
16
17#include <algorithm>
18#include <array>
19#include <cassert>
20#include <cstddef>
21#include <cstdint>
22#include <cstring>
23#include <iterator>
24#include <limits>
25#include <optional>
26#include <string>
27#include <utility>
28
29#include "absl/base/config.h"
30#include "absl/base/internal/endian.h"
31#include "absl/base/internal/raw_logging.h"
32#include "absl/base/internal/unaligned_access.h"
33#include "absl/base/macros.h"
34#include "absl/base/nullability.h"
35#include "absl/base/optimization.h"
36#include "absl/strings/ascii.h"
37#include "absl/strings/charset.h"
38#include "absl/strings/internal/append_and_overwrite.h"
39#include "absl/strings/internal/escaping.h"
40#include "absl/strings/internal/utf8.h"
41#include "absl/strings/numbers.h"
42#include "absl/strings/resize_and_overwrite.h"
43#include "absl/strings/str_cat.h"
44#include "absl/strings/string_view.h"
45
46namespace absl {
47ABSL_NAMESPACE_BEGIN
48namespace {
49
50// These are used for the leave_nulls_escaped argument to CUnescapeInternal().
51constexpr bool kUnescapeNulls = false;
52
53inline bool is_octal_digit(char c) { return ('0' <= c) && (c <= '7'); }
54
55inline unsigned int hex_digit_to_int(char c) {
56 static_assert('0' == 0x30 && 'A' == 0x41 && 'a' == 0x61,
57 "Character set must be ASCII.");
58 assert(absl::ascii_isxdigit(static_cast<unsigned char>(c)))(static_cast <bool> (absl::ascii_isxdigit(static_cast<
unsigned char>(c))) ? void (0) : __assert_fail ("absl::ascii_isxdigit(static_cast<unsigned char>(c))"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
59 unsigned int x = static_cast<unsigned char>(c);
60 if (x > '9') {
61 x += 9;
62 }
63 return x & 0xf;
64}
65
66inline char int_to_hex_digit(int i) {
67 assert(i >= 0 && i <= 15)(static_cast <bool> (i >= 0 && i <= 15) ?
void (0) : __assert_fail ("i >= 0 && i <= 15",
__builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
68 return ((i < 10) ? (static_cast<char>(i) + '0')
69 : (static_cast<char>(i - 10) + 'A'));
70}
71
72inline bool IsSurrogate(char32_t c, absl::string_view src,
73 std::string* absl_nullable error) {
74 if (c >= 0xD800 && c <= 0xDFFF) {
75 if (error) {
76 *error = absl::StrCat("invalid surrogate character (0xD800-DFFF): \\",
77 src);
78 }
79 return true;
80 }
81 return false;
82}
83
84// ----------------------------------------------------------------------
85// CUnescapeInternal()
86// Implements both CUnescape() and CUnescapeForNullTerminatedString().
87//
88// Unescapes C escape sequences and is the reverse of CEscape().
89//
90// If `src` is valid, stores the unescaped string in `dst` and the length of
91// unescaped string in `dst_size`, and returns true. Otherwise returns false
92// and optionally stores the error description in `error`. Set `error` to
93// nullptr to disable error reporting.
94//
95// `src` and `dst` may use the same underlying buffer (but keep in mind
96// that if this returns an error, it will leave both `src` and `dst` in
97// an unspecified state because they are using the same underlying buffer.)
98// `dst` must have at least as much space as `src`.
99// ----------------------------------------------------------------------
100
101bool CUnescapeInternal(absl::string_view src, bool leave_nulls_escaped,
102 char* absl_nonnull dst, size_t* absl_nonnull dst_size,
103 std::string* absl_nullable error) {
104 absl::string_view::size_type p = 0; // Current src position.
105 size_t d = 0; // Current dst position.
106
107 // When unescaping in-place, skip any prefix that does not have escaping.
108 if (src.data() == dst) {
109 while (p < src.size() && src[p] != '\\') p++, d++;
110 }
111
112 while (p < src.size()) {
113 if (src[p] != '\\') {
114 dst[d++] = src[p++];
115 } else {
116 if (++p >= src.size()) { // skip past the '\\'
117 if (error != nullptr) {
118 *error = "String cannot end with \\";
119 }
120 return false;
121 }
122 switch (src[p]) {
123 // clang-format off
124 case 'a': dst[d++] = '\a'; break;
125 case 'b': dst[d++] = '\b'; break;
126 case 'f': dst[d++] = '\f'; break;
127 case 'n': dst[d++] = '\n'; break;
128 case 'r': dst[d++] = '\r'; break;
129 case 't': dst[d++] = '\t'; break;
130 case 'v': dst[d++] = '\v'; break;
131 case '\\': dst[d++] = '\\'; break;
132 case '?': dst[d++] = '\?'; break;
133 case '\'': dst[d++] = '\''; break;
134 case '"': dst[d++] = '\"'; break;
135 // clang-format on
136 case '0':
137 case '1':
138 case '2':
139 case '3':
140 case '4':
141 case '5':
142 case '6':
143 case '7': {
144 // octal digit: 1 to 3 digits
145 auto octal_start = p;
146 unsigned int ch = static_cast<unsigned int>(src[p] - '0'); // digit 1
147 if (p + 1 < src.size() && is_octal_digit(src[p + 1]))
148 ch = ch * 8 + static_cast<unsigned int>(src[++p] - '0'); // digit 2
149 if (p + 1 < src.size() && is_octal_digit(src[p + 1]))
150 ch = ch * 8 + static_cast<unsigned int>(src[++p] - '0'); // digit 3
151 if (ch > 0xff) {
152 if (error != nullptr) {
153 *error =
154 "Value of \\" +
155 std::string(src.substr(octal_start, p + 1 - octal_start)) +
156 " exceeds 0xff";
157 }
158 return false;
159 }
160 if ((ch == 0) && leave_nulls_escaped) {
161 // Copy the escape sequence for the null character
162 dst[d++] = '\\';
163 while (octal_start <= p) {
164 dst[d++] = src[octal_start++];
165 }
166 break;
167 }
168 dst[d++] = static_cast<char>(ch);
169 break;
170 }
171 case 'x':
172 case 'X': {
173 if (p + 1 >= src.size()) {
174 if (error != nullptr) {
175 *error = "String cannot end with \\x";
176 }
177 return false;
178 } else if (!absl::ascii_isxdigit(
179 static_cast<unsigned char>(src[p + 1]))) {
180 if (error != nullptr) {
181 *error = "\\x cannot be followed by a non-hex digit";
182 }
183 return false;
184 }
185 unsigned int ch = 0;
186 auto hex_start = p;
187 while (p + 1 < src.size() &&
188 absl::ascii_isxdigit(static_cast<unsigned char>(src[p + 1]))) {
189 // Arbitrarily many hex digits
190 ch = (ch << 4) + hex_digit_to_int(src[++p]);
191 // If ch was 0xFF at the start of this loop, the most can it can be
192 // here is (0xFF << 4) + 0xF, which is 4095, thus ch cannot overflow
193 // 32-bits here. The check below is sufficient.
194 if (ch > 0xFF) {
195 if (error != nullptr) {
196 *error = "Value of \\" +
197 std::string(src.substr(hex_start, p + 1 - hex_start)) +
198 " exceeds 0xff";
199 }
200 return false;
201 }
202 }
203 if ((ch == 0) && leave_nulls_escaped) {
204 // Copy the escape sequence for the null character
205 dst[d++] = '\\';
206 while (hex_start <= p) {
207 dst[d++] = src[hex_start++];
208 }
209 break;
210 }
211 dst[d++] = static_cast<char>(ch);
212 break;
213 }
214 case 'u': {
215 // \uhhhh => convert 4 hex digits to UTF-8
216 char32_t rune = 0;
217 auto hex_start = p;
218 if (p + 4 >= src.size()) {
219 if (error != nullptr) {
220 *error = "\\u must be followed by 4 hex digits";
221 }
222 return false;
223 }
224 for (int i = 0; i < 4; ++i) {
225 // Look one char ahead.
226 if (absl::ascii_isxdigit(static_cast<unsigned char>(src[p + 1]))) {
227 rune = (rune << 4) + hex_digit_to_int(src[++p]);
228 } else {
229 if (error != nullptr) {
230 *error = "\\u must be followed by 4 hex digits: \\" +
231 std::string(src.substr(hex_start, p + 1 - hex_start));
232 }
233 return false;
234 }
235 }
236 if ((rune == 0) && leave_nulls_escaped) {
237 // Copy the escape sequence for the null character
238 dst[d++] = '\\';
239 while (hex_start <= p) {
240 dst[d++] = src[hex_start++];
241 }
242 break;
243 }
244 if (IsSurrogate(rune, src.substr(hex_start, 5), error)) {
245 return false;
246 }
247 d += strings_internal::EncodeUTF8Char(dst + d, rune);
248 break;
249 }
250 case 'U': {
251 // \Uhhhhhhhh => convert 8 hex digits to UTF-8
252 char32_t rune = 0;
253 auto hex_start = p;
254 if (p + 8 >= src.size()) {
255 if (error != nullptr) {
256 *error = "\\U must be followed by 8 hex digits";
257 }
258 return false;
259 }
260 for (int i = 0; i < 8; ++i) {
261 // Look one char ahead.
262 if (absl::ascii_isxdigit(static_cast<unsigned char>(src[p + 1]))) {
263 // Don't change rune until we're sure this
264 // is within the Unicode limit, but do advance p.
265 uint32_t newrune = (rune << 4) + hex_digit_to_int(src[++p]);
266 if (newrune > 0x10FFFF) {
267 if (error != nullptr) {
268 *error =
269 "Value of \\" +
270 std::string(src.substr(hex_start, p + 1 - hex_start)) +
271 " exceeds Unicode limit (0x10FFFF)";
272 }
273 return false;
274 } else {
275 rune = newrune;
276 }
277 } else {
278 if (error != nullptr) {
279 *error = "\\U must be followed by 8 hex digits: \\" +
280 std::string(src.substr(hex_start, p + 1 - hex_start));
281 }
282 return false;
283 }
284 }
285 if ((rune == 0) && leave_nulls_escaped) {
286 // Copy the escape sequence for the null character
287 dst[d++] = '\\';
288 // U00000000
289 while (hex_start <= p) {
290 dst[d++] = src[hex_start++];
291 }
292 break;
293 }
294 if (IsSurrogate(rune, src.substr(hex_start, 9), error)) {
295 return false;
296 }
297 d += strings_internal::EncodeUTF8Char(dst + d, rune);
298 break;
299 }
300 default: {
301 if (error != nullptr) {
302 *error = std::string("Unknown escape sequence: \\") + src[p];
303 }
304 return false;
305 }
306 }
307 p++; // Read past letter we escaped.
308 }
309 }
310
311 *dst_size = d;
312 return true;
313}
314
315// ----------------------------------------------------------------------
316// CEscape()
317// CHexEscape()
318// Utf8SafeCEscape()
319// Utf8SafeCHexEscape()
320// Escapes 'src' using C-style escape sequences. This is useful for
321// preparing query flags. The 'Hex' version uses hexadecimal rather than
322// octal sequences. The 'Utf8Safe' version does not touch UTF-8 bytes.
323//
324// Escaped chars: \n, \r, \t, ", ', \, and !absl::ascii_isprint().
325// ----------------------------------------------------------------------
326std::string CEscapeInternal(absl::string_view src, bool use_hex,
327 bool utf8_safe) {
328 std::string dest;
329 bool last_hex_escape = false; // true if last output char was \xNN.
330
331 for (char c : src) {
332 bool is_hex_escape = false;
333 switch (c) {
334 case '\n': dest.append("\\" "n"); break;
335 case '\r': dest.append("\\" "r"); break;
336 case '\t': dest.append("\\" "t"); break;
337 case '\"': dest.append("\\" "\""); break;
338 case '\'': dest.append("\\" "'"); break;
339 case '\\': dest.append("\\" "\\"); break;
340 default: {
341 // Note that if we emit \xNN and the src character after that is a hex
342 // digit then that digit must be escaped too to prevent it being
343 // interpreted as part of the character code by C.
344 const unsigned char uc = static_cast<unsigned char>(c);
345 if ((!utf8_safe || uc < 0x80) &&
346 (!absl::ascii_isprint(uc) ||
347 (last_hex_escape && absl::ascii_isxdigit(uc)))) {
348 if (use_hex) {
349 dest.append("\\" "x");
350 dest.push_back(numbers_internal::kHexChar[uc / 16]);
351 dest.push_back(numbers_internal::kHexChar[uc % 16]);
352 is_hex_escape = true;
353 } else {
354 dest.append("\\");
355 dest.push_back(numbers_internal::kHexChar[uc / 64]);
356 dest.push_back(numbers_internal::kHexChar[(uc % 64) / 8]);
357 dest.push_back(numbers_internal::kHexChar[uc % 8]);
358 }
359 } else {
360 dest.push_back(c);
361 break;
362 }
363 }
364 }
365 last_hex_escape = is_hex_escape;
366 }
367
368 return dest;
369}
370
371/* clang-format off */
372constexpr std::array<unsigned char, 256> kCEscapedLen = {
373 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4, // \t, \n, \r
374 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
375 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // ", '
376 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // '0'..'9'
377 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'A'..'O'
378 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, // 'P'..'Z', '\'
379 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'a'..'o'
380 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, // 'p'..'z', DEL
381 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
382 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
383 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
384 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
385 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
386 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
387 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
388 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
389};
390/* clang-format on */
391
392constexpr std::array<std::array<char, 4>, 256> kCEscapedSequence = []() {
393 std::array<std::array<char, 4>, 256> a{};
394 for (size_t c = 0; c < 256; ++c) {
395 size_t char_len = kCEscapedLen[c];
396 if (char_len == 1) {
397 a[c][0] = static_cast<char>(c);
398 } else if (char_len == 2) {
399 a[c][0] = '\\';
400 // clang-format off
401 switch (c) {
402 case '\n': a[c][1] = 'n'; break;
403 case '\r': a[c][1] = 'r'; break;
404 case '\t': a[c][1] = 't'; break;
405 case '\"': a[c][1] = '\"'; break;
406 case '\'': a[c][1] = '\''; break;
407 case '\\': a[c][1] = '\\'; break;
408 }
409 // clang-format on
410 } else {
411 assert(char_len == 4)(static_cast <bool> (char_len == 4) ? void (0) : __assert_fail
("char_len == 4", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
412 // A backslash followed by the octal value of the byte.
413 a[c][0] = '\\';
414 a[c][1] = static_cast<char>('0' + (c / 64));
415 a[c][2] = static_cast<char>('0' + ((c % 64) / 8));
416 a[c][3] = static_cast<char>('0' + (c % 8));
417 }
418 }
419 return a;
420}();
421
422// Calculates the length of the C-style escaped version of 'src'.
423// Assumes that non-printable characters are escaped using octal sequences, and
424// that UTF-8 bytes are not handled specially.
425inline size_t CEscapedLength(absl::string_view src) {
426 size_t escaped_len = 0;
427 // The maximum value of kCEscapedLen[x] is 4, so we can escape any string of
428 // length size_t_max/4 without checking for overflow.
429 size_t unchecked_limit =
430 std::min<size_t>(src.size(), std::numeric_limits<size_t>::max() / 4);
431 size_t i = 0;
432 while (i < unchecked_limit) {
433 // Common case: No need to check for overflow.
434 escaped_len += kCEscapedLen[static_cast<unsigned char>(src[i++])];
435 }
436 while (i < src.size()) {
437 // Beyond unchecked_limit we need to check for overflow before adding.
438 size_t char_len = kCEscapedLen[static_cast<unsigned char>(src[i++])];
439 ABSL_INTERNAL_CHECK(do { if ((__builtin_expect(false || (!(escaped_len <= std::
numeric_limits<size_t>::max() - char_len)), false))) { std
::string death_message = "Check " "escaped_len <= std::numeric_limits<size_t>::max() - char_len"
" failed: "; death_message += std::string("escaped_len overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 441, death_message)
; do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
440 escaped_len <= std::numeric_limits<size_t>::max() - char_len,do { if ((__builtin_expect(false || (!(escaped_len <= std::
numeric_limits<size_t>::max() - char_len)), false))) { std
::string death_message = "Check " "escaped_len <= std::numeric_limits<size_t>::max() - char_len"
" failed: "; death_message += std::string("escaped_len overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 441, death_message)
; do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
441 "escaped_len overflow")do { if ((__builtin_expect(false || (!(escaped_len <= std::
numeric_limits<size_t>::max() - char_len)), false))) { std
::string death_message = "Check " "escaped_len <= std::numeric_limits<size_t>::max() - char_len"
" failed: "; death_message += std::string("escaped_len overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 441, death_message)
; do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
;
442 escaped_len += char_len;
443 }
444 return escaped_len;
445}
446
447void CEscapeAndAppendInternal(absl::string_view src,
448 std::string* absl_nonnull dest) {
449 size_t escaped_len = CEscapedLength(src);
450 if (escaped_len == src.size()) {
451 dest->append(src.data(), src.size());
452 return;
453 }
454
455 // The small `memcpy` is faster when the size is a compile-time constant, so
456 // keep 3 slop bytes so that we can call memcpy with size=4.
457 constexpr size_t kSlopBytes = 3;
458 ABSL_INTERNAL_CHECK(do { if ((__builtin_expect(false || (!(escaped_len <= std::
numeric_limits<size_t>::max() - kSlopBytes)), false))) {
std::string death_message = "Check " "escaped_len <= std::numeric_limits<size_t>::max() - kSlopBytes"
" failed: "; death_message += std::string("CEscape length overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 460, death_message)
; do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
459 escaped_len <= std::numeric_limits<size_t>::max() - kSlopBytes,do { if ((__builtin_expect(false || (!(escaped_len <= std::
numeric_limits<size_t>::max() - kSlopBytes)), false))) {
std::string death_message = "Check " "escaped_len <= std::numeric_limits<size_t>::max() - kSlopBytes"
" failed: "; death_message += std::string("CEscape length overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 460, death_message)
; do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
460 "CEscape length overflow")do { if ((__builtin_expect(false || (!(escaped_len <= std::
numeric_limits<size_t>::max() - kSlopBytes)), false))) {
std::string death_message = "Check " "escaped_len <= std::numeric_limits<size_t>::max() - kSlopBytes"
" failed: "; death_message += std::string("CEscape length overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 460, death_message)
; do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
;
461 size_t append_buf_len = escaped_len + kSlopBytes;
462 strings_internal::StringAppendAndOverwrite(
463 *dest, append_buf_len, [src, escaped_len](char* append_ptr, size_t) {
464 for (char c : src) {
465 unsigned char uc = static_cast<unsigned char>(c);
466 memcpy(append_ptr, kCEscapedSequence[uc].data(), 4);
467 append_ptr += kCEscapedLen[uc];
468 }
469 return escaped_len;
470 });
471}
472
473// The two strings below provide maps from normal 6-bit characters to their
474// base64-escaped equivalent.
475// For the inverse case, see kUn(WebSafe)Base64 in the external
476// escaping.cc.
477constexpr char kBase64Chars[] =
478 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
479
480constexpr char kWebSafeBase64Chars[] =
481 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
482
483// ----------------------------------------------------------------------
484// Take the input in groups of 4 characters and turn each
485// character into a code 0 to 63 thus:
486// A-Z map to 0 to 25
487// a-z map to 26 to 51
488// 0-9 map to 52 to 61
489// +(- for WebSafe) maps to 62
490// /(_ for WebSafe) maps to 63
491// There will be four numbers, all less than 64 which can be represented
492// by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
493// Arrange the 6 digit binary numbers into three bytes as such:
494// aaaaaabb bbbbcccc ccdddddd
495// Equals signs (one or two) are used at the end of the encoded block to
496// indicate that the text was not an integer multiple of three bytes long.
497// ----------------------------------------------------------------------
498size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest,
499 size_t szdest, const char* base64,
500 bool do_padding) {
501 constexpr char kPad64 = '=';
502
503 constexpr size_t kMaxSize = (std::numeric_limits<size_t>::max() - 1) / 4 * 3;
504 if (ABSL_PREDICT_FALSE(szsrc > kMaxSize || szsrc * 4 > szdest * 3)(__builtin_expect(false || (szsrc > kMaxSize || szsrc * 4 >
szdest * 3), false))
) return 0;
505
506 char* cur_dest = dest;
507 const unsigned char* cur_src = src;
508
509 char* const limit_dest = dest + szdest;
510 const unsigned char* const limit_src = src + szsrc;
511
512 // (from https://tools.ietf.org/html/rfc3548)
513 // Special processing is performed if fewer than 24 bits are available
514 // at the end of the data being encoded. A full encoding quantum is
515 // always completed at the end of a quantity. When fewer than 24 input
516 // bits are available in an input group, zero bits are added (on the
517 // right) to form an integral number of 6-bit groups.
518 //
519 // If do_padding is true, padding at the end of the data is performed. This
520 // output padding uses the '=' character.
521
522 // Three bytes of data encodes to four characters of cyphertext.
523 // So we can pump through three-byte chunks atomically.
524 if (szsrc >= 3) { // "limit_src - 3" is UB if szsrc < 3.
525 while (cur_src < limit_src - 3) { // While we have >= 32 bits.
526 uint32_t in = absl::big_endian::Load32(cur_src) >> 8;
527
528 cur_dest[0] = base64[in >> 18];
529 in &= 0x3FFFF;
530 cur_dest[1] = base64[in >> 12];
531 in &= 0xFFF;
532 cur_dest[2] = base64[in >> 6];
533 in &= 0x3F;
534 cur_dest[3] = base64[in];
535
536 cur_dest += 4;
537 cur_src += 3;
538 }
539 }
540 // To save time, we didn't update szdest or szsrc in the loop. So do it now.
541 szdest = static_cast<size_t>(limit_dest - cur_dest);
542 szsrc = static_cast<size_t>(limit_src - cur_src);
543
544 /* now deal with the tail (<=3 bytes) */
545 switch (szsrc) {
546 case 0:
547 // Nothing left; nothing more to do.
548 break;
549 case 1: {
550 // One byte left: this encodes to two characters, and (optionally)
551 // two pad characters to round out the four-character cypherblock.
552 if (szdest < 2) return 0;
553 uint32_t in = cur_src[0];
554 cur_dest[0] = base64[in >> 2];
555 in &= 0x3;
556 cur_dest[1] = base64[in << 4];
557 cur_dest += 2;
558 szdest -= 2;
559 if (do_padding) {
560 if (szdest < 2) return 0;
561 cur_dest[0] = kPad64;
562 cur_dest[1] = kPad64;
563 cur_dest += 2;
564 szdest -= 2;
565 }
566 break;
567 }
568 case 2: {
569 // Two bytes left: this encodes to three characters, and (optionally)
570 // one pad character to round out the four-character cypherblock.
571 if (szdest < 3) return 0;
572 uint32_t in = absl::big_endian::Load16(cur_src);
573 cur_dest[0] = base64[in >> 10];
574 in &= 0x3FF;
575 cur_dest[1] = base64[in >> 4];
576 in &= 0x00F;
577 cur_dest[2] = base64[in << 2];
578 cur_dest += 3;
579 szdest -= 3;
580 if (do_padding) {
581 if (szdest < 1) return 0;
582 cur_dest[0] = kPad64;
583 cur_dest += 1;
584 szdest -= 1;
585 }
586 break;
587 }
588 case 3: {
589 // Three bytes left: same as in the big loop above. We can't do this in
590 // the loop because the loop above always reads 4 bytes, and the fourth
591 // byte is past the end of the input.
592 if (szdest < 4) return 0;
593 uint32_t in =
594 (uint32_t{cur_src[0]} << 16) + absl::big_endian::Load16(cur_src + 1);
595 cur_dest[0] = base64[in >> 18];
596 in &= 0x3FFFF;
597 cur_dest[1] = base64[in >> 12];
598 in &= 0xFFF;
599 cur_dest[2] = base64[in >> 6];
600 in &= 0x3F;
601 cur_dest[3] = base64[in];
602 cur_dest += 4;
603 szdest -= 4;
Value stored to 'szdest' is never read
604 break;
605 }
606 default:
607 // Should not be reached: blocks of 4 bytes are handled
608 // in the while loop before this switch statement.
609 ABSL_RAW_LOG(FATAL, "Logic problem? szsrc = %zu", szsrc)do { constexpr const char* absl_raw_log_internal_basename = ::
absl::raw_log_internal::Basename("./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
, sizeof("./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
) - 1); ::absl::raw_log_internal::RawLog(::absl::LogSeverity::
kFatal, absl_raw_log_internal_basename, 609, "Logic problem? szsrc = %zu"
, szsrc); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0)
;
610 break;
611 }
612 return static_cast<size_t>(cur_dest - dest);
613}
614
615std::string Base64EscapeToStringInternal(const unsigned char* src, size_t szsrc,
616 bool do_padding,
617 const char* base64_chars) {
618 std::string escaped;
619 const size_t calc_escaped_size =
620 strings_internal::CalculateBase64EscapedLenInternal(szsrc, do_padding);
621 StringResizeAndOverwrite(
622 escaped, calc_escaped_size,
623 [src, szsrc, base64_chars, do_padding](char* buf, size_t buf_size) {
624 const size_t escaped_len = Base64EscapeInternal(
625 src, szsrc, buf, buf_size, base64_chars, do_padding);
626 assert(escaped_len == buf_size)(static_cast <bool> (escaped_len == buf_size) ? void (0
) : __assert_fail ("escaped_len == buf_size", __builtin_FILE (
), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
627 return escaped_len;
628 });
629 return escaped;
630}
631
632// Reverses the mapping in Base64EscapeInternal; see that method's
633// documentation for details of the mapping.
634bool Base64UnescapeInternal(const char* absl_nullable src_param, size_t szsrc,
635 char* absl_nullable dest, size_t szdest,
636 const std::array<signed char, 256>& unbase64,
637 size_t* absl_nonnull len) {
638 static const char kPad64Equals = '=';
639 static const char kPad64Dot = '.';
640
641 size_t destidx = 0;
642 int decode = 0;
643 int state = 0;
644 unsigned char ch = 0;
645 unsigned int temp = 0;
646
647 // If "char" is signed by default, using *src as an array index results in
648 // accessing negative array elements. Treat the input as a pointer to
649 // unsigned char to avoid this.
650 const unsigned char* src = reinterpret_cast<const unsigned char*>(src_param);
651
652 // The GET_INPUT macro gets the next input character, skipping
653 // over any whitespace, and stopping when we reach the end of the
654 // string or when we read any non-data character. The arguments are
655 // an arbitrary identifier (used as a label for goto) and the number
656 // of data bytes that must remain in the input to avoid aborting the
657 // loop.
658#define GET_INPUT(label, remain) \
659 label: \
660 --szsrc; \
661 ch = *src++; \
662 decode = unbase64[ch]; \
663 if (decode < 0) { \
664 if (absl::ascii_isspace(ch) && szsrc >= remain) goto label; \
665 state = 4 - remain; \
666 break; \
667 }
668
669 // if dest is null, we're just checking to see if it's legal input
670 // rather than producing output. (I suspect this could just be done
671 // with a regexp...). We duplicate the loop so this test can be
672 // outside it instead of in every iteration.
673
674 if (dest) {
675 // This loop consumes 4 input bytes and produces 3 output bytes
676 // per iteration. We can't know at the start that there is enough
677 // data left in the string for a full iteration, so the loop may
678 // break out in the middle; if so 'state' will be set to the
679 // number of input bytes read.
680
681 while (szsrc >= 4) {
682 // We'll start by optimistically assuming that the next four
683 // bytes of the string (src[0..3]) are four good data bytes
684 // (that is, no nulls, whitespace, padding chars, or illegal
685 // chars). We need to test src[0..2] for nulls individually
686 // before constructing temp to preserve the property that we
687 // never read past a null in the string (no matter how long
688 // szsrc claims the string is).
689
690 if (!src[0] || !src[1] || !src[2] ||
691 ((temp = ((unsigned(unbase64[src[0]]) << 18) |
692 (unsigned(unbase64[src[1]]) << 12) |
693 (unsigned(unbase64[src[2]]) << 6) |
694 (unsigned(unbase64[src[3]])))) &
695 0x80000000)) {
696 // Iff any of those four characters was bad (null, illegal,
697 // whitespace, padding), then temp's high bit will be set
698 // (because unbase64[] is -1 for all bad characters).
699 //
700 // We'll back up and resort to the slower decoder, which knows
701 // how to handle those cases.
702
703 GET_INPUT(first, 4);
704 temp = static_cast<unsigned char>(decode);
705 GET_INPUT(second, 3);
706 temp = (temp << 6) | static_cast<unsigned char>(decode);
707 GET_INPUT(third, 2);
708 temp = (temp << 6) | static_cast<unsigned char>(decode);
709 GET_INPUT(fourth, 1);
710 temp = (temp << 6) | static_cast<unsigned char>(decode);
711 } else {
712 // We really did have four good data bytes, so advance four
713 // characters in the string.
714
715 szsrc -= 4;
716 src += 4;
717 }
718
719 // temp has 24 bits of input, so write that out as three bytes.
720
721 if (destidx + 3 > szdest) return false;
722 dest[destidx + 2] = static_cast<char>(temp);
723 temp >>= 8;
724 dest[destidx + 1] = static_cast<char>(temp);
725 temp >>= 8;
726 dest[destidx] = static_cast<char>(temp);
727 destidx += 3;
728 }
729 } else {
730 while (szsrc >= 4) {
731 if (!src[0] || !src[1] || !src[2] ||
732 ((temp = ((unsigned(unbase64[src[0]]) << 18) |
733 (unsigned(unbase64[src[1]]) << 12) |
734 (unsigned(unbase64[src[2]]) << 6) |
735 (unsigned(unbase64[src[3]])))) &
736 0x80000000)) {
737 GET_INPUT(first_no_dest, 4);
738 GET_INPUT(second_no_dest, 3);
739 GET_INPUT(third_no_dest, 2);
740 GET_INPUT(fourth_no_dest, 1);
741 } else {
742 szsrc -= 4;
743 src += 4;
744 }
745 destidx += 3;
746 }
747 }
748
749#undef GET_INPUT
750
751 // if the loop terminated because we read a bad character, return
752 // now.
753 if (decode < 0 && ch != kPad64Equals && ch != kPad64Dot &&
754 !absl::ascii_isspace(ch))
755 return false;
756
757 if (ch == kPad64Equals || ch == kPad64Dot) {
758 // if we stopped by hitting an '=' or '.', un-read that character -- we'll
759 // look at it again when we count to check for the proper number of
760 // equals signs at the end.
761 ++szsrc;
762 --src;
763 } else {
764 // This loop consumes 1 input byte per iteration. It's used to
765 // clean up the 0-3 input bytes remaining when the first, faster
766 // loop finishes. 'temp' contains the data from 'state' input
767 // characters read by the first loop.
768 while (szsrc > 0) {
769 --szsrc;
770 ch = *src++;
771 decode = unbase64[ch];
772 if (decode < 0) {
773 if (absl::ascii_isspace(ch)) {
774 continue;
775 } else if (ch == kPad64Equals || ch == kPad64Dot) {
776 // back up one character; we'll read it again when we check
777 // for the correct number of pad characters at the end.
778 ++szsrc;
779 --src;
780 break;
781 } else {
782 return false;
783 }
784 }
785
786 // Each input character gives us six bits of output.
787 temp = (temp << 6) | static_cast<unsigned char>(decode);
788 ++state;
789 if (state == 4) {
790 // If we've accumulated 24 bits of output, write that out as
791 // three bytes.
792 if (dest) {
793 if (destidx + 3 > szdest) return false;
794 dest[destidx + 2] = static_cast<char>(temp);
795 temp >>= 8;
796 dest[destidx + 1] = static_cast<char>(temp);
797 temp >>= 8;
798 dest[destidx] = static_cast<char>(temp);
799 }
800 destidx += 3;
801 state = 0;
802 temp = 0;
803 }
804 }
805 }
806
807 // Process the leftover data contained in 'temp' at the end of the input.
808 int expected_equals = 0;
809 switch (state) {
810 case 0:
811 // Nothing left over; output is a multiple of 3 bytes.
812 break;
813
814 case 1:
815 // Bad input; we have 6 bits left over.
816 return false;
817
818 case 2:
819 // Produce one more output byte from the 12 input bits we have left.
820 if (dest) {
821 if (destidx + 1 > szdest) return false;
822 temp >>= 4;
823 dest[destidx] = static_cast<char>(temp);
824 }
825 ++destidx;
826 expected_equals = 2;
827 break;
828
829 case 3:
830 // Produce two more output bytes from the 18 input bits we have left.
831 if (dest) {
832 if (destidx + 2 > szdest) return false;
833 temp >>= 2;
834 dest[destidx + 1] = static_cast<char>(temp);
835 temp >>= 8;
836 dest[destidx] = static_cast<char>(temp);
837 }
838 destidx += 2;
839 expected_equals = 1;
840 break;
841
842 default:
843 // state should have no other values at this point.
844 ABSL_RAW_LOG(FATAL, "This can't happen; base64 decoder state = %d",do { constexpr const char* absl_raw_log_internal_basename = ::
absl::raw_log_internal::Basename("./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
, sizeof("./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
) - 1); ::absl::raw_log_internal::RawLog(::absl::LogSeverity::
kFatal, absl_raw_log_internal_basename, 845, "This can't happen; base64 decoder state = %d"
, state); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0)
845 state)do { constexpr const char* absl_raw_log_internal_basename = ::
absl::raw_log_internal::Basename("./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
, sizeof("./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
) - 1); ::absl::raw_log_internal::RawLog(::absl::LogSeverity::
kFatal, absl_raw_log_internal_basename, 845, "This can't happen; base64 decoder state = %d"
, state); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0)
;
846 }
847
848 // The remainder of the string should be all whitespace, mixed with
849 // exactly 0 equals signs, or exactly 'expected_equals' equals
850 // signs. (Always accepting 0 equals signs is an Abseil extension
851 // not covered in the RFC, as is accepting dot as the pad character.)
852
853 int equals = 0;
854 while (szsrc > 0) {
855 if (*src == kPad64Equals || *src == kPad64Dot)
856 ++equals;
857 else if (!absl::ascii_isspace(*src))
858 return false;
859 --szsrc;
860 ++src;
861 }
862
863 const bool ok = (equals == 0 || equals == expected_equals);
864 if (ok) *len = destidx;
865 return ok;
866}
867
868// The arrays below map base64-escaped characters back to their original values.
869// For the inverse case, see k(WebSafe)Base64Chars in the internal
870// escaping.cc.
871// These arrays were generated by the following inversion code:
872// #include <sys/time.h>
873// #include <stdlib.h>
874// #include <string.h>
875// main()
876// {
877// static const char Base64[] =
878// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
879// char* pos;
880// int idx, i, j;
881// printf(" ");
882// for (i = 0; i < 255; i += 8) {
883// for (j = i; j < i + 8; j++) {
884// pos = strchr(Base64, j);
885// if ((pos == nullptr) || (j == 0))
886// idx = -1;
887// else
888// idx = pos - Base64;
889// if (idx == -1)
890// printf(" %2d, ", idx);
891// else
892// printf(" %2d/*%c*/,", idx, j);
893// }
894// printf("\n ");
895// }
896// }
897//
898// where the value of "Base64[]" was replaced by one of k(WebSafe)Base64Chars
899// in the internal escaping.cc.
900/* clang-format off */
901constexpr std::array<signed char, 256> kUnBase64 = {
902 -1, -1, -1, -1, -1, -1, -1, -1,
903 -1, -1, -1, -1, -1, -1, -1, -1,
904 -1, -1, -1, -1, -1, -1, -1, -1,
905 -1, -1, -1, -1, -1, -1, -1, -1,
906 -1, -1, -1, -1, -1, -1, -1, -1,
907 -1, -1, -1, 62/*+*/, -1, -1, -1, 63/*/ */,
908 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
909 60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1,
910 -1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
911 07/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
912 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
913 23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, -1,
914 -1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
915 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
916 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
917 49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1,
918 -1, -1, -1, -1, -1, -1, -1, -1,
919 -1, -1, -1, -1, -1, -1, -1, -1,
920 -1, -1, -1, -1, -1, -1, -1, -1,
921 -1, -1, -1, -1, -1, -1, -1, -1,
922 -1, -1, -1, -1, -1, -1, -1, -1,
923 -1, -1, -1, -1, -1, -1, -1, -1,
924 -1, -1, -1, -1, -1, -1, -1, -1,
925 -1, -1, -1, -1, -1, -1, -1, -1,
926 -1, -1, -1, -1, -1, -1, -1, -1,
927 -1, -1, -1, -1, -1, -1, -1, -1,
928 -1, -1, -1, -1, -1, -1, -1, -1,
929 -1, -1, -1, -1, -1, -1, -1, -1,
930 -1, -1, -1, -1, -1, -1, -1, -1,
931 -1, -1, -1, -1, -1, -1, -1, -1,
932 -1, -1, -1, -1, -1, -1, -1, -1,
933 -1, -1, -1, -1, -1, -1, -1, -1
934};
935
936constexpr std::array<signed char, 256> kUnWebSafeBase64 = {
937 -1, -1, -1, -1, -1, -1, -1, -1,
938 -1, -1, -1, -1, -1, -1, -1, -1,
939 -1, -1, -1, -1, -1, -1, -1, -1,
940 -1, -1, -1, -1, -1, -1, -1, -1,
941 -1, -1, -1, -1, -1, -1, -1, -1,
942 -1, -1, -1, -1, -1, 62/*-*/, -1, -1,
943 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
944 60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1,
945 -1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
946 07/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
947 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
948 23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, 63/*_*/,
949 -1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
950 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
951 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
952 49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1,
953 -1, -1, -1, -1, -1, -1, -1, -1,
954 -1, -1, -1, -1, -1, -1, -1, -1,
955 -1, -1, -1, -1, -1, -1, -1, -1,
956 -1, -1, -1, -1, -1, -1, -1, -1,
957 -1, -1, -1, -1, -1, -1, -1, -1,
958 -1, -1, -1, -1, -1, -1, -1, -1,
959 -1, -1, -1, -1, -1, -1, -1, -1,
960 -1, -1, -1, -1, -1, -1, -1, -1,
961 -1, -1, -1, -1, -1, -1, -1, -1,
962 -1, -1, -1, -1, -1, -1, -1, -1,
963 -1, -1, -1, -1, -1, -1, -1, -1,
964 -1, -1, -1, -1, -1, -1, -1, -1,
965 -1, -1, -1, -1, -1, -1, -1, -1,
966 -1, -1, -1, -1, -1, -1, -1, -1,
967 -1, -1, -1, -1, -1, -1, -1, -1,
968 -1, -1, -1, -1, -1, -1, -1, -1
969};
970/* clang-format on */
971
972template <typename String>
973bool Base64UnescapeInternal(const char* absl_nullable src, size_t slen,
974 String* absl_nonnull dest,
975 const std::array<signed char, 256>& unbase64) {
976 // Determine the size of the output string. Base64 encodes every 3 bytes into
977 // 4 characters. Any leftover chars are added directly for good measure.
978 const size_t dest_len = 3 * (slen / 4) + (slen % 4);
979
980 bool ok;
981 StringResizeAndOverwrite(
982 *dest, dest_len, [src, slen, unbase64, &ok](char* buf, size_t buf_size) {
983 size_t len;
984 ok = Base64UnescapeInternal(src, slen, buf, buf_size, unbase64, &len);
985 if (!ok) {
986 len = 0;
987 }
988 assert(len <= buf_size)(static_cast <bool> (len <= buf_size) ? void (0) : __assert_fail
("len <= buf_size", __builtin_FILE (), __builtin_LINE (),
__extension__ __PRETTY_FUNCTION__))
; // Could be shorter if there was padding.
989 return len;
990 });
991 return ok;
992}
993
994/* clang-format off */
995constexpr std::array<uint8_t, 256> kHexValueLenient = {
996 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
997 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
998 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
999 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9'
1000 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F'
1001 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1002 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f'
1003 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1004 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1005 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1006 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1007 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1008 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1009 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1010 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1011 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1012};
1013
1014constexpr std::array<int8_t, 256> kHexValueStrict = {
1015 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1016 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1017 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1018 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // '0'..'9'
1019 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 'A'..'F'
1020 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1021 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 'a'..'f'
1022 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1023 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1024 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1025 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1026 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1027 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1028 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1029 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1030 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1031};
1032/* clang-format on */
1033
1034// This is a templated function so that T can be either a char*
1035// or a string. This works because we use the [] operator to access
1036// individual characters at a time.
1037template <typename T>
1038void HexStringToBytesInternal(const char* absl_nullable from, T to,
1039 size_t num) {
1040 for (size_t i = 0; i < num; i++) {
1041 to[i] = static_cast<char>(kHexValueLenient[from[i * 2] & 0xFF] << 4) +
1042 static_cast<char>(kHexValueLenient[from[i * 2 + 1] & 0xFF]);
1043 }
1044}
1045
1046void BytesToHexStringInternal(const unsigned char* absl_nullable src,
1047 char* dest, size_t num) {
1048 for (auto src_ptr = src; src_ptr != (src + num); ++src_ptr, dest += 2) {
1049 const char* hex_p = &numbers_internal::kHexTable[*src_ptr * 2];
1050 std::copy(hex_p, hex_p + 2, dest);
1051 }
1052}
1053
1054} // namespace
1055
1056// ----------------------------------------------------------------------
1057// CUnescape()
1058//
1059// See CUnescapeInternal() for implementation details.
1060// ----------------------------------------------------------------------
1061
1062bool CUnescape(absl::string_view source, std::string* absl_nonnull dest,
1063 std::string* absl_nullable error) {
1064 bool success;
1065
1066 // `CUnescape()` allows for in-place unescaping, which means `source` may
1067 // alias `*dest`. However, absl::StringResizeAndOverwrite() invalidates all
1068 // iterators, pointers, and references into the string, regardless whether
1069 // reallocation occurs. Therefore we need to avoid calling
1070 // absl::StringResizeAndOverwrite() when `source.data() ==
1071 // dest->data()`. Comparing the sizes is sufficient to cover this case.
1072 if (dest->size() >= source.size()) {
1073 size_t dest_size = 0;
1074 success = CUnescapeInternal(source, kUnescapeNulls, dest->data(),
1075 &dest_size, error);
1076 ABSL_ASSERT(dest_size <= dest->size())((__builtin_expect(false || ((dest_size <= dest->size()
)), true)) ? static_cast<void>(0) : (static_cast <bool
> (false && "dest_size <= dest->size()") ? void
(0) : __assert_fail ("false && \"dest_size <= dest->size()\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)))
;
1077 dest->erase(dest_size);
1078 } else {
1079 StringResizeAndOverwrite(
1080 *dest, source.size(),
1081 [source, error, &success](char* buf, size_t buf_size) {
1082 size_t dest_size = 0;
1083 success =
1084 CUnescapeInternal(source, kUnescapeNulls, buf, &dest_size, error);
1085 ABSL_ASSERT(dest_size <= buf_size)((__builtin_expect(false || ((dest_size <= buf_size)), true
)) ? static_cast<void>(0) : (static_cast <bool> (
false && "dest_size <= buf_size") ? void (0) : __assert_fail
("false && \"dest_size <= buf_size\"", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)))
;
1086 return dest_size;
1087 });
1088 }
1089 return success;
1090}
1091
1092std::string CEscape(absl::string_view src) {
1093 std::string dest;
1094 CEscapeAndAppendInternal(src, &dest);
1095 return dest;
1096}
1097
1098std::string CHexEscape(absl::string_view src) {
1099 return CEscapeInternal(src, true, false);
1100}
1101
1102std::string Utf8SafeCEscape(absl::string_view src) {
1103 return CEscapeInternal(src, false, true);
1104}
1105
1106std::string Utf8SafeCHexEscape(absl::string_view src) {
1107 return CEscapeInternal(src, true, true);
1108}
1109
1110bool Base64Unescape(absl::string_view src, std::string* absl_nonnull dest) {
1111 return Base64UnescapeInternal(src.data(), src.size(), dest, kUnBase64);
1112}
1113
1114bool WebSafeBase64Unescape(absl::string_view src,
1115 std::string* absl_nonnull dest) {
1116 return Base64UnescapeInternal(src.data(), src.size(), dest, kUnWebSafeBase64);
1117}
1118
1119std::string Base64Escape(absl::string_view src) {
1120 return Base64EscapeToStringInternal(
1121 reinterpret_cast<const unsigned char*>(src.data()), src.size(), true,
1122 kBase64Chars);
1123}
1124
1125std::string WebSafeBase64Escape(absl::string_view src) {
1126 return Base64EscapeToStringInternal(
1127 reinterpret_cast<const unsigned char*>(src.data()), src.size(), false,
1128 kWebSafeBase64Chars);
1129}
1130
1131bool HexStringToBytes(absl::string_view hex, std::string* absl_nonnull bytes) {
1132 std::string output;
1133
1134 size_t num_bytes = hex.size() / 2;
1135 if (hex.size() != num_bytes * 2) {
1136 return false;
1137 }
1138
1139 StringResizeAndOverwrite(
1140 output, num_bytes, [hex](char* buf, size_t buf_size) {
1141 auto hex_p = hex.cbegin();
1142 for (size_t i = 0; i < buf_size; ++i) {
1143 int h1 = absl::kHexValueStrict[static_cast<size_t>(
1144 static_cast<uint8_t>(*hex_p++))];
1145 int h2 = absl::kHexValueStrict[static_cast<size_t>(
1146 static_cast<uint8_t>(*hex_p++))];
1147 if (h1 == -1 || h2 == -1) {
1148 return size_t{0};
1149 }
1150 buf[i] = static_cast<char>((h1 << 4) + h2);
1151 }
1152 return buf_size;
1153 });
1154
1155 if (output.size() != num_bytes) {
1156 return false;
1157 }
1158 *bytes = std::move(output);
1159 return true;
1160}
1161
1162std::string HexStringToBytes(absl::string_view from) {
1163 std::string result;
1164 const auto num = from.size() / 2;
1165 StringResizeAndOverwrite(result, num, [from](char* buf, size_t buf_size) {
1166 absl::HexStringToBytesInternal<char*>(from.data(), buf, buf_size);
1167 return buf_size;
1168 });
1169 return result;
1170}
1171
1172std::string BytesToHexString(absl::string_view from) {
1173 std::string result;
1174 ABSL_INTERNAL_CHECK(from.size() <= std::numeric_limits<size_t>::max() / 2,do { if ((__builtin_expect(false || (!(from.size() <= std::
numeric_limits<size_t>::max() / 2)), false))) { std::string
death_message = "Check " "from.size() <= std::numeric_limits<size_t>::max() / 2"
" failed: "; death_message += std::string("BytesToHexString() overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 1175, death_message
); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
1175 "BytesToHexString() overflow")do { if ((__builtin_expect(false || (!(from.size() <= std::
numeric_limits<size_t>::max() / 2)), false))) { std::string
death_message = "Check " "from.size() <= std::numeric_limits<size_t>::max() / 2"
" failed: "; death_message += std::string("BytesToHexString() overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 1175, death_message
); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
;
1176 StringResizeAndOverwrite(
1177 result, 2 * from.size(), [from](char* buf, size_t buf_size) {
1178 absl::BytesToHexStringInternal(
1179 reinterpret_cast<const unsigned char*>(from.data()), buf,
1180 from.size());
1181 return buf_size;
1182 });
1183 return result;
1184}
1185
1186static std::string UrlEscapeInternal(absl::string_view input,
1187 const bool escape_space_to_plus) {
1188 // Unreserved characters from RFC 3986.
1189 // See https://www.rfc-editor.org/info/rfc3986/#section-2.3.
1190 static constexpr absl::CharSet kRfc3986Unreserved =
1191 absl::CharSet::AsciiAlphanumerics() | absl::CharSet("-._~");
1192
1193 std::string output;
1194 absl::string_view::iterator in = input.begin();
1195
1196 // Fast path for when we don't need to do any escaping.
1197 while (in < input.end() && kRfc3986Unreserved.contains(*in)) {
1198 ++in;
1199 }
1200
1201 std::size_t initial_portion =
1202 static_cast<std::size_t>(std::distance(input.begin(), in));
1203
1204 if (initial_portion == input.size()) {
1205 return std::string(input);
1206 }
1207
1208 // We need a buffer with enough space to store at most the initial portion
1209 // plus 3 bytes for each remaining character since escapes use 3 characters.
1210 ABSL_INTERNAL_CHECK(do { if ((__builtin_expect(false || (!((input.size() - initial_portion
) <= (std::numeric_limits<size_t>::max() - initial_portion
) / 3)), false))) { std::string death_message = "Check " "(input.size() - initial_portion) <= (std::numeric_limits<size_t>::max() - initial_portion) / 3"
" failed: "; death_message += std::string("UrlEscape() overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 1213, death_message
); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
1211 (input.size() - initial_portion) <=do { if ((__builtin_expect(false || (!((input.size() - initial_portion
) <= (std::numeric_limits<size_t>::max() - initial_portion
) / 3)), false))) { std::string death_message = "Check " "(input.size() - initial_portion) <= (std::numeric_limits<size_t>::max() - initial_portion) / 3"
" failed: "; death_message += std::string("UrlEscape() overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 1213, death_message
); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
1212 (std::numeric_limits<size_t>::max() - initial_portion) / 3,do { if ((__builtin_expect(false || (!((input.size() - initial_portion
) <= (std::numeric_limits<size_t>::max() - initial_portion
) / 3)), false))) { std::string death_message = "Check " "(input.size() - initial_portion) <= (std::numeric_limits<size_t>::max() - initial_portion) / 3"
" failed: "; death_message += std::string("UrlEscape() overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 1213, death_message
); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
1213 "UrlEscape() overflow")do { if ((__builtin_expect(false || (!((input.size() - initial_portion
) <= (std::numeric_limits<size_t>::max() - initial_portion
) / 3)), false))) { std::string death_message = "Check " "(input.size() - initial_portion) <= (std::numeric_limits<size_t>::max() - initial_portion) / 3"
" failed: "; death_message += std::string("UrlEscape() overflow"
); do { constexpr const char* absl_raw_log_internal_filename =
"./../../../../../../third_party/abseil-cpp/absl/strings/escaping.cc"
; ::absl::raw_log_internal::internal_log_function( ::absl::LogSeverity
::kFatal, absl_raw_log_internal_filename, 1213, death_message
); do { (static_cast <bool> (false && "ABSL_UNREACHABLE reached"
) ? void (0) : __assert_fail ("false && \"ABSL_UNREACHABLE reached\""
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); __builtin_unreachable(); } while (false); } while (0); } }
while (0)
;
1214 StringResizeAndOverwrite(
1215 output, initial_portion + 3 * (input.size() - initial_portion),
1216 [&](char* buf, size_t) {
1217 char* out = buf;
1218
1219 // Copy the initial portion that did not need escaping.
1220 out = std::copy(input.begin(), in, out);
1221
1222 // Handle the rest of the string.
1223 while (in < input.end()) {
1224 char c = *in++;
1225 if (kRfc3986Unreserved.contains(c)) {
1226 *out++ = c;
1227 } else if (escape_space_to_plus && c == ' ') {
1228 *out++ = '+';
1229 } else {
1230 *out++ = '%';
1231 *out++ = static_cast<char>(
1232 int_to_hex_digit((static_cast<unsigned char>(c) >> 4) & 0xf));
1233 *out++ = static_cast<char>(
1234 int_to_hex_digit(static_cast<unsigned char>(c) & 0xf));
1235 }
1236 }
1237 return static_cast<size_t>(std::distance(buf, out));
1238 });
1239
1240 return output;
1241}
1242
1243static std::optional<std::string> UrlUnescapeInternal(
1244 absl::string_view input, const bool unescape_plus_to_space) {
1245 std::string output;
1246
1247 // Fast path for when we don't need to do any unescaping.
1248 // This case includes empty input, which allows us to return 0 from the
1249 // lambda below to signal the error case.
1250 size_t in =
1251 unescape_plus_to_space ? input.find_first_of("%+") : input.find('%');
1252 if (in == input.npos) {
1253 return std::string(input);
1254 }
1255
1256 StringResizeAndOverwrite(output, input.size(), [&](char* buf, size_t) {
1257 char* out = buf;
1258
1259 // Copy the initial portion that did not need unescaping.
1260 out = std::copy_n(input.data(), in, out);
1261
1262 // Handle the rest of the string.
1263 while (in < input.size()) {
1264 char c = input[in++];
1265 if (unescape_plus_to_space && c == '+') {
1266 *out++ = ' ';
1267 } else if (c == '%') {
1268 if (in + 1 >= input.size() ||
1269 !absl::ascii_isxdigit(static_cast<unsigned char>(input[in])) ||
1270 !absl::ascii_isxdigit(static_cast<unsigned char>(input[in + 1]))) {
1271 return size_t{0}; // Error.
1272 }
1273 int x = static_cast<int>(hex_digit_to_int(input[in++])) << 4;
1274 x += static_cast<int>(hex_digit_to_int(input[in++]));
1275 *out++ = static_cast<char>(x);
1276 } else {
1277 *out++ = c;
1278 }
1279 }
1280 return static_cast<size_t>(std::distance(buf, out));
1281 });
1282
1283 if (output.empty()) {
1284 // Empty output is only valid if the input was empty, and that case is
1285 // handled above.
1286 return std::nullopt;
1287 }
1288
1289 return output;
1290}
1291
1292std::string UrlEscape(absl::string_view input) {
1293 constexpr bool kEscapeSpaceToPlus = false;
1294 return UrlEscapeInternal(input, kEscapeSpaceToPlus);
1295}
1296
1297std::optional<std::string> UrlUnescape(absl::string_view input) {
1298 constexpr bool kUnescapePlusToSpace = false;
1299 return UrlUnescapeInternal(input, kUnescapePlusToSpace);
1300}
1301
1302std::string UrlEscapePlus(absl::string_view input) {
1303 constexpr bool kEscapeSpaceToPlus = true;
1304 return UrlEscapeInternal(input, kEscapeSpaceToPlus);
1305}
1306
1307std::optional<std::string> UrlUnescapePlus(absl::string_view input) {
1308 constexpr bool kUnescapePlusToSpace = true;
1309 return UrlUnescapeInternal(input, kUnescapePlusToSpace);
1310}
1311
1312ABSL_NAMESPACE_END
1313} // namespace absl