Blender V5.0
io/common/intern/string_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "IO_string_utils.hh"
6
7/* NOTE: we could use C++17 <charconv> from_chars to parse
8 * floats, but even if some compilers claim full support,
9 * their standard libraries are not quite there yet.
10 * LLVM/libc++ only has a float parser since LLVM 14,
11 * and GCC/libstdc++ since 11.1. So until at least these are
12 * the minimum spec, use an external library. */
13#include "fast_float.h"
14#include <charconv>
15
16namespace blender::io {
17
19{
20 const char *start = buffer.begin();
21 const char *end = buffer.end();
22 size_t len = 0;
23 const char *ptr = start;
24 while (ptr < end) {
25 char c = *ptr++;
26 if (c == '\n') {
27 break;
28 }
29 ++len;
30 }
31
32 buffer = StringRef(ptr, end);
33 return StringRef(start, len);
34}
35
36static bool is_whitespace(char c)
37{
38 return c <= ' ';
39}
40
41void fixup_line_continuations(char *p, char *end)
42{
43 while (true) {
44 /* Find next backslash, if any. */
45 char *backslash = std::find(p, end, '\\');
46 if (backslash == end) {
47 break;
48 }
49 /* Skip over possible white-space right after it. */
50 p = backslash + 1;
51 while (p < end && is_whitespace(*p) && *p != '\n') {
52 ++p;
53 }
54 /* If we have a newline, turn both backslash
55 * and the newline into regular spaces. */
56 if (p < end && *p == '\n') {
57 *backslash = ' ';
58 *p = ' ';
59 }
60 }
61}
62
63const char *drop_whitespace(const char *p, const char *end)
64{
65 while (p < end && is_whitespace(*p)) {
66 ++p;
67 }
68 return p;
69}
70
71const char *drop_non_whitespace(const char *p, const char *end)
72{
73 while (p < end && !is_whitespace(*p)) {
74 ++p;
75 }
76 return p;
77}
78
79static const char *drop_sign(const char *p, const char *end, int &sign)
80{
81 sign = 1;
82 if (p < end) {
83 if (*p == '+') {
84 ++p;
85 }
86 if (*p == '-') {
87 sign = -1;
88 ++p;
89 }
90 }
91 return p;
92}
93
94const char *try_parse_float(
95 const char *p, const char *end, int fallback, bool &success, float &dst, bool skip_space)
96{
97 if (skip_space) {
98 p = drop_whitespace(p, end);
99 }
100 int sign = 0;
101 p = drop_sign(p, end, sign);
102 fast_float::from_chars_result res = fast_float::from_chars(p, end, dst);
103 if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range) || res.ptr < end) {
104 dst = fallback;
105 success = false;
106 }
107 else {
108 dst *= sign;
109 success = true;
110 }
111 return res.ptr;
112}
113
114const char *try_parse_int(
115 const char *p, const char *end, int fallback, bool &success, int &dst, bool skip_space)
116{
117 if (skip_space) {
118 p = drop_whitespace(p, end);
119 }
120 int sign = 0;
121 p = drop_sign(p, end, sign);
122 std::from_chars_result res = std::from_chars(p, end, dst);
123 if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range) || res.ptr < end) {
124 dst = fallback;
125 success = false;
126 }
127 else {
128 dst *= sign;
129 success = true;
130 }
131 return res.ptr;
132}
133
134static const char *drop_plus(const char *p, const char *end)
135{
136 if (p < end && *p == '+') {
137 ++p;
138 }
139 return p;
140}
141
142const char *parse_float(const char *p,
143 const char *end,
144 float fallback,
145 float &dst,
146 bool skip_space,
147 bool require_trailing_space)
148{
149 if (skip_space) {
150 p = drop_whitespace(p, end);
151 }
152 p = drop_plus(p, end);
153 fast_float::from_chars_result res = fast_float::from_chars(p, end, dst);
154 if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range)) {
155 dst = fallback;
156 }
157 else if (require_trailing_space && res.ptr < end && !is_whitespace(*res.ptr)) {
158 /* If there are trailing non-space characters, do not eat up the number. */
159 dst = fallback;
160 return p;
161 }
162 return res.ptr;
163}
164
165const char *parse_floats(const char *p,
166 const char *end,
167 float fallback,
168 float *dst,
169 int count,
170 bool require_trailing_space)
171{
172 for (int i = 0; i < count; ++i) {
173 p = parse_float(p, end, fallback, dst[i], true, require_trailing_space);
174 }
175 return p;
176}
177
178const char *parse_int(const char *p, const char *end, int fallback, int &dst, bool skip_space)
179{
180 if (skip_space) {
181 p = drop_whitespace(p, end);
182 }
183 p = drop_plus(p, end);
184 std::from_chars_result res = std::from_chars(p, end, dst);
185 if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range)) {
186 dst = fallback;
187 }
188 return res.ptr;
189}
190
191} // namespace blender::io
#define ELEM(...)
constexpr const char * begin() const
constexpr const char * end() const
constexpr T sign(T) RET
int count
const char * parse_float(const char *p, const char *end, float fallback, float &dst, bool skip_space, bool require_trailing_space)
static const char * drop_plus(const char *p, const char *end)
const char * parse_floats(const char *p, const char *end, float fallback, float *dst, int count, bool require_trailing_space)
const char * drop_non_whitespace(const char *p, const char *end)
static bool is_whitespace(char c)
StringRef read_next_line(StringRef &buffer)
const char * try_parse_int(const char *p, const char *end, int fallback, bool &success, int &dst, bool skip_space)
const char * parse_int(const char *p, const char *end, int fallback, int &dst, bool skip_space)
void fixup_line_continuations(char *p, char *end)
static const char * drop_sign(const char *p, const char *end, int &sign)
const char * drop_whitespace(const char *p, const char *end)
const char * try_parse_float(const char *p, const char *end, int fallback, bool &success, float &dst, bool skip_space)
i
Definition text_draw.cc:230
uint len
PointerRNA * ptr
Definition wm_files.cc:4238