[libc-commits] [libc] ff13747 - [libc] Add Printf FormatSection Matcher

Michael Jones via libc-commits libc-commits at lists.llvm.org
Fri Apr 22 14:21:44 PDT 2022


Author: Michael Jones
Date: 2022-04-22T14:21:39-07:00
New Revision: ff1374785f82c2c184df7ca09a923acf62e4c277

URL: https://github.com/llvm/llvm-project/commit/ff1374785f82c2c184df7ca09a923acf62e4c277
DIFF: https://github.com/llvm/llvm-project/commit/ff1374785f82c2c184df7ca09a923acf62e4c277.diff

LOG: [libc] Add Printf FormatSection Matcher

This patch changes the printf parser tests to use a more robust matcher.
This allows for better debugging of parsing issues. This does not affect
the actual printf code at all, only the tests.

Reviewed By: sivachandra, lntue

Differential Revision: https://reviews.llvm.org/D124130

Added: 
    libc/utils/UnitTest/PrintfMatcher.cpp
    libc/utils/UnitTest/PrintfMatcher.h
    libc/utils/UnitTest/StringUtils.h

Modified: 
    libc/src/stdio/printf_core/core_structs.h
    libc/test/src/stdio/printf_core/CMakeLists.txt
    libc/test/src/stdio/printf_core/parser_test.cpp
    libc/utils/UnitTest/CMakeLists.txt
    libc/utils/UnitTest/FPMatcher.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index fe4c96a6bbb9b..deb348f665165 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -9,6 +9,8 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CORE_STRUCTS_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CORE_STRUCTS_H
 
+#include "src/__support/CPP/StringView.h"
+
 #include <inttypes.h>
 #include <stddef.h>
 
@@ -47,6 +49,32 @@ struct FormatSection {
   void *conv_val_ptr;
 
   char conv_name;
+
+  // This operator is only used for testing and should be automatically
+  // optimized out for release builds.
+  bool operator==(const FormatSection &other) {
+    if (has_conv != other.has_conv)
+      return false;
+
+    if (!cpp::StringView(raw_string, raw_len)
+             .equals(cpp::StringView(other.raw_string, other.raw_len)))
+      return false;
+
+    if (has_conv) {
+      if (!((static_cast<uint8_t>(flags) ==
+             static_cast<uint8_t>(other.flags)) &&
+            (min_width == other.min_width) && (precision == other.precision) &&
+            (length_modifier == other.length_modifier) &&
+            (conv_name == other.conv_name)))
+        return false;
+
+      if (conv_name == 'p' || conv_name == 'n' || conv_name == 's')
+        return (conv_val_ptr == other.conv_val_ptr);
+      else if (conv_name != '%')
+        return (conv_val_raw == other.conv_val_raw);
+    }
+    return true;
+  }
 };
 
 } // namespace printf_core

diff  --git a/libc/test/src/stdio/printf_core/CMakeLists.txt b/libc/test/src/stdio/printf_core/CMakeLists.txt
index 4eedc0a5bba2c..04544ff9d0a27 100644
--- a/libc/test/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/test/src/stdio/printf_core/CMakeLists.txt
@@ -8,3 +8,5 @@ add_libc_unittest(
     libc.src.stdio.printf_core.parser
     libc.src.__support.arg_list
 )
+
+target_link_libraries(libc.test.src.stdio.printf_core.parser_test PRIVATE LibcPrintfHelpers)

diff  --git a/libc/test/src/stdio/printf_core/parser_test.cpp b/libc/test/src/stdio/printf_core/parser_test.cpp
index e57ebdb1ceac0..73c40eab94a3d 100644
--- a/libc/test/src/stdio/printf_core/parser_test.cpp
+++ b/libc/test/src/stdio/printf_core/parser_test.cpp
@@ -12,37 +12,9 @@
 
 #include <stdarg.h>
 
+#include "utils/UnitTest/PrintfMatcher.h"
 #include "utils/UnitTest/Test.h"
 
