Skip to content

Commit

Permalink
IntrusiveDList: Add list operations.
Browse files Browse the repository at this point in the history
  • Loading branch information
SolidWallOfCode committed Sep 19, 2023
1 parent 572542d commit 6def36a
Show file tree
Hide file tree
Showing 4 changed files with 419 additions and 2 deletions.
278 changes: 277 additions & 1 deletion code/include/swoc/IntrusiveDList.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,101 @@ template <typename L> class IntrusiveDList {
/// @return @a limit
iterator erase(const iterator &start, const iterator &limit);

/** The nth element of the list.
*
* @param n Index of target element.
* @return An iterator to the element, an empty iterator if @a n is too large.
*
* @note This is linear in @a n, use with caution.
*/
iterator nth(unsigned n);

/** Remove and return an initial subsequence.
*
* @param n Number of elements.
* @return The list of elements.
*
* If @a n is more than the length of the list, the entire list is returned.
*/
self_type take_prefix(unsigned n);

/** Remove and return an initial subsequence.
*
* @param n Number of elements.
* @return The list of elements.
*
* If @a n is more than the length of the list @a this is unchanged and an empty list returned.
*/
self_type split_prefix(unsigned n);

/** Remove and return a trailing subsequence.
*
* @param n Number of elements.
* @return The list of elements.
*
* If @a n is more than the length of the list, the entire list is returned.
*/
self_type take_suffix(unsigned n);

/** Remove and return a trailing subsequence.
*
* @param n Number of elements.
* @return The list of elements.
*
* If @a n is more than the length of the list, @a this is unchanged and an empty list returned.
*/
self_type split_suffix(unsigned n);

/** Append a list.
*
* @param src List to append.
* @return @a this
*
* The elements are removed from @a src, which becomes empty.
*/
self_type & append(self_type & src);

/** Prepend a list.
*
* @param src List to prepend.
* @return @a this
*
* The elements are removed from @a src, which becomes empty.
*/
self_type & prepend(self_type & src);

/** Splice in a list after an existing element.
*
* @param target Element in the lsit.
* @param src List to splice.
* @return @a this
*/
self_type &insert_after(value_type *target, self_type & src);

/** Splice in a list before an existing element.
*
* @param target Element in the lsit.
* @param src List to splice.
* @return @a this
*/
self_type &insert_before(value_type *target, self_type & src);

/** Splice in a list after an existing element.
*
* @param target Element in the lsit.
* @param src List to splice.
* @return @a this
*/
self_type &insert_after(iterator const &target, self_type & src);

/** Splice in a list before an existing element.
*
* @param target Element in the lsit.
* @param src List to splice.
* @return @a this
*/
self_type &insert_before(iterator const &target, self_type & src);

/// Remove all elements.
/// @note @b No memory management is done!
/// @return This container.
Expand Down Expand Up @@ -744,6 +839,58 @@ IntrusiveDList<L>::insert_before(iterator const &target, value_type *v) -> self_
return this->insert_before(target._v, v);
}

template <typename L>
auto
IntrusiveDList<L>::insert_after(value_type *target, self_type & src) -> self_type & {
if (target && src._count > 0) {
if (_tail == target) {
this->append(src);
} else { // invariant - @a target is not tail therefore has a successor.
L::next_ptr(src._tail) = L::next_ptr(target); // link @a src tail to @a target successor
L::prev_ptr(L::next_ptr(src._tail)) = src._tail;

L::prev_ptr(src._head) = target; // link @a src head to @target
L::next_ptr(target) = src._head;

_count += src._count;
src.clear();
}
}
return *this;
}

template <typename L>
auto
IntrusiveDList<L>::insert_after(iterator const &target, self_type & src) -> self_type & {
return this->insert_after(target._v, src);
}

template <typename L>
auto
IntrusiveDList<L>::insert_before(value_type *target, self_type & src) -> self_type & {
if (target && src._count > 0) {
if (_head == target) {
this->prepend(src);
} else { // invariant - @a target is not head and therefore has a predecessor.
L::prev_ptr(src._head) = L::prev_ptr(target); // link @a src head to @a target predecessor
L::next_ptr(L::prev_ptr(target)) = src._head;

L::next_ptr(src._tail) = target; // link @a src tail to @a target
L::prev_ptr(target) = src._tail;

_count += src._count;
src.clear();
}
}
return *this;
}

