Files
test2/source/blender/io/common/intern/string_utils.cc
2025-03-13 13:41:17 +11:00

192 lines
4.5 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "IO_string_utils.hh"
/* NOTE: we could use C++17 <charconv> from_chars to parse
* floats, but even if some compilers claim full support,
* their standard libraries are not quite there yet.
* LLVM/libc++ only has a float parser since LLVM 14,
* and GCC/libstdc++ since 11.1. So until at least these are
* the minimum spec, use an external library. */
#include "fast_float.h"
#include <charconv>
namespace blender::io {
StringRef read_next_line(StringRef &buffer)
{
const char *start = buffer.begin();
const char *end = buffer.end();
size_t len = 0;
const char *ptr = start;
while (ptr < end) {
char c = *ptr++;
if (c == '\n') {
break;
}
++len;
}
buffer = StringRef(ptr, end);
return StringRef(start, len);
}
static bool is_whitespace(char c)
{
return c <= ' ';
}
void fixup_line_continuations(char *p, char *end)
{
while (true) {
/* Find next backslash, if any. */
char *backslash = std::find(p, end, '\\');
if (backslash == end) {
break;
}
/* Skip over possible white-space right after it. */
p = backslash + 1;
while (p < end && is_whitespace(*p) && *p != '\n') {
++p;
}
/* If then we have a newline, turn both backslash
* and the newline into regular spaces. */
if (p < end && *p == '\n') {
*backslash = ' ';
*p = ' ';
}
}
}
const char *drop_whitespace(const char *p, const char *end)
{
while (p < end && is_whitespace(*p)) {
++p;
}
return p;
}
const char *drop_non_whitespace(const char *p, const char *end)
{
while (p < end && !is_whitespace(*p)) {
++p;
}
return p;
}
static const char *drop_sign(const char *p, const char *end, int &sign)
{
sign = 1;
if (p < end) {
if (*p == '+') {
++p;
}
if (*p == '-') {
sign = -1;
++p;
}
}
return p;
}
const char *try_parse_float(
const char *p, const char *end, int fallback, bool &success, float &dst, bool skip_space)
{
if (skip_space) {
p = drop_whitespace(p, end);
}
int sign = 0;
p = drop_sign(p, end, sign);
fast_float::from_chars_result res = fast_float::from_chars(p, end, dst);
if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range) || res.ptr < end) {
dst = fallback;
success = false;
}
else {
dst *= sign;
success = true;
}
return res.ptr;
}
const char *try_parse_int(
const char *p, const char *end, int fallback, bool &success, int &dst, bool skip_space)
{
if (skip_space) {
p = drop_whitespace(p, end);
}
int sign = 0;
p = drop_sign(p, end, sign);
std::from_chars_result res = std::from_chars(p, end, dst);
if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range) || res.ptr < end) {
dst = fallback;
success = false;
}
else {
dst *= sign;
success = true;
}
return res.ptr;
}
static const char *drop_plus(const char *p, const char *end)
{
if (p < end && *p == '+') {
++p;
}
return p;
}
const char *parse_float(const char *p,
const char *end,
float fallback,
float &dst,
bool skip_space,
bool require_trailing_space)
{
if (skip_space) {
p = drop_whitespace(p, end);
}
p = drop_plus(p, end);
fast_float::from_chars_result res = fast_float::from_chars(p, end, dst);
if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range)) {
dst = fallback;
}
else if (require_trailing_space && res.ptr < end && !is_whitespace(*res.ptr)) {
/* If there are trailing non-space characters, do not eat up the number. */
dst = fallback;
return p;
}
return res.ptr;
}
const char *parse_floats(const char *p,
const char *end,
float fallback,
float *dst,
int count,
bool require_trailing_space)
{
for (int i = 0; i < count; ++i) {
p = parse_float(p, end, fallback, dst[i], true, require_trailing_space);
}
return p;
}
const char *parse_int(const char *p, const char *end, int fallback, int &dst, bool skip_space)
{
if (skip_space) {
p = drop_whitespace(p, end);
}
p = drop_plus(p, end);
std::from_chars_result res = std::from_chars(p, end, dst);
if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range)) {
dst = fallback;
}
return res.ptr;
}
} // namespace blender::io