xref: /aosp_15_r20/external/cronet/third_party/boringssl/src/pki/string_util.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "string_util.h"
6 
7 #include <algorithm>
8 #include <iomanip>
9 #include <sstream>
10 #include <string>
11 
12 #include <openssl/base64.h>
13 #include <openssl/mem.h>
14 
15 namespace bssl::string_util {
16 
IsAscii(std::string_view str)17 bool IsAscii(std::string_view str) {
18   for (unsigned char c : str) {
19     if (c > 127) {
20       return false;
21     }
22   }
23   return true;
24 }
25 
IsEqualNoCase(std::string_view str1,std::string_view str2)26 bool IsEqualNoCase(std::string_view str1, std::string_view str2) {
27   return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(),
28                     [](const unsigned char a, const unsigned char b) {
29                       return OPENSSL_tolower(a) == OPENSSL_tolower(b);
30                     });
31 }
32 
EndsWithNoCase(std::string_view str,std::string_view suffix)33 bool EndsWithNoCase(std::string_view str, std::string_view suffix) {
34   return suffix.size() <= str.size() &&
35          IsEqualNoCase(suffix, str.substr(str.size() - suffix.size()));
36 }
37 
StartsWithNoCase(std::string_view str,std::string_view prefix)38 bool StartsWithNoCase(std::string_view str, std::string_view prefix) {
39   return prefix.size() <= str.size() &&
40          IsEqualNoCase(prefix, str.substr(0, prefix.size()));
41 }
42 
FindAndReplace(std::string_view str,std::string_view find,std::string_view replace)43 std::string FindAndReplace(std::string_view str, std::string_view find,
44                            std::string_view replace) {
45   std::string ret;
46 
47   if (find.empty()) {
48     return std::string(str);
49   }
50   while (!str.empty()) {
51     size_t index = str.find(find);
52     if (index == std::string_view::npos) {
53       ret.append(str);
54       break;
55     }
56     ret.append(str.substr(0, index));
57     ret.append(replace);
58     str = str.substr(index + find.size());
59   }
60   return ret;
61 }
62 
63 // TODO(bbe) get rid of this once we can c++20.
EndsWith(std::string_view str,std::string_view suffix)64 bool EndsWith(std::string_view str, std::string_view suffix) {
65   return suffix.size() <= str.size() &&
66          suffix == str.substr(str.size() - suffix.size());
67 }
68 
69 // TODO(bbe) get rid of this once we can c++20.
StartsWith(std::string_view str,std::string_view prefix)70 bool StartsWith(std::string_view str, std::string_view prefix) {
71   return prefix.size() <= str.size() && prefix == str.substr(0, prefix.size());
72 }
73 
HexEncode(Span<const uint8_t> data)74 std::string HexEncode(Span<const uint8_t> data) {
75   std::ostringstream out;
76   for (uint8_t b : data) {
77     out << std::hex << std::setfill('0') << std::setw(2) << std::uppercase
78         << int{b};
79   }
80   return out.str();
81 }
82 
83 // TODO(bbe) get rid of this once extracted to boringssl. Everything else
84 // in third_party uses std::to_string
NumberToDecimalString(int i)85 std::string NumberToDecimalString(int i) {
86   std::ostringstream out;
87   out << std::dec << i;
88   return out.str();
89 }
90 
SplitString(std::string_view str,char split_char)91 std::vector<std::string_view> SplitString(std::string_view str,
92                                           char split_char) {
93   std::vector<std::string_view> out;
94 
95   if (str.empty()) {
96     return out;
97   }
98 
99   while (true) {
100     // Find end of current token
101     size_t i = str.find(split_char);
102 
103     // Add current token
104     out.push_back(str.substr(0, i));
105 
106     if (i == str.npos) {
107       // That was the last token
108       break;
109     }
110     // Continue to next
111     str = str.substr(i + 1);
112   }
113 
114   return out;
115 }
116 
IsUnicodeWhitespace(char c)117 static bool IsUnicodeWhitespace(char c) {
118   return c == 9 || c == 10 || c == 11 || c == 12 || c == 13 || c == ' ';
119 }
120 
CollapseWhitespaceASCII(std::string_view text,bool trim_sequences_with_line_breaks)121 std::string CollapseWhitespaceASCII(std::string_view text,
122                                     bool trim_sequences_with_line_breaks) {
123   std::string result;
124   result.resize(text.size());
125 
126   // Set flags to pretend we're already in a trimmed whitespace sequence, so we
127   // will trim any leading whitespace.
128   bool in_whitespace = true;
129   bool already_trimmed = true;
130 
131   int chars_written = 0;
132   for (auto i = text.begin(); i != text.end(); ++i) {
133     if (IsUnicodeWhitespace(*i)) {
134       if (!in_whitespace) {
135         // Reduce all whitespace sequences to a single space.
136         in_whitespace = true;
137         result[chars_written++] = L' ';
138       }
139       if (trim_sequences_with_line_breaks && !already_trimmed &&
140           ((*i == '\n') || (*i == '\r'))) {
141         // Whitespace sequences containing CR or LF are eliminated entirely.
142         already_trimmed = true;
143         --chars_written;
144       }
145     } else {
146       // Non-whitespace chracters are copied straight across.
147       in_whitespace = false;
148       already_trimmed = false;
149       result[chars_written++] = *i;
150     }
151   }
152 
153   if (in_whitespace && !already_trimmed) {
154     // Any trailing whitespace is eliminated.
155     --chars_written;
156   }
157 
158   result.resize(chars_written);
159   return result;
160 }
161 
Base64Encode(const std::string_view & input,std::string * output)162 bool Base64Encode(const std::string_view &input, std::string *output) {
163   size_t len;
164   if (!EVP_EncodedLength(&len, input.size())) {
165     return false;
166   }
167   std::vector<char> encoded(len);
168   len = EVP_EncodeBlock(reinterpret_cast<uint8_t *>(encoded.data()),
169                         reinterpret_cast<const uint8_t *>(input.data()),
170                         input.size());
171   if (!len) {
172     return false;
173   }
174   output->assign(encoded.data(), len);
175   return true;
176 }
177 
Base64Decode(const std::string_view & input,std::string * output)178 bool Base64Decode(const std::string_view &input, std::string *output) {
179   size_t len;
180   if (!EVP_DecodedLength(&len, input.size())) {
181     return false;
182   }
183   std::vector<char> decoded(len);
184   if (!EVP_DecodeBase64(reinterpret_cast<uint8_t *>(decoded.data()), &len, len,
185                         reinterpret_cast<const uint8_t *>(input.data()),
186                         input.size())) {
187     return false;
188   }
189   output->assign(decoded.data(), len);
190   return true;
191 }
192 
193 }  // namespace bssl::string_util
194