make lexical casts (string -> int/float) faster

This commit is contained in:
Kae 2024-09-16 23:02:22 +10:00
parent 40299558dd
commit 090441b80a
6 changed files with 3967 additions and 21 deletions

View File

@ -160,6 +160,7 @@ SET (star_core_SOURCES
StarJsonPatch.cpp
StarJsonRpc.cpp
StarFormattedJson.cpp
StarLexicalCast.cpp
StarListener.cpp
StarLogging.cpp
StarLua.cpp

View File

@ -0,0 +1,13 @@
#include "StarLexicalCast.hpp"
namespace Star {
void throwLexicalCastError(std::errc ec, const char* first, const char* last) {
StringView str(first, last - first);
if (ec == std::errc::invalid_argument)
throw BadLexicalCast(strf("Lexical cast failed on '{}' (invalid argument)", str));
else
throw BadLexicalCast(strf("Lexical cast failed on '{}'", str));
}
}

View File

@ -5,40 +5,58 @@
#include "StarStringView.hpp"
#include "StarMaybe.hpp"
#include <sstream>
#include <locale>
#include "fast_float.h"
namespace Star {
STAR_EXCEPTION(BadLexicalCast, StarException);
// Very simple basic lexical cast using stream input. Always operates in the
// "C" locale.
void throwLexicalCastError(std::errc ec, const char* first, const char* last);
template <typename Type>
Maybe<Type> maybeLexicalCast(StringView s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
Type result;
std::istringstream stream(std::string(s.utf8()));
stream.flags(flags);
stream.imbue(std::locale::classic());
bool tryLexicalCast(Type& result, const char* first, const char* last) {
auto res = fast_float::from_chars(first, last, result);
return res.ptr == last && (res.ec == std::errc() || res.ec == std::errc::result_out_of_range);
}
if (!(stream >> result))
template <typename Type>
bool tryLexicalCast(Type& result, String const& s) {
return tryLexicalCast<Type>(s.utf8Ptr(), s.utf8Ptr() + s.utf8Size());
}
template <typename Type>
bool tryLexicalCast(Type& result, StringView s) {
return tryLexicalCast<Type>(s.utf8Ptr(), s.utf8Ptr() + s.utf8Size());
}
template <typename Type>
Maybe<Type> maybeLexicalCast(const char* first, const char* last) {
Type result{};
if (tryLexicalCast(result, first, last))
return result;
else
return {};
}
// Confirm that we read everything out of the stream
char ch;
if (stream >> ch)
return {};
template <typename Type>
Maybe<Type> maybeLexicalCast(StringView s) {
return maybeLexicalCast<Type>(s.utf8Ptr(), s.utf8Ptr() + s.utf8Size());
}
template <typename Type>
Type lexicalCast(const char* first, const char* last) {
Type result{};
auto res = fast_float::from_chars(first, last, result);
if ((res.ec != std::errc() && res.ec != std::errc::result_out_of_range) || res.ptr != last)
throwLexicalCastError(res.ec, first, last);
return result;
}
template <typename Type>
Type lexicalCast(StringView s, std::ios_base::fmtflags flags = std::ios_base::boolalpha) {
auto m = maybeLexicalCast<Type>(s, flags);
if (m)
return m.take();
else
throw BadLexicalCast(strf("Lexical cast failed on '{}'", s));
Type lexicalCast(StringView s) {
return lexicalCast<Type>(s.utf8Ptr(), s.utf8Ptr() + s.utf8Size());
}
}

View File

@ -15,6 +15,7 @@ SET (star_extern_HEADERS
fmt/printf.h
fmt/ranges.h
fmt/std.h
fast_float.h
lauxlib.h
lua.h
lua.hpp

3913
source/extern/fast_float.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -262,7 +262,7 @@ String CommandProcessor::setTileProtection(ConnectionId connectionId, String con
return "Not enough arguments to /settileprotection. Use /settileprotection <dungeonId> <protected>";
try {
bool isProtected = lexicalCast<bool>(arguments.takeLast());
bool isProtected = Json::parse(arguments.takeLast()).toBool();
List<DungeonId> dungeonIds;
for (auto& banana : arguments) {
auto slices = banana.split("..");