Skip to content

Commit 6def36a

Browse files
IntrusiveDList: Add list operations.
1 parent 572542d commit 6def36a

File tree

4 files changed

+419
-2
lines changed

4 files changed

+419
-2
lines changed

code/include/swoc/IntrusiveDList.h

Lines changed: 277 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,101 @@ template <typename L> class IntrusiveDList {
294294
/// @return @a limit
295295
iterator erase(const iterator &start, const iterator &limit);
296296

297+
/** The nth element of the list.
298+
*
299+
* @param n Index of target element.
300+
* @return An iterator to the element, an empty iterator if @a n is too large.
301+
*
302+
* @note This is linear in @a n, use with caution.
303+
*/
304+
iterator nth(unsigned n);
305+
306+
/** Remove and return an initial subsequence.
307+
*
308+
* @param n Number of elements.
309+
* @return The list of elements.
310+
*
311+
* If @a n is more than the length of the list, the entire list is returned.
312+
*/
313+
self_type take_prefix(unsigned n);
314+
315+
/** Remove and return an initial subsequence.
316+
*
317+
* @param n Number of elements.
318+
* @return The list of elements.
319+
*
320+
* If @a n is more than the length of the list @a this is unchanged and an empty list returned.
321+
*/
322+
self_type split_prefix(unsigned n);
323+
324+
/** Remove and return a trailing subsequence.
325+
*
326+
* @param n Number of elements.
327+
* @return The list of elements.
328+
*
329+
* If @a n is more than the length of the list, the entire list is returned.
330+
*/
331+
self_type take_suffix(unsigned n);
332+
333+
/** Remove and return a trailing subsequence.
334+
*
335+
* @param n Number of elements.
336+
* @return The list of elements.
337+
*
338+
* If @a n is more than the length of the list, @a this is unchanged and an empty list returned.
339+
*/
340+
self_type split_suffix(unsigned n);
341+
342+
/** Append a list.
343+
*
344+
* @param src List to append.
345+
* @return @a this
346+
*
347+
* The elements are removed from @a src, which becomes empty.
348+
*/
349+
self_type & append(self_type & src);
350+
351+
/** Prepend a list.
352+
*
353+
* @param src List to prepend.
354+
* @return @a this
355+
*
356+
* The elements are removed from @a src, which becomes empty.
357+
*/
358+
self_type & prepend(self_type & src);
359+
360+
/** Splice in a list after an existing element.
361+
*
362+
* @param target Element in the lsit.
363+
* @param src List to splice.
364+
* @return @a this
365+
*/
366+
self_type &insert_after(value_type *target, self_type & src);
367+
368+
/** Splice in a list before an existing element.
369+
*
370+
* @param target Element in the lsit.
371+
* @param src List to splice.
372+
* @return @a this
373+
*/
374+
self_type &insert_before(value_type *target, self_type & src);
375+
376+
/** Splice in a list after an existing element.
377+
*
378+
* @param target Element in the lsit.
379+
* @param src List to splice.
380+
* @return @a this
381+
*/
382+
self_type &insert_after(iterator const &target, self_type & src);
383+
384+
/** Splice in a list before an existing element.
385+
*
386+
* @param target Element in the lsit.
387+
* @param src List to splice.
388+
* @return @a this
389+
*/
390+
self_type &insert_before(iterator const &target, self_type & src);
391+
297392
/// Remove all elements.
298393
/// @note @b No memory management is done!
299394
/// @return This container.
@@ -744,6 +839,58 @@ IntrusiveDList<L>::insert_before(iterator const &target, value_type *v) -> self_
744839
return this->insert_before(target._v, v);
745840
}
746841

