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