|  | // Copyright 2022 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "string_util.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <iomanip> | 
|  | #include <sstream> | 
|  | #include <string> | 
|  |  | 
|  | #include <openssl/base64.h> | 
|  | #include <openssl/mem.h> | 
|  |  | 
|  | BSSL_NAMESPACE_BEGIN | 
|  | namespace string_util { | 
|  |  | 
|  | bool IsAscii(std::string_view str) { | 
|  | for (unsigned char c : str) { | 
|  | if (c > 127) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool IsEqualNoCase(std::string_view str1, std::string_view str2) { | 
|  | return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(), | 
|  | [](const unsigned char a, const unsigned char b) { | 
|  | return OPENSSL_tolower(a) == OPENSSL_tolower(b); | 
|  | }); | 
|  | } | 
|  |  | 
|  | bool EndsWithNoCase(std::string_view str, std::string_view suffix) { | 
|  | return suffix.size() <= str.size() && | 
|  | IsEqualNoCase(suffix, str.substr(str.size() - suffix.size())); | 
|  | } | 
|  |  | 
|  | bool StartsWithNoCase(std::string_view str, std::string_view prefix) { | 
|  | return prefix.size() <= str.size() && | 
|  | IsEqualNoCase(prefix, str.substr(0, prefix.size())); | 
|  | } | 
|  |  | 
|  | std::string FindAndReplace(std::string_view str, std::string_view find, | 
|  | std::string_view replace) { | 
|  | std::string ret; | 
|  |  | 
|  | if (find.empty()) { | 
|  | return std::string(str); | 
|  | } | 
|  | while (!str.empty()) { | 
|  | size_t index = str.find(find); | 
|  | if (index == std::string_view::npos) { | 
|  | ret.append(str); | 
|  | break; | 
|  | } | 
|  | ret.append(str.substr(0, index)); | 
|  | ret.append(replace); | 
|  | str = str.substr(index + find.size()); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | // TODO(bbe) get rid of this once we can c++20. | 
|  | bool EndsWith(std::string_view str, std::string_view suffix) { | 
|  | return suffix.size() <= str.size() && | 
|  | suffix == str.substr(str.size() - suffix.size()); | 
|  | } | 
|  |  | 
|  | // TODO(bbe) get rid of this once we can c++20. | 
|  | bool StartsWith(std::string_view str, std::string_view prefix) { | 
|  | return prefix.size() <= str.size() && prefix == str.substr(0, prefix.size()); | 
|  | } | 
|  |  | 
|  | std::string HexEncode(Span<const uint8_t> data) { | 
|  | std::ostringstream out; | 
|  | for (uint8_t b : data) { | 
|  | out << std::hex << std::setfill('0') << std::setw(2) << std::uppercase | 
|  | << int{b}; | 
|  | } | 
|  | return out.str(); | 
|  | } | 
|  |  | 
|  | // TODO(bbe) get rid of this once extracted to boringssl. Everything else | 
|  | // in third_party uses std::to_string | 
|  | std::string NumberToDecimalString(int i) { | 
|  | std::ostringstream out; | 
|  | out << std::dec << i; | 
|  | return out.str(); | 
|  | } | 
|  |  | 
|  | std::vector<std::string_view> SplitString(std::string_view str, | 
|  | char split_char) { | 
|  | std::vector<std::string_view> out; | 
|  |  | 
|  | if (str.empty()) { | 
|  | return out; | 
|  | } | 
|  |  | 
|  | while (true) { | 
|  | // Find end of current token | 
|  | size_t i = str.find(split_char); | 
|  |  | 
|  | // Add current token | 
|  | out.push_back(str.substr(0, i)); | 
|  |  | 
|  | if (i == str.npos) { | 
|  | // That was the last token | 
|  | break; | 
|  | } | 
|  | // Continue to next | 
|  | str = str.substr(i + 1); | 
|  | } | 
|  |  | 
|  | return out; | 
|  | } | 
|  |  | 
|  | static bool IsUnicodeWhitespace(char c) { | 
|  | return c == 9 || c == 10 || c == 11 || c == 12 || c == 13 || c == ' '; | 
|  | } | 
|  |  | 
|  | std::string CollapseWhitespaceASCII(std::string_view text, | 
|  | bool trim_sequences_with_line_breaks) { | 
|  | std::string result; | 
|  | result.resize(text.size()); | 
|  |  | 
|  | // Set flags to pretend we're already in a trimmed whitespace sequence, so we | 
|  | // will trim any leading whitespace. | 
|  | bool in_whitespace = true; | 
|  | bool already_trimmed = true; | 
|  |  | 
|  | int chars_written = 0; | 
|  | for (auto i = text.begin(); i != text.end(); ++i) { | 
|  | if (IsUnicodeWhitespace(*i)) { | 
|  | if (!in_whitespace) { | 
|  | // Reduce all whitespace sequences to a single space. | 
|  | in_whitespace = true; | 
|  | result[chars_written++] = L' '; | 
|  | } | 
|  | if (trim_sequences_with_line_breaks && !already_trimmed && | 
|  | ((*i == '\n') || (*i == '\r'))) { | 
|  | // Whitespace sequences containing CR or LF are eliminated entirely. | 
|  | already_trimmed = true; | 
|  | --chars_written; | 
|  | } | 
|  | } else { | 
|  | // Non-whitespace chracters are copied straight across. | 
|  | in_whitespace = false; | 
|  | already_trimmed = false; | 
|  | result[chars_written++] = *i; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (in_whitespace && !already_trimmed) { | 
|  | // Any trailing whitespace is eliminated. | 
|  | --chars_written; | 
|  | } | 
|  |  | 
|  | result.resize(chars_written); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | bool Base64Encode(const std::string_view &input, std::string *output) { | 
|  | size_t len; | 
|  | if (!EVP_EncodedLength(&len, input.size())) { | 
|  | return false; | 
|  | } | 
|  | std::vector<char> encoded(len); | 
|  | len = EVP_EncodeBlock(reinterpret_cast<uint8_t *>(encoded.data()), | 
|  | reinterpret_cast<const uint8_t *>(input.data()), | 
|  | input.size()); | 
|  | if (!len) { | 
|  | return false; | 
|  | } | 
|  | output->assign(encoded.data(), len); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Base64Decode(const std::string_view &input, std::string *output) { | 
|  | size_t len; | 
|  | if (!EVP_DecodedLength(&len, input.size())) { | 
|  | return false; | 
|  | } | 
|  | std::vector<char> decoded(len); | 
|  | if (!EVP_DecodeBase64(reinterpret_cast<uint8_t *>(decoded.data()), &len, len, | 
|  | reinterpret_cast<const uint8_t *>(input.data()), | 
|  | input.size())) { | 
|  | return false; | 
|  | } | 
|  | output->assign(decoded.data(), len); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace string_util | 
|  | BSSL_NAMESPACE_END |