[libc-commits] [PATCH] D131301: [libc] add int to string for extended bases

Michael Jones via Phabricator via libc-commits libc-commits at lists.llvm.org
Fri Aug 5 15:07:17 PDT 2022


michaelrj created this revision.
michaelrj added a reviewer: lntue.
Herald added subscribers: libc-commits, ecnelises, tschuett.
Herald added projects: libc-project, All.
michaelrj requested review of this revision.

The default IntegerToString class only supports base 10, this patch adds
a version which supports any base between 2 and 36 inclusive. This will
be used in an upcoming patch.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131301

Files:
  libc/src/__support/integer_to_string.h


Index: libc/src/__support/integer_to_string.h
===================================================================
--- libc/src/__support/integer_to_string.h
+++ libc/src/__support/integer_to_string.h
@@ -79,6 +79,93 @@
   return IntegerToString<T>(val);
 }
 
+constexpr size_t floor_log_2(size_t num) {
+  size_t i = 0;
+  for (; num > 1; num /= 2) {
+    ++i;
+  }
+  return i;
+}
+
+template <typename T, size_t Base> class BaseIntegerToString {
+public:
+  // We size the string buffer using an approximation algorithm:
+  //
+  //   digits = ceil(bits in T / bits per digit)
+  //
+  // The value of bits per digit here is floor(log_2(base)). This gives us an
+  // upper bound on the size, where we always round up to the size of a power of
+  // 2 base. This is exact for all powers of two, but is rather loose for all
+  // other numbers. For this reason, it's better to use the default
+  // IntegerToString class if the base is known to be 10.
+
+  static constexpr size_t BITS_PER_DIGIT = floor_log_2(Base);
+  static constexpr size_t BUFSIZE =
+      ((sizeof(T) * 8 + (BITS_PER_DIGIT - 1)) / BITS_PER_DIGIT) +
+      (cpp::is_signed<T>() ? 1 : 0);
+
+private:
+  static_assert(
+      2 <= Base <= 36,
+      "BaseIntegerToString can only have a base between 2 and 36 inclusive.");
+  static_assert(cpp::is_integral_v<T>,
+                "BaseIntegerToString can only be used with integral types.");
+
+  using UnsignedType = cpp::make_unsigned_t<T>;
+
+  char strbuf[BUFSIZE] = {'\0'};
+  size_t len = 0;
+
+  constexpr void convert(T val, size_t conv_base, bool lowercase) {
+    // We can convert to larger bases, since they take fewer digits, but smaller
+    // bases may not fit.
+    if (conv_base < Base) {
+      return;
+    }
+
+    const char a = lowercase ? 'a' : 'A';
+
+    UnsignedType uval = val < 0 ? UnsignedType(-val) : UnsignedType(val);
+
+    size_t buffptr = BUFSIZE;
+    if (uval == 0) {
+      strbuf[buffptr - 1] = '0';
+      --buffptr;
+    } else {
+      for (; uval > 0; --buffptr, uval /= conv_base) {
+        UnsignedType digit = (uval % conv_base);
+        strbuf[buffptr - 1] = digit < 10 ? digit + '0' : digit + a - 10;
+      }
+    }
+    len = BUFSIZE - buffptr;
+
+    if (val < 0) {
+      // This branch will be taken only for negative signed values.
+      ++len;
+      strbuf[BUFSIZE - len] = '-';
+    }
+  }
+
+public:
+  constexpr explicit BaseIntegerToString() { ; }
+  constexpr explicit BaseIntegerToString(T val) { convert(val, Base, true); }
+
+  constexpr explicit BaseIntegerToString(T val, size_t conv_base) {
+    convert(val, conv_base, true);
+  }
+
+  constexpr explicit BaseIntegerToString(T val, size_t conv_base,
+                                         bool lowercase) {
+    convert(val, conv_base, lowercase);
+  }
+
+  cpp::StringView str() const {
+    return cpp::StringView(strbuf + BUFSIZE - len, len);
+  }
+
+  operator cpp::StringView() const { return str(); }
+};
+
 } // namespace __llvm_libc
 
 #endif // LLVM_LIBC_SRC_SUPPORT_INTEGER_TO_STRING_H


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D131301.450428.patch
Type: text/x-patch
Size: 3042 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/libc-commits/attachments/20220805/334bb8b2/attachment.bin>


More information about the libc-commits mailing list