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