[libc-commits] [libc] [llvm] [libc] Add user defined literals to ease testing (PR #81267)
Guillaume Chatelet via libc-commits
libc-commits at lists.llvm.org
Tue Feb 13 01:03:49 PST 2024
================
@@ -0,0 +1,166 @@
+//===-- User literal for unsigned integers ----------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+// This set of user defined literals allows uniform constructions of constants
+// up to 256 bits and also help with unit tests (EXPECT_EQ requires the same
+// type for LHS and RHS).
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_INTEGER_LITERALS_H
+#define LLVM_LIBC_SRC___SUPPORT_INTEGER_LITERALS_H
+
+#include "src/__support/CPP/limits.h" // CHAR_BIT
+#include "src/__support/UInt128.h" // UInt128
+#include "src/__support/macros/attributes.h" // LIBC_INLINE
+#include <stddef.h> // size_t
+#include <stdint.h> // uintxx_t
+
+namespace LIBC_NAMESPACE {
+
+LIBC_INLINE constexpr uint8_t operator""_u8(unsigned long long value) {
+ return value;
+}
+
+LIBC_INLINE constexpr uint16_t operator""_u16(unsigned long long value) {
+ return value;
+}
+
+LIBC_INLINE constexpr uint32_t operator""_u32(unsigned long long value) {
+ return value;
+}
+
+LIBC_INLINE constexpr uint64_t operator""_u64(unsigned long long value) {
+ return value;
+}
+
+namespace internal {
+
+// Creates a T by reading digits from an array.
+template <typename T>
+LIBC_INLINE constexpr T accumulate(int base, const uint8_t *digits,
+ size_t size) {
+ T value{};
+ for (; size; ++digits, --size) {
+ value *= base;
+ value += *digits;
+ }
+ return value;
+}
+
+// A static buffer to hold the digits for a T.
+template <typename T, int base> struct DigitBuffer {
+ static_assert(base == 2 || base == 10 || base == 16);
+ // Base 2: one char provides exactly one bit.
+ // Base 10: one char provides between three and four bits.
+ // Base 16: one char provides exactly four bits.
+ LIBC_INLINE_VAR static constexpr size_t BITS_PER_DIGIT = base == 2 ? 1
+ : base == 10 ? 3
+ : base == 16 ? 4
+ : 0;
+ LIBC_INLINE_VAR static constexpr size_t MAX_DIGITS =
+ sizeof(T) * CHAR_BIT / BITS_PER_DIGIT;
+
+ uint8_t digits[MAX_DIGITS] = {};
+ size_t size = 0;
+
+ constexpr DigitBuffer(const char *str) {
+ for (; *str != '\0'; ++str)
+ push(*str);
+ }
+
+ // Returns the digit for a particular character.
+ // Returns 255 if the character is invalid.
+ LIBC_INLINE static constexpr uint8_t get_digit_value(const char c) {
+ const auto to_lower = [](char c) { return c | 32; };
+ const auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
+ const auto is_alpha = [](char c) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
+ };
+ if (is_digit(c))
+ return c - '0';
+ if (base > 10 && is_alpha(c))
+ return to_lower(c) - 'a' + 10;
+ return 255;
+ }
+
+ // Adds a single character to this buffer.
+ LIBC_INLINE constexpr void push(char c) {
+ if (c == '\'')
+ return; // ' is valid but not taken into account.
+ const uint8_t value = get_digit_value(c);
+ if (value == 255 || size >= MAX_DIGITS)
+ __builtin_unreachable(); // invalid or too many characters.
+ digits[size] = value;
+ ++size;
+ }
+};
+
+// Generic implementation for native types (including __uint128_t or ExtInt
+// where available).
+template <typename T> struct Parser {
+ template <int base> LIBC_INLINE static constexpr T parse(const char *str) {
+ const DigitBuffer<T, base> buffer(str);
+ return accumulate<T>(base, buffer.digits, buffer.size);
+ }
+};
+
+// Specialization for cpp::UInt<N>.
+// Because this code runs at compile time we try to make it efficient. For
+// binary and hexadecimal formats we read digits by chunks of 64 bits and
+// produce the BigInt internal representation direcly. For decimal numbers we go
+// the slow path and use slower BigInt arithmetic.
+template <size_t N> struct Parser<LIBC_NAMESPACE::cpp::UInt<N>> {
+ using UIntT = cpp::UInt<N>;
+ template <int base> static constexpr UIntT parse(const char *str) {
+ const DigitBuffer<UIntT, base> buffer(str);
+ if constexpr (base == 10) {
+ // Slow path, we sum and multiply BigInt for each digit.
+ return accumulate<UIntT>(base, buffer.digits, buffer.size);
+ } else {
+ // Fast path, we consume blocks of uint64_t and creates the BigInt's
+ // internal representation directly.
+ using U64ArrayT = cpp::array<uint64_t, UIntT::WORDCOUNT>;
+ U64ArrayT array;
+ size_t size = buffer.size;
+ const uint8_t *digit_ptr = buffer.digits + size;
+ for (size_t i = 0; i < array.size(); ++i) {
+ constexpr size_t U64_DIGITS = DigitBuffer<uint64_t, base>::MAX_DIGITS;
+ const size_t chunk = size > U64_DIGITS ? U64_DIGITS : size;
+ digit_ptr -= chunk;
+ size -= chunk;
+ array[i] = accumulate<uint64_t>(base, digit_ptr, chunk);
+ }
+ return UIntT(array);
+ }
+ }
+};
+
+// Detects the base of the number and dispatches to the right implementation.
+template <typename T>
+LIBC_INLINE constexpr T parse_with_prefix(const char *ptr) {
+ using P = Parser<T>;
+ if (ptr[0] == '0' && ptr[1] == 'x')
+ return P::template parse<16>(ptr + 2);
+ else if (ptr[0] == '0' && ptr[1] == 'b')
+ return P::template parse<2>(ptr + 2);
+ else
+ return P::template parse<10>(ptr);
----------------
gchatelet wrote:
It's vexing parse kicking in.
See "The template disambiguator for dependent names" section in https://en.cppreference.com/w/cpp/language/dependent_name
https://github.com/llvm/llvm-project/pull/81267
More information about the libc-commits
mailing list