mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
sync
This commit is contained in:
parent
dd0775ab85
commit
fe2136fdca
|
@ -1,4 +1,5 @@
|
|||
dist: trusty
|
||||
os: linux
|
||||
dist: xenial
|
||||
language: cpp
|
||||
|
||||
#matrix:
|
||||
|
|
76
configure
vendored
76
configure
vendored
|
@ -33645,8 +33645,8 @@ _ACEOF
|
|||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
|
||||
$as_echo "" >&6; }
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${T_MD}Output Substitution:${T_ME}" >&5
|
||||
$as_echo "${T_MD}Output Substitution:${T_ME}" >&6; }
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${T_MD}Final check:${T_ME}" >&5
|
||||
$as_echo "${T_MD}Final check:${T_ME}" >&6; }
|
||||
|
||||
|
||||
ULIB_VERSION="$PACKAGE_VERSION"
|
||||
|
@ -33660,42 +33660,63 @@ ULIB_VERSION="$PACKAGE_VERSION"
|
|||
|
||||
|
||||
STD_GNU11=
|
||||
if test "x$ax_cv_cxx_compile_cxx11" = xno -a "x$ax_cv_cxx_compile_cxx11__std_gnupp11" = xyes; then
|
||||
CXXFLAGS="-std=gnu++11 $CXXFLAGS"
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking foTMPDIRr workaround to enable on Sabayon" >&5
|
||||
$as_echo_n "checking foTMPDIRr workaround to enable on Sabayon... " >&6; }
|
||||
to_match='gcc (Gentoo Hardened 4.9.3*'
|
||||
case `gcc --version | head -n 1` in
|
||||
$to_match)
|
||||
STD_GNU11="-std=gnu++11"
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5
|
||||
$as_echo "enabled" >&6; }
|
||||
;;
|
||||
*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; } ;;
|
||||
esac
|
||||
COMPILE_CXX11=
|
||||
COMPILE_CXX14=
|
||||
COMPILE_CXX17=
|
||||
if test "x$ax_cv_cxx_compile_cxx17" = x -o "x$ax_cv_cxx_compile_cxx17" = xno; then
|
||||
COMPILE_CXX17="no"
|
||||
fi
|
||||
|
||||
if test "x$ax_cv_cxx_compile_cxx14" = xno -a "x$ax_cv_cxx_compile_cxx14__std_gnupp14" = xyes; then
|
||||
CXXFLAGS="-std=gnu++14 $CXXFLAGS"
|
||||
fi
|
||||
if test "x$ax_cv_cxx_compile_cxx17" = xno -a "x$ax_cv_cxx_compile_cxx17__std_gnupp17" = xyes; then
|
||||
if test "x$COMPILE_CXX17" = xno -a "x$ax_cv_cxx_compile_cxx17__std_gnupp17" = xyes; then
|
||||
CXXFLAGS="-std=gnu++17 $CXXFLAGS"
|
||||
else
|
||||
if test "x$ax_cv_cxx_compile_cxx14" = x -o "x$ax_cv_cxx_compile_cxx14" = xno; then
|
||||
COMPILE_CXX14="no"
|
||||
fi
|
||||
if test "x$COMPILE_CXX14" = xno -a "x$ax_cv_cxx_compile_cxx14__std_gnupp14" = xyes; then
|
||||
CXXFLAGS="-std=gnu++14 $CXXFLAGS"
|
||||
else
|
||||
if test "x$ax_cv_cxx_compile_cxx11" = x -o "x$ax_cv_cxx_compile_cxx11" = xno; then
|
||||
COMPILE_CXX11="no"
|
||||
fi
|
||||
if test "x$COMPILE_CXX11" = xno -a "x$ax_cv_cxx_compile_cxx11__std_gnupp11" = xyes; then
|
||||
CXXFLAGS="-std=gnu++11 $CXXFLAGS"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for workaround to enable on Sabayon" >&5
|
||||
$as_echo_n "checking for workaround to enable on Sabayon... " >&6; }
|
||||
to_match='gcc (Gentoo Hardened 4.9.3*'
|
||||
case `gcc --version | head -n 1` in
|
||||
$to_match)
|
||||
STD_GNU11="-std=gnu++11"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5
|
||||
$as_echo "enabled" >&6; }
|
||||
;;
|
||||
*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; } ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if test "x$HAVE_CXX17" = x1; then
|
||||
echo "int main() { return 0; }" > /tmp/adhoc.cpp
|
||||
"$CXX_save" -std=c++2a /tmp/adhoc.cpp -o /tmp/adhoc.exe > /dev/null 2>&1
|
||||
if [ "$?" -eq 0 ]; then
|
||||
HAVE_CXX20=1
|
||||
CXXFLAGS="-std=c++2a $CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS -std=c++2a"
|
||||
|
||||
$as_echo "#define HAVE_CXX20 1" >>confdefs.h
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The compiler has C++20 support" >&5
|
||||
$as_echo "$as_me: The compiler has C++20 support" >&6;}
|
||||
"$CXX_save" -std=c++2a -fconcepts /tmp/adhoc.cpp -o /tmp/adhoc.exe > /dev/null 2>&1
|
||||
if [ "$?" -eq 0 ]; then
|
||||
CXXFLAGS="$CXXFLAGS -fconcepts"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The compiler has C++ concepts support" >&5
|
||||
$as_echo "$as_me: The compiler has C++ concepts support" >&6;}
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++ concepts support was found" >&5
|
||||
$as_echo "$as_me: No compiler with C++ concepts support was found" >&6;}
|
||||
fi
|
||||
else
|
||||
HAVE_CXX20=0
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++20 support was found" >&5
|
||||
|
@ -33768,6 +33789,13 @@ fi
|
|||
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
|
||||
$as_echo "" >&6; }
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${T_MD}Output Substitution:${T_ME}" >&5
|
||||
$as_echo "${T_MD}Output Substitution:${T_ME}" >&6; }
|
||||
|
||||
|
||||
# Create compiler version string
|
||||
if test x"$GCC" = x"yes" ; then
|
||||
cc_string=`${CC} --version | sed q`
|
||||
|
|
63
configure.ac
63
configure.ac
|
@ -2890,7 +2890,7 @@ AC_CXX_OLD_IOSTREAM
|
|||
|
||||
AC_DEFINE_UNQUOTED(ULIB_VERSION, "$PACKAGE_VERSION", [ULib version])
|
||||
|
||||
TWOCAN_CONF_MSG(Output Substitution)
|
||||
TWOCAN_CONF_MSG(Final check)
|
||||
|
||||
ULIB_VERSION="$PACKAGE_VERSION"
|
||||
|
||||
|
@ -2903,36 +2903,55 @@ AC_SUBST(USP_FLAGS)
|
|||
AC_SUBST(USP_LDFLAGS)
|
||||
|
||||
STD_GNU11=
|
||||
if test "x$ax_cv_cxx_compile_cxx11" = xno -a "x$ax_cv_cxx_compile_cxx11__std_gnupp11" = xyes; then
|
||||
CXXFLAGS="-std=gnu++11 $CXXFLAGS"
|
||||
|
||||
AC_MSG_CHECKING(foTMPDIRr workaround to enable on Sabayon)
|
||||
to_match='gcc (Gentoo Hardened 4.9.3*'
|
||||
case `gcc --version | head -n 1` in
|
||||
$to_match)
|
||||
STD_GNU11="-std=gnu++11"
|
||||
|
||||
AC_MSG_RESULT(enabled)
|
||||
;;
|
||||
*) AC_MSG_RESULT(no) ;;
|
||||
esac
|
||||
COMPILE_CXX11=
|
||||
COMPILE_CXX14=
|
||||
COMPILE_CXX17=
|
||||
if test "x$ax_cv_cxx_compile_cxx17" = x -o "x$ax_cv_cxx_compile_cxx17" = xno; then
|
||||
COMPILE_CXX17="no"
|
||||
fi
|
||||
if test "x$COMPILE_CXX17" = xno -a "x$ax_cv_cxx_compile_cxx17__std_gnupp17" = xyes; then
|
||||
CXXFLAGS="-std=gnu++17 $CXXFLAGS"
|
||||
else
|
||||
if test "x$ax_cv_cxx_compile_cxx14" = x -o "x$ax_cv_cxx_compile_cxx14" = xno; then
|
||||
COMPILE_CXX14="no"
|
||||
fi
|
||||
if test "x$COMPILE_CXX14" = xno -a "x$ax_cv_cxx_compile_cxx14__std_gnupp14" = xyes; then
|
||||
CXXFLAGS="-std=gnu++14 $CXXFLAGS"
|
||||
else
|
||||
if test "x$ax_cv_cxx_compile_cxx11" = x -o "x$ax_cv_cxx_compile_cxx11" = xno; then
|
||||
COMPILE_CXX11="no"
|
||||
fi
|
||||
if test "x$COMPILE_CXX11" = xno -a "x$ax_cv_cxx_compile_cxx11__std_gnupp11" = xyes; then
|
||||
CXXFLAGS="-std=gnu++11 $CXXFLAGS"
|
||||
AC_MSG_CHECKING(for workaround to enable on Sabayon)
|
||||
to_match='gcc (Gentoo Hardened 4.9.3*'
|
||||
case `gcc --version | head -n 1` in
|
||||
$to_match)
|
||||
STD_GNU11="-std=gnu++11"
|
||||
AC_MSG_RESULT(enabled)
|
||||
;;
|
||||
*) AC_MSG_RESULT(no) ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(STD_GNU11)
|
||||
if test "x$ax_cv_cxx_compile_cxx14" = xno -a "x$ax_cv_cxx_compile_cxx14__std_gnupp14" = xyes; then
|
||||
CXXFLAGS="-std=gnu++14 $CXXFLAGS"
|
||||
fi
|
||||
if test "x$ax_cv_cxx_compile_cxx17" = xno -a "x$ax_cv_cxx_compile_cxx17__std_gnupp17" = xyes; then
|
||||
CXXFLAGS="-std=gnu++17 $CXXFLAGS"
|
||||
fi
|
||||
|
||||
if test "x$HAVE_CXX17" = x1; then
|
||||
echo "int main() { return 0; }" > /tmp/adhoc.cpp
|
||||
"$CXX_save" -std=c++2a /tmp/adhoc.cpp -o /tmp/adhoc.exe > /dev/null 2>&1
|
||||
if [[ "$?" -eq 0 ]]; then
|
||||
HAVE_CXX20=1
|
||||
CXXFLAGS="-std=c++2a $CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS -std=c++2a"
|
||||
AC_DEFINE(HAVE_CXX20,1, [define if the compiler supports basic C++20 syntax])
|
||||
AC_MSG_NOTICE([The compiler has C++20 support])
|
||||
"$CXX_save" -std=c++2a -fconcepts /tmp/adhoc.cpp -o /tmp/adhoc.exe > /dev/null 2>&1
|
||||
if [[ "$?" -eq 0 ]]; then
|
||||
CXXFLAGS="$CXXFLAGS -fconcepts"
|
||||
AC_MSG_NOTICE([The compiler has C++ concepts support])
|
||||
else
|
||||
AC_MSG_NOTICE([No compiler with C++ concepts support was found])
|
||||
fi
|
||||
else
|
||||
HAVE_CXX20=0
|
||||
AC_MSG_NOTICE([No compiler with C++20 support was found])
|
||||
|
@ -2970,6 +2989,8 @@ for the exact reason.]])],
|
|||
[AC_MSG_RESULT([cross-compiling])])
|
||||
fi
|
||||
|
||||
TWOCAN_CONF_MSG(Output Substitution)
|
||||
|
||||
# Create compiler version string
|
||||
if test x"$GCC" = x"yes" ; then
|
||||
cc_string=`${CC} --version | sed q`
|
||||
|
|
|
@ -240,4 +240,19 @@ private:
|
|||
U_DISALLOW_COPY_AND_ASSIGN(UMongoDBClient)
|
||||
};
|
||||
|
||||
// by Victor Stewart
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(HAVE_CXX20) && defined(USE_MONGODB) && defined(U_LINUX)
|
||||
template <class StringType>
|
||||
static void bson_append_utf8(bson_t* doc, UCompileTimeStringType&& fieldname, StringType&& value)
|
||||
{
|
||||
if constexpr (std::is_same_v<StringType, UString>) (void) bson_append_utf8(doc, U_CTV_TO_PARAM(fieldname), U_STRING_TO_PARAM(value));
|
||||
else if constexpr (UCompileTimeStringFormatter::is_ctv_v<StringType>) (void) bson_append_utf8(doc, U_CTV_TO_PARAM(fieldname), U_CTV_TO_PARAM(value));
|
||||
}
|
||||
|
||||
static void bson_append_bool( bson_t* doc, UCompileTimeStringType&& fieldname, bool value) { (void) ::bson_append_bool( doc, U_CTV_TO_PARAM(fieldname), value); }
|
||||
static void bson_append_int32( bson_t* doc, UCompileTimeStringType&& fieldname, int32_t value) { (void) ::bson_append_int32( doc, U_CTV_TO_PARAM(fieldname), value); }
|
||||
static void bson_append_date_time(bson_t* doc, UCompileTimeStringType&& fieldname, int64_t value) { (void) ::bson_append_date_time(doc, U_CTV_TO_PARAM(fieldname), value); }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -993,8 +993,7 @@ private:
|
|||
|
||||
// by Victor Stewart
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(U_LINUX)
|
||||
# if defined(HAVE_CXX17)
|
||||
#if defined(U_STDCPP_ENABLE) && defined(U_LINUX) && defined(HAVE_CXX20)
|
||||
|
||||
typedef UREDISClient<UTCPSocket> UREDISClusterClient;
|
||||
|
||||
|
@ -1015,7 +1014,7 @@ struct RedisClusterNode {
|
|||
client->connect(ipAddress.c_str(), port);
|
||||
}
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
||||
#if defined(DEBUG)
|
||||
const char* dump(bool _reset) const { return ""; }
|
||||
#endif
|
||||
};
|
||||
|
@ -1117,7 +1116,7 @@ public:
|
|||
if (clusterNodes) U_DELETE(clusterNodes);
|
||||
}
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
||||
#if defined(DEBUG)
|
||||
const char* dump(bool _reset) const { return subscriptionClient->UREDISClient_Base::dump(_reset); }
|
||||
#endif
|
||||
};
|
||||
|
@ -1162,381 +1161,110 @@ public:
|
|||
spans.emplace_back(UREDISClusterMaster::hashslotForKey(hashableKey), beginning, pipeline.size(), spans.size());
|
||||
}
|
||||
|
||||
void append(size_t increaseCapacityBy, const UString& hashableKey, const char* format, uint32_t fmt_size, ...)
|
||||
template <auto format, typename... Ts>
|
||||
void append(const UString& hashableKey, Ts... ts)
|
||||
{
|
||||
size_t beginning = pipeline.size();
|
||||
|
||||
if (increaseCapacityBy > 0) pipeline.reserve(pipeline.size() + increaseCapacityBy);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt_size);
|
||||
pipeline.vsnprintf_add(format, fmt_size, args);
|
||||
va_end(args);
|
||||
UCompileTimeStringFormatter::snprintf_add<format>(pipeline, std::forward<Ts>(ts)...);
|
||||
|
||||
spans.emplace_back(UREDISClusterMaster::hashslotForKey(hashableKey), beginning, pipeline.size(), spans.size());
|
||||
}
|
||||
|
||||
AnonymousClusterPipeline() : pipeline(300U) {}
|
||||
};
|
||||
# endif
|
||||
|
||||
# if defined(HAVE_CXX20)
|
||||
|
||||
class UCompileTimeRESPEncoder {
|
||||
class UCompileTimeRESPEncoder : public UCompileTimeStringFormatter {
|
||||
private:
|
||||
|
||||
template <class X>
|
||||
static constexpr size_t countSegments(X rawFormat)
|
||||
|
||||
template<bool isPartial, size_t workingIndex = 0, typename StringClass, typename T, typename... Ts>
|
||||
static constexpr auto generateSegments(StringClass format, size_t& outputSegmentCount, T&& t, Ts&&... ts)
|
||||
{
|
||||
size_t index = 0;
|
||||
// "HSET {{}}.cache firstname {} lastname {} picture {} \r\n"
|
||||
constexpr size_t segmentStart = findChar<StringClass::instance, notChar>(workingIndex, ' ');
|
||||
|
||||
while (rawFormat[index] == ' ') ++index;
|
||||
|
||||
size_t segmentCount = 1;
|
||||
bool inSegment = true;
|
||||
|
||||
while (index < rawFormat.length)
|
||||
{
|
||||
char ch = rawFormat[index];
|
||||
|
||||
if (ch == '\r') return segmentCount;
|
||||
|
||||
if (inSegment && ch == ' ')
|
||||
{
|
||||
inSegment = false;
|
||||
}
|
||||
else if (!inSegment && ch != ' ')
|
||||
{
|
||||
inSegment = true;
|
||||
++segmentCount;
|
||||
}
|
||||
|
||||
++index;
|
||||
if constexpr (StringClass::instance[segmentStart] == '\r' || segmentStart == StringClass::instance.length)
|
||||
{
|
||||
if constexpr (isPartial) return std::make_tuple(std::forward<T>(t), std::forward<Ts>(ts)...);
|
||||
else return std::make_tuple("*"_ctv, outputSegmentCount, "\r\n"_ctv, std::forward<T>(t), std::forward<Ts>(ts)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
constexpr size_t segmentEnd = findChar<StringClass::instance>(segmentStart, ' ', '\r');
|
||||
constexpr size_t formatStart = findChar<StringClass::instance, skipDoubles, segmentEnd>(segmentStart, '{');
|
||||
|
||||
if constexpr (formatStart < segmentEnd)
|
||||
{
|
||||
constexpr size_t formatTermination = formatStart + 1;
|
||||
|
||||
return generateSegments<isPartial, segmentEnd + 1>(format, ++outputSegmentCount, std::forward<Ts>(ts)..., "$"_ctv, LengthSurplusPackage<T>{(segmentEnd + formatStart) - (segmentStart + formatTermination) - 1, t}, "\r\n"_ctv, StringClass::instance.template substr<segmentStart, formatStart>(), t, StringClass::instance.template substr<std::min(formatTermination + 1, segmentEnd), segmentEnd>() + "\r\n"_ctv);
|
||||
}
|
||||
else
|
||||
{
|
||||
constexpr auto segmentString = "$"_ctv + integerToString<segmentEnd - segmentStart>() + "\r\n"_ctv + StringClass::instance.template substr<segmentStart, segmentEnd>() + "\r\n"_ctv;
|
||||
|
||||
return generateSegments<isPartial, segmentEnd + 1>(format, ++outputSegmentCount, std::forward<T>(t), std::forward<Ts>(ts)..., segmentString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isPartial, auto format, typename... Ts>
|
||||
static size_t encode_impl(size_t writePosition, UString& workingString, Ts&&... ts)
|
||||
{
|
||||
size_t segmentCount = 0;
|
||||
|
||||
std::apply([&] (auto... params) {
|
||||
|
||||
UCompileTimeStringFormatter::snprintf_impl(writePosition, workingString, params...);
|
||||
|
||||
}, generateSegments<isPartial>(format, segmentCount, std::forward<Ts>(ts)...));
|
||||
|
||||
return segmentCount;
|
||||
}
|
||||
|
||||
enum class ParameterType {
|
||||
|
||||
none,
|
||||
_ustring, // %v
|
||||
cstring, // %s
|
||||
fstring, // %fbs flatbuffer string
|
||||
fbinary, // %fbb flatbuffer binary
|
||||
uinteger, // %iu unsigned integer
|
||||
sinteger, // %is signed integer
|
||||
boolean // %b
|
||||
};
|
||||
|
||||
struct Range {
|
||||
|
||||
size_t start;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
struct SegmentOutline {
|
||||
|
||||
const size_t length;
|
||||
const ParameterType parameter;
|
||||
const Range ranges[2]; // parameter always fits in the middle. regardless of ordering this collapses to correctness
|
||||
};
|
||||
|
||||
template <ParameterType parameter>
|
||||
static constexpr auto formatSpecifierTranslation(void) // RESPFormatter -> UString::snprintf
|
||||
{
|
||||
// no-op
|
||||
if constexpr (parameter == ParameterType::none) return ""_ctv;
|
||||
// no change
|
||||
else if constexpr (parameter == ParameterType::_ustring) return "%v"_ctv;
|
||||
else if constexpr (parameter == ParameterType::cstring) return "%s"_ctv;
|
||||
// change
|
||||
else if constexpr (parameter == ParameterType::boolean) return "%hhu"_ctv;
|
||||
else if constexpr (parameter == ParameterType::fstring) return "%.*s"_ctv;
|
||||
else if constexpr (parameter == ParameterType::fbinary) return "%.*s"_ctv;
|
||||
else if constexpr (parameter == ParameterType::uinteger) return "%llu"_ctv; // size up all integers to max
|
||||
else if constexpr (parameter == ParameterType::sinteger) return "%lld"_ctv;
|
||||
}
|
||||
|
||||
template <ParameterType parameter>
|
||||
static constexpr size_t parameterToLengthCorrection(void)
|
||||
{
|
||||
// no-op
|
||||
if constexpr (parameter == ParameterType::_ustring) return 2; // %v
|
||||
else if constexpr (parameter == ParameterType::cstring) return 2; // %s
|
||||
else if constexpr (parameter == ParameterType::boolean) return 2; // %b
|
||||
// change
|
||||
else if constexpr (parameter == ParameterType::fstring) return 4; // %fbs
|
||||
else if constexpr (parameter == ParameterType::fbinary) return 4; // %fbb
|
||||
else if constexpr (parameter == ParameterType::uinteger) return 3; // %iu
|
||||
else if constexpr (parameter == ParameterType::sinteger) return 3; // %is
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// runtime
|
||||
// faster implementations exist but they're long and ugly
|
||||
template <typename IntegralType, typename = std::enable_if_t<std::is_integral_v<IntegralType>>>
|
||||
static size_t countDigits(IntegralType number)
|
||||
{
|
||||
size_t digits = 0;
|
||||
|
||||
if (number < 0)
|
||||
{
|
||||
++digits;
|
||||
number *= -1;
|
||||
}
|
||||
|
||||
while (number != 0) { number /= 10; digits++; }
|
||||
|
||||
return digits;
|
||||
}
|
||||
|
||||
template <auto rawFormat, size_t argumentCount, size_t segmentCount = countSegments(rawFormat)>
|
||||
struct RESPFormatter {
|
||||
private:
|
||||
|
||||
template<ssize_t number, bool terminate = false, typename ...DigitStrings>
|
||||
static constexpr auto integerToString(DigitStrings... digitStrings)
|
||||
{
|
||||
if constexpr (terminate) return ((""_ctv + digitStrings) + ...);
|
||||
else
|
||||
{
|
||||
if constexpr (number < 0) return integerToString<number * -1>("-"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else
|
||||
{
|
||||
constexpr size_t digitValue = number % 10;
|
||||
constexpr bool newTerminate = number < 10;
|
||||
constexpr size_t newNumber = number / 10;
|
||||
|
||||
if constexpr (digitValue == 0) return integerToString<newNumber, newTerminate>("0"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 1) return integerToString<newNumber, newTerminate>("1"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 2) return integerToString<newNumber, newTerminate>("2"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 3) return integerToString<newNumber, newTerminate>("3"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 4) return integerToString<newNumber, newTerminate>("4"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 5) return integerToString<newNumber, newTerminate>("5"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 6) return integerToString<newNumber, newTerminate>("6"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 7) return integerToString<newNumber, newTerminate>("7"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 8) return integerToString<newNumber, newTerminate>("8"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 9) return integerToString<newNumber, newTerminate>("9"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t ... n>
|
||||
static constexpr auto generateSegmentMarkersHelper(std::integer_sequence<size_t, n...>)
|
||||
{
|
||||
size_t workingIndex = 0;
|
||||
return std::array<SegmentOutline, segmentCount>{getNextSegmentOutline(workingIndex, n) ...};
|
||||
}
|
||||
|
||||
static constexpr auto generateSegmentMarkers()
|
||||
{
|
||||
return generateSegmentMarkersHelper(std::make_integer_sequence<size_t, segmentCount>{});
|
||||
}
|
||||
|
||||
static constexpr SegmentOutline getNextSegmentOutline(size_t& workingIndex, size_t segmentIndex) // just to use parameter pack expansion
|
||||
{
|
||||
while (rawFormat[workingIndex] == ' ') ++workingIndex;
|
||||
|
||||
// if we encounter a format... always switch to range 2
|
||||
|
||||
Range ranges[2] = {};
|
||||
const size_t segmentStart = workingIndex;
|
||||
size_t subSegmentStart = workingIndex;
|
||||
ParameterType parameter = ParameterType::none;
|
||||
|
||||
while (workingIndex < rawFormat.length)
|
||||
{
|
||||
char ch = rawFormat[workingIndex];
|
||||
|
||||
if (ch == ' ' || ch == '\r') break;
|
||||
|
||||
if (ch == '%')
|
||||
{
|
||||
// aka there were charaters before the format
|
||||
if (subSegmentStart != workingIndex)
|
||||
{
|
||||
ranges[0].start = subSegmentStart;
|
||||
ranges[0].length = (workingIndex - subSegmentStart);
|
||||
}
|
||||
|
||||
ch = rawFormat[++workingIndex];
|
||||
|
||||
switch (ch)
|
||||
{
|
||||
case 'v':
|
||||
{
|
||||
// %v
|
||||
parameter = ParameterType::_ustring;
|
||||
break;
|
||||
}
|
||||
case 'b':
|
||||
{
|
||||
// %b
|
||||
parameter = ParameterType::boolean;
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
//%s
|
||||
parameter = ParameterType::cstring;
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
ch = rawFormat[++(++workingIndex)];
|
||||
|
||||
// %fbs
|
||||
if (ch == 's') parameter = ParameterType::fstring;
|
||||
// %fbb
|
||||
else parameter = ParameterType::fbinary;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
{
|
||||
ch = rawFormat[++workingIndex];
|
||||
|
||||
// %is
|
||||
if (ch == 's') parameter = ParameterType::sinteger;
|
||||
// $iu
|
||||
else parameter = ParameterType::uinteger;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
subSegmentStart = workingIndex + 1;
|
||||
}
|
||||
|
||||
++workingIndex;
|
||||
}
|
||||
|
||||
if (parameter == ParameterType::none)
|
||||
{
|
||||
ranges[0].start = subSegmentStart;
|
||||
ranges[0].length = (workingIndex - subSegmentStart);
|
||||
}
|
||||
else if (subSegmentStart != workingIndex)
|
||||
{
|
||||
ranges[1].start = subSegmentStart;
|
||||
ranges[1].length = (workingIndex - subSegmentStart);
|
||||
}
|
||||
|
||||
return {workingIndex - segmentStart, parameter, {ranges[0], ranges[1]}};
|
||||
}
|
||||
|
||||
template <auto segmentOutlines, size_t segmentIndex = 0, class StringClass, typename ...StringClasses>
|
||||
static constexpr auto parse(StringClass raw, std::array<size_t, argumentCount>& argumentToSegment, size_t argumentIndex = 0, StringClasses... strings)
|
||||
{
|
||||
if constexpr (segmentIndex >= segmentCount) return (strings + ... );
|
||||
else
|
||||
{
|
||||
constexpr SegmentOutline segmentOutline = segmentOutlines[segmentIndex];
|
||||
constexpr Range range0 = segmentOutline.ranges[0];
|
||||
constexpr Range range1 = segmentOutline.ranges[1];
|
||||
|
||||
if constexpr (segmentOutline.parameter == ParameterType::none)
|
||||
{
|
||||
constexpr auto segmentString = "$"_ctv + integerToString<segmentOutline.length>() + "\r\n"_ctv + StringClass::instance.template substr<range0.start, range0.start + range0.length>() + "\r\n"_ctv;
|
||||
|
||||
return parse<segmentOutlines, segmentIndex + 1>(raw, argumentToSegment, argumentIndex, std::forward<StringClasses>(strings)..., segmentString);
|
||||
}
|
||||
else
|
||||
{
|
||||
argumentToSegment[argumentIndex] = segmentIndex;
|
||||
|
||||
constexpr auto segmentString = "$%d\r\n"_ctv + StringClass::instance.template substr<range0.start, range0.start + range0.length>() + formatSpecifierTranslation<segmentOutline.parameter>() + StringClass::instance.template substr<range1.start, range1.start + range1.length>() + "\r\n"_ctv;
|
||||
|
||||
return parse<segmentOutlines, segmentIndex + 1>(raw, argumentToSegment, ++argumentIndex, std::forward<StringClasses>(strings)..., segmentString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static constexpr auto parseToRESP()
|
||||
{
|
||||
constexpr std::array<SegmentOutline, segmentCount> segmentOutlines = generateSegmentMarkers();
|
||||
|
||||
std::array<size_t, argumentCount> argumentToSegment = {};
|
||||
|
||||
return std::make_tuple("*"_ctv + integerToString<segmentCount>() + "\r\n"_ctv + parse<segmentOutlines>(rawFormat, argumentToSegment), segmentOutlines, argumentToSegment);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct decay_equiv : std::is_same<typename std::decay<T>::type, U>::type {};
|
||||
|
||||
template< class T, class U >
|
||||
static inline constexpr bool decay_equiv_v = decay_equiv<T, U>::value;
|
||||
|
||||
template <auto respformat, size_t argumentCount, size_t workingCount, typename T, typename ... Ts>
|
||||
static void fill(UString& workingString, T t, Ts... ts)
|
||||
{
|
||||
constexpr auto format = std::get<0>(respformat);
|
||||
constexpr auto segmentOutlines = std::get<1>(respformat);
|
||||
constexpr auto argumentToSegment = std::get<2>(respformat);
|
||||
|
||||
if constexpr (workingCount < argumentCount)
|
||||
{
|
||||
constexpr SegmentOutline outline = segmentOutlines[argumentToSegment[workingCount]];
|
||||
constexpr size_t lengthSurplus = outline.length - parameterToLengthCorrection<outline.parameter>();
|
||||
|
||||
if constexpr (decay_equiv_v<T, UString>)
|
||||
{
|
||||
if constexpr (std::is_pointer_v<T>) // will only accept single pointer depth
|
||||
{
|
||||
fill<respformat, argumentCount, workingCount + 1>(workingString, ts..., t->size() + lengthSurplus, t->rep);
|
||||
}
|
||||
else
|
||||
{
|
||||
fill<respformat, argumentCount, workingCount + 1>(workingString, ts..., t.size() + lengthSurplus, t.rep);
|
||||
}
|
||||
}
|
||||
else if constexpr (outline.parameter == ParameterType::boolean)
|
||||
{
|
||||
fill<respformat, argumentCount, workingCount + 1>(workingString, ts..., 1 + lengthSurplus, t);
|
||||
}
|
||||
else if constexpr (decay_equiv_v<T, char>)
|
||||
{
|
||||
fill<respformat, argumentCount, workingCount + 1>(workingString, ts..., strlen(t) + lengthSurplus, t);
|
||||
}
|
||||
else if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
fill<respformat, argumentCount, workingCount + 1>(workingString, ts..., countDigits(t) + lengthSurplus, t);
|
||||
}
|
||||
else if constexpr (outline.parameter == ParameterType::fstring)
|
||||
{
|
||||
fill<respformat, argumentCount, workingCount + 1>(workingString, ts..., t->size() + lengthSurplus, t->size() + lengthSurplus, t->c_str());
|
||||
}
|
||||
else if constexpr (outline.parameter == ParameterType::fbinary)
|
||||
{
|
||||
fill<respformat, argumentCount, workingCount + 1>(workingString, ts..., t->size() + lengthSurplus, t->size() + lengthSurplus, t->Data());
|
||||
}
|
||||
}
|
||||
else workingString.snprintf(format.string, format.length, t, ts...);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// only one parameter per segment allowed for now... aka {%v}.cache but not {%v%v}.cache
|
||||
// CLIENT REPLY ON
|
||||
static constexpr auto CLIENTREPLYON = "*3\r\n$6\r\nCLIENT\r\n$5\r\nREPLY\r\n$2\r\nON\r\n"_ctv;
|
||||
// CLIENT REPLY OFF
|
||||
static constexpr auto CLIENTREPLYOFF = "*3\r\n$6\r\nCLIENT\r\n$5\r\nREPLY\r\n$3\r\nOFF\r\n"_ctv;
|
||||
|
||||
// call this like this...
|
||||
//
|
||||
// UString workingString, first, last;
|
||||
// UCompileTimeRESPEncoder::encode<"HSET {%v}.cache firstname %v lastname %v \r\n"_ctv>(workingString, first, last);
|
||||
|
||||
template<auto rawFormat, typename ... Args>
|
||||
static void encode(UString& workingString, Args... args)
|
||||
// fulls
|
||||
template<auto format, typename ... Ts>
|
||||
static void encode(UString& workingString, Ts&&... ts)
|
||||
{
|
||||
constexpr size_t argumentCount = sizeof...(Args);
|
||||
constexpr auto respformat = RESPFormatter<rawFormat, argumentCount>::parseToRESP();
|
||||
(void)encode_impl<false, format>(0, workingString, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
fill<respformat, argumentCount, 0>(workingString, args...);
|
||||
template<auto format, typename ... Ts>
|
||||
static void encode_add(UString& workingString, Ts&&... ts)
|
||||
{
|
||||
(void)encode_impl<false, format>(workingString.size(), workingString, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template<auto format, typename ... Ts>
|
||||
static void encode_pos(size_t writePosition, UString& workingString, Ts&&... ts)
|
||||
{
|
||||
(void)encode_impl<false, format>(writePosition, workingString, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
// partials
|
||||
template<auto format, typename... Ts>
|
||||
static void encode_partial_pos(size_t& segmentCountAccumulator, size_t writePosition, UString& workingString, Ts&&... ts)
|
||||
{
|
||||
segmentCountAccumulator += encode_impl<true, format>(writePosition, workingString, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template<auto format, typename... Ts>
|
||||
static void encode_partial_add(size_t& segmentCountAccumulator, UString& workingString, Ts&&... ts)
|
||||
{
|
||||
segmentCountAccumulator += encode_impl<true, format>(workingString.size(), workingString, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
static void encode_partial_count(UString& workingString, size_t segmentCount)
|
||||
{
|
||||
UCompileTimeStringFormatter::snprintf_impl(0, workingString, "*"_ctv, segmentCount, "\r\n"_ctv);
|
||||
}
|
||||
};
|
||||
# endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -2853,7 +2853,7 @@ namespace std {
|
|||
};
|
||||
}
|
||||
# endif
|
||||
# if defined(HAVE_CXX17) && defined(U_LINUX)
|
||||
# if defined(HAVE_CXX20) && defined(U_LINUX)
|
||||
# include <utility> // std::index_sequence
|
||||
template <char... Chars>
|
||||
class UCompileTimeStringView {
|
||||
|
@ -2897,6 +2897,245 @@ constexpr auto operator""_ctv() // compile time view
|
|||
{
|
||||
return UCompileTimeStringView<static_cast<char>(Chars)...>{};
|
||||
}
|
||||
|
||||
#define U_CTV_TO_PARAM(ct_string) ct_string.string, ct_string.length
|
||||
|
||||
template<typename Lambda, typename T>
|
||||
static void snprintf_specialization(Lambda&& lambda, T t)
|
||||
{
|
||||
lambda(U_NULLPTR, 0);
|
||||
}
|
||||
|
||||
#include <tuple>
|
||||
|
||||
class UCompileTimeStringFormatter {
|
||||
protected:
|
||||
|
||||
static constexpr uint8_t notChar = 0x1;
|
||||
static constexpr uint8_t skipDoubles = 0x2;
|
||||
|
||||
template<auto format, uint8_t options = 0, size_t terminationIndex = format.length>
|
||||
static constexpr size_t findChar(size_t workingIndex, char ch, char chOther)
|
||||
{
|
||||
if constexpr (options & notChar)
|
||||
{
|
||||
// search until we find not some character
|
||||
while (workingIndex < terminationIndex && ((format[workingIndex] == ch) || (format[workingIndex] == chOther))) workingIndex++;
|
||||
}
|
||||
else if constexpr (options & skipDoubles)
|
||||
{
|
||||
// {{}}.cache
|
||||
while (workingIndex < terminationIndex && ((format[workingIndex] != ch) || (workingIndex < terminationIndex && format[workingIndex + 1] == ch))) workingIndex++;
|
||||
}
|
||||
else if constexpr (options == 0)
|
||||
{
|
||||
// search until we find some character
|
||||
while (workingIndex < terminationIndex && ((format[workingIndex] != ch) && (format[workingIndex] != chOther))) workingIndex++;
|
||||
}
|
||||
|
||||
return workingIndex;
|
||||
}
|
||||
|
||||
template<auto format, uint8_t options = 0, size_t terminationIndex = format.length>
|
||||
static constexpr size_t findChar(size_t workingIndex, char ch)
|
||||
{
|
||||
return findChar<format, options, terminationIndex>(workingIndex, ch, ch);
|
||||
}
|
||||
|
||||
template<size_t workingIndex, size_t argumentCount, typename StringClass, typename T, typename... Ts>
|
||||
static constexpr auto generateSegments(StringClass format, T&& t, Ts&&... ts)
|
||||
{
|
||||
if constexpr (argumentCount == 0) return std::make_tuple(std::forward<T>(t), std::forward<Ts>(ts)...);
|
||||
else
|
||||
{
|
||||
constexpr size_t nextFormat = findChar<StringClass::instance, skipDoubles>(workingIndex, '{');
|
||||
constexpr size_t formatTermination = nextFormat + 1;
|
||||
|
||||
return generateSegments<formatTermination + 1, argumentCount - 1>(format, std::forward<Ts>(ts)..., StringClass::instance.template substr<workingIndex, nextFormat>(), std::forward<T>(t));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
struct decay_equiv : std::is_same<typename std::decay<T>::type, U>::type {};
|
||||
|
||||
template <class T, class U>
|
||||
static inline constexpr bool decay_equiv_v = decay_equiv<T, U>::value;
|
||||
|
||||
template <typename T, template <char... CharsB> class Template>
|
||||
struct is_ctv : std::false_type {};
|
||||
|
||||
template <char... CharsA, template <char... CharsB> class Template>
|
||||
struct is_ctv<Template<CharsA...>, Template> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
static inline constexpr bool is_ctv_v = is_ctv<T, UCompileTimeStringView>::value;
|
||||
|
||||
template <typename T>
|
||||
static void writeBytes(char*& writeTo, T t)
|
||||
{
|
||||
if constexpr (is_ctv_v<T>)
|
||||
{
|
||||
writeTo = (char*)memcpy(writeTo, t.string, t.length) + t.length;
|
||||
}
|
||||
else if constexpr (decay_equiv_v<T, UString>)
|
||||
{
|
||||
writeTo = (char*)memcpy(writeTo, t.data(), t.size()) + t.size();
|
||||
}
|
||||
else if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
writeTo = u_num2str64s(t, writeTo);
|
||||
}
|
||||
else if constexpr (decay_equiv_v<T, char>) // assumes char pointer
|
||||
{
|
||||
size_t length = strlen(t);
|
||||
writeTo = (char*)memcpy(writeTo, t, length) + length;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lambda = [&] (const void *buffer, size_t bufferSize)
|
||||
{
|
||||
writeTo = (char*)memcpy(writeTo, buffer, bufferSize) + bufferSize;
|
||||
};
|
||||
|
||||
snprintf_specialization(lambda, t);
|
||||
}
|
||||
}
|
||||
|
||||
template<ssize_t number, bool terminate = false, typename ...DigitStrings>
|
||||
static constexpr auto integerToString(DigitStrings... digitStrings)
|
||||
{
|
||||
if constexpr (terminate) return ((""_ctv + digitStrings) + ...);
|
||||
else
|
||||
{
|
||||
if constexpr (number < 0) return integerToString<number * -1>("-"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else
|
||||
{
|
||||
constexpr size_t newNumber = number / 10,
|
||||
digitValue = number % 10;
|
||||
constexpr bool newTerminate = number < 10;
|
||||
|
||||
if constexpr (digitValue == 0) return integerToString<newNumber, newTerminate>("0"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 1) return integerToString<newNumber, newTerminate>("1"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 2) return integerToString<newNumber, newTerminate>("2"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 3) return integerToString<newNumber, newTerminate>("3"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 4) return integerToString<newNumber, newTerminate>("4"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 5) return integerToString<newNumber, newTerminate>("5"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 6) return integerToString<newNumber, newTerminate>("6"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 7) return integerToString<newNumber, newTerminate>("7"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 8) return integerToString<newNumber, newTerminate>("8"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
else if constexpr (digitValue == 9) return integerToString<newNumber, newTerminate>("9"_ctv, std::forward<DigitStrings>(digitStrings) ...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static size_t getLength(T t)
|
||||
{
|
||||
if constexpr (is_ctv_v<T>) return t.length;
|
||||
else if constexpr (decay_equiv_v<T, UString>) return t.size();
|
||||
else if constexpr (std::is_integral_v<T>) return countDigits(t);
|
||||
else if constexpr (decay_equiv_v<T, char>) return strlen(t);
|
||||
else
|
||||
{
|
||||
size_t length = 0;
|
||||
|
||||
auto lambda = [&] (const void *buffer, size_t bufferSize)
|
||||
{
|
||||
length = bufferSize;
|
||||
};
|
||||
|
||||
snprintf_specialization(lambda, t);
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct LengthSurplusPackage
|
||||
{
|
||||
size_t lengthSurplus;
|
||||
T binding;
|
||||
|
||||
size_t size()
|
||||
{
|
||||
return getLength(binding) + lengthSurplus;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
static void snprintf_impl(size_t writePosition, UString& workingString, Ts... ts)
|
||||
{
|
||||
size_t lengths = (getLength(ts) + ...);
|
||||
|
||||
// grow string to accomodate new size if necessary
|
||||
workingString.reserve(workingString.size() + lengths);
|
||||
|
||||
char* target = workingString.data() + writePosition;
|
||||
|
||||
// shift over existing contents
|
||||
if (writePosition < workingString.size()) (void) memcpy(target + lengths, target, lengths);
|
||||
|
||||
(writeBytes(target, ts), ...);
|
||||
workingString.size_adjust_force(target - workingString.data());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
template <typename IntegralType, typename = std::enable_if_t<std::is_integral_v<IntegralType>>>
|
||||
static constexpr size_t countDigits(IntegralType number)
|
||||
{
|
||||
size_t digits = 0;
|
||||
|
||||
if (number < 0)
|
||||
{
|
||||
++digits;
|
||||
number *= -1;
|
||||
}
|
||||
|
||||
while (number != 0) { number /= 10; digits++; }
|
||||
|
||||
return digits;
|
||||
}
|
||||
|
||||
template <auto format, typename... Ts>
|
||||
static void snprintf_pos(size_t writePosition, UString& workingString, Ts... ts)
|
||||
{
|
||||
std::apply([&] (auto... params) {
|
||||
|
||||
// adding this extra level of indirection allowing for the building of higher level abstraction parsers on top
|
||||
|
||||
snprintf_impl(writePosition, workingString, params...);
|
||||
|
||||
}, generateSegments<0, sizeof...(Ts)>(format, std::forward<Ts>(ts)...));
|
||||
}
|
||||
|
||||
template <auto format, typename... Ts>
|
||||
static void snprintf(UString& workingString, Ts... ts)
|
||||
{
|
||||
snprintf_pos<format>(0, workingString, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template <auto format, typename... Ts>
|
||||
static void snprintf_add(UString& workingString, Ts... ts)
|
||||
{
|
||||
snprintf_pos<format>(workingString.size(), workingString, std::forward<Ts>(ts)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Lambda, typename T>
|
||||
static void snprintf_specialization(Lambda&& lambda, UCompileTimeStringFormatter::LengthSurplusPackage<T>& t)
|
||||
{
|
||||
static char working[UCompileTimeStringFormatter::countDigits(INT64_MAX)];
|
||||
|
||||
char *end = u_num2str64s(t.size(), working);
|
||||
|
||||
lambda(working, end - working);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept bool UCompileTimeStringType = requires(T string) {
|
||||
UCompileTimeStringFormatter::is_ctv_v<T>;
|
||||
};
|
||||
# endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1 +1 @@
|
|||
0679
|
||||
069D
|
||||
|
|
|
@ -146,13 +146,19 @@ plugin/product1.lo: plugin/product1.cpp /usr/include/stdc-predef.h \
|
|||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/cxxabi_forced.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_function.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/backward/binders.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_algo.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/algorithmfwd.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/initializer_list \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/basic_string.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_heap.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_tempbuf.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_construct.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/alloc_traits.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/alloc_traits.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/string_view \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/uniform_int_dist.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/limits \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/basic_string.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/string_view \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/functional_hash.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/string_view.tcc \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/string_conversions.h \
|
||||
|
@ -211,11 +217,9 @@ plugin/product1.lo: plugin/product1.cpp /usr/include/stdc-predef.h \
|
|||
/usr/include/unicode/utf16.h /usr/include/unicode/utf_old.h \
|
||||
/usr/include/unicode/uenum.h /usr/include/unicode/localpointer.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/memory \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_construct.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_uninitialized.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/utility \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_relops.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_tempbuf.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_raw_storage_iter.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/concurrence.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/uses_allocator.h \
|
||||
|
@ -232,6 +236,7 @@ plugin/product1.lo: plugin/product1.cpp /usr/include/stdc-predef.h \
|
|||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/atomic_base.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/atomic_lockfree_defines.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/backward/auto_ptr.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bit \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/pstl/glue_memory_defs.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/pstl/execution_defs.h \
|
||||
/usr/include/libxml2/libxml/xmlIO.h \
|
||||
|
@ -684,20 +689,32 @@ plugin/product.h:
|
|||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/backward/binders.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h:
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_algo.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/algorithmfwd.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/initializer_list:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/basic_string.h:
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_heap.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_tempbuf.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_construct.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/alloc_traits.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/alloc_traits.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/string_view:
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/uniform_int_dist.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/limits:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/basic_string.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/string_view:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/functional_hash.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/string_view.tcc:
|
||||
|
@ -852,16 +869,12 @@ plugin/product.h:
|
|||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/memory:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_construct.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_uninitialized.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/utility:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_relops.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_tempbuf.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_raw_storage_iter.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/concurrence.h:
|
||||
|
@ -894,6 +907,8 @@ plugin/product.h:
|
|||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/backward/auto_ptr.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bit:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/pstl/glue_memory_defs.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/pstl/execution_defs.h:
|
||||
|
|
|
@ -146,13 +146,19 @@ plugin/product2.lo: plugin/product2.cpp /usr/include/stdc-predef.h \
|
|||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/cxxabi_forced.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_function.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/backward/binders.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_algo.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/algorithmfwd.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/initializer_list \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/basic_string.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_heap.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_tempbuf.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_construct.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/alloc_traits.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/alloc_traits.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/string_view \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/uniform_int_dist.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/limits \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/basic_string.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/string_view \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/functional_hash.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/string_view.tcc \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/string_conversions.h \
|
||||
|
@ -211,11 +217,9 @@ plugin/product2.lo: plugin/product2.cpp /usr/include/stdc-predef.h \
|
|||
/usr/include/unicode/utf16.h /usr/include/unicode/utf_old.h \
|
||||
/usr/include/unicode/uenum.h /usr/include/unicode/localpointer.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/memory \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_construct.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_uninitialized.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/utility \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_relops.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_tempbuf.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_raw_storage_iter.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/concurrence.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/uses_allocator.h \
|
||||
|
@ -232,6 +236,7 @@ plugin/product2.lo: plugin/product2.cpp /usr/include/stdc-predef.h \
|
|||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/atomic_base.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/atomic_lockfree_defines.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/backward/auto_ptr.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bit \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/pstl/glue_memory_defs.h \
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/pstl/execution_defs.h \
|
||||
/usr/include/libxml2/libxml/xmlIO.h \
|
||||
|
@ -684,20 +689,32 @@ plugin/product.h:
|
|||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/backward/binders.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h:
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_algo.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/algorithmfwd.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/initializer_list:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/basic_string.h:
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_heap.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_tempbuf.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_construct.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/alloc_traits.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/alloc_traits.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/string_view:
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/uniform_int_dist.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/limits:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/basic_string.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/string_view:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/functional_hash.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/string_view.tcc:
|
||||
|
@ -852,16 +869,12 @@ plugin/product.h:
|
|||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/memory:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_construct.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_uninitialized.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/utility:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_relops.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_tempbuf.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/stl_raw_storage_iter.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/ext/concurrence.h:
|
||||
|
@ -894,6 +907,8 @@ plugin/product.h:
|
|||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/backward/auto_ptr.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bit:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/pstl/glue_memory_defs.h:
|
||||
|
||||
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/pstl/execution_defs.h:
|
||||
|
|
|
@ -1490,6 +1490,10 @@ void U_EXPORT check_mmap(uint32_t map_size)
|
|||
|
||||
//#define U_USE_STRTOD // Parse number in full precision (but slower)
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(HAVE_CXX20)
|
||||
# include <ulib/net/client/redis.h>
|
||||
#endif
|
||||
|
||||
int
|
||||
U_EXPORT main(int argc, char* argv[])
|
||||
{
|
||||
|
@ -1497,6 +1501,27 @@ U_EXPORT main(int argc, char* argv[])
|
|||
|
||||
U_TRACE(5, "main(%d)", argc)
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(HAVE_CXX20) && defined(U_LINUX)
|
||||
UString token = U_STRING_FROM_CONSTANT("a"),
|
||||
firstname = U_STRING_FROM_CONSTANT("Victor"),
|
||||
lastname = U_STRING_FROM_CONSTANT("Stewart"),
|
||||
payload = U_STRING_FROM_CONSTANT("\xf4\x26\x3a\x79\xa1\x2f\xfd\xf5\xb0\x48");
|
||||
|
||||
UString redisString(300U);
|
||||
|
||||
UCompileTimeStringFormatter::snprintf<"HSET {{}}.cache firstname {} lastname {} picture {} \r\n"_ctv>(redisString, token, firstname, lastname, payload);
|
||||
|
||||
U_INTERNAL_DUMP("redisString = %V", redisString.rep);
|
||||
|
||||
// HSET {a}.cache firstname Victor lastname Stewart picture binary data here
|
||||
|
||||
redisString.setEmpty();
|
||||
|
||||
UCompileTimeRESPEncoder::encode<"HSET {{}}.cache firstname {} lastname {} picture {} \r\n"_ctv>(redisString, token, firstname, lastname, payload);
|
||||
|
||||
U_INTERNAL_DUMP("redisString = %V", redisString.rep);
|
||||
#endif
|
||||
|
||||
#if defined(U_STDCPP_ENABLE) && defined(HAVE_CXX11)
|
||||
std::unordered_map<UString, uint64_t> arounds;
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue
Block a user