template <typename L>
auto
IntrusiveDList<L>::insert_before(iterator const &target, self_type & src) -> self_type & {
return this->insert_before(target._v, src);
}

template <typename L>
auto
IntrusiveDList<L>::erase(value_type *v) -> value_type * {
Expand Down Expand Up @@ -887,11 +1034,140 @@ IntrusiveDList<L>::clear() -> self_type & {
return *this;
}

template <typename L>
auto
IntrusiveDList<L>::nth(unsigned int n) -> iterator {
if (n >= _count) {
return {};
}

value_type * spot;
if (n < _count/2) { // closer to head, count from there..
spot = _head;
while (n-- > 0) { spot = L::next_ptr(spot); }
} else { // count from tail
spot = _tail;
unsigned idx = _count - 1;
while (idx-- > n) { spot = L::prev_ptr(spot); }
}
return iterator_for(spot);
}

template <typename L>
auto
IntrusiveDList<L>::take_prefix(unsigned int n) -> self_type {

if (n == 0) {
return {};
}

if (_count <= n) {
return std::move(*this);
}

// Invariant - there is at least one element that will not be taken.

self_type zret;
value_type * spot = this->nth(n); // new @a head for @a this

zret._count = n;
zret._head = _head;
zret._tail = L::prev_ptr(spot);
L::next_ptr(zret._tail) = nullptr;

_count -= n;
_head = spot;
L::prev_ptr(_head) = nullptr;

return zret;
}

template <typename L>
auto
IntrusiveDList<L>::split_prefix(unsigned int n) -> self_type {
if (n <= _count) {
return this->take_prefix(n);
}
return {};
}

template <typename L>
auto
IntrusiveDList<L>::take_suffix(unsigned int n) -> self_type {

if (n == 0) {
return {};
}

if (_count <= n) {
return std::move(*this);
}

// Invariant - there is at least one element that will not be taken.

self_type zret;
value_type * spot = this->nth(_count - n - 1); // new @a tail for @a this

zret._count = n;
zret._head = L::next_ptr(spot);
L::prev_ptr(zret._head) = nullptr;
zret._tail = _tail;

_count -= n;
_tail = spot;
L::next_ptr(_tail) = nullptr;

return zret;
}

template <typename L>
auto
IntrusiveDList<L>::split_suffix(unsigned int n) -> self_type {
if (n <= _count) {
return this->take_suffix(n);
}
return {};
}

template <typename L>
auto
IntrusiveDList<L>::prepend(IntrusiveDList::self_type &src) -> self_type & {
if (src._count > 0) {
if (_count == 0) {
*this = std::move(src);
} else {
L::prev_ptr(_head) = src._tail;
L::next_ptr(src._tail) = _head;
_count += src._count;
_head = src._head;
src.clear();
}
}
return *this;
}

template <typename L>
auto
IntrusiveDList<L>::append(IntrusiveDList::self_type &src) -> self_type & {
if (src._count > 0) {
if (_count == 0) {
*this = std::move(src);
} else {
L::next_ptr(_tail) = src._head;
L::prev_ptr(src._head) = _tail;
_count += src._count;
_tail = src._tail;
src.clear();
}
}
return *this;
}

namespace detail {
/// @cond INTERNAL_DETAIL
// Make @c apply more convenient by allowing the function to take a reference type or pointer type
// to the container elements. The pointer type is the base, plus a shim to convert from a reference
// type functor to a pointer pointer type. The complex return type definition forces only one, but
// type functor to a pointer type. The complex return type definition forces only one, but
// not both, to be valid for a particular functor. This also must be done via free functions and not
// method overloads because the compiler forces a match up of method definitions and declarations
// before any template instantiation.
Expand Down
2 changes: 1 addition & 1 deletion example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ if (CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(ex_flat_space PRIVATE -Wall -Wextra -Werror)
endif()

add_executable(ex_diskstats ex_diskstats.cc)
add_executable(ex_diskstats ex_diskstats.cc ../image/include/swoc/IntrusiveDList.h)
target_link_libraries(ex_diskstats PUBLIC libswoc-static)
if (CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(ex_diskstats PRIVATE -Wall -Wextra -Werror)
Expand Down
Loading

0 comments on commit 6def36a

Please sign in to comment.