aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbabenko <babenko@yandex-team.com>2022-08-26 10:04:55 +0300
committerbabenko <babenko@yandex-team.com>2022-08-26 10:04:55 +0300
commitaf6b0b93037268a9ca1343dd09629cf910c944d9 (patch)
tree609b08bbb2cd7498d55b6f7a92aaf1a4d51d003c
parentd40e0a72d30643f29b6fe0dee30d6d686f563e3f (diff)
downloadydb-af6b0b93037268a9ca1343dd09629cf910c944d9.tar.gz
Natively support %x and %X in Format
-rw-r--r--library/cpp/yt/string/CMakeLists.txt1
-rw-r--r--library/cpp/yt/string/format-inl.h115
-rw-r--r--library/cpp/yt/string/format.cpp110
-rw-r--r--library/cpp/yt/string/string.cpp118
-rw-r--r--library/cpp/yt/string/string.h9
-rw-r--r--library/cpp/yt/string/unittests/format_ut.cpp24
6 files changed, 289 insertions, 88 deletions
diff --git a/library/cpp/yt/string/CMakeLists.txt b/library/cpp/yt/string/CMakeLists.txt
index 757c10151f..4ee8e8324a 100644
--- a/library/cpp/yt/string/CMakeLists.txt
+++ b/library/cpp/yt/string/CMakeLists.txt
@@ -20,4 +20,5 @@ target_sources(cpp-yt-string PRIVATE
${CMAKE_SOURCE_DIR}/library/cpp/yt/string/enum.cpp
${CMAKE_SOURCE_DIR}/library/cpp/yt/string/guid.cpp
${CMAKE_SOURCE_DIR}/library/cpp/yt/string/string.cpp
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/string/format.cpp
)
diff --git a/library/cpp/yt/string/format-inl.h b/library/cpp/yt/string/format-inl.h
index 5484d4a216..c437d7ab34 100644
--- a/library/cpp/yt/string/format-inl.h
+++ b/library/cpp/yt/string/format-inl.h
@@ -13,6 +13,8 @@
#include <library/cpp/yt/misc/enum.h>
+#include <util/system/platform.h>
+
#include <cctype>
#include <optional>
@@ -20,7 +22,7 @@ namespace NYT {
////////////////////////////////////////////////////////////////////////////////
-static const char GenericSpecSymbol = 'v';
+static constexpr char GenericSpecSymbol = 'v';
inline bool IsQuotationSpecSymbol(char symbol)
{
@@ -91,8 +93,8 @@ inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBu
builder->AppendString("\\t");
} else if (ch < PrintableASCIILow || ch > PrintableASCIIHigh) {
builder->AppendString("\\x");
- builder->AppendChar(Int2Hex[static_cast<ui8>(ch) >> 4]);
- builder->AppendChar(Int2Hex[static_cast<ui8>(ch) & 0xf]);
+ builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) >> 4]);
+ builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) & 0xf]);
} else if ((singleQuotes && ch == '\'') || (doubleQuotes && ch == '\"')) {
builder->AppendChar('\\');
builder->AppendChar(ch);
@@ -121,6 +123,12 @@ inline void FormatValue(TStringBuilderBase* builder, const char* value, TStringB
FormatValue(builder, TStringBuf(value), format);
}
+// char*
+inline void FormatValue(TStringBuilderBase* builder, char* value, TStringBuf format)
+{
+ FormatValue(builder, TStringBuf(value), format);
+}
+
// char
inline void FormatValue(TStringBuilderBase* builder, char value, TStringBuf format)
{
@@ -438,91 +446,54 @@ auto FormatValue(TStringBuilderBase* builder, const TValue& value, TStringBuf fo
return TValueFormatter<TValue>::Do(builder, value, format);
}
+namespace NDetail {
+
template <class TValue>
void FormatValueViaSprintf(
TStringBuilderBase* builder,
TValue value,
TStringBuf format,
- TStringBuf genericSpec)
-{
- constexpr int MaxFormatSize = 64;
- constexpr int SmallResultSize = 64;
-
- auto copyFormat = [] (char* destination, const char* source, int length) {
- int position = 0;
- for (int index = 0; index < length; ++index) {
- if (IsQuotationSpecSymbol(source[index])) {
- continue;
- }
- destination[position] = source[index];
- ++position;
- }
- return destination + position;
- };
-
- char formatBuf[MaxFormatSize];
- YT_VERIFY(format.length() >= 1 && format.length() <= MaxFormatSize - 2); // one for %, one for \0
- formatBuf[0] = '%';
- if (format[format.length() - 1] == GenericSpecSymbol) {
- char* formatEnd = copyFormat(formatBuf + 1, format.begin(), format.length() - 1);
- ::memcpy(formatEnd, genericSpec.begin(), genericSpec.length());
- formatEnd[genericSpec.length()] = '\0';
- } else {
- char* formatEnd = copyFormat(formatBuf + 1, format.begin(), format.length());
- *formatEnd = '\0';
- }
-
- char* result = builder->Preallocate(SmallResultSize);
- size_t resultSize = ::snprintf(result, SmallResultSize, formatBuf, value);
- if (resultSize >= SmallResultSize) {
- result = builder->Preallocate(resultSize + 1);
- YT_VERIFY(::snprintf(result, resultSize + 1, formatBuf, value) == static_cast<int>(resultSize));
- }
- builder->Advance(resultSize);
-}
+ TStringBuf genericSpec);
template <class TValue>
-char* WriteIntToBufferBackwards(char* buffer, TValue value);
+void FormatIntValue(
+ TStringBuilderBase* builder,
+ TValue value,
+ TStringBuf format,
+ TStringBuf genericSpec);
-template <class TValue>
-void FormatValueViaHelper(TStringBuilderBase* builder, TValue value, TStringBuf format, TStringBuf genericSpec)
-{
- if (format == TStringBuf("v")) {
- const int MaxResultSize = 64;
- char buffer[MaxResultSize];
- char* end = buffer + MaxResultSize;
- char* start = WriteIntToBufferBackwards(end, value);
- builder->AppendString(TStringBuf(start, end));
- } else {
- FormatValueViaSprintf(builder, value, format, genericSpec);
- }
-}
+} // namespace NDetail
#define XX(valueType, castType, genericSpec) \
inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \
{ \
- FormatValueViaHelper(builder, static_cast<castType>(value), format, genericSpec); \
- }
-
-XX(i8, int, TStringBuf("d"))
-XX(ui8, unsigned int, TStringBuf("u"))
-XX(i16, int, TStringBuf("d"))
-XX(ui16, unsigned int, TStringBuf("u"))
-XX(i32, int, TStringBuf("d"))
-XX(ui32, unsigned int, TStringBuf("u"))
-XX(long, long, TStringBuf("ld"))
-XX(unsigned long, unsigned long, TStringBuf("lu"))
+ NYT::NDetail::FormatIntValue(builder, static_cast<castType>(value), format, genericSpec); \
+ }
+
+XX(i8, i32, TStringBuf("d"))
+XX(ui8, ui32, TStringBuf("u"))
+XX(i16, i32, TStringBuf("d"))
+XX(ui16, ui32, TStringBuf("u"))
+XX(i32, i32, TStringBuf("d"))
+XX(ui32, ui32, TStringBuf("u"))
+#ifdef _win_
+XX(long long, i64, TStringBuf("lld"))
+XX(unsigned long long, ui64, TStringBuf("llu"))
+#else
+XX(long, i64, TStringBuf("ld"))
+XX(unsigned long, ui64, TStringBuf("lu"))
+#endif
#undef XX
#define XX(valueType, castType, genericSpec) \
inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \
{ \
- FormatValueViaSprintf(builder, static_cast<castType>(value), format, genericSpec); \
+ NYT::NDetail::FormatValueViaSprintf(builder, static_cast<castType>(value), format, genericSpec); \
}
-XX(double, double, TStringBuf("lf"))
-XX(float, float, TStringBuf("f"))
+XX(double, double, TStringBuf("lf"))
+XX(float, float, TStringBuf("f"))
#undef XX
@@ -530,7 +501,7 @@ XX(float, float, TStringBuf("f"))
template <class T>
void FormatValue(TStringBuilderBase* builder, T* value, TStringBuf format)
{
- FormatValueViaSprintf(builder, value, format, TStringBuf("p"));
+ NYT::NDetail::FormatValueViaSprintf(builder, static_cast<const void*>(value), format, TStringBuf("p"));
}
// TDuration (specialize for performance reasons)
@@ -548,6 +519,8 @@ inline void FormatValue(TStringBuilderBase* builder, TInstant value, TStringBuf
////////////////////////////////////////////////////////////////////////////////
+namespace NDetail {
+
template <class TArgFormatter>
void FormatImpl(
TStringBuilderBase* builder,
@@ -649,6 +622,8 @@ void FormatImpl(
}
}
+} // namespace NDetail
+
////////////////////////////////////////////////////////////////////////////////
template <class T>
@@ -716,7 +691,7 @@ void Format(
TArgs&&... args)
{
TArgFormatterImpl<0, TArgs...> argFormatter(args...);
- FormatImpl(builder, format, argFormatter);
+ NYT::NDetail::FormatImpl(builder, format, argFormatter);
}
template <size_t Length, class... TArgs>
diff --git a/library/cpp/yt/string/format.cpp b/library/cpp/yt/string/format.cpp
new file mode 100644
index 0000000000..6c824e8ba3
--- /dev/null
+++ b/library/cpp/yt/string/format.cpp
@@ -0,0 +1,110 @@
+#include "format.h"
+#include "string.h"
+
+namespace NYT::NDetail {
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class TValue>
+void FormatValueViaSprintf(
+ TStringBuilderBase* builder,
+ TValue value,
+ TStringBuf format,
+ TStringBuf genericSpec)
+{
+ constexpr int MaxFormatSize = 64;
+ constexpr int SmallResultSize = 64;
+
+ auto copyFormat = [] (char* destination, const char* source, int length) {
+ int position = 0;
+ for (int index = 0; index < length; ++index) {
+ if (IsQuotationSpecSymbol(source[index])) {
+ continue;
+ }
+ destination[position] = source[index];
+ ++position;
+ }
+ return destination + position;
+ };
+
+ char formatBuf[MaxFormatSize];
+ YT_VERIFY(format.length() >= 1 && format.length() <= MaxFormatSize - 2); // one for %, one for \0
+ formatBuf[0] = '%';
+ if (format[format.length() - 1] == GenericSpecSymbol) {
+ char* formatEnd = copyFormat(formatBuf + 1, format.begin(), format.length() - 1);
+ ::memcpy(formatEnd, genericSpec.begin(), genericSpec.length());
+ formatEnd[genericSpec.length()] = '\0';
+ } else {
+ char* formatEnd = copyFormat(formatBuf + 1, format.begin(), format.length());
+ *formatEnd = '\0';
+ }
+
+ char* result = builder->Preallocate(SmallResultSize);
+ size_t resultSize = ::snprintf(result, SmallResultSize, formatBuf, value);
+ if (resultSize >= SmallResultSize) {
+ result = builder->Preallocate(resultSize + 1);
+ YT_VERIFY(::snprintf(result, resultSize + 1, formatBuf, value) == static_cast<int>(resultSize));
+ }
+ builder->Advance(resultSize);
+}
+
+#define XX(type) \
+ template \
+ void FormatValueViaSprintf( \
+ TStringBuilderBase* builder, \
+ type value, \
+ TStringBuf format, \
+ TStringBuf genericSpec);
+
+XX(i32)
+XX(ui32)
+XX(i64)
+XX(ui64)
+XX(float)
+XX(double)
+XX(const void*)
+
+#undef XX
+
+template <class TValue>
+void FormatIntValue(
+ TStringBuilderBase* builder,
+ TValue value,
+ TStringBuf format,
+ TStringBuf genericSpec)
+{
+ if (format == TStringBuf("v")) {
+ constexpr int MaxResultSize = 64;
+ char buffer[MaxResultSize];
+ char* end = buffer + MaxResultSize;
+ char* start = WriteDecIntToBufferBackwards(end, value);
+ builder->AppendString(TStringBuf(start, end));
+ } else if (format == TStringBuf("x") || format == TStringBuf("X")) {
+ constexpr int MaxResultSize = 64;
+ char buffer[MaxResultSize];
+ char* end = buffer + MaxResultSize;
+ char* start = WriteHexIntToBufferBackwards(end, value, format[0] == 'X');
+ builder->AppendString(TStringBuf(start, end));
+ } else {
+ FormatValueViaSprintf(builder, value, format, genericSpec);
+ }
+}
+
+#define XX(type) \
+ template \
+ void FormatIntValue( \
+ TStringBuilderBase* builder, \
+ type value, \
+ TStringBuf format, \
+ TStringBuf genericSpec);
+
+XX(i32)
+XX(ui32)
+XX(i64)
+XX(ui64)
+
+#undef XX
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT::NDetail
diff --git a/library/cpp/yt/string/string.cpp b/library/cpp/yt/string/string.cpp
index 7440ac3fdd..a96f212cdf 100644
--- a/library/cpp/yt/string/string.cpp
+++ b/library/cpp/yt/string/string.cpp
@@ -105,7 +105,7 @@ TString Trim(const TString& str, const TString& whitespaces)
namespace {
-ui16 DecimalDigits2[100] = {
+const ui16 DecimalDigits2[100] = {
12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640,
12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641,
12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642,
@@ -119,7 +119,7 @@ ui16 DecimalDigits2[100] = {
};
template <class T>
-char* WriteSignedIntToBufferBackwardsImpl(char* ptr, T value, TStringBuf min)
+char* WriteSignedDecIntToBufferBackwardsImpl(char* ptr, T value, TStringBuf min)
{
if (value == 0) {
--ptr;
@@ -141,8 +141,8 @@ char* WriteSignedIntToBufferBackwardsImpl(char* ptr, T value, TStringBuf min)
}
while (value >= 10) {
- i64 rem = value % 100;
- i64 quot = value / 100;
+ auto rem = value % 100;
+ auto quot = value / 100;
ptr -= 2;
::memcpy(ptr, &DecimalDigits2[rem], 2);
value = quot;
@@ -162,7 +162,7 @@ char* WriteSignedIntToBufferBackwardsImpl(char* ptr, T value, TStringBuf min)
}
template <class T>
-char* WriteUnsignedIntToBufferBackwardsImpl(char* ptr, T value)
+char* WriteUnsignedDecIntToBufferBackwardsImpl(char* ptr, T value)
{
if (value == 0) {
--ptr;
@@ -171,8 +171,8 @@ char* WriteUnsignedIntToBufferBackwardsImpl(char* ptr, T value)
}
while (value >= 10) {
- i64 rem = value % 100;
- i64 quot = value / 100;
+ auto rem = value % 100;
+ auto quot = value / 100;
ptr -= 2;
::memcpy(ptr, &DecimalDigits2[rem], 2);
value = quot;
@@ -189,27 +189,115 @@ char* WriteUnsignedIntToBufferBackwardsImpl(char* ptr, T value)
} // namespace
template <>
-char* WriteIntToBufferBackwards(char* ptr, i32 value)
+char* WriteDecIntToBufferBackwards(char* ptr, i32 value)
{
- return WriteSignedIntToBufferBackwardsImpl(ptr, value, TStringBuf("-2147483647"));
+ return WriteSignedDecIntToBufferBackwardsImpl(ptr, value, TStringBuf("-2147483647"));
}
template <>
-char* WriteIntToBufferBackwards(char* ptr, i64 value)
+char* WriteDecIntToBufferBackwards(char* ptr, i64 value)
{
- return WriteSignedIntToBufferBackwardsImpl(ptr, value, TStringBuf("-9223372036854775808"));
+ return WriteSignedDecIntToBufferBackwardsImpl(ptr, value, TStringBuf("-9223372036854775808"));
}
template <>
-char* WriteIntToBufferBackwards(char* ptr, ui32 value)
+char* WriteDecIntToBufferBackwards(char* ptr, ui32 value)
{
- return WriteUnsignedIntToBufferBackwardsImpl(ptr, value);
+ return WriteUnsignedDecIntToBufferBackwardsImpl(ptr, value);
}
template <>
-char* WriteIntToBufferBackwards(char* ptr, ui64 value)
+char* WriteDecIntToBufferBackwards(char* ptr, ui64 value)
{
- return WriteUnsignedIntToBufferBackwardsImpl(ptr, value);
+ return WriteUnsignedDecIntToBufferBackwardsImpl(ptr, value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+template <class T>
+char* WriteSignedHexIntToBufferBackwardsImpl(char* ptr, T value, bool uppercase, TStringBuf min)
+{
+ if (value == 0) {
+ --ptr;
+ *ptr = '0';
+ return ptr;
+ }
+
+ // The negative value handling code below works incorrectly for min values.
+ if (value == std::numeric_limits<T>::min()) {
+ ptr -= min.length();
+ ::memcpy(ptr, min.begin(), min.length());
+ return ptr;
+ }
+
+ bool negative = false;
+ if (value < 0) {
+ negative = true;
+ value = -value;
+ }
+
+ while (value != 0) {
+ auto rem = value & 0xf;
+ auto quot = value >> 4;
+ --ptr;
+ *ptr = uppercase ? IntToHexUppercase[rem] : IntToHexLowercase[rem];
+ value = quot;
+ }
+
+ if (negative) {
+ --ptr;
+ *ptr = '-';
+ }
+
+ return ptr;
+}
+
+template <class T>
+char* WriteUnsignedHexIntToBufferBackwardsImpl(char* ptr, T value, bool uppercase)
+{
+ if (value == 0) {
+ --ptr;
+ *ptr = '0';
+ return ptr;
+ }
+
+ while (value != 0) {
+ auto rem = value & 0xf;
+ auto quot = value >> 4;
+ --ptr;
+ *ptr = uppercase ? IntToHexUppercase[rem] : IntToHexLowercase[rem];
+ value = quot;
+ }
+
+ return ptr;
+}
+
+} // namespace
+
+template <>
+char* WriteHexIntToBufferBackwards(char* ptr, i32 value, bool uppercase)
+{
+ return WriteSignedHexIntToBufferBackwardsImpl(ptr, value, uppercase, TStringBuf("-80000000"));
+}
+
+template <>
+char* WriteHexIntToBufferBackwards(char* ptr, i64 value, bool uppercase)
+{
+ return WriteSignedHexIntToBufferBackwardsImpl(ptr, value, uppercase, TStringBuf("-8000000000000000"));
+}
+
+template <>
+char* WriteHexIntToBufferBackwards(char* ptr, ui32 value, bool uppercase)
+{
+ return WriteUnsignedHexIntToBufferBackwardsImpl(ptr, value, uppercase);
+}
+
+template <>
+char* WriteHexIntToBufferBackwards(char* ptr, ui64 value, bool uppercase)
+{
+ return WriteUnsignedHexIntToBufferBackwardsImpl(ptr, value, uppercase);
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/string/string.h b/library/cpp/yt/string/string.h
index ae6c99caab..04eb115cb2 100644
--- a/library/cpp/yt/string/string.h
+++ b/library/cpp/yt/string/string.h
@@ -36,7 +36,8 @@ static constexpr TStringBuf DefaultRangeEllipsisFormat = "...";
// ASCII characters from 0x20 = ' ' to 0x7e = '~' are printable.
static constexpr char PrintableASCIILow = 0x20;
static constexpr char PrintableASCIIHigh = 0x7e;
-static constexpr TStringBuf Int2Hex = "0123456789abcdef";
+static constexpr TStringBuf IntToHexLowercase = "0123456789abcdef";
+static constexpr TStringBuf IntToHexUppercase = "0123456789ABCDEF";
//! Joins a range of items into a string intermixing them with the delimiter.
/*!
@@ -196,7 +197,11 @@ TString Trim(const TString& str, const TString& whitespaces);
//! Implemented for |[u]i(32|64)|.
template <class T>
-char* WriteIntToBufferBackwards(char* ptr, T value);
+char* WriteDecIntToBufferBackwards(char* ptr, T value);
+
+//! Implemented for |[u]i(32|64)|.
+template <class T>
+char* WriteHexIntToBufferBackwards(char* ptr, T value, bool uppercase);
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/string/unittests/format_ut.cpp b/library/cpp/yt/string/unittests/format_ut.cpp
index ee069bb2c0..20ec6392b5 100644
--- a/library/cpp/yt/string/unittests/format_ut.cpp
+++ b/library/cpp/yt/string/unittests/format_ut.cpp
@@ -74,7 +74,7 @@ TEST(TFormatTest, Strings)
EXPECT_EQ(100, std::ssize(Format("%100v", "abc")));
}
-TEST(TFormatTest, Integers)
+TEST(TFormatTest, DecIntegers)
{
EXPECT_EQ("123", Format("%d", 123));
EXPECT_EQ("123", Format("%v", 123));
@@ -101,6 +101,28 @@ TEST(TFormatTest, Integers)
EXPECT_EQ("18446744073709551615", Format("%v", std::numeric_limits<ui64>::max()));
}
+TEST(TFormatTest, HexIntegers)
+{
+ EXPECT_EQ("7b", Format("%x", 123));
+ EXPECT_EQ("7B", Format("%X", 123));
+
+ EXPECT_EQ("02a", Format("%03x", 42));
+ EXPECT_EQ("2a", Format("%01x", 42));
+
+ EXPECT_EQ("7fffffff", Format("%x", std::numeric_limits<i32>::max()));
+ EXPECT_EQ("-80000000", Format("%x", std::numeric_limits<i32>::min()));
+
+ EXPECT_EQ("0", Format("%x", 0U));
+ EXPECT_EQ("0", Format("%X", 0U));
+ EXPECT_EQ("ffffffff", Format("%x", std::numeric_limits<ui32>::max()));
+
+ EXPECT_EQ("7fffffffffffffff", Format("%x", std::numeric_limits<i64>::max()));
+ EXPECT_EQ("-8000000000000000", Format("%x", std::numeric_limits<i64>::min()));
+
+ EXPECT_EQ("0", Format("%x", 0ULL));
+ EXPECT_EQ("ffffffffffffffff", Format("%x", std::numeric_limits<ui64>::max()));
+}
+
TEST(TFormatTest, Floats)
{
EXPECT_EQ("3.14", Format("%.2f", 3.1415F));