aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/yt/string/format_analyser.h
diff options
context:
space:
mode:
authorarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-11-18 14:48:05 +0300
committerarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-11-18 15:04:54 +0300
commit9e876c7c66440327e3bba353d37e99d68eabb0b9 (patch)
treeb7daabaa3386ab7b1a783fad91da2f014f9354e7 /library/cpp/yt/string/format_analyser.h
parent13bff9a72fbc1bd6a2643251982afcc3b4a7e93a (diff)
downloadydb-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.h42
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);
}
};