842+
template <typename L>
843+
auto
844+
IntrusiveDList<L>::insert_after(value_type *target, self_type & src) -> self_type & {
845+
if (target && src._count > 0) {
846+
if (_tail == target) {
847+
this->append(src);
848+
} else { // invariant - @a target is not tail therefore has a successor.
849+
L::next_ptr(src._tail) = L::next_ptr(target); // link @a src tail to @a target successor
850+
L::prev_ptr(L::next_ptr(src._tail)) = src._tail;
851+
852+
L::prev_ptr(src._head) = target; // link @a src head to @target
853+
L::next_ptr(target) = src._head;
854+
855+
_count += src._count;
856+
src.clear();
857+
}
858+
}
859+
return *this;
860+
}
861+
862+
template <typename L>
863+
auto
864+
IntrusiveDList<L>::insert_after(iterator const &target, self_type & src) -> self_type & {
865+
return this->insert_after(target._v, src);
866+
}
867+
868+
template <typename L>
869+
auto
870+
IntrusiveDList<L>::insert_before(value_type *target, self_type & src) -> self_type & {
871+
if (target && src._count > 0) {
872+
if (_head == target) {
873+
this->prepend(src);
874+
} else { // invariant - @a target is not head and therefore has a predecessor.
875+
L::prev_ptr(src._head) = L::prev_ptr(target); // link @a src head to @a target predecessor
876+
L::next_ptr(L::prev_ptr(target)) = src._head;
877+
878+
L::next_ptr(src._tail) = target; // link @a src tail to @a target
879+
L::prev_ptr(target) = src._tail;
880+
881+
_count += src._count;
882+
src.clear();
883+
}
884+
}
885+
return *this;
886+
}
887+
888+
template <typename L>
889+
auto
890+
IntrusiveDList<L>::insert_before(iterator const &target, self_type & src) -> self_type & {
891+
return this->insert_before(target._v, src);
892+
}
893+
747894
template <typename L>
748895
auto
749896
IntrusiveDList<L>::erase(value_type *v) -> value_type * {
@@ -887,11 +1034,140 @@ IntrusiveDList<L>::clear() -> self_type & {
8871034
return *this;
8881035
}
8891036

1037+
template <typename L>
1038+
auto
1039+
IntrusiveDList<L>::nth(unsigned int n) -> iterator {
1040+
if (n >= _count) {
1041+
return {};
1042+
}
1043+
1044+
value_type * spot;
1045+
if (n < _count/2) { // closer to head, count from there..
1046+
spot = _head;
1047+
while (n-- > 0) { spot = L::next_ptr(spot); }
1048+
} else { // count from tail
1049+
spot = _tail;
1050+
unsigned idx = _count - 1;
1051+
while (idx-- > n) { spot = L::prev_ptr(spot); }
1052+
}
1053+
return iterator_for(spot);
1054+
}
1055+
1056+
template <typename L>
1057+
auto
1058+
IntrusiveDList<L>::take_prefix(unsigned int n) -> self_type {
1059+
1060+
if (n == 0) {
1061+
return {};
1062+
}
1063+
1064+
if (_count <= n) {
1065+
return std::move(*this);
1066+
}
1067+
1068+
// Invariant - there is at least one element that will not be taken.
1069+
1070+
self_type zret;
1071+
value_type * spot = this->nth(n); // new @a head for @a this
1072+
1073+
zret._count = n;
1074+
zret._head = _head;
1075+
zret._tail = L::prev_ptr(spot);
1076+
L::next_ptr(zret._tail) = nullptr;
1077+
1078+
_count -= n;
1079+
_head = spot;
1080+
L::prev_ptr(_head) = nullptr;
1081+
1082+
return zret;
1083+
}
1084+
1085+
template <typename L>
1086+
auto
1087+
IntrusiveDList<L>::split_prefix(unsigned int n) -> self_type {
1088+
if (n <= _count) {
1089+
return this->take_prefix(n);
1090+
}
1091+
return {};
1092+
}
1093+
1094+
template <typename L>
1095+
auto
1096+
IntrusiveDList<L>::take_suffix(unsigned int n) -> self_type {
1097+
1098+
if (n == 0) {
1099+
return {};
1100+
}
1101+
1102+
if (_count <= n) {
1103+
return std::move(*this);
1104+
}
1105+
1106+
// Invariant - there is at least one element that will not be taken.
1107+
1108+
self_type zret;
1109+
value_type * spot = this->nth(_count - n - 1); // new @a tail for @a this
1110+
1111+
zret._count = n;
1112+
zret._head = L::next_ptr(spot);
1113+
L::prev_ptr(zret._head) = nullptr;
1114+
zret._tail = _tail;
1115+
1116+
_count -= n;
1117+
_tail = spot;
1118+
L::next_ptr(_tail) = nullptr;
1119+
1120+
return zret;
1121+
}
1122+
1123+
template <typename L>
1124+
auto
1125+
IntrusiveDList<L>::split_suffix(unsigned int n) -> self_type {
1126+
if (n <= _count) {
1127+
return this->take_suffix(n);
1128+
}
1129+
return {};
1130+
}
1131+
1132+
template <typename L>
1133+
auto
1134+
IntrusiveDList<L>::prepend(IntrusiveDList::self_type &src) -> self_type & {
1135+
if (src._count > 0) {
1136+
if (_count == 0) {
1137+
*this = std::move(src);
1138+
} else {
1139+
L::prev_ptr(_head) = src._tail;
1140+
L::next_ptr(src._tail) = _head;
1141+
_count += src._count;
1142+
_head = src._head;
1143+
src.clear();
1144+
}
1145+
}
1146+
return *this;
1147+
}
1148+
1149+
template <typename L>
1150+
auto
1151+
IntrusiveDList<L>::append(IntrusiveDList::self_type &src) -> self_type & {
1152+
if (src._count > 0) {
1153+
if (_count == 0) {
1154+
*this = std::move(src);
1155+
} else {
1156+
L::next_ptr(_tail) = src._head;
1157+
L::prev_ptr(src._head) = _tail;
1158+
_count += src._count;
1159+
_tail = src._tail;
1160+
src.clear();
1161+
}
1162+
}
1163+
return *this;
1164+
}
1165+
8901166
namespace detail {
8911167
/// @cond INTERNAL_DETAIL
8921168
// Make @c apply more convenient by allowing the function to take a reference type or pointer type
8931169
// to the container elements. The pointer type is the base, plus a shim to convert from a reference
894-
// type functor to a pointer pointer type. The complex return type definition forces only one, but
1170+
// type functor to a pointer type. The complex return type definition forces only one, but
8951171
// not both, to be valid for a particular functor. This also must be done via free functions and not
8961172
// method overloads because the compiler forces a match up of method definitions and declarations
8971173
// before any template instantiation.

example/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ if (CMAKE_COMPILER_IS_GNUCXX)
3030
target_compile_options(ex_flat_space PRIVATE -Wall -Wextra -Werror)
3131
endif()
3232

33-
add_executable(ex_diskstats ex_diskstats.cc)
33+
add_executable(ex_diskstats ex_diskstats.cc ../image/include/swoc/IntrusiveDList.h)
3434
target_link_libraries(ex_diskstats PUBLIC libswoc-static)
3535
if (CMAKE_COMPILER_IS_GNUCXX)
3636
target_compile_options(ex_diskstats PRIVATE -Wall -Wextra -Werror)

0 commit comments

Comments
 (0)