1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
This commit is contained in:
stefanocasazza 2019-10-29 15:47:03 +01:00
parent fe2136fdca
commit 65a7070a9d
5 changed files with 278 additions and 235 deletions

View File

@ -241,18 +241,19 @@ private:
};
// 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); }
template <UStringType A, UStringType B>
static void bson_append_utf8( bson_t *doc, A&& fieldname, B&& value) { (void) ::bson_append_utf8(doc, U_STRING_TO_PARAM(fieldname), U_STRING_TO_PARAM(value));}
template <UStringType A, UStringType B>
static void bson_append_binary( bson_t *doc, A&& fieldname, B&& value) { (void) ::bson_append_binary( doc, U_STRING_TO_PARAM(fieldname), BSON_SUBTYPE_BINARY, U_BINARY_TO_PARAM(value));}
static void bson_append_bool( bson_t *doc, UStringType&& fieldname, bool value) { (void) ::bson_append_bool( doc, U_STRING_TO_PARAM(fieldname), value); }
static void bson_append_int32( bson_t *doc, UStringType&& fieldname, int32_t value) { (void) ::bson_append_int32( doc, U_STRING_TO_PARAM(fieldname), value); }
static void bson_append_double( bson_t *doc, UStringType&& fieldname, double value) { (void) ::bson_append_double( doc, U_STRING_TO_PARAM(fieldname), value); }
static void bson_append_date_time( bson_t *doc, UStringType&& fieldname, int64_t value) { (void) ::bson_append_date_time( doc, U_STRING_TO_PARAM(fieldname), value); }
static void bson_append_now_utc( bson_t *doc, UStringType&& fieldname) { (void) ::bson_append_now_utc( doc, U_STRING_TO_PARAM(fieldname) ); }
static void bson_append_document_begin(bson_t *doc, UStringType&& fieldname, bson_t *sub) { (void) ::bson_append_document_begin( doc, U_STRING_TO_PARAM(fieldname), sub); }
static void bson_append_array_begin( bson_t *doc, UStringType&& fieldname, bson_t *sub) { (void) ::bson_append_array_begin( doc, U_STRING_TO_PARAM(fieldname), sub); }
#endif
#endif

View File

