blob: 45d23ea1b749494388f683f386c3a5d1f4089b38 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5//===-- StringExtras.cpp - Implement the StringExtras header --------------===//
6//
7// The LLVM Compiler Infrastructure
8//
9// This file is distributed under the University of Illinois Open Source
10// License. See LICENSE.TXT for details.
11//
12//===----------------------------------------------------------------------===//
13//
14// This file implements the StringExtras.h header
15//
16//===----------------------------------------------------------------------===//
17
18#include "wpi/StringExtras.h"
19
20#include <algorithm>
21#include <cstdlib>
22#include <string_view>
23
24#include "wpi/SmallString.h"
25#include "wpi/SmallVector.h"
26
27// strncasecmp() is not available on non-POSIX systems, so define an
28// alternative function here.
29static int ascii_strncasecmp(const char* lhs, const char* rhs,
30 size_t length) noexcept {
31 for (size_t i = 0; i < length; ++i) {
32 unsigned char lhc = wpi::toLower(lhs[i]);
33 unsigned char rhc = wpi::toLower(rhs[i]);
34 if (lhc != rhc) {
35 return lhc < rhc ? -1 : 1;
36 }
37 }
38 return 0;
39}
40
41int wpi::compare_lower(std::string_view lhs, std::string_view rhs) noexcept {
42 if (int Res = ascii_strncasecmp(lhs.data(), rhs.data(),
43 (std::min)(lhs.size(), rhs.size()))) {
44 return Res;
45 }
46 if (lhs.size() == rhs.size()) {
47 return 0;
48 }
49 return lhs.size() < rhs.size() ? -1 : 1;
50}
51
52std::string_view::size_type wpi::find_lower(
53 std::string_view str, char ch, std::string_view::size_type from) noexcept {
54 char lch = toLower(ch);
55 auto s = drop_front(str, from);
56 while (!s.empty()) {
57 if (toLower(s.front()) == lch) {
58 return str.size() - s.size();
59 }
60 s.remove_prefix(1);
61 }
62 return std::string_view::npos;
63}
64
65std::string_view::size_type wpi::find_lower(
66 std::string_view str, std::string_view other,
67 std::string_view::size_type from) noexcept {
68 auto s = str.substr(from);
69 while (s.size() >= other.size()) {
70 if (starts_with_lower(s, other)) {
71 return from;
72 }
73 s.remove_prefix(1);
74 ++from;
75 }
76 return std::string_view::npos;
77}
78
79std::string_view::size_type wpi::rfind_lower(
80 std::string_view str, char ch, std::string_view::size_type from) noexcept {
81 from = (std::min)(from, str.size());
82 auto data = str.data();
83 std::string_view::size_type i = from;
84 while (i != 0) {
85 --i;
86 if (toLower(data[i]) == toLower(ch)) {
87 return i;
88 }
89 }
90 return std::string_view::npos;
91}
92
93std::string_view::size_type wpi::rfind_lower(std::string_view str,
94 std::string_view other) noexcept {
95 std::string_view::size_type n = other.size();
96 if (n > str.size()) {
97 return std::string_view::npos;
98 }
99 for (size_t i = str.size() - n + 1, e = 0; i != e;) {
100 --i;
101 if (equals_lower(str.substr(i, n), other)) {
102 return i;
103 }
104 }
105 return std::string_view::npos;
106}
107
108bool wpi::starts_with_lower(std::string_view str,
109 std::string_view prefix) noexcept {
110 return str.size() >= prefix.size() &&
111 ascii_strncasecmp(str.data(), prefix.data(), prefix.size()) == 0;
112}
113
114bool wpi::ends_with_lower(std::string_view str,
115 std::string_view suffix) noexcept {
116 return str.size() >= suffix.size() &&
117 ascii_strncasecmp(str.data() + str.size() - suffix.size(),
118 suffix.data(), suffix.size()) == 0;
119}
120
121void wpi::split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
122 std::string_view separator, int maxSplit,
123 bool keepEmpty) noexcept {
124 std::string_view s = str;
125
126 // Count down from maxSplit. When maxSplit is -1, this will just split
127 // "forever". This doesn't support splitting more than 2^31 times
128 // intentionally; if we ever want that we can make maxSplit a 64-bit integer
129 // but that seems unlikely to be useful.
130 while (maxSplit-- != 0) {
131 auto idx = s.find(separator);
132 if (idx == std::string_view::npos) {
133 break;
134 }
135
136 // Push this split.
137 if (keepEmpty || idx > 0) {
138 arr.push_back(slice(s, 0, idx));
139 }
140
141 // Jump forward.
142 s = slice(s, idx + separator.size(), std::string_view::npos);
143 }
144
145 // Push the tail.
146 if (keepEmpty || !s.empty()) {
147 arr.push_back(s);
148 }
149}
150
151void wpi::split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
152 char separator, int maxSplit, bool keepEmpty) noexcept {
153 std::string_view s = str;
154
155 // Count down from maxSplit. When maxSplit is -1, this will just split
156 // "forever". This doesn't support splitting more than 2^31 times
157 // intentionally; if we ever want that we can make maxSplit a 64-bit integer
158 // but that seems unlikely to be useful.
159 while (maxSplit-- != 0) {
160 size_t idx = s.find(separator);
161 if (idx == std::string_view::npos) {
162 break;
163 }
164
165 // Push this split.
166 if (keepEmpty || idx > 0) {
167 arr.push_back(slice(s, 0, idx));
168 }
169
170 // Jump forward.
171 s = slice(s, idx + 1, std::string_view::npos);
172 }
173
174 // Push the tail.
175 if (keepEmpty || !s.empty()) {
176 arr.push_back(s);
177 }
178}
179
180static unsigned GetAutoSenseRadix(std::string_view& str) noexcept {
181 if (str.empty()) {
182 return 10;
183 }
184
185 if (wpi::starts_with(str, "0x") || wpi::starts_with(str, "0X")) {
186 str.remove_prefix(2);
187 return 16;
188 }
189
190 if (wpi::starts_with(str, "0b") || wpi::starts_with(str, "0B")) {
191 str.remove_prefix(2);
192 return 2;
193 }
194
195 if (wpi::starts_with(str, "0o")) {
196 str.remove_prefix(2);
197 return 8;
198 }
199
200 if (str[0] == '0' && str.size() > 1 && wpi::isDigit(str[1])) {
201 str.remove_prefix(1);
202 return 8;
203 }
204
205 return 10;
206}
207
208bool wpi::detail::ConsumeUnsignedInteger(
209 std::string_view& str, unsigned radix,
210 unsigned long long& result) noexcept { // NOLINT(runtime/int)
211 // Autosense radix if not specified.
212 if (radix == 0) {
213 radix = GetAutoSenseRadix(str);
214 }
215
216 // Empty strings (after the radix autosense) are invalid.
217 if (str.empty()) {
218 return true;
219 }
220
221 // Parse all the bytes of the string given this radix. Watch for overflow.
222 std::string_view str2 = str;
223 result = 0;
224 while (!str2.empty()) {
225 unsigned charVal;
226 if (str2[0] >= '0' && str2[0] <= '9') {
227 charVal = str2[0] - '0';
228 } else if (str2[0] >= 'a' && str2[0] <= 'z') {
229 charVal = str2[0] - 'a' + 10;
230 } else if (str2[0] >= 'A' && str2[0] <= 'Z') {
231 charVal = str2[0] - 'A' + 10;
232 } else {
233 break;
234 }
235
236 // If the parsed value is larger than the integer radix, we cannot
237 // consume any more characters.
238 if (charVal >= radix) {
239 break;
240 }
241
242 // Add in this character.
243 unsigned long long prevResult = result; // NOLINT(runtime/int)
244 result = result * radix + charVal;
245
246 // Check for overflow by shifting back and seeing if bits were lost.
247 if (result / radix < prevResult) {
248 return true;
249 }
250
251 str2.remove_prefix(1);
252 }
253
254 // We consider the operation a failure if no characters were consumed
255 // successfully.
256 if (str.size() == str2.size()) {
257 return true;
258 }
259
260 str = str2;
261 return false;
262}
263
264bool wpi::detail::ConsumeSignedInteger(
265 std::string_view& str, unsigned radix,
266 long long& result) noexcept { // NOLINT(runtime/int)
267 unsigned long long ullVal; // NOLINT(runtime/int)
268
269 // Handle positive strings first.
270 if (str.empty() || str.front() != '-') {
271 if (wpi::detail::ConsumeUnsignedInteger(str, radix, ullVal) ||
272 // Check for value so large it overflows a signed value.
273 static_cast<long long>(ullVal) < 0) { // NOLINT(runtime/int)
274 return true;
275 }
276 result = ullVal;
277 return false;
278 }
279
280 // Get the positive part of the value.
281 std::string_view str2 = wpi::drop_front(str, 1);
282 if (wpi::detail::ConsumeUnsignedInteger(str2, radix, ullVal) ||
283 // Reject values so large they'd overflow as negative signed, but allow
284 // "-0". This negates the unsigned so that the negative isn't undefined
285 // on signed overflow.
286 static_cast<long long>(-ullVal) > 0) { // NOLINT(runtime/int)
287 return true;
288 }
289
290 str = str2;
291 result = -ullVal;
292 return false;
293}
294
295bool wpi::detail::GetAsUnsignedInteger(
296 std::string_view str, unsigned radix,
297 unsigned long long& result) noexcept { // NOLINT(runtime/int)
298 if (wpi::detail::ConsumeUnsignedInteger(str, radix, result)) {
299 return true;
300 }
301
302 // For getAsUnsignedInteger, we require the whole string to be consumed or
303 // else we consider it a failure.
304 return !str.empty();
305}
306
307bool wpi::detail::GetAsSignedInteger(
308 std::string_view str, unsigned radix,
309 long long& result) noexcept { // NOLINT(runtime/int)
310 if (wpi::detail::ConsumeSignedInteger(str, radix, result)) {
311 return true;
312 }
313
314 // For getAsSignedInteger, we require the whole string to be consumed or else
315 // we consider it a failure.
316 return !str.empty();
317}
318
319template <>
320std::optional<float> wpi::parse_float<float>(std::string_view str) noexcept {
321 if (str.empty()) {
322 return std::nullopt;
323 }
324 wpi::SmallString<32> storage{str};
325 char* end;
326 float val = std::strtof(storage.c_str(), &end);
327 if (*end != '\0') {
328 return std::nullopt;
329 }
330 return val;
331}
332
333template <>
334std::optional<double> wpi::parse_float<double>(std::string_view str) noexcept {
335 if (str.empty()) {
336 return std::nullopt;
337 }
338 wpi::SmallString<32> storage{str};
339 char* end;
340 double val = std::strtod(storage.c_str(), &end);
341 if (*end != '\0') {
342 return std::nullopt;
343 }
344 return val;
345}
346
347template <>
348std::optional<long double> wpi::parse_float<long double>(
349 std::string_view str) noexcept {
350 if (str.empty()) {
351 return std::nullopt;
352 }
353 wpi::SmallString<32> storage{str};
354 char* end;
355 long double val = std::strtold(storage.c_str(), &end);
356 if (*end != '\0') {
357 return std::nullopt;
358 }
359 return val;
360}