blob: 5aa17cdd7796ec7ed62c711f2c522b757a71f953 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ZIRCON_SYSTEM_ULIB_UART_PARSE_H_
#define ZIRCON_SYSTEM_ULIB_UART_PARSE_H_
#include <stdio.h>
#include <stdlib.h>
#include <string_view>
namespace uart::internal {
// Parse a comma-separated list of integers of the form `,1,2,3,...,1000`,
// where the list must begin with a comma.
//
// Input integers may be decimal (42), hexadecimal (0x2a) or octal (052).
//
// The integral values are parsed into a variadic list of pointers. `true` is
// is returned on success; `false` is returned if the string could not be
// parsed.
template <typename... Uint>
inline bool ParseInts(std::string_view string, Uint*... args) {
auto next = [&](auto* arg) -> bool {
if (string.size() < 2 || string.front() != ',') {
return false;
}
string.remove_prefix(1); // Remove the leading comma.
// Parse the leading sign and 0x (hex) or 0 (octal) indicator. Note that
// strtoul would do this with base=0, but doing it ahead of time allows us
// to trim leading zeros from the string so that we can always use a small
// buffer for the NUL-terminated copy of the digits to parse.
bool negative = false;
if ((string[0] == '-' || string[0] == '+') && string.size() > 1) {
negative = string[0] == '-';
string.remove_prefix(1);
}
int base = 10;
if (string[0] == '0' && string.size() > 1) {
if (string[1] == 'x') {
base = 16;
string.remove_prefix(2);
} else {
base = 8;
}
while (string[0] == '0' && string.size() > 1) {
string.remove_prefix(1);
}
}
size_t end = string.find_first_not_of("0123456789abcdefABCDEF");
if (end == std::string_view::npos) {
end = string.size();
} else if (end == 0) {
// The comma was followed by a non-numerical character.
return false;
}
// Since leading zeros were removed, any string of digits that doesn't fit
// in this buffer would be an integer that overflows the type anyway.
char buf[32];
if (end < sizeof(buf)) {
buf[string.substr(0, end).copy(buf, sizeof(buf) - 1)] = '\0';
string.remove_prefix(end);
char* p = nullptr;
uint64_t value = strtoul(buf, &p, base);
if (p == &buf[end]) {
if (negative) {
value = -value;
}
*arg = static_cast<std::decay_t<decltype(*arg)>>(value);
return true;
}
}
return false;
};
return (next(args) && ... && string.empty());
}
template <typename... Uint>
inline void UnparseInts(FILE* out, Uint... args) {
(fprintf(out, ",%#lx", static_cast<unsigned long int>(args)), ...);
}
} // namespace uart::internal
#endif // ZIRCON_SYSTEM_ULIB_UART_PARSE_H_