From 0e63edd5387a2fe39f3979644a0e060c1d0bdfb1 Mon Sep 17 00:00:00 2001 From: jyelon Date: Tue, 4 Feb 2025 18:35:26 -0500 Subject: [PATCH] Modify LuaToken so that it emits in base36. The emit and parse routines are 64-bit clean, ie, they can parse and emit any 64-bit number in base36. --- luprex/Makefile | 14 ++++---- luprex/cpp/core/luastack.cpp | 21 ++++++----- luprex/cpp/core/luastack.hpp | 67 ++++++++++++++++++++++++++++++------ 3 files changed, 76 insertions(+), 26 deletions(-) diff --git a/luprex/Makefile b/luprex/Makefile index 02fef99c..176694a5 100644 --- a/luprex/Makefile +++ b/luprex/Makefile @@ -113,9 +113,9 @@ ifeq "$(OS)" "Linux" LUPREX_EXE=luprex LUPREXLIB_DLL=luprexlib.so LUPREXSTATIC_EXE=luprexstatic - COMPILE=g++ -Wall $(OPT) -std=c++17 -fvisibility=hidden -c -MMD -fPIC -o - LINKDLL=g++ -Wall $(OPT) -std=c++17 -export-dynamic -Wl,--no-allow-shlib-undefined -Wl,-z,defs -shared -o - LINKEXE=g++ -Wall $(OPT) -std=c++17 -o + COMPILE=g++ -Wall $(OPT) -std=c++20 -fvisibility=hidden -c -MMD -fPIC -o + LINKDLL=g++ -Wall $(OPT) -std=c++20 -export-dynamic -Wl,--no-allow-shlib-undefined -Wl,-z,defs -shared -o + LINKEXE=g++ -Wall $(OPT) -std=c++20 -o MAKEDEPS=true OPENSSL_INCLUDE=-I./ext/openssl-3.0.1/inc LUA_FLAGS=-DLUA_USE_APICHECK -DLUA_USE_POSIX @@ -136,10 +136,10 @@ ifeq "$(OS)" "Windows" LUPREX_EXE=luprex.exe LUPREXLIB_DLL=luprexlib.dll LUPREXSTATIC_EXE=luprexstatic.exe - COMPILE=CL $(OPT) /std:c++17 /EHsc /nologo /MD /TP /c /Fo: - LINKDLL=CL $(OPT) /std:c++17 /EHsc /nologo /LDd /Fe: - LINKEXE=CL $(OPT) /std:c++17 /EHsc /nologo /Fe: - MAKEDEPS=g++ -Wall -std=c++17 -MMD -E -o + COMPILE=CL $(OPT) /std:c++20 /EHsc /nologo /MD /TP /c /Fo: + LINKDLL=CL $(OPT) /std:c++20 /EHsc /nologo /LDd /Fe: + LINKEXE=CL $(OPT) /std:c++20 /EHsc /nologo /Fe: + MAKEDEPS=g++ -Wall -std=c++20 -MMD -E -o OPENSSL_INCLUDE=-I./ext/openssl-3.1.0/inc LUA_FLAGS=-DLUA_USE_APICHECK -DLUA_COMPAT_ALL LIBS=ext/openssl-3.1.0/lib/visual/libcrypto.lib ext/openssl-3.1.0/lib/visual/libssl.lib ws2_32.lib crypt32.lib cryptui.lib user32.lib advapi32.lib diff --git a/luprex/cpp/core/luastack.cpp b/luprex/cpp/core/luastack.cpp index cdb78d53..dd92856c 100644 --- a/luprex/cpp/core/luastack.cpp +++ b/luprex/cpp/core/luastack.cpp @@ -44,15 +44,20 @@ LuaConstantReg *LuaConstantReg::All; eng::string LuaToken::str() const { - uint64_t token = (uint64_t)value; - char buffer[9]; - for (int i = 0; i < 8; i++) { - unsigned char c = token; - buffer[7-i] = c; - token >>= 8; + uint64_t n = (uint64_t)value; + char buffer[20]; + int pos = 19; + buffer[pos] = 0; + while (n > 0) { + uint64_t digit = (n % 36); + n /= 36; + if (digit < 10) { + buffer[--pos] = '0' + digit; + } else { + buffer[--pos] = 'a' + (digit - 10); + } } - buffer[8] = 0; - return eng::string(buffer); + return eng::string(buffer + pos, 19 - pos); } static int panicf(lua_State *L) { diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index 1eed44ad..565a4068 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -407,6 +407,41 @@ enum LuaTableType { //////////////////////////////////////////////////////////////////// struct LuaToken { +public: + // Convert a base36 number into a 64-bit unsigned integer. If the + // base36 number is not valid, or if it exceeds 64 bits, then return + // nullopt. Leading zeros are not allowed. Empty string gets parsed + // as zero. + // + static constexpr std::optional parse_base36(const char *str) { + uint64_t result = 0; + uint64_t maxint = uint64_t(-1); + // Leading zeros are not allowed. + // Note: Token(0) is emitted as the empty string, not as '0'. + if ((*str == '0')) return std::nullopt; + while (*str) { + char c = *str++; + uint64_t digit = 0; + if ((c >= '0') && (c <= '9')) { + digit = uint64_t(c - '0'); + } else if ((c >= 'a') && (c <= 'z')) { + digit = uint64_t(c - 'a' + 10); + } else if ((c >= 'A') && (c <= 'Z')) { + digit = uint64_t(c - 'A' + 10); + } else { + // Invalid digits return nullopt. + return std::nullopt; + } + // Multiply existing number by 36, then add the digit. + // We have two checks to prevent integer overflow. + if (result > (maxint / 36)) return std::nullopt; + result *= 36; + if (digit > (maxint - result)) return std::nullopt; + result += digit; + } + return result; + } + public: uint64_t value; @@ -417,7 +452,19 @@ public: // Construct a token from a short string. // - constexpr LuaToken(const char *str) : value(literal_to_token(str)) {} + // This is meant to be used for constants in the code. + // It cannot be used for runtime creation of tokens. It is + // limited to 10-character tokens. + // + // Note: using 'throw' in a consteval gives largely the + // effect of a static_assert. + // + consteval LuaToken(const char *str) : value(0) { + auto opt = parse_base36(str); + if (!opt.has_value()) throw "not a valid token"; + if (opt.value_or(0) > 3656158440062975) throw "token may have at most 10 characters"; + value = opt.value_or(0); + } // Construct a token from an int64. // @@ -431,6 +478,9 @@ public: // LuaToken() : value(0) {} + // Assignment operator. + void operator =(const LuaToken &other) { value = other.value; } + // Empty: return true if the token is all zero bytes. // bool empty() const { return value == 0; } @@ -445,18 +495,13 @@ public: // Convert the token to a string. // + // The conversion to string consists of expressing the value + // in base 36. The value 0 is expressed as the empty string. + // eng::string str() const; -private: - static constexpr uint64_t literal_to_token(const char *str) { - uint64_t result = 0; - for (int i = 0; i < 8; i++) { - unsigned char c = *str; - result = (result << 8) + c; - if (*str) str++; - } - return result; - } +public: + }; ////////////////////////////////////////////////////////////////////