Skip to content

Commit 819c14a

Browse files
Replace custom ctz() with C++20 std::countr_zero
Remove the custom platform-specific count trailing zeros implementation and use std::countr_zero from the <bit> header instead. This also enables upgrading shared_len() and get_shared_length() to full constexpr, since std::countr_zero is constexpr on all platforms including MSVC. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 61773d1 commit 819c14a

3 files changed

Lines changed: 21 additions & 59 deletions

File tree

art_common.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ template <typename T>
187187
const T t = v | (v - 1u); // t gets v's least significant 0 bits set to 1
188188
// Next set to 1 the most significant bit to change, set to 0 the
189189
// least significant ones, and add the necessary 1 bits.
190-
const T w = (t + 1) | (((~t & -~t) - 1) >> (ctz(v) + 1));
190+
const T w = (t + 1) | (((~t & -~t) - 1) >> (std::countr_zero(v) + 1));
191191
return w;
192192
}
193193

art_internal_impl.hpp

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <algorithm>
99
#include <array>
10+
#include <bit>
1011
#include <cstddef>
1112
#include <cstdint>
1213
#include <iostream>
@@ -578,7 +579,7 @@ union [[nodiscard]] key_prefix_snapshot {
578579
/// Return the number of bytes in common between this key_prefix and a view of
579580
/// the next 64-bits (max) of some the shifted_key from which any leading
580581
/// bytes already matched by the traversal path have been discarded.
581-
[[nodiscard]] UNODB_DETAIL_CONSTEXPR_NOT_MSVC auto get_shared_length(
582+
[[nodiscard]] constexpr auto get_shared_length(
582583
std::uint64_t shifted_key_u64) const noexcept {
583584
return shared_len(shifted_key_u64, u64, length());
584585
}
@@ -591,14 +592,13 @@ union [[nodiscard]] key_prefix_snapshot {
591592

592593
private:
593594
// from unodb::detail::key_prefix
594-
[[nodiscard, gnu::const]] static UNODB_DETAIL_CONSTEXPR_NOT_MSVC unsigned
595-
shared_len(std::uint64_t k1, std::uint64_t k2,
596-
unsigned clamp_byte_pos) noexcept {
595+
[[nodiscard, gnu::const]] static constexpr unsigned shared_len(
596+
std::uint64_t k1, std::uint64_t k2, unsigned clamp_byte_pos) noexcept {
597597
UNODB_DETAIL_ASSERT(clamp_byte_pos < 8);
598598

599599
const auto diff = k1 ^ k2;
600600
const auto clamped = diff | (1ULL << (clamp_byte_pos * 8U));
601-
return static_cast<unsigned>(detail::ctz(clamped) >> 3U);
601+
return static_cast<unsigned>(std::countr_zero(clamped) >> 3U);
602602
}
603603
}; // class key_prefix_snapshot
604604
static_assert(sizeof(key_prefix_snapshot) == sizeof(std::uint64_t));
@@ -733,7 +733,7 @@ union [[nodiscard]] key_prefix {
733733

734734
const auto diff = k1 ^ k2;
735735
const auto clamped = diff | (1ULL << (clamp_byte_pos * 8U));
736-
return static_cast<unsigned>(detail::ctz(clamped) >> 3U);
736+
return static_cast<unsigned>(std::countr_zero(clamped) >> 3U);
737737
}
738738

739739
[[nodiscard, gnu::const]] static constexpr std::uint64_t make_u64(
@@ -1480,7 +1480,7 @@ class basic_inode_4 : public basic_inode_4_parent<ArtPolicy> {
14801480
const auto bit_field =
14811481
static_cast<unsigned>(_mm_movemask_epi8(matching_key_positions)) & mask;
14821482
if (bit_field != 0) {
1483-
const auto i = detail::ctz(bit_field);
1483+
const auto i = static_cast<std::uint8_t>(std::countr_zero(bit_field));
14841484
return std::make_pair(
14851485
i, static_cast<critical_section_policy<node_ptr> *>(&children[i]));
14861486
}
@@ -1502,7 +1502,7 @@ class basic_inode_4 : public basic_inode_4_parent<ArtPolicy> {
15021502

15031503
if (masked_pos == 0) return parent_class::child_not_found;
15041504

1505-
const auto i = static_cast<unsigned>(detail::ctz(masked_pos) >> 3U);
1505+
const auto i = static_cast<unsigned>(std::countr_zero(masked_pos) >> 3U);
15061506
return std::make_pair(
15071507
i, static_cast<critical_section_policy<node_ptr> *>(&children[i]));
15081508
#else // #ifdef UNODB_DETAIL_X86_64
@@ -1902,7 +1902,7 @@ class basic_inode_16 : public basic_inode_16_parent<ArtPolicy> {
19021902
const auto bit_field =
19031903
static_cast<unsigned>(_mm_movemask_epi8(matching_key_positions)) & mask;
19041904
if (bit_field != 0) {
1905-
const auto i = detail::ctz(bit_field);
1905+
const auto i = static_cast<std::uint8_t>(std::countr_zero(bit_field));
19061906
return std::make_pair(
19071907
i, static_cast<critical_section_policy<node_ptr> *>(&children[i]));
19081908
}
@@ -1924,7 +1924,7 @@ class basic_inode_16 : public basic_inode_16_parent<ArtPolicy> {
19241924

19251925
if (masked_pos == 0) return parent_class::child_not_found;
19261926

1927-
const auto i = static_cast<unsigned>(detail::ctz(masked_pos) >> 2U);
1927+
const auto i = static_cast<unsigned>(std::countr_zero(masked_pos) >> 2U);
19281928
return std::make_pair(
19291929
i, static_cast<critical_section_policy<node_ptr> *>(&children[i]));
19301930
#else
@@ -2053,9 +2053,10 @@ class basic_inode_16 : public basic_inode_16_parent<ArtPolicy> {
20532053
const auto mask = (1U << children_count_) - 1;
20542054
const auto bit_field =
20552055
static_cast<unsigned>(_mm_movemask_epi8(lesser_key_positions)) & mask;
2056-
const auto result = (bit_field != 0)
2057-
? detail::ctz(bit_field)
2058-
: static_cast<std::uint8_t>(children_count_);
2056+
const auto result =
2057+
(bit_field != 0)
2058+
? static_cast<std::uint8_t>(std::countr_zero(bit_field))
2059+
: static_cast<std::uint8_t>(children_count_);
20592060
#else
20602061
// This is also the best current ARM implementation
20612062
const auto result = static_cast<std::uint8_t>(
@@ -2233,7 +2234,8 @@ class basic_inode_48 : public basic_inode_48_parent<ArtPolicy> {
22332234
const auto cmp_mask =
22342235
static_cast<std::uint64_t>(_mm_movemask_epi8(vec_cmp));
22352236
if (cmp_mask != 0) {
2236-
i = (i << 1U) + (((detail::ctz(cmp_mask)) + 1U) >> 1U);
2237+
i = (i << 1U) +
2238+
((static_cast<unsigned>(std::countr_zero(cmp_mask)) + 1U) >> 1U);
22372239
break;
22382240
}
22392241
i += 4;
@@ -2265,7 +2267,8 @@ class basic_inode_48 : public basic_inode_48_parent<ArtPolicy> {
22652267
_mm256_permute4x64_epi64(interleaved_vec_cmp, 0b11'01'10'00);
22662268
const auto cmp_mask =
22672269
static_cast<std::uint64_t>(_mm256_movemask_epi8(vec_cmp));
2268-
i = (i << 2U) + (detail::ctz(cmp_mask) >> 1U);
2270+
i = (i << 2U) +
2271+
(static_cast<unsigned>(std::countr_zero(cmp_mask)) >> 1U);
22692272
break;
22702273
}
22712274
i += 4;
@@ -2298,7 +2301,8 @@ class basic_inode_48 : public basic_inode_48_parent<ArtPolicy> {
22982301
// NOLINTNEXTLINE(misc-const-correctness)
22992302
vget_lane_u64(vreinterpret_u64_u8(narrowed_cmp), 0);
23002303
if (scalar_pos != 0) {
2301-
i = (i << 1U) + static_cast<unsigned>(detail::ctz(scalar_pos) >> 3U);
2304+
i = (i << 1U) +
2305+
static_cast<unsigned>(std::countr_zero(scalar_pos) >> 3U);
23022306
break;
23032307
}
23042308
i += 4;

portability_builtins.hpp

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -54,48 +54,6 @@ template <typename T>
5454
#endif // UNODB_DETAIL_MSVC
5555
}
5656

57-
/// Return the number of trailing zero bits in \a x.
58-
/// \pre Argument may not be zero
59-
template <typename T>
60-
[[nodiscard, gnu::const]] UNODB_DETAIL_CONSTEXPR_NOT_MSVC std::uint8_t ctz(
61-
T x) noexcept {
62-
static_assert(std::is_same_v<unsigned, T> ||
63-
// NOLINTNEXTLINE(google-runtime-int)
64-
std::is_same_v<unsigned long, T> || // NOLINT(runtime/int)
65-
// NOLINTNEXTLINE(google-runtime-int)
66-
std::is_same_v<unsigned long long, T>); // NOLINT(runtime/int)
67-
68-
if constexpr (std::is_same_v<unsigned, T>) {
69-
#ifndef UNODB_DETAIL_MSVC
70-
return static_cast<std::uint8_t>(__builtin_ctz(x));
71-
#else
72-
unsigned long result; // NOLINT(runtime/int)
73-
_BitScanForward(&result, x);
74-
return static_cast<std::uint8_t>(result);
75-
#endif
76-
}
77-
// NOLINTNEXTLINE(google-runtime-int)
78-
if constexpr (std::is_same_v<unsigned long, T>) { // NOLINT(runtime/int)
79-
#ifndef UNODB_DETAIL_MSVC
80-
return static_cast<std::uint8_t>(__builtin_ctzl(x));
81-
#else
82-
unsigned long result; // NOLINT(runtime/int)
83-
_BitScanForward(&result, x);
84-
return static_cast<std::uint8_t>(result);
85-
#endif
86-
}
87-
// NOLINTNEXTLINE(google-runtime-int)
88-
if constexpr (std::is_same_v<unsigned long long, T>) { // NOLINT(runtime/int)
89-
#ifndef UNODB_DETAIL_MSVC
90-
return static_cast<std::uint8_t>(__builtin_ctzll(x));
91-
#else
92-
unsigned long result; // NOLINT(runtime/int)
93-
_BitScanForward64(&result, x);
94-
return static_cast<std::uint8_t>(result);
95-
#endif
96-
} // cppcheck-suppress missingReturn
97-
}
98-
9957
/// Return the number of one bits in \a x.
10058
[[nodiscard, gnu::const]] UNODB_DETAIL_CONSTEXPR_NOT_MSVC unsigned popcount(
10159
unsigned x) noexcept {

0 commit comments

Comments
 (0)