#include "absl/strings/escaping.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <limits>
#include <string>
#include "absl/base/internal/endian.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/unaligned_access.h"
#include "absl/strings/internal/char_map.h"
#include "absl/strings/internal/escaping.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/internal/utf8.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace {
constexpr bool kUnescapeNulls = false;
inline bool is_octal_digit(char c) { return ('0' <= c) && (c <= '7'); }
inline int hex_digit_to_int(char c) {
static_assert('0' == 0x30 && 'A' == 0x41 && 'a' == 0x61,
"Character set must be ASCII.");
assert(absl::ascii_isxdigit(c));
int x = static_cast<unsigned char>(c);
if (x > '9') {
x += 9;
}
return x & 0xf;
}
inline bool IsSurrogate(char32_t c, absl::string_view src, std::string* error) {
if (c >= 0xD800 && c <= 0xDFFF) {
if (error) {
*error = absl::StrCat("invalid surrogate character (0xD800-DFFF): \\",
src);
}
return true;
}
return false;
}
bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
char* dest, ptrdiff_t* dest_len, std::string* error) {
char* d = dest;
const char* p = source.data();
const char* end = p + source.size();
const char* last_byte = end - 1;
while (p == d && p < end && *p != '\\') p++, d++;
while (p < end) {
if (*p != '\\') {
*d++ = *p++;
} else {
if (++p > last_byte) {
if (error) *error = "String cannot end with \\";
return false;
}
switch (*p) {
case 'a': *d++ = '\a'; break;
case 'b': *d++ = '\b'; break;
case 'f': *d++ = '\f'; break;
case 'n': *d++ = '\n'; break;
case 'r': *d++ = '\r'; break;
case 't': *d++ = '\t'; break;
case 'v': *d++ = '\v'; break;
case '\\': *d++ = '\\'; break;
case '?': *d++ = '\?'; break;
case '\'': *d++ = '\''; break;
case '"': *d++ = '\"'; break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': {
const char* octal_start = p;
unsigned int ch = *p - '0';
if (p < last_byte && is_octal_digit(p[1])) ch = ch * 8 + *++p - '0';
if (p < last_byte && is_octal_digit(p[1]))
ch = ch * 8 + *++p - '0';
if (ch > 0xff) {
if (error) {
*error = "Value of \\" +
std::string(octal_start, p + 1 - octal_start) +
" exceeds 0xff";
}
return false;
}
if ((ch == 0) && leave_nulls_escaped) {
const ptrdiff_t octal_size = p + 1 - octal_start;
*d++ = '\\';
memmove(d, octal_start, octal_size);
d += octal_size;
break;
}
*d++ = ch;
break;
}
case 'x':
case 'X': {
if (p >= last_byte) {
if (error) *error = "String cannot end with \\x";
return false;
} else if (!absl::ascii_isxdigit(p[1])) {
if (error) *error = "\\x cannot be followed by a non-hex digit";
return false;
}
unsigned int ch = 0;
const char* hex_start = p;
while (p < last_byte && absl::ascii_isxdigit(p[1]))
ch = (ch << 4) + hex_digit_to_int(*++p);
if (ch > 0xFF) {
if (error) {
*error = "Value of \\" +
std::string(hex_start, p + 1 - hex_start) +
" exceeds 0xff";
}
return false;
}
if ((ch == 0) && leave_nulls_escaped) {
const ptrdiff_t hex_size = p + 1 - hex_start;
*d++ = '\\';
memmove(d, hex_start, hex_size);
d += hex_size;
break;
}
*d++ = ch;
break;
}
case 'u': {
char32_t rune = 0;
const char* hex_start = p;
if (p + 4 >= end) {
if (error) {
*error = "\\u must be followed by 4 hex digits: \\" +
std::string(hex_start, p + 1 - hex_start);
}
return false;
}
for (int i = 0; i < 4; ++i) {
if (absl::ascii_isxdigit(p[1])) {
rune = (rune << 4) + hex_digit_to_int(*++p);
} else {
if (error) {
*error = "\\u must be followed by 4 hex digits: \\" +
std::string(hex_start, p + 1 - hex_start);
}
return false;
}
}
if ((rune == 0) && leave_nulls_escaped) {
*d++ = '\\';
memmove(d, hex_start, 5);
d += 5;
break;
}
if (IsSurrogate(rune, absl::string_view(hex_start, 5), error)) {
return false;
}
d += strings_internal::EncodeUTF8Char(d, rune);
break;
}
case 'U': {
char32_t rune = 0;
const char* hex_start = p;
if (p + 8 >= end) {
if (error) {
*error = "\\U must be followed by 8 hex digits: \\" +
std::string(hex_start, p + 1 - hex_start);
}
return false;
}
for (int i = 0; i < 8; ++i) {
if (absl::ascii_isxdigit(p[1])) {
uint32_t newrune = (rune << 4) + hex_digit_to_int(*++p);
if (newrune > 0x10FFFF) {
if (error) {
*error = "Value of \\" +
std::string(hex_start, p + 1 - hex_start) +
" exceeds Unicode limit (0x10FFFF)";
}
return false;
} else {
rune = newrune;
}
} else {
if (error) {
*error = "\\U must be followed by 8 hex digits: \\" +
std::string(hex_start, p + 1 - hex_start);
}
return false;
}
}
if ((rune == 0) && leave_nulls_escaped) {
*d++ = '\\';
memmove(d, hex_start, 9);
d += 9;
break;
}
if (IsSurrogate(rune, absl::string_view(hex_start, 9), error)) {
return false;
}
d += strings_internal::EncodeUTF8Char(d, rune);
break;
}
default: {
if (error) *error = std::string("Unknown escape sequence: \\") + *p;
return false;
}
}
p++;
}
}
*dest_len = d - dest;
return true;
}
bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped,
std::string* dest, std::string* error) {
strings_internal::STLStringResizeUninitialized(dest, source.size());
ptrdiff_t dest_size;
if (!CUnescapeInternal(source,
leave_nulls_escaped,
&(*dest)[0],
&dest_size,
error)) {
return false;
}
dest->erase(dest_size);
return true;
}
std::string CEscapeInternal(absl::string_view src, bool use_hex,
bool utf8_safe) {
std::string dest;
bool last_hex_escape = false;
for (unsigned char c : src) {
bool is_hex_escape = false;
switch (c) {
case '\n': dest.append("\\" "n"); break;
case '\r': dest.append("\\" "r"); break;
case '\t': dest.append("\\" "t"); break;
case '\"': dest.append("\\" "\""); break;
case '\'': dest.append("\\" "'"); break;
case '\\': dest.append("\\" "\\"); break;
default:
if ((!utf8_safe || c < 0x80) &&
(!absl::ascii_isprint(c) ||
(last_hex_escape && absl::ascii_isxdigit(c)))) {
if (use_hex) {
dest.append("\\" "x");
dest.push_back(numbers_internal::kHexChar[c / 16]);
dest.push_back(numbers_internal::kHexChar[c % 16]);
is_hex_escape = true;
} else {
dest.append("\\");
dest.push_back(numbers_internal::kHexChar[c / 64]);
dest.push_back(numbers_internal::kHexChar[(c % 64) / 8]);
dest.push_back(numbers_internal::kHexChar[c % 8]);
}
} else {
dest.push_back(c);
break;
}
}
last_hex_escape = is_hex_escape;
}
return dest;
}
constexpr char c_escaped_len[256] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
};
inline size_t CEscapedLength(absl::string_view src) {
size_t escaped_len = 0;
for (unsigned char c : src) escaped_len += c_escaped_len[c];
return escaped_len;
}
void CEscapeAndAppendInternal(absl::string_view src, std::string* dest) {
size_t escaped_len = CEscapedLength(src);
if (escaped_len == src.size()) {
dest->append(src.data(), src.size());
return;
}
size_t cur_dest_len = dest->size();
strings_internal::STLStringResizeUninitialized(dest,
cur_dest_len + escaped_len);
char* append_ptr = &(*dest)[cur_dest_len];
for (unsigned char c : src) {
int char_len = c_escaped_len[c];
if (char_len == 1) {
*append_ptr++ = c;
} else if (char_len == 2) {
switch (c) {
case '\n':
*append_ptr++ = '\\';
*append_ptr++ = 'n';
break;
case '\r':
*append_ptr++ = '\\';
*append_ptr++ = 'r';
break;
case '\t':
*append_ptr++ = '\\';
*append_ptr++ = 't';
break;
case '\"':
*append_ptr++ = '\\';
*append_ptr++ = '\"';
break;
case '\'':
*append_ptr++ = '\\';
*append_ptr++ = '\'';
break;
case '\\':
*append_ptr++ = '\\';
*append_ptr++ = '\\';
break;
}
} else {
*append_ptr++ = '\\';
*append_ptr++ = '0' + c / 64;
*append_ptr++ = '0' + (c % 64) / 8;
*append_ptr++ = '0' + c % 8;
}
}
}
bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest,
size_t szdest, const signed char* unbase64,
size_t* len) {
static const char kPad64Equals = '=';
static const char kPad64Dot = '.';
size_t destidx = 0;
int decode = 0;
int state = 0;
unsigned int ch = 0;
unsigned int temp = 0;
const unsigned char* src = reinterpret_cast<const unsigned char*>(src_param);
#define GET_INPUT(label, remain) \
label: \
--szsrc; \
ch = *src++; \
decode = unbase64[ch]; \
if (decode < 0) { \
if (absl::ascii_isspace(ch) && szsrc >= remain) goto label; \
state = 4 - remain; \
break; \
}
if (dest) {
while (szsrc >= 4) {
if (!src[0] || !src[1] || !src[2] ||
((temp = ((unsigned(unbase64[src[0]]) << 18) |
(unsigned(unbase64[src[1]]) << 12) |
(unsigned(unbase64[src[2]]) << 6) |
(unsigned(unbase64[src[3]])))) &
0x80000000)) {
GET_INPUT(first, 4);
temp = decode;
GET_INPUT(second, 3);
temp = (temp << 6) | decode;
GET_INPUT(third, 2);
temp = (temp << 6) | decode;
GET_INPUT(fourth, 1);
temp = (temp << 6) | decode;
} else {
szsrc -= 4;
src += 4;
}
if (destidx + 3 > szdest) return false;
dest[destidx + 2] = temp;
temp >>= 8;
dest[destidx + 1] = temp;
temp >>= 8;
dest[destidx] = temp;
destidx += 3;
}
} else {
while (szsrc >= 4) {
if (!src[0] || !src[1] || !src[2] ||
((temp = ((unsigned(unbase64[src[0]]) << 18) |
(unsigned(unbase64[src[1]]) << 12) |
(unsigned(unbase64[src[2]]) << 6) |
(unsigned(unbase64[src[3]])))) &
0x80000000)) {
GET_INPUT(first_no_dest, 4);
GET_INPUT(second_no_dest, 3);
GET_INPUT(third_no_dest, 2);
GET_INPUT(fourth_no_dest, 1);
} else {
szsrc -= 4;
src += 4;
}
destidx += 3;
}
}
#undef GET_INPUT
if (decode < 0 && ch != kPad64Equals && ch != kPad64Dot &&
!absl::ascii_isspace(ch))
return false;
if (ch == kPad64Equals || ch == kPad64Dot) {
++szsrc;
--src;
} else {
while (szsrc > 0) {
--szsrc;
ch = *src++;
decode = unbase64[ch];
if (decode < 0) {
if (absl::ascii_isspace(ch)) {
continue;
} else if (ch == kPad64Equals || ch == kPad64Dot) {
++szsrc;
--src;
break;
} else {
return false;
}
}
temp = (temp << 6) | decode;
++state;
if (state == 4) {
if (dest) {
if (destidx + 3 > szdest) return false;
dest[destidx + 2] = temp;
temp >>= 8;
dest[destidx + 1] = temp;
temp >>= 8;
dest[destidx] = temp;
}
destidx += 3;
state = 0;
temp = 0;
}
}
}
int expected_equals = 0;
switch (state) {
case 0:
break;
case 1:
return false;
case 2:
if (dest) {
if (destidx + 1 > szdest) return false;
temp >>= 4;
dest[destidx] = temp;
}
++destidx;
expected_equals = 2;
break;
case 3:
if (dest) {
if (destidx + 2 > szdest) return false;
temp >>= 2;
dest[destidx + 1] = temp;
temp >>= 8;
dest[destidx] = temp;
}
destidx += 2;
expected_equals = 1;
break;
default:
ABSL_RAW_LOG(FATAL, "This can't happen; base64 decoder state = %d",
state);
}
int equals = 0;
while (szsrc > 0) {
if (*src == kPad64Equals || *src == kPad64Dot)
++equals;
else if (!absl::ascii_isspace(*src))
return false;
--szsrc;
++src;
}
const bool ok = (equals == 0 || equals == expected_equals);
if (ok) *len = destidx;
return ok;
}
constexpr signed char kUnBase64[] = {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6,
07, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
};
constexpr signed char kUnWebSafeBase64[] = {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 62, -1, -1,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6,
07, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, -1, -1, -1, -1, 63,
-1, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
};
constexpr char kWebSafeBase64Chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
template <typename String>
bool Base64UnescapeInternal(const char* src, size_t slen, String* dest,
const signed char* unbase64) {
const size_t dest_len = 3 * (slen / 4) + (slen % 4);
strings_internal::STLStringResizeUninitialized(dest, dest_len);
size_t len;
const bool ok =
Base64UnescapeInternal(src, slen, &(*dest)[0], dest_len, unbase64, &len);
if (!ok) {
dest->clear();
return false;
}
assert(len <= dest_len);
dest->erase(len);
return true;
}
constexpr char kHexValueLenient[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
template <typename T>
void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) {
for (int i = 0; i < num; i++) {
to[i] = (kHexValueLenient[from[i * 2] & 0xFF] << 4) +
(kHexValueLenient[from[i * 2 + 1] & 0xFF]);
}
}
template <typename T>
void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) {
auto dest_ptr = &dest[0];
for (auto src_ptr = src; src_ptr != (src + num); ++src_ptr, dest_ptr += 2) {
const char* hex_p = &numbers_internal::kHexTable[*src_ptr * 2];
std::copy(hex_p, hex_p + 2, dest_ptr);
}
}
}
bool CUnescape(absl::string_view source, std::string* dest,
std::string* error) {
return CUnescapeInternal(source, kUnescapeNulls, dest, error);
}
std::string CEscape(absl::string_view src) {
std::string dest;
CEscapeAndAppendInternal(src, &dest);
return dest;
}
std::string CHexEscape(absl::string_view src) {
return CEscapeInternal(src, true, false);
}
std::string Utf8SafeCEscape(absl::string_view src) {
return CEscapeInternal(src, false, true);
}
std::string Utf8SafeCHexEscape(absl::string_view src) {
return CEscapeInternal(src, true, true);
}
bool Base64Unescape(absl::string_view src, std::string* dest) {
return Base64UnescapeInternal(src.data(), src.size(), dest, kUnBase64);
}
bool WebSafeBase64Unescape(absl::string_view src, std::string* dest) {
return Base64UnescapeInternal(src.data(), src.size(), dest, kUnWebSafeBase64);
}
void Base64Escape(absl::string_view src, std::string* dest) {
strings_internal::Base64EscapeInternal(
reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest,
true, strings_internal::kBase64Chars);
}
void WebSafeBase64Escape(absl::string_view src, std::string* dest) {
strings_internal::Base64EscapeInternal(
reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest,
false, kWebSafeBase64Chars);
}
std::string Base64Escape(absl::string_view src) {
std::string dest;
strings_internal::Base64EscapeInternal(
reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest,
true, strings_internal::kBase64Chars);
return dest;
}
std::string WebSafeBase64Escape(absl::string_view src) {
std::string dest;
strings_internal::Base64EscapeInternal(
reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest,
false, kWebSafeBase64Chars);
return dest;
}
std::string HexStringToBytes(absl::string_view from) {
std::string result;
const auto num = from.size() / 2;
strings_internal::STLStringResizeUninitialized(&result, num);
absl::HexStringToBytesInternal<std::string&>(from.data(), result, num);
return result;
}
std::string BytesToHexString(absl::string_view from) {
std::string result;
strings_internal::STLStringResizeUninitialized(&result, 2 * from.size());
absl::BytesToHexStringInternal<std::string&>(
reinterpret_cast<const unsigned char*>(from.data()), result, from.size());
return result;
}
ABSL_NAMESPACE_END
}