@ -825,10 +825,12 @@ protected:
void processResponse();
bool processRequest(char recvtype);
#if defined(HAVE_CXX20) && !defined(__clang__)
bool sendRequest(UStringType&& pipeline)
#else
bool sendRequest(const UString& pipeline)
#endif
{
U_TRACE(0, "UREDISClient_Base::sendRequest(%V)", pipeline.rep)
UClient_Base::iov[0].iov_base = (caddr_t)pipeline.data();
UClient_Base::iov[0].iov_len = pipeline.size();
UClient_Base::iov[1].iov_base = (caddr_t)U_CRLF;
@ -993,7 +995,7 @@ private:
// by Victor Stewart
#if defined(U_STDCPP_ENABLE) && defined(U_LINUX) && defined(HAVE_CXX20)
#if defined(U_STDCPP_ENABLE) && defined(U_LINUX) && defined(HAVE_CXX20) && !defined(__clang__)
typedef UREDISClient<UTCPSocket> UREDISClusterClient;
@ -1037,7 +1039,7 @@ private:
UREDISClusterClient *managementClient;
UHashMap<RedisClusterNode *> *clusterNodes;
static uint16_t hashslotForKey(const UString& hashableKey) { return u_crc16(U_STRING_TO_PARAM(hashableKey)) % 16384; }
static uint16_t hashslotForKey(UStringType&& hashableKey) {return u_crc16(U_STRING_TO_PARAM(hashableKey)) % 16384;}
UREDISClusterClient* clientForHashslot(uint16_t hashslot)
{
@ -1065,13 +1067,65 @@ private:
return managementClient; // never reached
}
UREDISClusterClient* clientForHashableKey(const UString& hashableKey) { return clientForHashslot(hashslotForKey(hashableKey)); }
template <UStringType A>
UREDISClusterClient* clientForHashableKey(A&& hashableKey) { return clientForHashslot(hashslotForKey(std::forward<A>(hashableKey)));}
// this might delete cluster nodes so be careful of client pointers after
void calculateNodeMap();
void sendToCluster(UREDISClusterClient*& workingClient, const UString& hashableKey, const UString& pipeline);
static ClusterError checkResponseForClusterErrors(const UString& response, size_t offset);
template<UStringType A, UStringType B>
UREDISClusterClient* sendToCluster(A&& hashableKey, B&& pipeline)
{
ClusterError error;
UREDISClusterClient* workingClient = clientForHashableKey(std::forward<A>(hashableKey));
retry:
workingClient->clear();
workingClient->UREDISClient_Base::sendRequest(pipeline);
workingClient->UClient_Base::response.setEmpty();
workingClient->UClient_Base::readResponse(U_SINGLE_READ);
error = checkResponseForClusterErrors(workingClient->UClient_Base::response, 0);
while (error != ClusterError::none)
{
switch (error)
{
case ClusterError::moved:
{
calculateNodeMap();
workingClient = clientForHashableKey(std::forward<A>(hashableKey));
break;
}
case ClusterError::ask:
{
uint32_t _start = workingClient->UClient_Base::response.find(' ', U_CONSTANT_SIZE("-ASK 3999")) + 1,
end = workingClient->UClient_Base::response.find(':', _start);
workingClient = clientForIP(workingClient->UClient_Base::response.substr(_start, end - _start));
break;
}
case ClusterError::tryagain:
{
UTimeVal(0L, 1000L).nanosleep(); // 0 sec, 1000 microsec = 1ms
break;
}
case ClusterError::none: break;
}
goto retry;
}
workingClient->UREDISClient_Base::processResponse();
return workingClient;
}
public:
U_MEMORY_TEST
@ -1080,19 +1134,17 @@ public:
bool connect(const char* host = U_NULLPTR, unsigned int _port = 6379);
UString clusterSingle(const UString& hashableKey, const UString& pipeline)
{
UREDISClusterClient* workingClient;
sendToCluster(workingClient, hashableKey, pipeline);
return workingClient->UREDISClient_Base::vitem[0];
template<UStringType A, UStringType B>
const UString clusterSingle(A&& hashableKey, B&& pipeline)
{
return sendToCluster(std::forward<A>(hashableKey), std::forward<B>(pipeline))->vitem[0];
}
// both of these multis require all keys to exist within a single hash slot (on the same node isn't good enough)
const UVector<UString>& clusterMulti( const UString& hashableKey, const UString& pipeline)
template<UStringType A, UStringType B>
const UVector<UString>& clusterMulti(A&& hashableKey, B&& pipeline)
{
UREDISClusterClient* workingClient;
sendToCluster(workingClient, hashableKey, pipeline);
return workingClient->UREDISClient_Base::vitem;
return sendToCluster(std::forward<A>(hashableKey), std::forward<B>(pipeline))->vitem;
}
// if reorderable == false, commands are grouped and pushed SEQUENTIALLY BY HASHSLOT. even if other commands point to hashslots on the same cluster node, we are unable to garuntee ordering since Redis only checks for -MOVED etc errors command by command as it executes them, and does not fail upon reaching a -MOVED etc error. this requires waiting for each response, to ensure no errors occured, before moving onto the next batch of commands.
@ -1121,6 +1173,118 @@ public:
#endif
};
class UCompileTimeRESPEncoder : public UCompileTimeStringFormatter {
private:
template<bool isPartial, size_t workingIndex = 0, size_t workingSegmentCount = 0, typename StringClass, typename... Xs, typename T, typename... Ts>
static constexpr auto generateSegments(StringClass format, size_t& outputSegmentCount, std::tuple<Xs...>&& workingCommand, T&& t, Ts&&... ts)
{
constexpr size_t segmentStart = StringClass::instance.find(workingIndex, " "_ctv, StringClass::notChars);
if constexpr (segmentStart == StringClass::length || StringClass::string[segmentStart] == '\r')
{
if constexpr (isPartial)
{
outputSegmentCount = workingSegmentCount;
return workingCommand;
}
else
{
constexpr auto segmentCountString = "*"_ctv + integerToString<workingSegmentCount>() + "\r\n"_ctv;
constexpr size_t nextCommand = StringClass::instance.find(segmentStart, " \r\n"_ctv, StringClass::notChars);
if constexpr (nextCommand < StringClass::length)
{
return std::apply([&] (auto... params) {
return generateSegments<isPartial, nextCommand>(format, outputSegmentCount, std::tuple(), std::forward<T>(t), std::forward<Ts>(ts)..., segmentCountString, params...);
}, workingCommand);
}
else return std::tuple_cat(std::forward_as_tuple(ts...), std::tie(segmentCountString), workingCommand);
}
}
else
{
constexpr size_t segmentEnd = StringClass::instance.find(segmentStart, " \r"_ctv);
constexpr size_t formatStart = StringClass::instance.find(segmentStart, "{"_ctv, StringClass::skipDoubles, segmentEnd);
if constexpr (formatStart < segmentEnd)
{
constexpr size_t formatTermination = formatStart + 1;
return generateSegments<isPartial, segmentEnd, workingSegmentCount + 1>(format, outputSegmentCount, std::tuple_cat(workingCommand, std::make_tuple("$"_ctv, LengthSurplusPackage<T>{(segmentEnd + formatStart) - (segmentStart + formatTermination) - 1, std::forward<T>(t)}, "\r\n"_ctv, StringClass::instance.template substr<segmentStart, formatStart>(), std::forward<T>(t), StringClass::instance.template substr<(std::min(formatTermination + 1, segmentEnd)), segmentEnd>() + "\r\n"_ctv)), std::forward<Ts>(ts)...);
}
else
{
constexpr auto segmentString = "$"_ctv + integerToString<segmentEnd - segmentStart>() + "\r\n"_ctv + StringClass::instance.template substr<segmentStart, segmentEnd>() + "\r\n"_ctv;
return generateSegments<isPartial, segmentEnd, workingSegmentCount + 1>(format, outputSegmentCount, std::tuple_cat(workingCommand, std::tie(segmentString)), std::forward<T>(t), std::forward<Ts>(ts)...);
}
}
}
template<bool isPartial, bool overwrite, 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<overwrite>(writePosition, workingString, params...);
}, generateSegments<isPartial>(format, segmentCount, std::tuple(), std::forward<Ts>(ts)..., ""_ctv));
return segmentCount;
}
public:
// 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;
// CLIENT REPLY SKIP
static constexpr auto CLIENTREPLYSKIP = "*3\r\n$6\r\nCLIENT\r\n$5\r\nREPLY\r\n$4\r\nSKIP\r\n"_ctv;
// fulls
template<auto format, typename ... Ts>
static void encode(UString& workingString, Ts&&... ts)
{
(void)encode_impl<false, true, format>(0, workingString, std::forward<Ts>(ts)...);
}
template<auto format, typename ... Ts>
static void encode_add(UString& workingString, Ts&&... ts)
{
(void)encode_impl<false, 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, false, format>(writePosition, workingString, std::forward<Ts>(ts)...);
}
// partials
template<auto format, typename... Ts>
static void encode_partial_pos(size_t writePosition, size_t& segmentCountAccumulator, UString& workingString, Ts&&... ts)
{
segmentCountAccumulator += encode_impl<true, false, 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, false, format>(workingString.size(), workingString, std::forward<Ts>(ts)...);
}
static void encode_partial_count(UString& workingString, size_t segmentCount)
{
UCompileTimeStringFormatter::snprintf_impl<false>(0, workingString, "*"_ctv, segmentCount, "\r\n"_ctv);
}
};
class AnonymousClusterPipeline {
private:
@ -1149,8 +1313,9 @@ public:
pipeline.setEmpty();
spans.clear();
}
void append(const UString& hashableKey, const UString& command)
template <UStringType A>
void append(A&& hashableKey, const UString& command)
{
size_t beginning = pipeline.size();
@ -1158,113 +1323,20 @@ public:
pipeline.append(command);
spans.emplace_back(UREDISClusterMaster::hashslotForKey(hashableKey), beginning, pipeline.size(), spans.size());
spans.emplace_back(UREDISClusterMaster::hashslotForKey(std::forward<A>(hashableKey)), beginning, pipeline.size(), spans.size());
}
template <auto format, typename... Ts>
void append(const UString& hashableKey, Ts... ts)
template <auto format, UStringType A, typename... Ts>
void append(A&& hashableKey, Ts&&... ts)
{
size_t beginning = pipeline.size();
UCompileTimeStringFormatter::snprintf_add<format>(pipeline, std::forward<Ts>(ts)...);
UCompileTimeRESPEncoder::encode_add<format>(pipeline, std::forward<Ts>(ts)...);
spans.emplace_back(UREDISClusterMaster::hashslotForKey(hashableKey), beginning, pipeline.size(), spans.size());
spans.emplace_back(UREDISClusterMaster::hashslotForKey(std::forward<A>(hashableKey)), beginning, pipeline.size(), spans.size());
}
AnonymousClusterPipeline() : pipeline(300U) {}
};
class UCompileTimeRESPEncoder : public UCompileTimeStringFormatter {
private:
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)
{
// "HSET {{}}.cache firstname {} lastname {} picture {} \r\n"
constexpr size_t segmentStart = findChar<StringClass::instance, notChar>(workingIndex, ' ');
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;
}
public:
// 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;
// fulls
template<auto format, typename ... Ts>
static void encode(UString& workingString, Ts&&... ts)
{
(void)encode_impl<false, format>(0, workingString, std::forward<Ts>(ts)...);
}
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