-class LlvmLibcPrintfParserTest : public __llvm_libc::testing::Test {
-public:
-  void assert_eq_fs(__llvm_libc::printf_core::FormatSection expected,
-                    __llvm_libc::printf_core::FormatSection actual) {
-    ASSERT_EQ(expected.has_conv, actual.has_conv);
-    ASSERT_EQ(expected.raw_len, actual.raw_len);
-
-    for (size_t i = 0; i < expected.raw_len; ++i) {
-      EXPECT_EQ(expected.raw_string[i], actual.raw_string[i]);
-    }
-
-    if (expected.has_conv) {
-      ASSERT_EQ(static_cast<uint8_t>(expected.flags),
-                static_cast<uint8_t>(actual.flags));
-      ASSERT_EQ(expected.min_width, actual.min_width);
-      ASSERT_EQ(expected.precision, actual.precision);
-      ASSERT_TRUE(expected.length_modifier == actual.length_modifier);
-      ASSERT_EQ(expected.conv_name, actual.conv_name);
-
-      if (expected.conv_name == 'p' || expected.conv_name == 'n' ||
-          expected.conv_name == 's') {
-        ASSERT_EQ(expected.conv_val_ptr, actual.conv_val_ptr);
-      } else if (expected.conv_name != '%') {
-        ASSERT_EQ(expected.conv_val_raw, actual.conv_val_raw);
-      }
-    }
-  }
-};
-
 void init(const char *__restrict str, ...) {
   va_list vlist;
   va_start(vlist, str);
@@ -68,9 +40,9 @@ void evaluate(__llvm_libc::printf_core::FormatSection *format_arr,
   }
 }
 
