Skip to content

Commit 4e94319

Browse files
goldvitalycopybara-github
authored andcommitted
Move hashtable_control_bytes tests into their own file.
PiperOrigin-RevId: 761584628 Change-Id: I39fd1b14ae71754e058e23305761a2e43d9e989a
1 parent 282d0fc commit 4e94319

File tree

4 files changed

+282
-220
lines changed

4 files changed

+282
-220
lines changed

absl/container/BUILD.bazel

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,19 @@ cc_library(
698698
],
699699
)
700700

701+
cc_test(
702+
name = "hashtable_control_bytes_test",
703+
srcs = ["internal/hashtable_control_bytes_test.cc"],
704+
copts = ABSL_TEST_COPTS,
705+
linkopts = ABSL_DEFAULT_LINKOPTS,
706+
deps = [
707+
":hashtable_control_bytes",
708+
"//absl/base:config",
709+
"@googletest//:gtest",
710+
"@googletest//:gtest_main",
711+
],
712+
)
713+
701714
cc_library(
702715
name = "raw_hash_set_resize_impl",
703716
hdrs = ["internal/raw_hash_set_resize_impl.h"],

absl/container/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,19 @@ absl_cc_library(
762762
absl::endian
763763
)
764764

765+
absl_cc_test(
766+
NAME
767+
hashtable_control_bytes_test
768+
SRCS
769+
"internal/hashtable_control_bytes_test.cc"
770+
COPTS
771+
${ABSL_TEST_COPTS}
772+
DEPS
773+
absl::config
774+
absl::hashtable_control_bytes
775+
GTest::gmock_main
776+
)
777+
765778
# Internal-only target, do not depend on directly.
766779
absl_cc_library(
767780
NAME
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// Copyright 2025 The Abseil Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "absl/container/internal/hashtable_control_bytes.h"
16+
17+
#include <array>
18+
#include <cstddef>
19+
#include <cstdint>
20+
#include <vector>
21+
22+
#include "gmock/gmock.h"
23+
#include "gtest/gtest.h"
24+
#include "absl/base/config.h"
25+
26+
namespace absl {
27+
ABSL_NAMESPACE_BEGIN
28+
namespace container_internal {
29+
namespace {
30+
31+
using ::testing::ElementsAre;
32+
using ::testing::ElementsAreArray;
33+
34+
// Convenience function to static cast to ctrl_t.
35+
ctrl_t CtrlT(int i) { return static_cast<ctrl_t>(i); }
36+
37+
TEST(BitMask, Smoke) {
38+
EXPECT_FALSE((BitMask<uint8_t, 8>(0)));
39+
EXPECT_TRUE((BitMask<uint8_t, 8>(5)));
40+
41+
EXPECT_THAT((BitMask<uint8_t, 8>(0)), ElementsAre());
42+
EXPECT_THAT((BitMask<uint8_t, 8>(0x1)), ElementsAre(0));
43+
EXPECT_THAT((BitMask<uint8_t, 8>(0x2)), ElementsAre(1));
44+
EXPECT_THAT((BitMask<uint8_t, 8>(0x3)), ElementsAre(0, 1));
45+
EXPECT_THAT((BitMask<uint8_t, 8>(0x4)), ElementsAre(2));
46+
EXPECT_THAT((BitMask<uint8_t, 8>(0x5)), ElementsAre(0, 2));
47+
EXPECT_THAT((BitMask<uint8_t, 8>(0x55)), ElementsAre(0, 2, 4, 6));
48+
EXPECT_THAT((BitMask<uint8_t, 8>(0xAA)), ElementsAre(1, 3, 5, 7));
49+
}
50+
51+
TEST(BitMask, WithShift_MatchPortable) {
52+
// See the non-SSE version of Group for details on what this math is for.
53+
uint64_t ctrl = 0x1716151413121110;
54+
uint64_t hash = 0x12;
55+
constexpr uint64_t lsbs = 0x0101010101010101ULL;
56+
auto x = ctrl ^ (lsbs * hash);
57+
uint64_t mask = (x - lsbs) & ~x & kMsbs8Bytes;
58+
EXPECT_EQ(0x0000000080800000, mask);
59+
60+
BitMask<uint64_t, 8, 3> b(mask);
61+
EXPECT_EQ(*b, 2);
62+
}
63+
64+
constexpr uint64_t kSome8BytesMask = /* */ 0x8000808080008000ULL;
65+
constexpr uint64_t kSome8BytesMaskAllOnes = 0xff00ffffff00ff00ULL;
66+
constexpr auto kSome8BytesMaskBits = std::array<int, 5>{1, 3, 4, 5, 7};
67+
68+
TEST(BitMask, WithShift_FullMask) {
69+
EXPECT_THAT((BitMask<uint64_t, 8, 3>(kMsbs8Bytes)),
70+
ElementsAre(0, 1, 2, 3, 4, 5, 6, 7));
71+
EXPECT_THAT(
72+
(BitMask<uint64_t, 8, 3, /*NullifyBitsOnIteration=*/true>(kMsbs8Bytes)),
73+
ElementsAre(0, 1, 2, 3, 4, 5, 6, 7));
74+
EXPECT_THAT(
75+
(BitMask<uint64_t, 8, 3, /*NullifyBitsOnIteration=*/true>(~uint64_t{0})),
76+
ElementsAre(0, 1, 2, 3, 4, 5, 6, 7));
77+
}
78+
79+
TEST(BitMask, WithShift_EmptyMask) {
80+
EXPECT_THAT((BitMask<uint64_t, 8, 3>(0)), ElementsAre());
81+
EXPECT_THAT((BitMask<uint64_t, 8, 3, /*NullifyBitsOnIteration=*/true>(0)),
82+
ElementsAre());
83+
}
84+
85+
TEST(BitMask, WithShift_SomeMask) {
86+
EXPECT_THAT((BitMask<uint64_t, 8, 3>(kSome8BytesMask)),
87+
ElementsAreArray(kSome8BytesMaskBits));
88+
EXPECT_THAT((BitMask<uint64_t, 8, 3, /*NullifyBitsOnIteration=*/true>(
89+
kSome8BytesMask)),
90+
ElementsAreArray(kSome8BytesMaskBits));
91+
EXPECT_THAT((BitMask<uint64_t, 8, 3, /*NullifyBitsOnIteration=*/true>(
92+
kSome8BytesMaskAllOnes)),
93+
ElementsAreArray(kSome8BytesMaskBits));
94+
}
95+
96+
TEST(BitMask, WithShift_SomeMaskExtraBitsForNullify) {
97+
// Verify that adding extra bits into non zero bytes is fine.
98+
uint64_t extra_bits = 77;
99+
for (int i = 0; i < 100; ++i) {
100+
// Add extra bits, but keep zero bytes untouched.
101+
uint64_t extra_mask = extra_bits & kSome8BytesMaskAllOnes;
102+
EXPECT_THAT((BitMask<uint64_t, 8, 3, /*NullifyBitsOnIteration=*/true>(
103+
kSome8BytesMask | extra_mask)),
104+
ElementsAreArray(kSome8BytesMaskBits))
105+
<< i << " " << extra_mask;
106+
extra_bits = (extra_bits + 1) * 3;
107+
}
108+
}
109+
110+
TEST(BitMask, LeadingTrailing) {
111+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).LeadingZeros()), 3);
112+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).TrailingZeros()), 6);
113+
114+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).LeadingZeros()), 15);
115+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).TrailingZeros()), 0);
116+
117+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).LeadingZeros()), 0);
118+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).TrailingZeros()), 15);
119+
120+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).LeadingZeros()), 3);
121+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).TrailingZeros()), 1);
122+
123+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).LeadingZeros()), 7);
124+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).TrailingZeros()), 0);
125+
126+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).LeadingZeros()), 0);
127+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).TrailingZeros()), 7);
128+
}
129+
130+
TEST(Group, Match) {
131+
if (Group::kWidth == 16) {
132+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
133+
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
134+
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
135+
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
136+
EXPECT_THAT(Group{group}.Match(0), ElementsAre());
137+
EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
138+
EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
139+
EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
140+
EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
141+
} else if (Group::kWidth == 8) {
142+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
143+
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
144+
ctrl_t::kSentinel, CtrlT(1)};
145+
EXPECT_THAT(Group{group}.Match(0), ElementsAre());
146+
EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
147+
EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
148+
} else {
149+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
150+
}
151+
}
152+
153+
TEST(Group, MaskEmpty) {
154+
if (Group::kWidth == 16) {
155+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
156+
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
157+
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
158+
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
159+
EXPECT_THAT(Group{group}.MaskEmpty().LowestBitSet(), 0);
160+
EXPECT_THAT(Group{group}.MaskEmpty().HighestBitSet(), 4);
161+
} else if (Group::kWidth == 8) {
162+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
163+
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
164+
ctrl_t::kSentinel, CtrlT(1)};
165+
EXPECT_THAT(Group{group}.MaskEmpty().LowestBitSet(), 0);
166+
EXPECT_THAT(Group{group}.MaskEmpty().HighestBitSet(), 0);
167+
} else {
168+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
169+
}
170+
}
171+
172+
TEST(Group, MaskFull) {
173+
if (Group::kWidth == 16) {
174+
ctrl_t group[] = {
175+
ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
176+
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
177+
CtrlT(7), CtrlT(5), ctrl_t::kDeleted, CtrlT(1),
178+
CtrlT(1), ctrl_t::kSentinel, ctrl_t::kEmpty, CtrlT(1)};
179+
EXPECT_THAT(Group{group}.MaskFull(),
180+
ElementsAre(1, 3, 5, 7, 8, 9, 11, 12, 15));
181+
} else if (Group::kWidth == 8) {
182+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty,
183+
ctrl_t::kDeleted, CtrlT(2), ctrl_t::kSentinel,
184+
ctrl_t::kSentinel, CtrlT(1)};
185+
EXPECT_THAT(Group{group}.MaskFull(), ElementsAre(1, 4, 7));
186+
} else {
187+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
188+
}
189+
}
190+
191+
TEST(Group, MaskNonFull) {
192+
if (Group::kWidth == 16) {
193+
ctrl_t group[] = {
194+
ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
195+
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
196+
CtrlT(7), CtrlT(5), ctrl_t::kDeleted, CtrlT(1),
197+
CtrlT(1), ctrl_t::kSentinel, ctrl_t::kEmpty, CtrlT(1)};
198+
EXPECT_THAT(Group{group}.MaskNonFull(),
199+
ElementsAre(0, 2, 4, 6, 10, 13, 14));
200+
} else if (Group::kWidth == 8) {
201+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty,
202+
ctrl_t::kDeleted, CtrlT(2), ctrl_t::kSentinel,
203+
ctrl_t::kSentinel, CtrlT(1)};
204+
EXPECT_THAT(Group{group}.MaskNonFull(), ElementsAre(0, 2, 3, 5, 6));
205+
} else {
206+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
207+
}
208+
}
209+
210+
TEST(Group, MaskEmptyOrDeleted) {
211+
if (Group::kWidth == 16) {
212+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty, CtrlT(3),
213+
ctrl_t::kDeleted, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
214+
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
215+
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
216+
EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().LowestBitSet(), 0);
217+
EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().HighestBitSet(), 4);
218+
} else if (Group::kWidth == 8) {
219+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
220+
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
221+
ctrl_t::kSentinel, CtrlT(1)};
222+
EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().LowestBitSet(), 0);
223+
EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().HighestBitSet(), 3);
224+
} else {
225+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
226+
}
227+
}
228+
229+
TEST(Group, CountLeadingEmptyOrDeleted) {
230+
const std::vector<ctrl_t> empty_examples = {ctrl_t::kEmpty, ctrl_t::kDeleted};
231+
const std::vector<ctrl_t> full_examples = {
232+
CtrlT(0), CtrlT(1), CtrlT(2), CtrlT(3),
233+
CtrlT(5), CtrlT(9), CtrlT(127), ctrl_t::kSentinel};
234+
235+
for (ctrl_t empty : empty_examples) {
236+
std::vector<ctrl_t> e(Group::kWidth, empty);
237+
EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted());
238+
for (ctrl_t full : full_examples) {
239+
for (size_t i = 0; i != Group::kWidth; ++i) {
240+
std::vector<ctrl_t> f(Group::kWidth, empty);
241+
f[i] = full;
242+
EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted());
243+
}
244+
std::vector<ctrl_t> f(Group::kWidth, empty);
245+
f[Group::kWidth * 2 / 3] = full;
246+
f[Group::kWidth / 2] = full;
247+
EXPECT_EQ(Group::kWidth / 2,
248+
Group{f.data()}.CountLeadingEmptyOrDeleted());
249+
}
250+
}
251+
}
252+
253+
} // namespace
254+
} // namespace container_internal
255+
ABSL_NAMESPACE_END
256+
} // namespace absl

0 commit comments

Comments
 (0)