View File

@ -61,6 +61,7 @@ enum StringAllocationIndex {
#endif
#define U_STRING_TO_PARAM(str) (str).data(), (str).size()
#define U_STRING_TO_RANGE(str) (str).data(), (str).pend()
#define U_BINARY_TO_PARAM(str) (const uint8_t*)str.data(), str.size()
/**
* UStringRep: string representation
@ -2853,7 +2854,7 @@ namespace std {
};
}
# endif
# if defined(HAVE_CXX20) && defined(U_LINUX)
# if defined(HAVE_CXX20) && defined(U_LINUX) && !defined(__clang__)
# include <utility> // std::index_sequence
template <char... Chars>
class UCompileTimeStringView {
@ -2890,6 +2891,40 @@ public:
if constexpr (From == To) return UCompileTimeStringView<>();
else return substr<From>(std::make_index_sequence<To - From>{});
}
template<size_t index = 0>
constexpr bool contains(char value) const
{
if constexpr (index >= length) return false;
else if (string[index] == value) return true;
else return contains<index+1>(value);
}
// so that CTV is transparently a UString to U_STRING_TO_PARAM
char const *data() const noexcept { return string; }
size_t size() const noexcept { return length; }
static constexpr uint8_t notChars = 0x1;
static constexpr uint8_t skipDoubles = 0x2;
template <class String>
constexpr ssize_t find(size_t index, String findTheseChars, uint8_t options = 0, size_t terminalIndex = length) const
{
while (index < terminalIndex)
{
char ch = string[index];
if (options == skipDoubles)
{
if (findTheseChars.contains(ch) && ((index + 1 < terminalIndex) ? !findTheseChars.contains(string[index + 1]) : true)) return index;
}
else if ((options == notChars) == !findTheseChars.contains(ch)) return index;
++index;
}
return index;
}
};
template <class Char, Char... Chars>
@ -2900,6 +2935,16 @@ constexpr auto operator""_ctv() // compile time view
#define U_CTV_TO_PARAM(ct_string) ct_string.string, ct_string.length
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 Lambda, typename T>
static void snprintf_specialization(Lambda&& lambda, T t)
{
@ -2911,44 +2956,16 @@ static void snprintf_specialization(Lambda&& lambda, T t)
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)...);
if constexpr (argumentCount == 0)
{
return std::make_tuple(std::forward<T>(t), std::forward<Ts>(ts)..., StringClass::instance.template substr<workingIndex, StringClass::length>());
}
else
{
constexpr size_t nextFormat = findChar<StringClass::instance, skipDoubles>(workingIndex, '{');
constexpr size_t nextFormat = StringClass::instance.find(workingIndex, "{"_ctv, StringClass::skipDoubles);
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));
@ -2961,15 +2978,6 @@ protected:
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)
{
@ -3062,21 +3070,27 @@ protected:
}
};
template<typename... Ts>
template<bool overwrite, 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);
// grow string to accomodate new size if necessary
if constexpr (overwrite) workingString.reserve(lengths);
else
{
workingString.reserve(workingString.size() + lengths);
// shift over existing contents
if (writePosition < workingString.size()) (void) memcpy(target + lengths, target, workingString.size() - writePosition);
}
(writeBytes(target, ts), ...);
workingString.size_adjust_force(target - workingString.data());
if constexpr (overwrite) workingString.size_adjust_force(lengths);
else workingString.size_adjust_force(workingString.size() + lengths);
}
public:
@ -3097,14 +3111,14 @@ public:
return digits;
}
template <auto format, typename... Ts>
template <auto format, bool overwrite = false, 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...);
snprintf_impl<overwrite>(writePosition, workingString, params...);
}, generateSegments<0, sizeof...(Ts)>(format, std::forward<Ts>(ts)...));
}
@ -3112,13 +3126,13 @@ public:
template <auto format, typename... Ts>
static void snprintf(UString& workingString, Ts... ts)
{
snprintf_pos<format>(0, workingString, std::forward<Ts>(ts)...);
snprintf_pos<format, true>(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)...);
snprintf_pos<format, false>(workingString.size(), workingString, std::forward<Ts>(ts)...);
}
};
@ -3134,7 +3148,13 @@ static void snprintf_specialization(Lambda&& lambda, UCompileTimeStringFormatter
template<typename T>
concept bool UCompileTimeStringType = requires(T string) {
UCompileTimeStringFormatter::is_ctv_v<T>;
is_ctv_v<T>;
};
template<typename T>
concept bool UStringType = requires(T string)
{
(std::is_same_v<T, UString> || is_ctv_v<T>);
};
# endif
#endif

View File

@ -3980,7 +3980,7 @@ typedef struct mimeentry {
uint32_t name_len;
} mimeentry;
#define MIME_ENTRY(name,type) { type, name+1, U_CONSTANT_SIZE(name)-1 }
#define MIME_ENTRY(name,type) { type, &name[0]+1, U_CONSTANT_SIZE(name)-1 }
/**
* Complete list of MIME types

View File

@ -550,10 +550,9 @@ int UREDISClient_Base::handlerRead()
// 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) && !defined(__clang__)
static ClusterError checkResponseForClusterErrors(const UString& response, size_t offset)
ClusterError UREDISClusterMaster::checkResponseForClusterErrors(const UString& response, size_t offset)
{
U_TRACE_NO_PARAM(0, "checkResponseForClusterErrors()")
@ -723,54 +722,6 @@ bool UREDISClusterMaster::clusterSubscribe(const UString& channel, vPFcscs callb
U_RETURN(false);
}
void UREDISClusterMaster::sendToCluster(UREDISClusterClient*& workingClient, const UString& hashableKey, const UString& pipeline)
{
ClusterError error;
workingClient = clientForHashableKey(hashableKey);
retry:
workingClient->clear();
workingClient->UREDISClient_Base::sendRequest(pipeline);
workingClient->UClient_Base::response.setEmpty();
workingClient->UClient_Base::readResponse(U_SINGLE_READ);
error = checkResponseForClusterErrors(workingClient->UClient_Base::response, 0);
while (error != ClusterError::none)
{
switch (error)
{
case ClusterError::moved:
{
calculateNodeMap();
workingClient = clientForHashableKey(hashableKey);
break;
}
case ClusterError::ask:
{
uint32_t _start = workingClient->UClient_Base::response.find(' ', U_CONSTANT_SIZE("-ASK 3999")) + 1,
end = workingClient->UClient_Base::response.find(':', _start);
workingClient = clientForIP(workingClient->UClient_Base::response.substr(_start, end - _start));
break;
}
case ClusterError::tryagain:
{
UTimeVal(0L, 1000L).nanosleep(); // 0 sec, 1000 microsec = 1ms
break;
}
case ClusterError::none: break;
}
goto retry;
}
workingClient->UREDISClient_Base::processResponse();
}
static void getNextMark(const UString& string, size_t& marker)
{
int8_t depth = 0;
@ -1085,7 +1036,6 @@ finish:
return managementClient->vitem;
}
# endif
#endif
// DEBUG
@ -1108,4 +1058,4 @@ const char* UREDISClient_Base::dump(bool _reset) const
return U_NULLPTR;
}
# endif
#endif