|
1 | 1 | #pragma once
|
2 | 2 |
|
| 3 | +#include <stdx/compiler.hpp> |
3 | 4 | #include <stdx/concepts.hpp>
|
4 | 5 | #include <stdx/type_traits.hpp>
|
5 | 6 | #include <stdx/utility.hpp>
|
@@ -340,37 +341,78 @@ template <typename To, typename From> constexpr auto bit_unpack(From arg) {
|
340 | 341 | }
|
341 | 342 |
|
342 | 343 | namespace detail {
|
343 |
| -template <typename T, std::size_t Bit> |
344 |
| -constexpr auto mask_bits() |
345 |
| - -> std::enable_if_t<Bit <= std::numeric_limits<T>::digits, T> { |
346 |
| - if constexpr (Bit == std::numeric_limits<T>::digits) { |
347 |
| - return std::numeric_limits<T>::max(); |
348 |
| - } else { |
349 |
| - return static_cast<T>(T{1} << Bit) - T{1}; |
| 344 | +template <typename T> struct num_digits_t { |
| 345 | + constexpr static std::size_t value = std::numeric_limits<T>::digits; |
| 346 | +}; |
| 347 | +template <typename T> constexpr auto num_digits_v = num_digits_t<T>::value; |
| 348 | +template <typename T, std::size_t N> struct num_digits_t<std::array<T, N>> { |
| 349 | + constexpr static std::size_t value = (N * num_digits_v<T>); |
| 350 | +}; |
| 351 | + |
| 352 | +template <typename T> struct mask_bits_t { |
| 353 | + static_assert(std::is_unsigned_v<T>, |
| 354 | + "bit_mask must be used with unsigned types"); |
| 355 | + |
| 356 | + constexpr auto operator()(std::size_t bit) const -> T { |
| 357 | + if (bit == num_digits_v<T>) { |
| 358 | + return std::numeric_limits<T>::max(); |
| 359 | + } |
| 360 | + return static_cast<T>(T{1} << bit) - T{1}; |
350 | 361 | }
|
351 |
| -} |
| 362 | +}; |
352 | 363 |
|
353 |
| -template <typename T> constexpr auto mask_bits(std::size_t Bit) -> T { |
354 |
| - if (Bit == std::numeric_limits<T>::digits) { |
355 |
| - return std::numeric_limits<T>::max(); |
| 364 | +template <typename T, std::size_t N> struct mask_bits_t<std::array<T, N>> { |
| 365 | + constexpr auto operator()(std::size_t bit) const -> std::array<T, N> { |
| 366 | + constexpr auto t_bits = num_digits_v<T>; |
| 367 | + auto const quot = bit / t_bits; |
| 368 | + auto const rem = bit % t_bits; |
| 369 | + |
| 370 | + std::array<T, N> r{}; |
| 371 | + T *p = std::data(r); |
| 372 | + for (auto i = std::size_t{}; i < quot; ++i) { |
| 373 | + *p++ = mask_bits_t<T>{}(t_bits); |
| 374 | + } |
| 375 | + if (rem != 0) { |
| 376 | + *p = mask_bits_t<T>{}(rem); |
| 377 | + } |
| 378 | + return r; |
356 | 379 | }
|
357 |
| - return static_cast<T>(T{1} << Bit) - T{1}; |
358 |
| -} |
| 380 | +}; |
| 381 | + |
| 382 | +template <typename T> struct bitmask_subtract { |
| 383 | + static_assert(std::is_unsigned_v<T>, |
| 384 | + "bit_mask must be used with unsigned types"); |
| 385 | + constexpr auto operator()(T x, T y) const -> T { return x ^ y; } |
| 386 | +}; |
| 387 | + |
| 388 | +template <typename T, std::size_t N> struct bitmask_subtract<std::array<T, N>> { |
| 389 | + constexpr auto operator()(std::array<T, N> const &x, |
| 390 | + std::array<T, N> const &y) const |
| 391 | + -> std::array<T, N> { |
| 392 | + std::array<T, N> r{}; |
| 393 | + for (auto i = std::size_t{}; i < N; ++i) { |
| 394 | + r[i] = bitmask_subtract<T>{}(x[i], y[i]); |
| 395 | + } |
| 396 | + return r; |
| 397 | + } |
| 398 | +}; |
359 | 399 | } // namespace detail
|
360 | 400 |
|
361 |
| -template <typename T, std::size_t Msb = std::numeric_limits<T>::digits - 1, |
| 401 | +template <typename T, std::size_t Msb = detail::num_digits_v<T> - 1, |
362 | 402 | std::size_t Lsb = 0>
|
363 |
| -[[nodiscard]] constexpr auto bit_mask() noexcept |
364 |
| - -> std::enable_if_t<std::is_unsigned_v<T> and Msb >= Lsb, T> { |
365 |
| - static_assert(Msb < std::numeric_limits<T>::digits); |
366 |
| - return detail::mask_bits<T, Msb + 1>() - detail::mask_bits<T, Lsb>(); |
| 403 | +[[nodiscard]] CONSTEVAL auto bit_mask() noexcept -> T { |
| 404 | + static_assert(Msb < detail::num_digits_v<T>, |
| 405 | + "bit_mask requested exceeds the range of the type"); |
| 406 | + static_assert(Msb >= Lsb, "bit_mask range is invalid"); |
| 407 | + return detail::bitmask_subtract<T>{}(detail::mask_bits_t<T>{}(Msb + 1), |
| 408 | + detail::mask_bits_t<T>{}(Lsb)); |
367 | 409 | }
|
368 | 410 |
|
369 | 411 | template <typename T>
|
370 | 412 | [[nodiscard]] constexpr auto bit_mask(std::size_t Msb,
|
371 |
| - std::size_t Lsb = 0) noexcept |
372 |
| - -> std::enable_if_t<std::is_unsigned_v<T>, T> { |
373 |
| - return detail::mask_bits<T>(Msb + 1) - detail::mask_bits<T>(Lsb); |
| 413 | + std::size_t Lsb = 0) noexcept -> T { |
| 414 | + return detail::bitmask_subtract<T>{}(detail::mask_bits_t<T>{}(Msb + 1), |
| 415 | + detail::mask_bits_t<T>{}(Lsb)); |
374 | 416 | }
|
375 | 417 |
|
376 | 418 | template <typename T> constexpr auto bit_size() -> std::size_t {
|
|
0 commit comments