cachelib/allocator/datastruct/DList-inl.h (162 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace facebook {
namespace cachelib {
/* Linked list implemenation */
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::linkAtHead(T& node) noexcept {
XDCHECK_NE(reinterpret_cast<uintptr_t>(&node),
reinterpret_cast<uintptr_t>(head_));
setNext(node, head_);
setPrev(node, nullptr);
// fix the prev ptr of head
if (head_ != nullptr) {
setPrev(*head_, &node);
}
head_ = &node;
if (tail_ == nullptr) {
tail_ = &node;
}
size_++;
}
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::linkAtTail(T& node) noexcept {
XDCHECK_NE(reinterpret_cast<uintptr_t>(&node),
reinterpret_cast<uintptr_t>(tail_));
setNext(node, nullptr);
setPrev(node, tail_);
// Fix the next ptr for tail
if (tail_ != nullptr) {
setNext(*tail_, &node);
}
tail_ = &node;
if (head_ == nullptr) {
head_ = &node;
}
size_++;
}
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::insertBefore(T& nextNode, T& node) noexcept {
XDCHECK_NE(reinterpret_cast<uintptr_t>(&nextNode),
reinterpret_cast<uintptr_t>(&node));
XDCHECK(getNext(node) == nullptr);
XDCHECK(getPrev(node) == nullptr);
auto* const prev = getPrev(nextNode);
XDCHECK_NE(reinterpret_cast<uintptr_t>(prev),
reinterpret_cast<uintptr_t>(&node));
setPrev(node, prev);
if (prev != nullptr) {
setNext(*prev, &node);
} else {
head_ = &node;
}
setPrev(nextNode, &node);
setNext(node, &nextNode);
size_++;
}
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::unlink(const T& node) noexcept {
XDCHECK_GT(size_, 0u);
// fix head_ and tail_ if the node is either of that.
auto* const prev = getPrev(node);
auto* const next = getNext(node);
if (&node == head_) {
head_ = next;
}
if (&node == tail_) {
tail_ = prev;
}
// fix the next and prev ptrs of the node before and after us.
if (prev != nullptr) {
setNextFrom(*prev, node);
}
if (next != nullptr) {
setPrevFrom(*next, node);
}
size_--;
}
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::remove(T& node) noexcept {
unlink(node);
setNext(node, nullptr);
setPrev(node, nullptr);
}
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::replace(T& oldNode, T& newNode) noexcept {
// Update head and tail links if needed
if (&oldNode == head_) {
head_ = &newNode;
}
if (&oldNode == tail_) {
tail_ = &newNode;
}
// Make the previous and next nodes point to the new node
auto* const prev = getPrev(oldNode);
auto* const next = getNext(oldNode);
if (prev != nullptr) {
setNext(*prev, &newNode);
}
if (next != nullptr) {
setPrev(*next, &newNode);
}
// Make the new node point to the previous and next nodes
setPrev(newNode, prev);
setNext(newNode, next);
// Cleanup the old node
setPrev(oldNode, nullptr);
setNext(oldNode, nullptr);
}
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::moveToHead(T& node) noexcept {
if (&node == head_) {
return;
}
unlink(node);
linkAtHead(node);
}
/* Iterator Implementation */
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::Iterator::goForward() noexcept {
if (dir_ == Direction::FROM_TAIL) {
curr_ = dlist_->getPrev(*curr_);
} else {
curr_ = dlist_->getNext(*curr_);
}
}
template <typename T, DListHook<T> T::*HookPtr>
void DList<T, HookPtr>::Iterator::goBackward() noexcept {
if (dir_ == Direction::FROM_TAIL) {
curr_ = dlist_->getNext(*curr_);
} else {
curr_ = dlist_->getPrev(*curr_);
}
}
template <typename T, DListHook<T> T::*HookPtr>
typename DList<T, HookPtr>::Iterator&
DList<T, HookPtr>::Iterator::operator++() noexcept {
XDCHECK(curr_ != nullptr);
if (curr_ != nullptr) {
goForward();
}
return *this;
}
template <typename T, DListHook<T> T::*HookPtr>
typename DList<T, HookPtr>::Iterator&
DList<T, HookPtr>::Iterator::operator--() noexcept {
XDCHECK(curr_ != nullptr);
if (curr_ != nullptr) {
goBackward();
}
return *this;
}
template <typename T, DListHook<T> T::*HookPtr>
typename DList<T, HookPtr>::Iterator DList<T, HookPtr>::begin() const noexcept {
return DList<T, HookPtr>::Iterator(head_, Iterator::Direction::FROM_HEAD,
*this);
}
template <typename T, DListHook<T> T::*HookPtr>
typename DList<T, HookPtr>::Iterator DList<T, HookPtr>::rbegin()
const noexcept {
return DList<T, HookPtr>::Iterator(tail_, Iterator::Direction::FROM_TAIL,
*this);
}
template <typename T, DListHook<T> T::*HookPtr>
typename DList<T, HookPtr>::Iterator DList<T, HookPtr>::end() const noexcept {
return DList<T, HookPtr>::Iterator(nullptr, Iterator::Direction::FROM_HEAD,
*this);
}
template <typename T, DListHook<T> T::*HookPtr>
typename DList<T, HookPtr>::Iterator DList<T, HookPtr>::rend() const noexcept {
return DList<T, HookPtr>::Iterator(nullptr, Iterator::Direction::FROM_TAIL,
*this);
}
} // namespace cachelib
} // namespace facebook