blob: eb92de630effce9fcef945483172cd348923887e [file] [log] [blame]
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_JFR_UTILITIES_JFRDOUBLYLINKEDLIST_HPP
#define SHARE_JFR_UTILITIES_JFRDOUBLYLINKEDLIST_HPP
#include "memory/allocation.hpp"
template <typename T>
class JfrDoublyLinkedList {
private:
T* _head;
T* _tail;
size_t _count;
T** list_head() { return &_head; }
T** list_tail() { return &_tail; }
public:
typedef T Node;
JfrDoublyLinkedList() : _head(NULL), _tail(NULL), _count(0) {}
T* head() const { return _head; }
T* tail() const { return _tail; }
size_t count() const { return _count; }
T* clear(bool return_tail = false);
T* remove(T* const node);
void prepend(T* const node);
void append(T* const node);
void append_list(T* const head_node, T* const tail_node, size_t count);
bool in_list(const T* const target_node) const;
bool locate(const T* start_node, const T* const target_node) const;
};
template <typename T>
inline void JfrDoublyLinkedList<T>::prepend(T* const node) {
assert(node != NULL, "invariant");
node->set_prev(NULL);
assert(!in_list(node), "already in list error");
T** lh = list_head();
if (*lh != NULL) {
(*lh)->set_prev(node);
node->set_next(*lh);
} else {
T** lt = list_tail();
assert(*lt == NULL, "invariant");
*lt = node;
node->set_next(NULL);
assert(tail() == node, "invariant");
assert(node->next() == NULL, "invariant");
}
*lh = node;
++_count;
assert(head() == node, "head error");
assert(in_list(node), "not in list error");
assert(node->prev() == NULL, "invariant");
}
template <typename T>
void JfrDoublyLinkedList<T>::append(T* const node) {
assert(node != NULL, "invariant");
node->set_next(NULL);
assert(!in_list(node), "already in list error");
T** lt = list_tail();
if (*lt != NULL) {
// already an existing tail
node->set_prev(*lt);
(*lt)->set_next(node);
} else {
// if no tail, also update head
assert(*lt == NULL, "invariant");
T** lh = list_head();
assert(*lh == NULL, "invariant");
node->set_prev(NULL);
*lh = node;
assert(head() == node, "invariant");
}
*lt = node;
++_count;
assert(tail() == node, "invariant");
assert(in_list(node), "not in list error");
assert(node->next() == NULL, "invariant");
}
template <typename T>
T* JfrDoublyLinkedList<T>::remove(T* const node) {
assert(node != NULL, "invariant");
assert(in_list(node), "invariant");
T* const prev = (T*)node->prev();
T* const next = (T*)node->next();
if (prev == NULL) {
assert(head() == node, "head error");
if (next != NULL) {
next->set_prev(NULL);
} else {
assert(next == NULL, "invariant");
assert(tail() == node, "tail error");
T** lt = list_tail();
*lt = NULL;
assert(tail() == NULL, "invariant");
}
T** lh = list_head();
*lh = next;
assert(head() == next, "invariant");
} else {
assert(prev != NULL, "invariant");
if (next == NULL) {
assert(tail() == node, "tail error");
T** lt = list_tail();
*lt = prev;
assert(tail() == prev, "invariant");
} else {
next->set_prev(prev);
}
prev->set_next(next);
}
--_count;
assert(_count >= 0, "invariant");
assert(!in_list(node), "still in list error");
return node;
}
template <typename T>
T* JfrDoublyLinkedList<T>::clear(bool return_tail /* false */) {
T* const node = return_tail ? tail() : head();
T** l = list_head();
*l = NULL;
l = list_tail();
*l = NULL;
_count = 0;
assert(head() == NULL, "invariant");
assert(tail() == NULL, "invariant");
return node;
}
template <typename T>
bool JfrDoublyLinkedList<T>::locate(const T* node, const T* const target) const {
assert(target != NULL, "invariant");
while (node != NULL) {
if (node == target) {
return true;
}
node = (T*)node->next();
}
return false;
}
template <typename T>
bool JfrDoublyLinkedList<T>::in_list(const T* const target) const {
assert(target != NULL, "invariant");
return locate(head(), target);
}
template <typename T>
inline void validate_count_param(T* node, size_t count_param) {
assert(node != NULL, "invariant");
size_t count = 0;
while (node) {
++count;
node = (T*)node->next();
}
assert(count_param == count, "invariant");
}
template <typename T>
void JfrDoublyLinkedList<T>::append_list(T* const head_node, T* const tail_node, size_t count) {
assert(head_node != NULL, "invariant");
assert(!in_list(head_node), "already in list error");
assert(tail_node != NULL, "invariant");
assert(!in_list(tail_node), "already in list error");
assert(tail_node->next() == NULL, "invariant");
// ensure passed in list nodes are connected
assert(locate(head_node, tail_node), "invariant");
T** lt = list_tail();
if (*lt != NULL) {
head_node->set_prev(*lt);
(*lt)->set_next(head_node);
} else {
// no head
assert(*lt == NULL, "invariant");
T** lh = list_head();
assert(*lh == NULL, "invariant");
head_node->set_prev(NULL);
*lh = head_node;
assert(head() == head_node, "invariant");
}
*lt = tail_node;
const T* node = head_node;
debug_only(validate_count_param(node, count);)
_count += count;
assert(tail() == tail_node, "invariant");
assert(in_list(tail_node), "not in list error");
assert(in_list(head_node), "not in list error");
}
#endif // SHARE_JFR_UTILITIES_JFRDOUBLYLINKEDLIST_HPP