-TEST_F(LlvmLibcPrintfParserTest, Constructor) { init("test", 1, 2); }
+TEST(LlvmLibcPrintfParserTest, Constructor) { init("test", 1, 2); }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalRaw) {
+TEST(LlvmLibcPrintfParserTest, EvalRaw) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "test";
   evaluate(format_arr, str);
@@ -80,10 +52,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalRaw) {
   expected.raw_len = 4;
   expected.raw_string = str;
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalSimple) {
+TEST(LlvmLibcPrintfParserTest, EvalSimple) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "test %% test";
   evaluate(format_arr, str);
@@ -93,23 +65,23 @@ TEST_F(LlvmLibcPrintfParserTest, EvalSimple) {
   expected0.raw_len = 5;
   expected0.raw_string = str;
 
-  assert_eq_fs(expected0, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected0, format_arr[0]);
 
   expected1.has_conv = true;
   expected1.raw_len = 2;
   expected1.raw_string = str + 5;
   expected1.conv_name = '%';
 
-  assert_eq_fs(expected1, format_arr[1]);
+  ASSERT_FORMAT_EQ(expected1, format_arr[1]);
 
   expected2.has_conv = false;
   expected2.raw_len = 5;
   expected2.raw_string = str + 7;
 
-  assert_eq_fs(expected2, format_arr[2]);
+  ASSERT_FORMAT_EQ(expected2, format_arr[2]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalOneArg) {
+TEST(LlvmLibcPrintfParserTest, EvalOneArg) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "%d";
   int arg1 = 12345;
@@ -122,10 +94,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalOneArg) {
   expected.conv_val_raw = arg1;
   expected.conv_name = 'd';
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithFlags) {
+TEST(LlvmLibcPrintfParserTest, EvalOneArgWithFlags) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "%+-0 #d";
   int arg1 = 12345;
@@ -144,10 +116,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithFlags) {
   expected.conv_val_raw = arg1;
   expected.conv_name = 'd';
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithWidth) {
+TEST(LlvmLibcPrintfParserTest, EvalOneArgWithWidth) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "%12d";
   int arg1 = 12345;
@@ -161,10 +133,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithWidth) {
   expected.conv_val_raw = arg1;
   expected.conv_name = 'd';
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithPrecision) {
+TEST(LlvmLibcPrintfParserTest, EvalOneArgWithPrecision) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "%.34d";
   int arg1 = 12345;
@@ -178,10 +150,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithPrecision) {
   expected.conv_val_raw = arg1;
   expected.conv_name = 'd';
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithTrivialPrecision) {
+TEST(LlvmLibcPrintfParserTest, EvalOneArgWithTrivialPrecision) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "%.d";
   int arg1 = 12345;
@@ -195,10 +167,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithTrivialPrecision) {
   expected.conv_val_raw = arg1;
   expected.conv_name = 'd';
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithShortLengthModifier) {
+TEST(LlvmLibcPrintfParserTest, EvalOneArgWithShortLengthModifier) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "%hd";
   int arg1 = 12345;
@@ -212,10 +184,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithShortLengthModifier) {
   expected.conv_val_raw = arg1;
   expected.conv_name = 'd';
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithLongLengthModifier) {
+TEST(LlvmLibcPrintfParserTest, EvalOneArgWithLongLengthModifier) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "%lld";
   int arg1 = 12345;
@@ -229,10 +201,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithLongLengthModifier) {
   expected.conv_val_raw = arg1;
   expected.conv_name = 'd';
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithAllOptions) {
+TEST(LlvmLibcPrintfParserTest, EvalOneArgWithAllOptions) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "% -056.78jd";
   int arg1 = 12345;
@@ -252,10 +224,10 @@ TEST_F(LlvmLibcPrintfParserTest, EvalOneArgWithAllOptions) {
   expected.conv_val_raw = arg1;
   expected.conv_name = 'd';
 
-  assert_eq_fs(expected, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected, format_arr[0]);
 }
 
-TEST_F(LlvmLibcPrintfParserTest, EvalThreeArgs) {
+TEST(LlvmLibcPrintfParserTest, EvalThreeArgs) {
   __llvm_libc::printf_core::FormatSection format_arr[10];
   const char *str = "%d%f%s";
   int arg1 = 12345;
@@ -270,7 +242,7 @@ TEST_F(LlvmLibcPrintfParserTest, EvalThreeArgs) {
   expected0.conv_val_raw = arg1;
   expected0.conv_name = 'd';
 
-  assert_eq_fs(expected0, format_arr[0]);
+  ASSERT_FORMAT_EQ(expected0, format_arr[0]);
 
   expected1.has_conv = true;
   expected1.raw_len = 2;
@@ -278,7 +250,7 @@ TEST_F(LlvmLibcPrintfParserTest, EvalThreeArgs) {
   expected1.conv_val_raw = __llvm_libc::bit_cast<uint64_t>(arg2);
   expected1.conv_name = 'f';
 
-  assert_eq_fs(expected1, format_arr[1]);
+  ASSERT_FORMAT_EQ(expected1, format_arr[1]);
 
   expected2.has_conv = true;
   expected2.raw_len = 2;
@@ -286,5 +258,5 @@ TEST_F(LlvmLibcPrintfParserTest, EvalThreeArgs) {
   expected2.conv_val_ptr = const_cast<char *>(arg3);
   expected2.conv_name = 's';
 
-  assert_eq_fs(expected2, format_arr[2]);
+  ASSERT_FORMAT_EQ(expected2, format_arr[2]);
 }

diff  --git a/libc/utils/UnitTest/CMakeLists.txt b/libc/utils/UnitTest/CMakeLists.txt
index e7890409bdffd..88d6ab84d7f55 100644
--- a/libc/utils/UnitTest/CMakeLists.txt
+++ b/libc/utils/UnitTest/CMakeLists.txt
@@ -17,6 +17,14 @@ target_include_directories(LibcUnitTestMain PUBLIC ${LIBC_SOURCE_DIR})
 add_dependencies(LibcUnitTestMain LibcUnitTest)
 target_link_libraries(LibcUnitTestMain PUBLIC LibcUnitTest libc_test_utils)
 
+add_header_library(
+  string_utils
+  HDRS
+    StringUtils.h
+  DEPENDS
+  libc.src.__support.CPP.type_traits
+)
+
 add_library(
   LibcFPTestHelpers
     FPExceptMatcher.cpp
@@ -29,6 +37,7 @@ target_link_libraries(LibcFPTestHelpers LibcUnitTest)
 add_dependencies(
   LibcFPTestHelpers
   LibcUnitTest
+  libc.utils.UnitTest.string_utils
   libc.src.__support.FPUtil.fputil
 )
 
@@ -44,3 +53,17 @@ add_dependencies(
   LibcUnitTest
   libc.src.__support.CPP.array_ref
 )
+
+add_library(
+  LibcPrintfHelpers
+    PrintfMatcher.h
+    PrintfMatcher.cpp
+)
+target_include_directories(LibcPrintfHelpers PUBLIC ${LIBC_SOURCE_DIR})
+target_link_libraries(LibcPrintfHelpers LibcUnitTest)
+add_dependencies(
+  LibcPrintfHelpers
+  LibcUnitTest
+  libc.utils.UnitTest.string_utils
+  libc.src.stdio.printf_core.core_structs
+)

diff  --git a/libc/utils/UnitTest/FPMatcher.cpp b/libc/utils/UnitTest/FPMatcher.cpp
index a5ef268eaba3c..d13cfa041b1dc 100644
--- a/libc/utils/UnitTest/FPMatcher.cpp
+++ b/libc/utils/UnitTest/FPMatcher.cpp
@@ -10,6 +10,8 @@
 
 #include "src/__support/FPUtil/FPBits.h"
 
+#include "utils/UnitTest/StringUtils.h"
+
 #include <sstream>
 #include <string>
 
@@ -17,20 +19,6 @@ namespace __llvm_libc {
 namespace fputil {
 namespace testing {
 
-// Return the first N hex digits of an integer as a string in upper case.
-template <typename T>
-cpp::EnableIfType<cpp::IsIntegral<T>::Value, std::string>
-uintToHex(T X, size_t Length = sizeof(T) * 2) {
-  std::string s(Length, '0');
-
-  for (auto it = s.rbegin(), end = s.rend(); it != end; ++it, X >>= 4) {
-    unsigned char Mod = static_cast<unsigned char>(X) & 15;
-    *it = (Mod < 10 ? '0' + Mod : 'a' + Mod - 10);
-  }
-
-  return s;
-}
-
 template <typename ValType, typename StreamType>
 cpp::EnableIfType<cpp::IsFloatingPointType<ValType>::Value, void>
 describeValue(const char *label, ValType value, StreamType &stream) {
@@ -53,13 +41,13 @@ describeValue(const char *label, ValType value, StreamType &stream) {
         sizeof(typename fputil::FPBits<ValType>::UIntType) * 2;
 
     stream << "0x"
-           << uintToHex<typename fputil::FPBits<ValType>::UIntType>(
+           << int_to_hex<typename fputil::FPBits<ValType>::UIntType>(
                   bits.uintval(), bitsWidthInHex)
            << ", (S | E | M) = (" << (bits.get_sign() ? '1' : '0') << " | 0x"
-           << uintToHex<uint16_t>(bits.get_unbiased_exponent(),
-                                  exponentWidthInHex)
+           << int_to_hex<uint16_t>(bits.get_unbiased_exponent(),
+                                   exponentWidthInHex)
            << " | 0x"
-           << uintToHex<typename fputil::FPBits<ValType>::UIntType>(
+           << int_to_hex<typename fputil::FPBits<ValType>::UIntType>(
                   bits.get_mantissa(), mantissaWidthInHex)
            << ")";
   }

diff  --git a/libc/utils/UnitTest/PrintfMatcher.cpp b/libc/utils/UnitTest/PrintfMatcher.cpp
new file mode 100644
index 0000000000000..f30b1ce31b0d0
--- /dev/null
+++ b/libc/utils/UnitTest/PrintfMatcher.cpp
@@ -0,0 +1,90 @@
+//===-- PrintfMatcher.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrintfMatcher.h"
+#include "src/stdio/printf_core/core_structs.h"
+
+#include "utils/UnitTest/StringUtils.h"
+
+#include <stdint.h>
+
+namespace __llvm_libc {
+namespace printf_core {
+namespace testing {
+
+bool FormatSectionMatcher::match(FormatSection actualValue) {
+  actual = actualValue;
+  return expected == actual;
+}
+
+namespace {
+
+#define IF_FLAG_SHOW_FLAG(flag_name)                                           \
+  do {                                                                         \
+    if ((form.flags & FormatFlags::flag_name) == FormatFlags::flag_name)       \
+      stream << "\n\t\t" << #flag_name;                                        \
+  } while (false)
+#define CASE_LM(lm)                                                            \
+  case (LengthModifier::lm):                                                   \
+    stream << #lm;                                                             \
+    break
+
+void display(testutils::StreamWrapper &stream, FormatSection form) {
+  stream << "Raw String (len " << form.raw_len << "): \"";
+  for (size_t i = 0; i < form.raw_len; ++i) {
+    stream << form.raw_string[i];
+  }
+  stream << "\"";
+  if (form.has_conv) {
+    stream << "\n\tHas Conv\n\tFlags:";
+    IF_FLAG_SHOW_FLAG(LEFT_JUSTIFIED);
+    IF_FLAG_SHOW_FLAG(FORCE_SIGN);
+    IF_FLAG_SHOW_FLAG(SPACE_PREFIX);
+    IF_FLAG_SHOW_FLAG(ALTERNATE_FORM);
+    IF_FLAG_SHOW_FLAG(LEADING_ZEROES);
+    stream << "\n";
+    stream << "\tmin width: " << form.min_width << "\n";
+    stream << "\tprecision: " << form.precision << "\n";
+    stream << "\tlength modifier: ";
+    switch (form.length_modifier) {
+      CASE_LM(none);
+      CASE_LM(l);
+      CASE_LM(ll);
+      CASE_LM(h);
+      CASE_LM(hh);
+      CASE_LM(j);
+      CASE_LM(z);
+      CASE_LM(t);
+      CASE_LM(L);
+    }
+    stream << "\n";
+    stream << "\tconversion name: " << form.conv_name << "\n";
+    if (form.conv_name == 'p' || form.conv_name == 'n' || form.conv_name == 's')
+      stream << "\tpointer value: "
+             << int_to_hex<uintptr_t>(
+                    reinterpret_cast<uintptr_t>(form.conv_val_ptr))
+             << "\n";
+    else if (form.conv_name != '%')
+      stream << "\tvalue: " << int_to_hex<__uint128_t>(form.conv_val_raw)
+             << "\n";
+  }
+}
+} // anonymous namespace
+
+void FormatSectionMatcher::explainError(testutils::StreamWrapper &stream) {
+  stream << "expected format section: ";
+  display(stream, expected);
+  stream << '\n';
+  stream << "actual format section  : ";
+  display(stream, actual);
+  stream << '\n';
+}
+
+} // namespace testing
+} // namespace printf_core
+} // namespace __llvm_libc

diff  --git a/libc/utils/UnitTest/PrintfMatcher.h b/libc/utils/UnitTest/PrintfMatcher.h
new file mode 100644
index 0000000000000..004b9721f1d36
--- /dev/null
+++ b/libc/utils/UnitTest/PrintfMatcher.h
@@ -0,0 +1,46 @@
+//===-- PrintfMatcher.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_UTILS_UNITTEST_PRINTF_MATCHER_H
+#define LLVM_LIBC_UTILS_UNITTEST_PRINTF_MATCHER_H
+
+#include "src/stdio/printf_core/core_structs.h"
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+
+namespace __llvm_libc {
+namespace printf_core {
+namespace testing {
+
+class FormatSectionMatcher
+    : public __llvm_libc::testing::Matcher<FormatSection> {
+  FormatSection expected;
+  FormatSection actual;
+
+public:
+  FormatSectionMatcher(FormatSection expectedValue) : expected(expectedValue) {}
+
+  bool match(FormatSection actualValue);
+
+  void explainError(testutils::StreamWrapper &stream) override;
+};
+
+} // namespace testing
+} // namespace printf_core
+} // namespace __llvm_libc
+
+#define EXPECT_FORMAT_EQ(expected, actual)                                     \
+  EXPECT_THAT(actual, __llvm_libc::printf_core::testing::FormatSectionMatcher( \
+                          expected))
+
+#define ASSERT_FORMAT_EQ(expected, actual)                                     \
+  ASSERT_THAT(actual, __llvm_libc::printf_core::testing::FormatSectionMatcher( \
+                          expected))
+
+#endif // LLVM_LIBC_UTILS_UNITTEST_PRINTF_MATCHER_H

diff  --git a/libc/utils/UnitTest/StringUtils.h b/libc/utils/UnitTest/StringUtils.h
new file mode 100644
index 0000000000000..3d5577c8077ec
--- /dev/null
+++ b/libc/utils/UnitTest/StringUtils.h
@@ -0,0 +1,34 @@
+//===-- String utils for matchers -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_UTILS_UNITTEST_SIMPLE_STRING_CONV_H
+#define LLVM_LIBC_UTILS_UNITTEST_SIMPLE_STRING_CONV_H
+
+#include "src/__support/CPP/TypeTraits.h"
+
+#include <string>
+
+namespace __llvm_libc {
+
+// Return the first N hex digits of an integer as a string in upper case.
+template <typename T>
+cpp::EnableIfType<cpp::IsIntegral<T>::Value, std::string>
+int_to_hex(T X, size_t Length = sizeof(T) * 2) {
+  std::string s(Length, '0');
+
+  for (auto it = s.rbegin(), end = s.rend(); it != end; ++it, X >>= 4) {
+    unsigned char Mod = static_cast<unsigned char>(X) & 15;
+    *it = (Mod < 10 ? '0' + Mod : 'a' + Mod - 10);
+  }
+
+  return s;
+}
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_UNITTEST_SIMPLE_STRING_CONV_H


        


More information about the libc-commits mailing list