1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4
5
6 #include <cmath>
7 #include <cstdarg>
8 #include <cstdint>
9 #include <cstdlib>
10 #include <iostream>
11 #include <limits>
12 #include <locale.h>
13 #include <mutex>
14 #include <sstream>
15 #include <string>
16 #include <vector>
17 #if defined(__APPLE__) || defined(__FreeBSD__)
18 # include <xlocale.h>
19 #endif
20
21 #include <boost/algorithm/string.hpp>
22 #include <boost/algorithm/string/find.hpp>
23
24 #include <OpenImageIO/dassert.h>
25 #include <OpenImageIO/platform.h>
26 #include <OpenImageIO/string_view.h>
27 #include <OpenImageIO/strutil.h>
28 #include <OpenImageIO/ustring.h>
29
30 #ifdef _WIN32
31 # include <shellapi.h>
32 #endif
33
34
35 // We use the public domain stb implementation of vsnprintf because
36 // not all platforms support a locale-independent version of vsnprintf.
37 // See: https://github.com/nothings/stb/blob/master/stb_sprintf.h
38 #define STB_SPRINTF_DECORATE(name) oiio_stbsp_##name
39 #define STB_SPRINTF_IMPLEMENTATION 1
40 #if defined(__GNUG__) && !defined(__clang__)
41 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
42 # define STBI__ASAN OIIO_NO_SANITIZE_ADDRESS
43 #endif
44 #define stbsp__uintptr std::uintptr_t
45 #include "stb_sprintf.h"
46
47
48 OIIO_NAMESPACE_BEGIN
49
50
51 namespace {
52 static std::mutex output_mutex;
53
54 // On systems that support it, get a location independent locale.
55 #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) \
56 || defined(__FreeBSD_kernel__) || defined(__GLIBC__)
57 static locale_t c_loc = newlocale(LC_ALL_MASK, "C", nullptr);
58 #elif defined(_WIN32)
59 static _locale_t c_loc = _create_locale(LC_ALL, "C");
60 #endif
61
62 }; // namespace
63
64
65
66 // Locale-independent quickie ASCII digit and alphanum tests, good enough
67 // for our parsing.
68 inline int
isupper(char c)69 isupper(char c)
70 {
71 return c >= 'A' && c <= 'Z';
72 }
73 inline int
islower(char c)74 islower(char c)
75 {
76 return c >= 'a' && c <= 'z';
77 }
78 inline int
isalpha(char c)79 isalpha(char c)
80 {
81 return isupper(c) || islower(c);
82 }
83 inline int
isdigit(char c)84 isdigit(char c)
85 {
86 return c >= '0' && c <= '9';
87 }
88
89
90
91 OIIO_NO_SANITIZE_ADDRESS
92 const char*
c_str() const93 string_view::c_str() const
94 {
95 // Usual case: either empty, or null-terminated
96 if (m_len == 0) // empty string
97 return "";
98
99 // This clause attempts to find out if there's a string-teriminating
100 // '\0' character just beyond the boundary of the string_view, in which
101 // case, simply returning m_chars (with no ustring creation) is a valid
102 // C string.
103 //
104 // BUG: if the string_view does not simply wrap a null-terminated string
105 // (including a std::string or ustring) or substring thereof, and the
106 // the character past the end of the string is beyond an allocation
107 // boundary, this will be flagged by address sanitizer. And it
108 // misbehaves if the memory just beyond the string_view, which isn't
109 // part of the string, gets changed during the lifetime of the
110 // string_view, and no longer has that terminating null. I think that in
111 // the long run, we can't use this trick. I'm kicking that can down the
112 // road just a bit because it's such a performance win. But we
113 // eventually want to get rid of this method anyway, since it won't be
114 // in C++17 string_view. So maybe we'll find ourselves relying on it a
115 // lot less, and therefore the performance hit of doing it foolproof
116 // won't be as onerous.
117 if (m_chars[m_len] == 0) // 0-terminated
118 return m_chars;
119
120 // Rare case: may not be 0-terminated. Bite the bullet and construct a
121 // 0-terminated string. We use ustring as a way to avoid any issues of
122 // who cleans up the allocation, though it means that it will stay in
123 // the ustring table forever. Punt on this for now, it's an edge case
124 // that we need to handle, but is not likely to ever be an issue.
125 return ustring(m_chars, 0, m_len).c_str();
126 }
127
128
129
130 void
sync_output(FILE * file,string_view str)131 Strutil::sync_output(FILE* file, string_view str)
132 {
133 if (str.size() && file) {
134 std::unique_lock<std::mutex> lock(output_mutex);
135 fwrite(str.data(), 1, str.size(), file);
136 fflush(file);
137 }
138 }
139
140
141
142 void
sync_output(std::ostream & file,string_view str)143 Strutil::sync_output(std::ostream& file, string_view str)
144 {
145 if (str.size()) {
146 std::unique_lock<std::mutex> lock(output_mutex);
147 file << str;
148 file.flush();
149 }
150 }
151
152
153
154 std::string
vsprintf(const char * fmt,va_list ap)155 Strutil::vsprintf(const char* fmt, va_list ap)
156 {
157 // Allocate a buffer on the stack that's big enough for us almost
158 // all the time. Be prepared to allocate dynamically if it doesn't fit.
159 size_t size = 1024;
160 char stackbuf[1024];
161 std::vector<char> dynamicbuf;
162 char* buf = &stackbuf[0];
163
164 while (1) {
165 // Try to vsnprintf into our buffer.
166 va_list apsave;
167 #ifdef va_copy
168 va_copy(apsave, ap);
169 #else
170 apsave = ap;
171 #endif
172
173 int needed = oiio_stbsp_vsnprintf(buf, size, fmt, ap);
174 va_end(ap);
175
176 // NB. C99 says vsnprintf returns -1 on an encoding error. Otherwise
177 // it's the number of characters that would have been written if the
178 // buffer were large enough.
179 if (needed == -1)
180 return std::string("ENCODING ERROR");
181 if (needed < (int)size) {
182 // It fit fine so we're done.
183 return std::string(buf, (size_t)needed);
184 }
185
186 // vsnprintf reported that it wanted to write more characters
187 // than we allotted. So try again using a dynamic buffer. This
188 // doesn't happen very often if we chose our initial size well.
189 size = (needed > 0) ? (needed + 1) : (size * 2);
190 dynamicbuf.resize(size);
191 buf = &dynamicbuf[0];
192 #ifdef va_copy
193 va_copy(ap, apsave);
194 #else
195 ap = apsave;
196 #endif
197 }
198 }
199
200
201
202 std::string
vformat(const char * fmt,va_list ap)203 Strutil::vformat(const char* fmt, va_list ap)
204 {
205 // For now, just treat as a synonym for vsprintf
206 return vsprintf(fmt, ap);
207 }
208
209
210
211 std::string
memformat(long long bytes,int digits)212 Strutil::memformat(long long bytes, int digits)
213 {
214 const long long KB = (1 << 10);
215 const long long MB = (1 << 20);
216 const long long GB = (1 << 30);
217 const char* units = "B";
218 double d = (double)bytes;
219 if (bytes >= GB) {
220 units = "GB";
221 d = (double)bytes / GB;
222 } else if (bytes >= MB) {
223 units = "MB";
224 d = (double)bytes / MB;
225 } else if (bytes >= KB) {
226 // Just KB, don't bother with decimalization
227 return format("%lld KB", (long long)bytes / KB);
228 } else {
229 // Just bytes, don't bother with decimalization
230 return format("%lld B", (long long)bytes);
231 }
232 return format("%1.*f %s", digits, d, units);
233 }
234
235
236
237 std::string
timeintervalformat(double secs,int digits)238 Strutil::timeintervalformat(double secs, int digits)
239 {
240 const double mins = 60;
241 const double hours = mins * 60;
242 const double days = hours * 24;
243
244 std::string out;
245 int d = (int)floor(secs / days);
246 secs = fmod(secs, days);
247 int h = (int)floor(secs / hours);
248 secs = fmod(secs, hours);
249 int m = (int)floor(secs / mins);
250 secs = fmod(secs, mins);
251 if (d)
252 out += format("%dd %dh ", d, h);
253 else if (h)
254 out += format("%dh ", h);
255 if (m || h || d)
256 out += format("%dm %1.*fs", m, digits, secs);
257 else
258 out += format("%1.*fs", digits, secs);
259 return out;
260 }
261
262
263
264 bool
get_rest_arguments(const std::string & str,std::string & base,std::map<std::string,std::string> & result)265 Strutil::get_rest_arguments(const std::string& str, std::string& base,
266 std::map<std::string, std::string>& result)
267 {
268 // Disregard the Windows long path question style prefix "\\?\"
269 static const std::string longPathQPrefix("\\\\?\\");
270 auto find_start_pos = starts_with(str, longPathQPrefix)
271 ? longPathQPrefix.size()
272 : size_t(0);
273
274 std::string::size_type mark_pos = str.find_first_of("?", find_start_pos);
275 if (mark_pos == std::string::npos) {
276 base = str;
277 return true;
278 }
279
280 base = str.substr(0, mark_pos);
281 std::string rest = str.substr(mark_pos + 1);
282 std::vector<std::string> rest_tokens;
283 Strutil::split(rest, rest_tokens, "&");
284 for (const std::string& keyval : rest_tokens) {
285 mark_pos = keyval.find_first_of("=");
286 if (mark_pos == std::string::npos)
287 return false;
288 result[keyval.substr(0, mark_pos)] = keyval.substr(mark_pos + 1);
289 }
290
291 return true;
292 }
293
294
295
296 std::string
escape_chars(string_view unescaped)297 Strutil::escape_chars(string_view unescaped)
298 {
299 std::string s = unescaped;
300 for (size_t i = 0; i < s.length(); ++i) {
301 char c = s[i];
302 if (c == '\n' || c == '\t' || c == '\v' || c == '\b' || c == '\r'
303 || c == '\f' || c == '\a' || c == '\\' || c == '\"') {
304 s[i] = '\\';
305 ++i;
306 switch (c) {
307 case '\n': c = 'n'; break;
308 case '\t': c = 't'; break;
309 case '\v': c = 'v'; break;
310 case '\b': c = 'b'; break;
311 case '\r': c = 'r'; break;
312 case '\f': c = 'f'; break;
313 case '\a': c = 'a'; break;
314 }
315 s.insert(i, &c, 1);
316 }
317 }
318 return s;
319 }
320
321
322
323 std::string
unescape_chars(string_view escaped)324 Strutil::unescape_chars(string_view escaped)
325 {
326 std::string s = escaped;
327 for (size_t i = 0, len = s.length(); i < len; ++i) {
328 if (s[i] == '\\') {
329 char c = s[i + 1];
330 if (c == 'n' || c == 't' || c == 'v' || c == 'b' || c == 'r'
331 || c == 'f' || c == 'a' || c == '\\' || c == '\"') {
332 s.erase(i, 1);
333 --len;
334 switch (c) {
335 case 'n': s[i] = '\n'; break;
336 case 't': s[i] = '\t'; break;
337 case 'v': s[i] = '\v'; break;
338 case 'b': s[i] = '\b'; break;
339 case 'r': s[i] = '\r'; break;
340 case 'f': s[i] = '\f'; break;
341 case 'a':
342 s[i] = '\a';
343 break;
344 // default case: the deletion is enough (backslash and quote)
345 }
346 } else if (c >= '0' && c < '8') {
347 // up to 3 octal digits
348 int octalChar = 0;
349 for (int j = 0; j < 3 && c >= '0' && c <= '7'; ++j) {
350 octalChar = 8 * octalChar + (c - '0');
351 s.erase(i, 1);
352 --len;
353 c = i + 1 < len ? s[i + 1] : '\0';
354 }
355 s[i] = (char)octalChar;
356 }
357 }
358 }
359 return s;
360 }
361
362
363
364 std::string
wordwrap(string_view src,int columns,int prefix,string_view sep,string_view presep)365 Strutil::wordwrap(string_view src, int columns, int prefix, string_view sep,
366 string_view presep)
367 {
368 if (columns < prefix + 20)
369 return src; // give up, no way to make it wrap
370 std::ostringstream out;
371 columns -= prefix; // now columns is the real width we have to work with
372 std::string allsep = Strutil::sprintf("%s%s", sep, presep);
373 while ((int)src.length() > columns) {
374 // Find the last `sep` break before the column limit.
375 size_t breakpoint = src.find_last_of(allsep, columns);
376 // If presep is not empty, find the last presep break before the
377 // column limit and break one character AFTER that, if it's a better
378 // breakpost than the sep break.
379 if (presep.size()) {
380 size_t presep_break = src.find_last_of(presep, columns);
381 if (presep_break >= breakpoint && presep_break < src.size())
382 breakpoint = presep_break + 1;
383 }
384 // If no break was found, punt and hard break at the column limit.
385 if (breakpoint == string_view::npos)
386 breakpoint = columns;
387 // Copy up to the breakpoint, then newline and prefex blanks.
388 out << src.substr(0, breakpoint) << "\n" << std::string(prefix, ' ');
389 // Pick up where we left off
390 src = src.substr(breakpoint);
391 // Skip any separator characters at the start of the string
392 while (sep.find(src[0]) != string_view::npos)
393 src.remove_prefix(1);
394 }
395 out << src;
396 return out.str();
397 }
398
399
400
401 // DEPRECATED(2.0) -- for link compatibility
402 namespace Strutil {
403 std::string
wordwrap(string_view src,int columns,int prefix)404 wordwrap(string_view src, int columns, int prefix)
405 {
406 return wordwrap(src, columns, prefix, " ", "");
407 }
408 } // namespace Strutil
409
410
411
412 bool
iequals(string_view a,string_view b)413 Strutil::iequals(string_view a, string_view b)
414 {
415 return boost::algorithm::iequals(a, b, std::locale::classic());
416 }
417
418
419 bool
iless(string_view a,string_view b)420 Strutil::iless(string_view a, string_view b)
421 {
422 return boost::algorithm::ilexicographical_compare(a, b,
423 std::locale::classic());
424 }
425
426
427 bool
starts_with(string_view a,string_view b)428 Strutil::starts_with(string_view a, string_view b)
429 {
430 return boost::algorithm::starts_with(a, b);
431 }
432
433
434 bool
istarts_with(string_view a,string_view b)435 Strutil::istarts_with(string_view a, string_view b)
436 {
437 return boost::algorithm::istarts_with(a, b, std::locale::classic());
438 }
439
440
441 bool
ends_with(string_view a,string_view b)442 Strutil::ends_with(string_view a, string_view b)
443 {
444 return boost::algorithm::ends_with(a, b);
445 }
446
447
448 bool
iends_with(string_view a,string_view b)449 Strutil::iends_with(string_view a, string_view b)
450 {
451 return boost::algorithm::iends_with(a, b, std::locale::classic());
452 }
453
454
455 bool
contains(string_view a,string_view b)456 Strutil::contains(string_view a, string_view b)
457 {
458 return boost::algorithm::contains(a, b);
459 }
460
461
462 bool
icontains(string_view a,string_view b)463 Strutil::icontains(string_view a, string_view b)
464 {
465 return boost::algorithm::icontains(a, b, std::locale::classic());
466 }
467
468
469 size_t
find(string_view a,string_view b)470 Strutil::find(string_view a, string_view b)
471 {
472 return a.find(b);
473 }
474
475
476 size_t
rfind(string_view a,string_view b)477 Strutil::rfind(string_view a, string_view b)
478 {
479 return a.rfind(b);
480 }
481
482
483 size_t
ifind(string_view a,string_view b)484 Strutil::ifind(string_view a, string_view b)
485 {
486 if (a.empty())
487 return string_view::npos;
488 if (b.empty())
489 return 0;
490 auto f = boost::algorithm::ifind_first(a, b, std::locale::classic());
491 return f.empty() ? string_view::npos : f.begin() - a.data();
492 }
493
494
495 size_t
irfind(string_view a,string_view b)496 Strutil::irfind(string_view a, string_view b)
497 {
498 if (a.empty())
499 return string_view::npos;
500 if (b.empty())
501 return a.size();
502 auto f = boost::algorithm::ifind_last(a, b, std::locale::classic());
503 return f.empty() ? string_view::npos : f.begin() - a.data();
504 }
505
506
507 void
to_lower(std::string & a)508 Strutil::to_lower(std::string& a)
509 {
510 boost::algorithm::to_lower(a, std::locale::classic());
511 }
512
513
514 void
to_upper(std::string & a)515 Strutil::to_upper(std::string& a)
516 {
517 boost::algorithm::to_upper(a, std::locale::classic());
518 }
519
520
521
522 bool
operator ()(const char * a,const char * b) const523 Strutil::StringIEqual::operator()(const char* a, const char* b) const noexcept
524 {
525 return boost::algorithm::iequals(a, b, std::locale::classic());
526 }
527
528
529 bool
operator ()(const char * a,const char * b) const530 Strutil::StringILess::operator()(const char* a, const char* b) const noexcept
531 {
532 return boost::algorithm::ilexicographical_compare(a, b,
533 std::locale::classic());
534 }
535
536
537
538 string_view
lstrip(string_view str,string_view chars)539 Strutil::lstrip(string_view str, string_view chars)
540 {
541 if (chars.empty())
542 chars = string_view(" \t\n\r\f\v", 6);
543 size_t b = str.find_first_not_of(chars);
544 return str.substr(b, string_view::npos);
545 }
546
547
548
549 string_view
rstrip(string_view str,string_view chars)550 Strutil::rstrip(string_view str, string_view chars)
551 {
552 if (chars.empty())
553 chars = string_view(" \t\n\r\f\v", 6);
554 size_t e = str.find_last_not_of(chars);
555 return e != string_view::npos ? str.substr(0, e + 1) : string_view();
556 }
557
558
559
560 string_view
strip(string_view str,string_view chars)561 Strutil::strip(string_view str, string_view chars)
562 {
563 if (chars.empty())
564 chars = string_view(" \t\n\r\f\v", 6);
565 size_t b = str.find_first_not_of(chars);
566 if (b == std::string::npos)
567 return string_view();
568 size_t e = str.find_last_not_of(chars);
569 OIIO_DASSERT(e != std::string::npos);
570 return str.substr(b, e - b + 1);
571 }
572
573
574
575 static void
split_whitespace(string_view str,std::vector<string_view> & result,int maxsplit)576 split_whitespace(string_view str, std::vector<string_view>& result,
577 int maxsplit)
578 {
579 // Implementation inspired by Pystring
580 string_view::size_type i, j, len = str.size();
581 for (i = j = 0; i < len;) {
582 while (i < len && ::isspace(str[i]))
583 i++;
584 j = i;
585 while (i < len && !::isspace(str[i]))
586 i++;
587 if (j < i) {
588 if (--maxsplit <= 0)
589 break;
590 result.push_back(str.substr(j, i - j));
591 while (i < len && ::isspace(str[i]))
592 i++;
593 j = i;
594 }
595 }
596 if (j < len)
597 result.push_back(str.substr(j, len - j));
598 }
599
600
601
602 std::vector<std::string>
splits(string_view str,string_view sep,int maxsplit)603 Strutil::splits(string_view str, string_view sep, int maxsplit)
604 {
605 auto sr_result = splitsv(str, sep, maxsplit);
606 std::vector<std::string> result;
607 result.reserve(sr_result.size());
608 for (auto& s : sr_result)
609 result.push_back(s);
610 return result;
611 }
612
613
614
615 void
split(string_view str,std::vector<std::string> & result,string_view sep,int maxsplit)616 Strutil::split(string_view str, std::vector<std::string>& result,
617 string_view sep, int maxsplit)
618 {
619 result = splits(str, sep, maxsplit);
620 }
621
622
623
624 std::vector<string_view>
splitsv(string_view str,string_view sep,int maxsplit)625 Strutil::splitsv(string_view str, string_view sep, int maxsplit)
626 {
627 std::vector<string_view> result;
628 if (str.size() == 0)
629 return result; // No source string --> no pieces
630
631 // Implementation inspired by Pystring
632 if (maxsplit < 0)
633 maxsplit = std::numeric_limits<int>::max();
634 if (sep.size() == 0) {
635 split_whitespace(str, result, maxsplit);
636 return result;
637 }
638 size_t i = 0, j = 0, len = str.size(), n = sep.size();
639 while (i + n <= len) {
640 if (str[i] == sep[0] && str.substr(i, n) == sep) {
641 if (--maxsplit <= 0)
642 break;
643 result.push_back(str.substr(j, i - j));
644 i = j = i + n;
645 } else {
646 i++;
647 }
648 }
649 result.push_back(str.substr(j, len - j));
650 return result;
651 }
652
653
654
655 void
split(string_view str,std::vector<string_view> & result,string_view sep,int maxsplit)656 Strutil::split(string_view str, std::vector<string_view>& result,
657 string_view sep, int maxsplit)
658 {
659 result = splitsv(str, sep, maxsplit);
660 }
661
662
663
664 std::string
concat(string_view s,string_view t)665 Strutil::concat(string_view s, string_view t)
666 {
667 size_t sl = s.size();
668 size_t tl = t.size();
669 size_t len = sl + tl;
670 std::unique_ptr<char[]> heap_buf;
671 char local_buf[256];
672 char* buf = local_buf;
673 if (len > sizeof(local_buf)) {
674 heap_buf.reset(new char[len]);
675 buf = heap_buf.get();
676 }
677 memcpy(buf, s.data(), sl);
678 memcpy(buf + sl, t.data(), tl);
679 return std::string(buf, len);
680 }
681
682
683
684 std::string
repeat(string_view str,int n)685 Strutil::repeat(string_view str, int n)
686 {
687 size_t sl = str.size();
688 size_t len = sl * std::max(0, n);
689 std::unique_ptr<char[]> heap_buf;
690 char local_buf[256];
691 char* buf = local_buf;
692 if (len > sizeof(local_buf)) {
693 heap_buf.reset(new char[len]);
694 buf = heap_buf.get();
695 }
696 for (int i = 0; i < n; ++i)
697 memcpy(buf + i * sl, str.data(), sl);
698 return std::string(buf, len);
699 }
700
701
702
703 std::string
replace(string_view str,string_view pattern,string_view replacement,bool global)704 Strutil::replace(string_view str, string_view pattern, string_view replacement,
705 bool global)
706 {
707 std::string r;
708 while (1) {
709 size_t f = str.find(pattern);
710 if (f != str.npos) {
711 // Pattern found -- copy the part of str prior to the pattern,
712 // then copy the replacement, and skip str up to the part after
713 // the pattern and continue for another go at it.
714 r.append(str.data(), f);
715 r.append(replacement.data(), replacement.size());
716 str.remove_prefix(f + pattern.size());
717 if (global)
718 continue; // Try for another match
719 }
720 // Pattern not found -- copy remaining string and we're done
721 r.append(str.data(), str.size());
722 break;
723 }
724 return r;
725 }
726
727
728
729 #ifdef _WIN32
730 std::wstring
utf8_to_utf16(string_view str)731 Strutil::utf8_to_utf16(string_view str) noexcept
732 {
733 std::wstring native;
734
735 native.resize(
736 MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), NULL, 0));
737 MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), &native[0],
738 (int)native.size());
739
740 return native;
741 }
742
743
744
745 std::string
utf16_to_utf8(const std::wstring & str)746 Strutil::utf16_to_utf8(const std::wstring& str) noexcept
747 {
748 std::string utf8;
749
750 utf8.resize(WideCharToMultiByte(CP_UTF8, 0, str.data(), str.length(), NULL,
751 0, NULL, NULL));
752 WideCharToMultiByte(CP_UTF8, 0, str.data(), str.length(), &utf8[0],
753 (int)utf8.size(), NULL, NULL);
754
755 return utf8;
756 }
757 #endif
758
759
760
761 char*
safe_strcpy(char * dst,string_view src,size_t size)762 Strutil::safe_strcpy(char* dst, string_view src, size_t size) noexcept
763 {
764 if (src.size()) {
765 size_t end = std::min(size - 1, src.size());
766 for (size_t i = 0; i < end; ++i)
767 dst[i] = src[i];
768 for (size_t i = end; i < size; ++i)
769 dst[i] = 0;
770 } else {
771 for (size_t i = 0; i < size; ++i)
772 dst[i] = 0;
773 }
774 return dst;
775 }
776
777
778
779 void
skip_whitespace(string_view & str)780 Strutil::skip_whitespace(string_view& str) noexcept
781 {
782 while (str.size() && isspace(str.front()))
783 str.remove_prefix(1);
784 }
785
786
787
788 void
remove_trailing_whitespace(string_view & str)789 Strutil::remove_trailing_whitespace(string_view& str) noexcept
790 {
791 while (str.size() && isspace(str.back()))
792 str.remove_suffix(1);
793 }
794
795
796
797 bool
parse_char(string_view & str,char c,bool skip_whitespace,bool eat)798 Strutil::parse_char(string_view& str, char c, bool skip_whitespace,
799 bool eat) noexcept
800 {
801 string_view p = str;
802 if (skip_whitespace)
803 Strutil::skip_whitespace(p);
804 if (p.size() && p[0] == c) {
805 if (eat) {
806 p.remove_prefix(1);
807 str = p;
808 }
809 return true;
810 }
811 return false;
812 }
813
814
815
816 bool
parse_until_char(string_view & str,char c,bool eat)817 Strutil::parse_until_char(string_view& str, char c, bool eat) noexcept
818 {
819 string_view p = str;
820 while (p.size() && p[0] != c)
821 p.remove_prefix(1);
822 if (eat)
823 str = p;
824 return p.size() && p[0] == c;
825 }
826
827
828
829 bool
parse_prefix(string_view & str,string_view prefix,bool eat)830 Strutil::parse_prefix(string_view& str, string_view prefix, bool eat) noexcept
831 {
832 string_view p = str;
833 skip_whitespace(p);
834 if (Strutil::starts_with(p, prefix)) {
835 p.remove_prefix(prefix.size());
836 if (eat)
837 str = p;
838 return true;
839 }
840 return false;
841 }
842
843
844
845 bool
parse_int(string_view & str,int & val,bool eat)846 Strutil::parse_int(string_view& str, int& val, bool eat) noexcept
847 {
848 string_view p = str;
849 skip_whitespace(p);
850 if (!p.size())
851 return false;
852 size_t endpos = 0;
853 int v = Strutil::stoi(p, &endpos);
854 if (endpos == 0)
855 return false; // no integer found
856 if (eat)
857 str = p.substr(endpos);
858 val = v;
859 return true;
860 }
861
862
863
864 bool
parse_float(string_view & str,float & val,bool eat)865 Strutil::parse_float(string_view& str, float& val, bool eat) noexcept
866 {
867 string_view p = str;
868 skip_whitespace(p);
869 if (!p.size())
870 return false;
871 size_t endpos = 0;
872 float v = Strutil::stof(p, &endpos);
873 if (endpos == 0)
874 return false; // no integer found
875 if (eat)
876 str = p.substr(endpos);
877 val = v;
878 return true;
879 }
880
881
882
883 bool
parse_string(string_view & str,string_view & val,bool eat,QuoteBehavior keep_quotes)884 Strutil::parse_string(string_view& str, string_view& val, bool eat,
885 QuoteBehavior keep_quotes) noexcept
886 {
887 string_view p = str;
888 skip_whitespace(p);
889 if (str.empty())
890 return false;
891 char lead_char = p.front();
892 bool quoted = parse_char(p, '\"') || parse_char(p, '\'');
893 const char *begin = p.begin(), *end = p.begin();
894 bool escaped = false; // was the prior character a backslash
895 while (end != p.end()) {
896 if (isspace(*end) && !quoted)
897 break; // not quoted and we hit whitespace: we're done
898 if (quoted && *end == lead_char && !escaped)
899 break; // closing quote -- we're done (beware embedded quote)
900 escaped = (end[0] == '\\') && (!escaped);
901 ++end;
902 }
903 if (quoted && keep_quotes == KeepQuotes) {
904 if (*end == lead_char)
905 val = string_view(begin - 1, size_t(end - begin) + 2);
906 else
907 val = string_view(begin - 1, size_t(end - begin) + 1);
908 } else {
909 val = string_view(begin, size_t(end - begin));
910 }
911 p.remove_prefix(size_t(end - begin));
912 if (quoted && p.size() && p[0] == lead_char)
913 p.remove_prefix(1); // eat closing quote
914 if (eat)
915 str = p;
916 return quoted || val.size();
917 }
918
919
920
921 string_view
parse_word(string_view & str,bool eat)922 Strutil::parse_word(string_view& str, bool eat) noexcept
923 {
924 string_view p = str;
925 skip_whitespace(p);
926 const char *begin = p.begin(), *end = p.begin();
927 while (end != p.end() && isalpha(*end))
928 ++end;
929 size_t wordlen = end - begin;
930 if (eat && wordlen) {
931 p.remove_prefix(wordlen);
932 str = p;
933 }
934 return string_view(begin, wordlen);
935 }
936
937
938
939 string_view
parse_identifier(string_view & str,bool eat)940 Strutil::parse_identifier(string_view& str, bool eat) noexcept
941 {
942 string_view p = str;
943 skip_whitespace(p);
944 const char *begin = p.begin(), *end = p.begin();
945 if (end != p.end() && (isalpha(*end) || *end == '_'))
946 ++end;
947 else
948 return string_view(); // not even the start of an identifier
949 while (end != p.end() && (isalpha(*end) || isdigit(*end) || *end == '_'))
950 ++end;
951 if (eat) {
952 p.remove_prefix(size_t(end - begin));
953 str = p;
954 }
955 return string_view(begin, size_t(end - begin));
956 }
957
958
959
960 string_view
parse_identifier(string_view & str,string_view allowed,bool eat)961 Strutil::parse_identifier(string_view& str, string_view allowed,
962 bool eat) noexcept
963 {
964 string_view p = str;
965 skip_whitespace(p);
966 const char *begin = p.begin(), *end = p.begin();
967 if (end != p.end()
968 && (isalpha(*end) || *end == '_'
969 || allowed.find(*end) != string_view::npos))
970 ++end;
971 else
972 return string_view(); // not even the start of an identifier
973 while (end != p.end()
974 && (isalpha(*end) || isdigit(*end) || *end == '_'
975 || allowed.find(*end) != string_view::npos))
976 ++end;
977 if (eat) {
978 p.remove_prefix(size_t(end - begin));
979 str = p;
980 }
981 return string_view(begin, size_t(end - begin));
982 }
983
984
985
986 bool
parse_identifier_if(string_view & str,string_view id,bool eat)987 Strutil::parse_identifier_if(string_view& str, string_view id,
988 bool eat) noexcept
989 {
990 string_view head = parse_identifier(str, false /* don't eat */);
991 if (head == id) {
992 if (eat)
993 parse_identifier(str);
994 return true;
995 }
996 return false;
997 }
998
999
1000
1001 string_view
parse_until(string_view & str,string_view sep,bool eat)1002 Strutil::parse_until(string_view& str, string_view sep, bool eat) noexcept
1003 {
1004 string_view p = str;
1005 const char *begin = p.begin(), *end = p.begin();
1006 while (end != p.end() && sep.find(*end) == string_view::npos)
1007 ++end;
1008 size_t wordlen = end - begin;
1009 if (eat && wordlen) {
1010 p.remove_prefix(wordlen);
1011 str = p;
1012 }
1013 return string_view(begin, wordlen);
1014 }
1015
1016
1017
1018 string_view
parse_while(string_view & str,string_view set,bool eat)1019 Strutil::parse_while(string_view& str, string_view set, bool eat) noexcept
1020 {
1021 string_view p = str;
1022 const char *begin = p.begin(), *end = p.begin();
1023 while (end != p.end() && set.find(*end) != string_view::npos)
1024 ++end;
1025 size_t wordlen = end - begin;
1026 if (eat && wordlen) {
1027 p.remove_prefix(wordlen);
1028 str = p;
1029 }
1030 return string_view(begin, wordlen);
1031 }
1032
1033
1034
1035 string_view
parse_nested(string_view & str,bool eat)1036 Strutil::parse_nested(string_view& str, bool eat) noexcept
1037 {
1038 // Make sure we have a valid string and ascertain the characters that
1039 // nest and unnest.
1040 string_view p = str;
1041 if (!p.size())
1042 return string_view(); // No proper opening
1043 char opening = p[0];
1044 char closing = 0;
1045 if (opening == '(')
1046 closing = ')';
1047 else if (opening == '[')
1048 closing = ']';
1049 else if (opening == '{')
1050 closing = '}';
1051 else
1052 return string_view();
1053
1054 // Walk forward in the string until we exactly unnest compared to the
1055 // start.
1056 size_t len = 1;
1057 int nesting = 1;
1058 for (; nesting && len < p.size(); ++len) {
1059 if (p[len] == opening)
1060 ++nesting;
1061 else if (p[len] == closing)
1062 --nesting;
1063 }
1064
1065 if (nesting)
1066 return string_view(); // No proper closing
1067
1068 OIIO_ASSERT(p[len - 1] == closing);
1069
1070 // The result is the first len characters
1071 string_view result = str.substr(0, len);
1072 if (eat)
1073 str.remove_prefix(len);
1074 return result;
1075 }
1076
1077
1078
1079 std::string
excise_string_after_head(std::string & str,string_view head)1080 Strutil::excise_string_after_head(std::string& str, string_view head)
1081 {
1082 std::string result;
1083 string_view s(str);
1084 size_t pattern_start = s.find(head);
1085 if (pattern_start != string_view::npos) {
1086 // Reposition s to be after the head
1087 s.remove_prefix(pattern_start + head.size());
1088 string_view m = Strutil::parse_until(s, " \t\r\n");
1089 Strutil::skip_whitespace(s);
1090 result = m;
1091 str = str.substr(0, pattern_start) + std::string(s);
1092 }
1093 return result;
1094 }
1095
1096
1097
1098 /*
1099 Copyright for decode function:
1100 See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
1101
1102 MIT license
1103
1104 Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
1105
1106 Permission is hereby granted, free of charge, to any person obtaining a copy
1107 of this software and associated documentation files (the "Software"), to
1108 deal in the Software without restriction, including without limitation the
1109 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
1110 sell copies of the Software, and to permit persons to whom the Software is
1111 furnished to do so, subject to the following conditions:
1112
1113 The above copyright notice and this permission notice shall be included in
1114 all copies or substantial portions of the Software.
1115
1116 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1117 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1118 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1119 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1120 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1121 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1122 IN THE SOFTWARE.
1123 */
1124
1125 #define UTF8_ACCEPT 0
1126 #define UTF8_REJECT 12
1127
1128 static const uint8_t utf8d[] = {
1129 // clang-format off
1130 // The first part of the table maps bytes to character classes that
1131 // to reduce the size of the transition table and create bitmasks.
1132 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1133 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1134 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1135 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1136 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
1137 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1138 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
1139 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
1140
1141 // The second part is a transition table that maps a combination
1142 // of a state of the automaton and a character class to a state.
1143 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
1144 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
1145 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
1146 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
1147 12,36,12,12,12,12,12,12,12,12,12,12,
1148 // clang-format on
1149 };
1150
1151 inline uint32_t
decode(uint32_t * state,uint32_t * codep,uint32_t byte)1152 decode(uint32_t* state, uint32_t* codep, uint32_t byte)
1153 {
1154 uint32_t type = utf8d[byte];
1155 *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6)
1156 : (0xff >> type) & (byte);
1157 *state = utf8d[256 + *state + type];
1158 return *state;
1159 }
1160
1161 void
utf8_to_unicode(string_view str,std::vector<uint32_t> & uvec)1162 Strutil::utf8_to_unicode(string_view str, std::vector<uint32_t>& uvec)
1163 {
1164 const char* begin = str.begin();
1165 const char* end = str.end();
1166 uint32_t state = 0;
1167 for (; begin != end; ++begin) {
1168 uint32_t codepoint = 0;
1169 if (!decode(&state, &codepoint, (unsigned char)*begin))
1170 uvec.push_back(codepoint);
1171 }
1172 }
1173
1174
1175
1176 /* base64 code is based upon: http://www.adp-gmbh.ch/cpp/common/base64.html
1177 https://github.com/ReneNyffenegger/cpp-base64
1178
1179 Zlib license
1180
1181 Copyright (C) 2004-2008 René Nyffenegger
1182
1183 This source code is provided 'as-is', without any express or implied
1184 warranty. In no event will the author be held liable for any damages
1185 arising from the use of this software.
1186
1187 Permission is granted to anyone to use this software for any purpose,
1188 including commercial applications, and to alter it and redistribute it
1189 freely, subject to the following restrictions:
1190
1191 1. The origin of this source code must not be misrepresented; you must not
1192 claim that you wrote the original source code. If you use this source code
1193 in a product, an acknowledgment in the product documentation would be
1194 appreciated but is not required.
1195 2. Altered source versions must be plainly marked as such, and must not be
1196 misrepresented as being the original source code.
1197 3. This notice may not be removed or altered from any source distribution.
1198
1199 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
1200 */
1201 std::string
base64_encode(string_view str)1202 Strutil::base64_encode(string_view str)
1203 {
1204 static const char* base64_chars
1205 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1206 std::string ret;
1207 ret.reserve((str.size() * 4 + 2) / 3);
1208 int i = 0;
1209 unsigned char char_array_3[3];
1210 unsigned char char_array_4[4];
1211 while (str.size()) {
1212 char_array_3[i++] = str.front();
1213 str.remove_prefix(1);
1214 if (i == 3) {
1215 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1216 char_array_4[1] = ((char_array_3[0] & 0x03) << 4)
1217 + ((char_array_3[1] & 0xf0) >> 4);
1218 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2)
1219 + ((char_array_3[2] & 0xc0) >> 6);
1220 char_array_4[3] = char_array_3[2] & 0x3f;
1221 for (int j = 0; j < 4; j++)
1222 ret += base64_chars[char_array_4[j]];
1223 i = 0;
1224 }
1225 }
1226 if (i) {
1227 for (int j = i; j < 3; j++)
1228 char_array_3[j] = '\0';
1229 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1230 char_array_4[1] = ((char_array_3[0] & 0x03) << 4)
1231 + ((char_array_3[1] & 0xf0) >> 4);
1232 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2)
1233 + ((char_array_3[2] & 0xc0) >> 6);
1234 char_array_4[3] = char_array_3[2] & 0x3f;
1235 for (int j = 0; j < i + 1; j++)
1236 ret += base64_chars[char_array_4[j]];
1237 while (i++ < 3)
1238 ret += '=';
1239 }
1240 return ret;
1241 }
1242
1243
1244
1245 // Helper: Eat the given number of chars from str, then return the next
1246 // char, or 0 if no more chars are in str.
1247 inline unsigned char
cnext(string_view & str,int eat=1)1248 cnext(string_view& str, int eat = 1)
1249 {
1250 str.remove_prefix(eat);
1251 return OIIO_LIKELY(str.size()) ? str.front() : 0;
1252 }
1253
1254
1255
1256 int
stoi(string_view str,size_t * pos,int base)1257 Strutil::stoi(string_view str, size_t* pos, int base)
1258 {
1259 // We roll our own stoi so that we can directly use it with a
1260 // string_view and also hardcode it without any locale dependence. The
1261 // system stoi/atoi/strtol/etc. needs to be null-terminated.
1262 string_view str_orig = str;
1263 Strutil::skip_whitespace(str);
1264
1265 // c is always the next char to parse, or 0 if there's no more
1266 unsigned char c = cnext(str, 0); // don't eat the char
1267
1268 // Handle leading - or +
1269 bool neg = (c == '-');
1270 if (c == '-' || c == '+')
1271 c = cnext(str);
1272
1273 // Allow "0x" to start hex number if base is 16 or 0 (any)
1274 if ((base == 0 || base == 16) && c == '0' && str.size() >= 1
1275 && (str[1] == 'x' || str[1] == 'X')) {
1276 base = 16;
1277 c = cnext(str, 2);
1278 }
1279 // For "any" base, collapse to base 10 unless leading 0 means it's octal
1280 if (base == 0)
1281 base = c == '0' ? 8 : 10;
1282
1283 // Accumulate into a 64 bit int to make overflow handling simple.
1284 // If we ever need a str-to-int64 conversion, we'll need to be more
1285 // careful with figuring out overflow. But the 32 bit case is easy.
1286 int64_t acc = 0;
1287 bool overflow = false; // Set if we overflow
1288 bool anydigits = false; // Have we parsed any digits at all?
1289 int64_t maxval = neg ? -int64_t(std::numeric_limits<int>::min())
1290 : std::numeric_limits<int>::max();
1291 for (; OIIO_LIKELY(c); c = cnext(str)) {
1292 if (OIIO_LIKELY(isdigit(c)))
1293 c -= '0';
1294 else if (isalpha(c))
1295 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
1296 else {
1297 break; // done
1298 }
1299 if (c >= base)
1300 break;
1301 acc = acc * base + c;
1302 anydigits = true;
1303 if (OIIO_UNLIKELY(acc > maxval))
1304 overflow = true;
1305 }
1306 if (OIIO_UNLIKELY(!anydigits)) {
1307 str = str_orig;
1308 } else if (OIIO_UNLIKELY(overflow)) {
1309 acc = neg ? std::numeric_limits<int>::min()
1310 : std::numeric_limits<int>::max();
1311 } else {
1312 if (neg)
1313 acc = -acc;
1314 }
1315 if (pos)
1316 *pos = size_t(str.data() - str_orig.data());
1317 return static_cast<int>(acc);
1318 }
1319
1320
1321
1322 float
strtof(const char * nptr,char ** endptr)1323 Strutil::strtof(const char* nptr, char** endptr) noexcept
1324 {
1325 // Can use strtod_l on platforms that support it
1326 #ifdef __APPLE__
1327 // On OSX, strtod_l is for some reason drastically faster than strtof_l.
1328 return static_cast<float>(strtod_l(nptr, endptr, c_loc));
1329 #elif defined(__linux__) || defined(__FreeBSD__) \
1330 || defined(__FreeBSD_kernel__) || defined(__GLIBC__)
1331 return strtof_l(nptr, endptr, c_loc);
1332 #elif defined(_WIN32)
1333 // Windows has _strtod_l
1334 return static_cast<float>(_strtod_l(nptr, endptr, c_loc));
1335 #else
1336 // On platforms without strtof_l...
1337 std::locale native; // default ctr gets current global locale
1338 char nativepoint
1339 = std::use_facet<std::numpunct<char>>(native).decimal_point();
1340 // If the native locale uses decimal, just directly use strtof.
1341 if (nativepoint == '.')
1342 return ::strtof(nptr, endptr);
1343 // Complex case -- CHEAT by making a copy of the string and replacing
1344 // the decimal, then use system strtof!
1345 std::string s(nptr);
1346 const char* pos = strchr(nptr, nativepoint);
1347 if (pos) {
1348 s[pos - nptr] = nativepoint;
1349 auto d = strtof(s.c_str(), endptr);
1350 if (endptr)
1351 *endptr = (char*)nptr + (*endptr - s.c_str());
1352 return d;
1353 }
1354 // No decimal point at all -- use regular strtof
1355 return ::strtof(s.c_str(), endptr);
1356 #endif
1357 }
1358
1359
1360 double
strtod(const char * nptr,char ** endptr)1361 Strutil::strtod(const char* nptr, char** endptr) noexcept
1362 {
1363 // Can use strtod_l on platforms that support it
1364 #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) \
1365 || defined(__FreeBSD_kernel__) || defined(__GLIBC__)
1366 // static initialization inside function is thread-safe by C++11 rules!
1367 return strtod_l(nptr, endptr, c_loc);
1368 #elif defined(_WIN32)
1369 // Windows has _strtod_l
1370 return _strtod_l(nptr, endptr, c_loc);
1371 #else
1372 // On platforms without strtod_l...
1373 std::locale native; // default ctr gets current global locale
1374 char nativepoint
1375 = std::use_facet<std::numpunct<char>>(native).decimal_point();
1376 // If the native locale uses decimal, just directly use strtod.
1377 if (nativepoint == '.')
1378 return ::strtod(nptr, endptr);
1379 // Complex case -- CHEAT by making a copy of the string and replacing
1380 // the decimal, then use system strtod!
1381 std::string s(nptr);
1382 const char* pos = strchr(nptr, nativepoint);
1383 if (pos) {
1384 s[pos - nptr] = nativepoint;
1385 auto d = ::strtod(s.c_str(), endptr);
1386 if (endptr)
1387 *endptr = (char*)nptr + (*endptr - s.c_str());
1388 return d;
1389 }
1390 // No decimal point at all -- use regular strtod
1391 return ::strtod(s.c_str(), endptr);
1392 #endif
1393 }
1394
1395 // Notes:
1396 //
1397 // FreeBSD's implementation of strtod:
1398 // https://svnweb.freebsd.org/base/stable/10/contrib/gdtoa/strtod.c?view=markup
1399 // Python's implementation of strtod: (BSD license)
1400 // https://hg.python.org/cpython/file/default/Python/pystrtod.c
1401 // Julia's implementation (combo of strtod_l and Python's impl):
1402 // https://github.com/JuliaLang/julia/blob/master/src/support/strtod.c
1403 // MSDN documentation on Windows _strtod_l and friends:
1404 // https://msdn.microsoft.com/en-us/library/kxsfc1ab.aspx (_strtod_l)
1405 // https://msdn.microsoft.com/en-us/library/4zx9aht2.aspx (_create_locale)
1406 // cppreference on locale:
1407 // http://en.cppreference.com/w/cpp/locale/locale
1408
1409
1410
1411 float
stof(const char * s,size_t * pos)1412 Strutil::stof(const char* s, size_t* pos)
1413 {
1414 if (s) {
1415 char* endptr;
1416 float r = Strutil::strtof(s, &endptr);
1417 if (endptr != s) {
1418 if (pos)
1419 *pos = size_t(endptr - s);
1420 return r;
1421 }
1422 }
1423 // invalid
1424 if (pos)
1425 *pos = 0;
1426 return 0;
1427 }
1428
1429
1430 float
stof(const std::string & s,size_t * pos)1431 Strutil::stof(const std::string& s, size_t* pos)
1432 {
1433 return Strutil::stof(s.c_str(), pos);
1434 }
1435
1436
1437 float
stof(string_view s,size_t * pos)1438 Strutil::stof(string_view s, size_t* pos)
1439 {
1440 // string_view can't be counted on to end with a terminating null, so
1441 // for safety, create a temporary string. This looks wasteful, but it's
1442 // not as bad as you think -- fully compliant C++ >= 11 implementations
1443 // will use the "short string optimization", meaning that this string
1444 // creation will NOT need an allocation/free for most strings we expect
1445 // to hold a text representation of a float.
1446 return Strutil::stof(std::string(s).c_str(), pos);
1447 }
1448
1449
1450
1451 double
stod(const char * s,size_t * pos)1452 Strutil::stod(const char* s, size_t* pos)
1453 {
1454 if (s) {
1455 char* endptr;
1456 double r = Strutil::strtod(s, &endptr);
1457 if (endptr != s) {
1458 if (pos)
1459 *pos = size_t(endptr - s);
1460 return r;
1461 }
1462 }
1463 // invalid
1464 if (pos)
1465 *pos = 0;
1466 return 0;
1467 }
1468
1469
1470 double
stod(const std::string & s,size_t * pos)1471 Strutil::stod(const std::string& s, size_t* pos)
1472 {
1473 return Strutil::stod(s.c_str(), pos);
1474 }
1475
1476
1477 double
stod(string_view s,size_t * pos)1478 Strutil::stod(string_view s, size_t* pos)
1479 {
1480 // string_view can't be counted on to end with a terminating null, so
1481 // for safety, create a temporary string. This looks wasteful, but it's
1482 // not as bad as you think -- fully compliant C++ >= 11 implementations
1483 // will use the "short string optimization", meaning that this string
1484 // creation will NOT need an allocation/free for most strings we expect
1485 // to hold a text representation of a float.
1486 return Strutil::stod(std::string(s).c_str(), pos);
1487 }
1488
1489
1490
1491 bool
string_is_int(string_view s)1492 Strutil::string_is_int(string_view s)
1493 {
1494 size_t pos;
1495 Strutil::stoi(s, &pos);
1496 if (pos) { // skip remaining whitespace
1497 s.remove_prefix(pos);
1498 Strutil::skip_whitespace(s);
1499 }
1500 return pos && s.empty(); // consumed the whole string
1501 }
1502
1503
1504 bool
string_is_float(string_view s)1505 Strutil::string_is_float(string_view s)
1506 {
1507 size_t pos;
1508 Strutil::stof(s, &pos);
1509 if (pos) { // skip remaining whitespace
1510 s.remove_prefix(pos);
1511 Strutil::skip_whitespace(s);
1512 }
1513 return pos && s.empty(); // consumed the whole string
1514 }
1515
1516
1517 OIIO_NAMESPACE_END
1518