diff options
author | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-11-18 14:48:05 +0300 |
---|---|---|
committer | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-11-18 15:04:54 +0300 |
commit | 9e876c7c66440327e3bba353d37e99d68eabb0b9 (patch) | |
tree | b7daabaa3386ab7b1a783fad91da2f014f9354e7 /library/cpp/yt/string/format_analyser.h | |
parent | 13bff9a72fbc1bd6a2643251982afcc3b4a7e93a (diff) | |
download | ydb-9e876c7c66440327e3bba353d37e99d68eabb0b9.tar.gz |
YT-23435: Parse format string at compile time
commit_hash:804530d1ee861ff42d7d8cad25d9f569b4feaacf
Diffstat (limited to 'library/cpp/yt/string/format_analyser.h')
-rw-r--r-- | library/cpp/yt/string/format_analyser.h | 42 |
1 files changed, 33 insertions, 9 deletions
diff --git a/library/cpp/yt/string/format_analyser.h b/library/cpp/yt/string/format_analyser.h index 20eee60580..9f194144dc 100644 --- a/library/cpp/yt/string/format_analyser.h +++ b/library/cpp/yt/string/format_analyser.h @@ -4,6 +4,7 @@ #include <util/generic/strbuf.h> +#include <algorithm> #include <array> #include <string_view> @@ -14,13 +15,26 @@ namespace NYT::NDetail { struct TFormatAnalyser { public: + using TMarkerLocation = std::tuple<int, int>; + // NB(arkady-e1ppa): Location is considered invalid (e.g. not filled) + // if get<0> == get<1> == 0. + template <class... TArgs> + using TMarkerLocations = std::array<TMarkerLocation, sizeof...(TArgs)>; + // NB(arkady-e1ppa): We can't cover all of them since that would require + // dynamic storage for their coordinates and we do not have + // constexpr context large enough to deallocate dynamic memory at the + // correct time. Thus we store first 5 position and scanning afterwards + // is pessimized. |-1| is for no position at all. + // |-2| is used to imply runtime format. + using TEscapeLocations = std::array<int, 5>; + // TODO(arkady-e1ppa): Until clang-19 consteval functions // defined out of line produce symbols in rare cases // causing linker to crash. template <class... TArgs> - static consteval void ValidateFormat(std::string_view fmt) + static consteval auto AnalyzeFormat(std::string_view fmt) { - DoValidateFormat<TArgs...>(fmt); + return DoAnalyzeFormat<TArgs...>(fmt); } private: @@ -51,11 +65,16 @@ private: static constexpr char IntroductorySymbol = '%'; template <class... TArgs> - static consteval void DoValidateFormat(std::string_view format) + static consteval auto DoAnalyzeFormat(std::string_view format) { - std::array<std::string_view, sizeof...(TArgs)> markers = {}; std::array<TSpecifiers, sizeof...(TArgs)> specifiers{GetSpecifiers<TArgs>()...}; + TMarkerLocations<TArgs...> markerLocations = {}; + TEscapeLocations escapeLocations = {}; + std::ranges::fill(escapeLocations, -1); + + int escapesCount = 0; + int markerCount = 0; int currentMarkerStart = -1; @@ -81,12 +100,17 @@ private: throw "You may not terminate flag sequence other than %% with \'%\' symbol"; } // '%%' detected --- skip + if (escapesCount < std::ssize(escapeLocations)) { + escapeLocations[escapesCount] = currentMarkerStart; + ++escapesCount; + } + currentMarkerStart = -1; continue; } // We are inside of marker. - if (markerCount == std::ssize(markers)) { + if (markerCount == std::ssize(markerLocations)) { // Too many markers throw "Number of arguments supplied to format is smaller than the number of flag sequences"; } @@ -94,8 +118,8 @@ private: if (Contains(specifiers[markerCount].Conversion, symbol)) { // Marker has finished. - markers[markerCount] - = std::string_view(format.begin() + currentMarkerStart, index - currentMarkerStart + 1); + markerLocations[markerCount] + = std::tuple{currentMarkerStart, index + 1}; currentMarkerStart = -1; ++markerCount; @@ -110,16 +134,16 @@ private: if (currentMarkerStart != -1) { // Runaway marker. throw "Unterminated flag sequence detected; Use \'%%\' to type plain %"; - return; } - if (markerCount < std::ssize(markers)) { + if (markerCount < std::ssize(markerLocations)) { // Missing markers. throw "Number of arguments supplied to format is greater than the number of flag sequences"; } // TODO(arkady-e1ppa): Consider per-type verification // of markers. + return std::tuple(markerLocations, escapeLocations); } }; |