Skip to content

Commit b5d038d

Browse files
committed
More string fixups -- fix more latent bugs assuming null-terminated string_view
1 parent 3b668cf commit b5d038d

File tree

6 files changed

+314
-136
lines changed

6 files changed

+314
-136
lines changed

src/include/OpenImageIO/strutil.h

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -290,102 +290,108 @@ float OIIO_API strtof (const char *nptr, char **endptr, const std::locale& loc);
290290

291291

292292

293-
// Helper template to test if a string is a generic type
294-
template<typename T>
295-
inline bool string_is (string_view /*s*/) {
296-
return false; // Generic: assume there is an explicit specialization
297-
}
298-
// Special case for int
299-
template <> inline bool string_is<int> (string_view s) {
300-
if (s.empty())
301-
return false;
302-
char *endptr = 0;
303-
strtol (s.data(), &endptr, 10);
304-
return (s.data() + s.size() == endptr);
305-
}
306-
// Special case for float. Note that by using Strutil::strtof, this always
307-
// treats '.' as the decimal character.
308-
template <> inline bool string_is<float> (string_view s) {
309-
if (s.empty())
310-
return false;
311-
char *endptr = 0;
312-
Strutil::strtof (s.data(), &endptr);
313-
return (s.data() + s.size() == endptr);
314-
}
315-
316-
317-
318293
// stoi() returns the int conversion of text from several string types.
319294
// No exceptions or errors -- parsing errors just return 0.
320-
inline int stoi (const char* s) {
321-
return s && s[0] ? strtol (s, nullptr, 10) : 0;
322-
}
323-
inline int stoi (const std::string& s) { return Strutil::stoi(s.c_str()); }
324-
inline int stoi (string_view s) { return Strutil::stoi (std::string(s)); }
325-
295+
OIIO_API int stoi (const char* s, size_t* pos=0, int base=10);
296+
OIIO_API int stoi (const std::string& s, size_t* pos=0, int base=10);
297+
OIIO_API int stoi (string_view s, size_t* pos=0, int base=10);
298+
// N.B. For users of ustring, there's a stoi(ustring) defined in ustring.h.
326299

327300

328301
// stoul() returns the unsigned int conversion of text from several string
329302
// types. No exceptions or errors -- parsing errors just return 0.
330-
inline unsigned int stoul (const char* s) {
331-
return s && s[0] ? strtoul (s, nullptr, 10) : 0;
332-
}
333-
inline unsigned int stoul (const std::string& s) { return Strutil::stoul(s.c_str()); }
334-
inline unsigned int stoul (string_view s) { return Strutil::stoul (std::string(s)); }
335-
303+
OIIO_API unsigned int stoul (const char* s, size_t* pos=0, int base=10);
304+
OIIO_API unsigned int stoul (const std::string& s, size_t* pos=0, int base=10);
305+
OIIO_API unsigned int stoul (string_view s, size_t* pos=0, int base=10);
306+
// N.B. For users of ustring, there's a stoi(ustring) defined in ustring.h.
336307

337308

338309
/// stof() returns the float conversion of text from several string types.
339310
/// No exceptions or errors -- parsing errors just return 0.0. These always
340311
/// use '.' for the decimal mark (versus atof and std::strtof, which are
341312
/// locale-dependent).
342-
inline float stof (const std::string& s) {
343-
return s.size() ? Strutil::strtof (s.c_str(), nullptr) : 0.0f;
344-
}
345-
inline float stof (const char* s) {
346-
return Strutil::strtof (s, nullptr);
347-
}
348-
inline float stof (string_view s) {
349-
return Strutil::strtof (std::string(s).c_str(), nullptr);
350-
}
313+
OIIO_API float stof (const std::string& s, size_t* pos=0);
314+
OIIO_API float stof (const char* s, size_t* pos=0);
315+
OIIO_API float stof (string_view s, size_t* pos=0);
316+
// N.B. For users of ustring, there's a stof(ustring) defined in ustring.h.
351317

