Skip to content
This repository was archived by the owner on Mar 24, 2026. It is now read-only.

Commit 1595c9d

Browse files
Privacy Sandbox Teamcopybara-github
authored andcommitted
feat: Copy doubly_linked_list.h from B&A to common repo
This is part of a series of CLs to copy the KV BYOS clients from B&A to common repo. Bug: 411430242 Change-Id: Ibf5948d67d95908041848e9290a881af505e61a9 GitOrigin-RevId: 73cc5961241d7f0970b151e6dee0b8017caf430f
1 parent 2216c59 commit 1595c9d

3 files changed

Lines changed: 276 additions & 0 deletions

File tree

src/util/containers/BUILD.bazel

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright 2025 Google LLC
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+
# http://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+
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
16+
17+
cc_library(
18+
name = "doubly_linked_list",
19+
hdrs = [
20+
"doubly_linked_list.h",
21+
],
22+
visibility = ["//visibility:public"],
23+
deps = [
24+
"//src/core/event",
25+
],
26+
)
27+
28+
cc_test(
29+
name = "doubly_linked_list_test",
30+
size = "small",
31+
srcs = [
32+
"doubly_linked_list_test.cc",
33+
],
34+
deps = [
35+
":doubly_linked_list",
36+
"@com_google_googletest//:gtest_main",
37+
],
38+
)
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Copyright 2025 Google LLC
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+
// http://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+
#ifndef SRC_UTIL_CONTAINERS_DOUBLY_LINKED_LIST_H_
16+
#define SRC_UTIL_CONTAINERS_DOUBLY_LINKED_LIST_H_
17+
18+
#include <memory>
19+
#include <utility>
20+
21+
#include "src/core/event/event.h"
22+
#include "src/logger/request_context_logger.h"
23+
24+
namespace privacy_sandbox::server_common {
25+
26+
// K-anon data stored in the DLL.
27+
template <typename KeyT, typename ValueT>
28+
struct CacheHashData {
29+
KeyT key;
30+
ValueT value;
31+
std::unique_ptr<Event> timer_event = nullptr;
32+
};
33+
34+
// DLL Node.
35+
template <typename KeyT, typename ValueT>
36+
struct Node {
37+
std::unique_ptr<CacheHashData<KeyT, ValueT>> data;
38+
Node* next = nullptr;
39+
Node* prev = nullptr;
40+
41+
explicit Node(
42+
std::unique_ptr<CacheHashData<KeyT, ValueT>> cache_hash_data = nullptr)
43+
: data(std::move(cache_hash_data)) {}
44+
};
45+
46+
template <typename KeyT, typename ValueT>
47+
class DoublyLinkedList {
48+
public:
49+
DoublyLinkedList()
50+
: head_(std::make_unique<Node<KeyT, ValueT>>()),
51+
tail_(std::make_unique<Node<KeyT, ValueT>>()) {
52+
head_->next = tail_.get();
53+
tail_->prev = head_.get();
54+
}
55+
56+
~DoublyLinkedList() {
57+
auto* cur = head_->next;
58+
while (cur != tail_.get()) {
59+
auto* tmp_next = cur->next;
60+
cur->next = nullptr;
61+
cur->prev = nullptr;
62+
delete cur;
63+
cur = tmp_next;
64+
}
65+
head_->next = nullptr;
66+
tail_->prev = nullptr;
67+
}
68+
69+
Node<KeyT, ValueT>* InsertAtFront(
70+
std::unique_ptr<CacheHashData<KeyT, ValueT>> cache_hash_data) {
71+
auto node =
72+
std::make_unique<Node<KeyT, ValueT>>(std::move(cache_hash_data));
73+
auto* node_ptr = node.release();
74+
node_ptr->next = head_->next;
75+
node_ptr->prev = head_.get();
76+
// Head's next is guaranteed to be present since we use a sentinel for tail
77+
// as well.
78+
head_->next->prev = node_ptr;
79+
head_->next = node_ptr;
80+
return node_ptr;
81+
}
82+
83+
void MoveToFront(Node<KeyT, ValueT>* node) {
84+
// Disconnect from neighbors
85+
node->prev->next = node->next;
86+
node->next->prev = node->prev;
87+
88+
// Link at front
89+
node->next = head_->next;
90+
node->prev = head_.get();
91+
head_->next->prev = node;
92+
head_->next = node;
93+
}
94+
95+
void Remove(Node<KeyT, ValueT>* node) {
96+
PS_VLOG(5) << __func__ << ": Removing node";
97+
auto node_to_clean = std::unique_ptr<Node<KeyT, ValueT>>(node);
98+
node->prev->next = node->next;
99+
node->next->prev = node->prev;
100+
node_to_clean.reset();
101+
}
102+
103+
Node<KeyT, ValueT>* Head() {
104+
// Head and tail are always sentinels, so if they point at each other that
105+
// implies an empty list.
106+
if (head_->next == tail_.get()) {
107+
return nullptr;
108+
}
109+
110+
return head_->next;
111+
}
112+
113+
Node<KeyT, ValueT>* Tail() {
114+
// Head and tail are always sentinels, so if they point at each other that
115+
// implies an empty list.
116+
if (tail_->prev == head_.get()) {
117+
return nullptr;
118+
}
119+
120+
return tail_->prev;
121+
}
122+
123+
private:
124+
std::unique_ptr<Node<KeyT, ValueT>> head_;
125+
std::unique_ptr<Node<KeyT, ValueT>> tail_;
126+
};
127+
128+
} // namespace privacy_sandbox::server_common
129+
130+
#endif // SRC_UTIL_CONTAINERS_DOUBLY_LINKED_LIST_H_
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "src/util/containers/doubly_linked_list.h"
18+
19+
#include <string>
20+
#include <string_view>
21+
22+
#include "include/gtest/gtest.h"
23+
24+
namespace privacy_sandbox::server_common {
25+
namespace {
26+
27+
constexpr std::string_view kTestKey1 = "key1";
28+
constexpr std::string_view kTestKey2 = "key2";
29+
30+
class DoublyLinkedListTest : public ::testing::Test {
31+
protected:
32+
DoublyLinkedList<std::string, std::string> dll_;
33+
34+
std::unique_ptr<CacheHashData<std::string, std::string>> TestHashData1() {
35+
return std::make_unique<CacheHashData<std::string, std::string>>(
36+
CacheHashData<std::string, std::string>{
37+
.key = std::string(kTestKey1),
38+
});
39+
}
40+
41+
std::unique_ptr<CacheHashData<std::string, std::string>> TestHashData2() {
42+
return std::make_unique<CacheHashData<std::string, std::string>>(
43+
CacheHashData<std::string, std::string>{
44+
.key = std::string(kTestKey2),
45+
});
46+
}
47+
};
48+
49+
TEST_F(DoublyLinkedListTest, EmptyList) { EXPECT_EQ(dll_.Tail(), nullptr); }
50+
51+
TEST_F(DoublyLinkedListTest, CanAddNode) {
52+
auto* node = dll_.InsertAtFront(TestHashData1());
53+
ASSERT_NE(node, nullptr);
54+
EXPECT_EQ(node->data->key, kTestKey1);
55+
56+
ASSERT_NE(dll_.Tail(), nullptr);
57+
EXPECT_EQ(dll_.Tail()->data->key, kTestKey1);
58+
}
59+
60+
TEST_F(DoublyLinkedListTest, CanRemoveNode) {
61+
ASSERT_EQ(dll_.Tail(), nullptr) << "Expected no item in an empty list";
62+
63+
auto* node = dll_.InsertAtFront(TestHashData1());
64+
ASSERT_NE(node, nullptr);
65+
EXPECT_EQ(node->data->key, kTestKey1);
66+
67+
ASSERT_NE(dll_.Tail(), nullptr);
68+
dll_.Remove(dll_.Tail());
69+
70+
EXPECT_EQ(dll_.Tail(), nullptr) << "Expected no item in an empty list";
71+
}
72+
73+
TEST_F(DoublyLinkedListTest, InsertsInOrder) {
74+
ASSERT_EQ(dll_.Tail(), nullptr) << "Expected no item in an empty list";
75+
76+
dll_.InsertAtFront(TestHashData1());
77+
EXPECT_EQ(dll_.Tail()->data->key, kTestKey1);
78+
79+
dll_.InsertAtFront(TestHashData2());
80+
// Tail node still has key1.
81+
EXPECT_EQ(dll_.Tail()->data->key, kTestKey1);
82+
83+
// Remove the tail node and the new tail node should now contain key2.
84+
dll_.Remove(dll_.Tail());
85+
EXPECT_EQ(dll_.Tail()->data->key, kTestKey2);
86+
87+
// Remove tail node again and there should be no node remaining in the list.
88+
dll_.Remove(dll_.Tail());
89+
EXPECT_EQ(dll_.Tail(), nullptr);
90+
}
91+
92+
TEST_F(DoublyLinkedListTest, MovesToFront) {
93+
ASSERT_EQ(dll_.Tail(), nullptr) << "Expected no item in an empty list";
94+
95+
dll_.InsertAtFront(TestHashData1());
96+
EXPECT_EQ(dll_.Tail()->data->key, kTestKey1);
97+
98+
dll_.InsertAtFront(TestHashData2());
99+
// Tail node still has key1.
100+
EXPECT_EQ(dll_.Tail()->data->key, kTestKey1);
101+
102+
// Move the tail node to front.
103+
dll_.MoveToFront(dll_.Tail());
104+
EXPECT_EQ(dll_.Tail()->data->key, kTestKey2);
105+
}
106+
107+
} // namespace
108+
} // namespace privacy_sandbox::server_common

0 commit comments

Comments
 (0)