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

View File

@ -15,6 +15,7 @@ SET (star_extern_HEADERS
fmt/printf.h fmt/printf.h
fmt/ranges.h fmt/ranges.h
fmt/std.h fmt/std.h
fast_float.h
lauxlib.h lauxlib.h
lua.h lua.h
lua.hpp 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>"; return "Not enough arguments to /settileprotection. Use /settileprotection <dungeonId> <protected>";
try { try {
bool isProtected = lexicalCast<bool>(arguments.takeLast()); bool isProtected = Json::parse(arguments.takeLast()).toBool();
List<DungeonId> dungeonIds; List<DungeonId> dungeonIds;
for (auto& banana : arguments) { for (auto& banana : arguments) {
auto slices = banana.split(".."); auto slices = banana.split("..");