352318
// stof() version that takes an explicit locale (for example, if you pass a
353319
// default-constructed std::locale, it will use the current native locale's
354320
// decimal conventions).
355-
inline float stof (const std::string& s, const std::locale& loc) {
356-
return s.size() ? Strutil::strtof (s.c_str(), nullptr, loc) : 0.0f;
321+
OIIO_API float stof (const std::string& s, const std::locale& loc, size_t* pos=0);
322+
OIIO_API float stof (const char* s, const std::locale& loc, size_t* pos=0);
323+
OIIO_API float stof (string_view s, const std::locale& loc, size_t* pos=0);
324+
325+
326+
327+
/// Return true if the string is exactly (other than leading whitespace)
328+
/// a valid int.
329+
inline bool string_is_int (string_view s) {
330+
size_t pos;
331+
Strutil::stoi (s, &pos);
332+
return pos && pos >= s.size(); // consumed the whole string
357333
}
358-
inline float stof (const char* s, const std::locale& loc) {
359-
return Strutil::strtof (s, nullptr, loc);
334+
335+
/// Return true if the string is exactly (other than leading whitespace)
336+
/// a valid float. This operations in a locale-independent manner, i.e.,
337+
/// it assumes '.' as the decimal mark.
338+
inline bool string_is_float (string_view s) {
339+
size_t pos;
340+
Strutil::stof (s, &pos);
341+
return pos && pos >= s.size(); // consumed the whole string
360342
}
361-
inline float stof (string_view s, const std::locale& loc) {
362-
return Strutil::strtof (std::string(s).c_str(), nullptr, loc);
343+
344+
/// Return true if the string is exactly (other than leading whitespace)
345+
/// a valid float. This operations uses an explicit locale.
346+
inline bool string_is_float (string_view s, const std::locale& loc) {
347+
size_t pos;
348+
Strutil::stof (s, loc, &pos);
349+
return pos && pos >= s.size(); // consumed the whole string
363350
}
364351

365352

366353

367354
// Helper template to convert from generic type to string (using default
368-
// locale).
355+
// locale). Used when you want stoX but you're in a template.
369356
template<typename T>
370357
inline T from_string (string_view s) {
371358
return T(s); // Generic: assume there is an explicit converter
372359
}
373360
// Special case for int
374361
template<> inline int from_string<int> (string_view s) {
375-
return s.size() ? strtol (s.c_str(), nullptr, 10) : 0;
362+
return Strutil::stoi(s);
376363
}
377364
// Special case for uint
378365
template<> inline unsigned int from_string<unsigned int> (string_view s) {
379-
return s.size() ? strtoul (s.c_str(), nullptr, 10) : (unsigned int)0;
366+
return Strutil::stoul(s);
380367
}
381368
// Special case for float -- note that by using Strutil::strtof, this
382369
// always treats '.' as the decimal mark.
383370
template<> inline float from_string<float> (string_view s) {
384-
return s.size() ? Strutil::strtof (s.c_str(), nullptr) : 0.0f;
371+
return Strutil::stof(s);
385372
}
386373

387374

388375

376+
// Helper template to test if a string is a generic type. Used instead of
377+
// string_is_X, but when you're inside templated code.
378+
template<typename T>
379+
inline bool string_is (string_view /*s*/) {
380+
return false; // Generic: assume there is an explicit specialization
381+
}
382+
// Special case for int
383+
template <> inline bool string_is<int> (string_view s) {
384+
return string_is_int (s);
385+
}
386+
// Special case for float. Note that by using Strutil::stof, this always
387+
// treats '.' as the decimal character.
388+
template <> inline bool string_is<float> (string_view s) {
389+
return string_is_float (s);
390+
}
391+
392+
393+
394+
389395
/// Given a string containing values separated by a comma (or optionally
390396
/// another separator), extract the individual values, placing them into
391397
/// vals[] which is presumed to already contain defaults. If only a single

src/include/OpenImageIO/ustring.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,9 +765,9 @@ inline bool iequals (const std::string &a, ustring b) {
765765
// add ustring variants of stoi and stof from OpenImageIO/strutil.h
766766
namespace Strutil {
767767
inline int stoi (ustring s) { return Strutil::stoi (s.c_str()); }
768-
inline int stof (ustring s) { return Strutil::strtof (s.c_str()); }
768+
inline int stof (ustring s) { return Strutil::stof (s.c_str()); }
769769
inline int stof (ustring s, const std::locale& loc) {
770-
return Strutil::strtof (s.c_str(), nullptr, loc);
770+
return Strutil::stof (s.c_str(), loc);
771771
}
772772
} // end namespace Strutil
773773

src/libutil/filesystem.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -723,16 +723,16 @@ Filesystem::enumerate_sequence (string_view desc, std::vector<int> &numbers)
723723
// If 'y' is used, generate the complement.
724724
std::vector<std::string> range;
725725
Strutil::split (s, range, "-", 2);
726-
int first = Strutil::from_string<int> (range[0]);
726+
int first = Strutil::stoi (range[0]);
727727
int last = first;
728728
int step = 1;
729729
bool complement = false;
730730
if (range.size() > 1) {
731-
last = Strutil::from_string<int> (range[1]);
731+
last = Strutil::stoi (range[1]);
732732
if (const char *x = strchr (range[1].c_str(), 'x'))
733-
step = (int) strtol (x+1, NULL, 10);
733+
step = Strutil::stoi (x+1);
734734
else if (const char *x = strchr (range[1].c_str(), 'y')) {
735-
step = (int) strtol (x+1, NULL, 10);
735+
step = Strutil::stoi (x+1);
736736
complement = true;
737737
}
738738
if (step == 0)
@@ -989,7 +989,7 @@ Filesystem::scan_for_matching_filenames(const std::string &pattern_,
989989
match_results<std::string::const_iterator> frame_match;
990990
if (regex_match (f, frame_match, pattern_re)) {
991991
std::string thenumber (frame_match[1].first, frame_match[1].second);
992-
int frame = (int)strtol (thenumber.c_str(), NULL, 10);
992+
int frame = Strutil::stoi (thenumber);
993993
matches.push_back (std::make_pair (frame, f));
994994
}
995995
}

0 commit comments

Comments
 (0)