Rename lists internally.

This commit is contained in:
Egor Pugin 2024-11-23 19:39:22 +03:00
parent 363bf4e00d
commit 6d7ec87ee8
3 changed files with 270 additions and 267 deletions

View File

@ -33,59 +33,57 @@ namespace tesseract {
* Generic list class for singly linked CONS cell lists * Generic list class for singly linked CONS cell lists
**********************************************************************/ **********************************************************************/
template <typename CLASSNAME> template <typename T>
class CLIST { class ConsList {
friend class LINK; friend class Link;
//friend class ITERATOR;
public: public:
/********************************************************************** /**********************************************************************
* CLASS - LINK * CLASS - Link
* *
* Generic link class for singly linked CONS cell lists * Generic link class for singly linked CONS cell lists
* *
* Note: No destructor - elements are assumed to be destroyed EITHER after * Note: No destructor - elements are assumed to be destroyed EITHER after
* they have been extracted from a list OR by the CLIST destructor which * they have been extracted from a list OR by the ConsList destructor which
* walks the list. * walks the list.
**********************************************************************/ **********************************************************************/
struct LINK { struct Link {
LINK *next{}; Link *next{};
CLASSNAME *data{}; T *data{};
LINK() = default; Link() = default;
LINK(const LINK &) = delete; Link(const Link &) = delete;
void operator=(const LINK &) = delete; void operator=(const Link &) = delete;
}; };
/*********************************************************************** /***********************************************************************
* CLASS - ITERATOR * CLASS - Iterator
* *
* Generic iterator class for singly linked lists with embedded * Generic iterator class for singly linked lists with embedded
*links *links
**********************************************************************/ **********************************************************************/
class ITERATOR { class Iterator {
CLIST *list; // List being iterated ConsList *list; // List being iterated
LINK *prev; // prev element Link *prev; // prev element
LINK *current; // current element Link *current; // current element
LINK *next; // next element Link *next; // next element
LINK *cycle_pt; // point we are cycling the list to. Link *cycle_pt; // point we are cycling the list to.
bool ex_current_was_last; // current extracted was end of list bool ex_current_was_last; // current extracted was end of list
bool ex_current_was_cycle_pt; // current extracted was cycle point bool ex_current_was_cycle_pt; // current extracted was cycle point
bool started_cycling; // Have we moved off the start? bool started_cycling; // Have we moved off the start?
/*********************************************************************** /***********************************************************************
* ITERATOR::extract_sublist() * Iterator::extract_sublist()
* *
* This is a private member, used only by CLIST::assign_to_sublist. * This is a private member, used only by ConsList::assign_to_sublist.
* Given another iterator for the same list, extract the links from THIS to * Given another iterator for the same list, extract the links from THIS to
* OTHER inclusive, link them into a new circular list, and return a * OTHER inclusive, link them into a new circular list, and return a
* pointer to the last element. * pointer to the last element.
* (Can't inline this function because it contains a loop) * (Can't inline this function because it contains a loop)
**********************************************************************/ **********************************************************************/
LINK *extract_sublist( // from this current Link *extract_sublist( // from this current
ITERATOR *other_it) { // to other current Iterator *other_it) { // to other current
ITERATOR temp_it = *this; Iterator temp_it = *this;
constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list");
#ifndef NDEBUG #ifndef NDEBUG
@ -93,12 +91,12 @@ public:
constexpr ERRCODE DONT_EXTRACT_DELETED("Can't extract a sublist marked by deleted points"); constexpr ERRCODE DONT_EXTRACT_DELETED("Can't extract a sublist marked by deleted points");
if (list != other_it->list) if (list != other_it->list)
BAD_EXTRACTION_PTS.error("ITERATOR.extract_sublist", ABORT); BAD_EXTRACTION_PTS.error("Iterator.extract_sublist", ABORT);
if (list->empty()) if (list->empty())
EMPTY_LIST.error("ITERATOR::extract_sublist", ABORT); EMPTY_LIST.error("Iterator::extract_sublist", ABORT);
if (!current || !other_it->current) if (!current || !other_it->current)
DONT_EXTRACT_DELETED.error("ITERATOR.extract_sublist", ABORT); DONT_EXTRACT_DELETED.error("Iterator.extract_sublist", ABORT);
#endif #endif
ex_current_was_last = other_it->ex_current_was_last = false; ex_current_was_last = other_it->ex_current_was_last = false;
@ -108,7 +106,7 @@ public:
temp_it.mark_cycle_pt(); temp_it.mark_cycle_pt();
do { // walk sublist do { // walk sublist
if (temp_it.cycled_list()) { // can't find end pt if (temp_it.cycled_list()) { // can't find end pt
BAD_SUBLIST.error("ITERATOR.extract_sublist", ABORT); BAD_SUBLIST.error("Iterator.extract_sublist", ABORT);
} }
if (temp_it.at_last()) { if (temp_it.at_last()) {
@ -146,28 +144,28 @@ public:
} }
public: public:
ITERATOR() { // constructor Iterator() { // constructor
list = nullptr; list = nullptr;
} // unassigned list } // unassigned list
/*********************************************************************** /***********************************************************************
* ITERATOR::ITERATOR * Iterator::Iterator
* *
* CONSTRUCTOR - set iterator to specified list; * CONSTRUCTOR - set iterator to specified list;
**********************************************************************/ **********************************************************************/
ITERATOR( // constructor Iterator( // constructor
CLIST *list_to_iterate) { ConsList *list_to_iterate) {
set_to_list(list_to_iterate); set_to_list(list_to_iterate);
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::set_to_list * Iterator::set_to_list
* *
* (Re-)initialise the iterator to point to the start of the list_to_iterate * (Re-)initialise the iterator to point to the start of the list_to_iterate
* over. * over.
**********************************************************************/ **********************************************************************/
void set_to_list( // change list void set_to_list( // change list
CLIST *list_to_iterate) { ConsList *list_to_iterate) {
list = list_to_iterate; list = list_to_iterate;
prev = list->last; prev = list->last;
current = list->First(); current = list->First();
@ -179,20 +177,20 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::add_after_then_move * Iterator::add_after_then_move
* *
* Add a new element to the list after the current element and move the * Add a new element to the list after the current element and move the
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_after_then_move( // add after current & void add_after_then_move( // add after current &
CLASSNAME *new_data) { T *new_data) {
#ifndef NDEBUG #ifndef NDEBUG
if (!new_data) { if (!new_data) {
BAD_PARAMETER.error("ITERATOR::add_after_then_move", ABORT, "new_data is nullptr"); BAD_PARAMETER.error("Iterator::add_after_then_move", ABORT, "new_data is nullptr");
} }
#endif #endif
auto new_element = new LINK; auto new_element = new Link;
new_element->data = new_data; new_element->data = new_data;
if (list->empty()) { if (list->empty()) {
@ -222,20 +220,20 @@ public:
} // move to new } // move to new
/*********************************************************************** /***********************************************************************
* ITERATOR::add_after_stay_put * Iterator::add_after_stay_put
* *
* Add a new element to the list after the current element but do not move * Add a new element to the list after the current element but do not move
* the iterator to the new element. * the iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_after_stay_put( // add after current & void add_after_stay_put( // add after current &
CLASSNAME *new_data) { T *new_data) {
#ifndef NDEBUG #ifndef NDEBUG
if (!new_data) { if (!new_data) {
BAD_PARAMETER.error("ITERATOR::add_after_stay_put", ABORT, "new_data is nullptr"); BAD_PARAMETER.error("Iterator::add_after_stay_put", ABORT, "new_data is nullptr");
} }
#endif #endif
auto new_element = new LINK; auto new_element = new Link;
new_element->data = new_data; new_element->data = new_data;
if (list->empty()) { if (list->empty()) {
@ -267,20 +265,20 @@ public:
} // stay at current } // stay at current
/*********************************************************************** /***********************************************************************
* ITERATOR::add_before_then_move * Iterator::add_before_then_move
* *
* Add a new element to the list before the current element and move the * Add a new element to the list before the current element and move the
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_before_then_move( // add before current & void add_before_then_move( // add before current &
CLASSNAME *new_data) { T *new_data) {
#ifndef NDEBUG #ifndef NDEBUG
if (!new_data) { if (!new_data) {
BAD_PARAMETER.error("ITERATOR::add_before_then_move", ABORT, "new_data is nullptr"); BAD_PARAMETER.error("Iterator::add_before_then_move", ABORT, "new_data is nullptr");
} }
#endif #endif
auto new_element = new LINK; auto new_element = new Link;
new_element->data = new_data; new_element->data = new_data;
if (list->empty()) { if (list->empty()) {
@ -306,20 +304,20 @@ public:
} // move to new } // move to new
/*********************************************************************** /***********************************************************************
* ITERATOR::add_before_stay_put * Iterator::add_before_stay_put
* *
* Add a new element to the list before the current element but don't move the * Add a new element to the list before the current element but don't move the
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_before_stay_put( // add before current & void add_before_stay_put( // add before current &
CLASSNAME *new_data) { T *new_data) {
#ifndef NDEBUG #ifndef NDEBUG
if (!new_data) { if (!new_data) {
BAD_PARAMETER.error("ITERATOR::add_before_stay_put", ABORT, "new_data is nullptr"); BAD_PARAMETER.error("Iterator::add_before_stay_put", ABORT, "new_data is nullptr");
} }
#endif #endif
auto new_element = new LINK; auto new_element = new Link;
new_element->data = new_data; new_element->data = new_data;
if (list->empty()) { if (list->empty()) {
@ -346,14 +344,14 @@ public:
} // stay at current } // stay at current
/*********************************************************************** /***********************************************************************
* ITERATOR::add_list_after * Iterator::add_list_after
* *
* Insert another list to this list after the current element but don't move * Insert another list to this list after the current element but don't move
*the *the
* iterator. * iterator.
**********************************************************************/ **********************************************************************/
void add_list_after( // add a list & void add_list_after( // add a list &
CLIST *list_to_add) { ConsList *list_to_add) {
if (!list_to_add->empty()) { if (!list_to_add->empty()) {
if (list->empty()) { if (list->empty()) {
list->last = list_to_add->last; list->last = list_to_add->last;
@ -384,14 +382,14 @@ public:
} // stay at current } // stay at current
/*********************************************************************** /***********************************************************************
* ITERATOR::add_list_before * Iterator::add_list_before
* *
* Insert another list to this list before the current element. Move the * Insert another list to this list before the current element. Move the
* iterator to the start of the inserted elements * iterator to the start of the inserted elements
* iterator. * iterator.
**********************************************************************/ **********************************************************************/
void add_list_before( // add a list & void add_list_before( // add a list &
CLIST *list_to_add) { ConsList *list_to_add) {
if (!list_to_add->empty()) { if (!list_to_add->empty()) {
if (list->empty()) { if (list->empty()) {
list->last = list_to_add->last; list->last = list_to_add->last;
@ -419,33 +417,33 @@ public:
} }
} // move to it 1st item } // move to it 1st item
CLASSNAME *data() { // get current data T *data() { // get current data
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ITERATOR::data", ABORT); NO_LIST.error("Iterator::data", ABORT);
} }
#endif #endif
return current->data; return current->data;
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::data_relative * Iterator::data_relative
* *
* Return the data pointer to the element "offset" elements from current. * Return the data pointer to the element "offset" elements from current.
* "offset" must not be less than -1. * "offset" must not be less than -1.
* (This function can't be INLINEd because it contains a loop) * (This function can't be INLINEd because it contains a loop)
**********************************************************************/ **********************************************************************/
CLASSNAME *data_relative( // get data + or - ... T *data_relative( // get data + or - ...
int8_t offset) { // offset from current int8_t offset) { // offset from current
LINK *ptr; Link *ptr;
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
NO_LIST.error("ITERATOR::data_relative", ABORT); NO_LIST.error("Iterator::data_relative", ABORT);
if (list->empty()) if (list->empty())
EMPTY_LIST.error("ITERATOR::data_relative", ABORT); EMPTY_LIST.error("Iterator::data_relative", ABORT);
if (offset < -1) if (offset < -1)
BAD_PARAMETER.error("ITERATOR::data_relative", ABORT, "offset < -l"); BAD_PARAMETER.error("Iterator::data_relative", ABORT, "offset < -l");
#endif #endif
if (offset == -1) { if (offset == -1) {
@ -460,12 +458,12 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::forward * Iterator::forward
* *
* Move the iterator to the next element of the list. * Move the iterator to the next element of the list.
* REMEMBER: ALL LISTS ARE CIRCULAR. * REMEMBER: ALL LISTS ARE CIRCULAR.
**********************************************************************/ **********************************************************************/
CLASSNAME *forward() { T *forward() {
if (list->empty()) { if (list->empty()) {
return nullptr; return nullptr;
} }
@ -488,18 +486,18 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::extract * Iterator::extract
* *
* Do extraction by removing current from the list, deleting the cons cell * Do extraction by removing current from the list, deleting the cons cell
* and returning the data to the caller, but NOT updating the iterator. (So * and returning the data to the caller, but NOT updating the iterator. (So
* that any calling loop can do this.) The iterator's current points to * that any calling loop can do this.) The iterator's current points to
* nullptr. If the data is to be deleted, this is the callers responsibility. * nullptr. If the data is to be deleted, this is the callers responsibility.
**********************************************************************/ **********************************************************************/
CLASSNAME *extract() { T *extract() {
#ifndef NDEBUG #ifndef NDEBUG
if (!current) { // list empty or if (!current) { // list empty or
// element extracted // element extracted
NULL_CURRENT.error("ITERATOR::extract", ABORT); NULL_CURRENT.error("Iterator::extract", ABORT);
} }
#endif #endif
@ -525,12 +523,12 @@ public:
} // remove from list } // remove from list
/*********************************************************************** /***********************************************************************
* ITERATOR::move_to_first() * Iterator::move_to_first()
* *
* Move current so that it is set to the start of the list. * Move current so that it is set to the start of the list.
* Return data just in case anyone wants it. * Return data just in case anyone wants it.
**********************************************************************/ **********************************************************************/
CLASSNAME *move_to_first() { T *move_to_first() {
current = list->First(); current = list->First();
prev = list->last; prev = list->last;
next = current != nullptr ? current->next : nullptr; next = current != nullptr ? current->next : nullptr;
@ -538,13 +536,13 @@ public:
} // go to start of list } // go to start of list
/*********************************************************************** /***********************************************************************
* ITERATOR::move_to_last() * Iterator::move_to_last()
* *
* Move current so that it is set to the end of the list. * Move current so that it is set to the end of the list.
* Return data just in case anyone wants it. * Return data just in case anyone wants it.
* (This function can't be INLINEd because it contains a loop) * (This function can't be INLINEd because it contains a loop)
**********************************************************************/ **********************************************************************/
CLASSNAME *move_to_last() { T *move_to_last() {
while (current != list->last) { while (current != list->last) {
forward(); forward();
} }
@ -557,7 +555,7 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::mark_cycle_pt() * Iterator::mark_cycle_pt()
* *
* Remember the current location so that we can tell whether we've returned * Remember the current location so that we can tell whether we've returned
* to this point later. * to this point later.
@ -569,7 +567,7 @@ public:
void mark_cycle_pt() { void mark_cycle_pt() {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ITERATOR::mark_cycle_pt", ABORT); NO_LIST.error("Iterator::mark_cycle_pt", ABORT);
} }
#endif #endif
@ -590,7 +588,7 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::at_first() * Iterator::at_first()
* *
* Are we at the start of the list? * Are we at the start of the list?
* *
@ -603,7 +601,7 @@ public:
} // Current is first? } // Current is first?
/*********************************************************************** /***********************************************************************
* ITERATOR::at_last() * Iterator::at_last()
* *
* Are we at the end of the list? * Are we at the end of the list?
* *
@ -616,7 +614,7 @@ public:
} // Current is last? } // Current is last?
/*********************************************************************** /***********************************************************************
* ITERATOR::cycled_list() * Iterator::cycled_list()
* *
* Have we returned to the cycle_pt since it was set? * Have we returned to the cycle_pt since it was set?
* *
@ -626,7 +624,7 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::add_to_end * Iterator::add_to_end
* *
* Add a new element to the end of the list without moving the iterator. * Add a new element to the end of the list without moving the iterator.
* This is provided because a single linked list cannot move to the last as * This is provided because a single linked list cannot move to the last as
@ -635,13 +633,13 @@ public:
queues. queues.
**********************************************************************/ **********************************************************************/
void add_to_end( // element to add void add_to_end( // element to add
CLASSNAME *new_data) { T *new_data) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ITERATOR::add_to_end", ABORT); NO_LIST.error("Iterator::add_to_end", ABORT);
} }
if (!new_data) { if (!new_data) {
BAD_PARAMETER.error("ITERATOR::add_to_end", ABORT, "new_data is nullptr"); BAD_PARAMETER.error("Iterator::add_to_end", ABORT, "new_data is nullptr");
} }
#endif #endif
@ -652,7 +650,7 @@ public:
this->add_before_stay_put(new_data); this->add_before_stay_put(new_data);
list->last = prev; list->last = prev;
} else { // Iteratr is elsewhere } else { // Iteratr is elsewhere
auto new_element = new LINK; auto new_element = new Link;
new_element->data = new_data; new_element->data = new_data;
new_element->next = list->last->next; new_element->next = list->last->next;
@ -663,7 +661,7 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::exchange() * Iterator::exchange()
* *
* Given another iterator, whose current element is a different element on * Given another iterator, whose current element is a different element on
* the same list list OR an element of another list, exchange the two current * the same list list OR an element of another list, exchange the two current
@ -672,7 +670,7 @@ public:
* (This function hasn't been in-lined because its a bit big!) * (This function hasn't been in-lined because its a bit big!)
**********************************************************************/ **********************************************************************/
void exchange( // positions of 2 links void exchange( // positions of 2 links
ITERATOR *other_it) { // other iterator Iterator *other_it) { // other iterator
constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists"); constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists");
/* Do nothing if either list is empty or if both iterators reference the same /* Do nothing if either list is empty or if both iterators reference the same
@ -685,7 +683,7 @@ public:
/* Error if either current element is deleted */ /* Error if either current element is deleted */
if (!current || !other_it->current) { if (!current || !other_it->current) {
DONT_EXCHANGE_DELETED.error("ITERATOR.exchange", ABORT); DONT_EXCHANGE_DELETED.error("Iterator.exchange", ABORT);
} }
/* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements
@ -747,7 +745,7 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::length() * Iterator::length()
* *
* Return the length of the list * Return the length of the list
* *
@ -757,38 +755,39 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ITERATOR::sort() * Iterator::sort()
* *
* Sort the elements of the list, then reposition at the start. * Sort the elements of the list, then reposition at the start.
* *
**********************************************************************/ **********************************************************************/
void sort( // sort elements void sort( // sort elements
int comparator( // comparison routine int comparator( // comparison routine
const CLASSNAME *, const CLASSNAME *)) { const T *, const T *)) {
list->sort(comparator); list->sort(comparator);
move_to_first(); move_to_first();
} }
}; };
using ITERATOR = Iterator; // compat
private: private:
LINK *last = nullptr; // End of list Link *last = nullptr; // End of list
//(Points to head) //(Points to head)
LINK *First() { // return first Link *First() { // return first
return last != nullptr ? last->next : nullptr; return last != nullptr ? last->next : nullptr;
} }
const LINK *First() const { // return first const Link *First() const { // return first
return last != nullptr ? last->next : nullptr; return last != nullptr ? last->next : nullptr;
} }
public: public:
~CLIST() { // destructor ~ConsList() { // destructor
shallow_clear(); shallow_clear();
} }
/*********************************************************************** /***********************************************************************
* CLIST::internal_deep_clear * ConsList::internal_deep_clear
* *
* Used by the "deep_clear" member function of derived list * Used by the "deep_clear" member function of derived list
* classes to destroy all the elements on the list. * classes to destroy all the elements on the list.
@ -816,7 +815,7 @@ public:
} }
/*********************************************************************** /***********************************************************************
* CLIST::shallow_clear * ConsList::shallow_clear
* *
* Used by the destructor and the "shallow_clear" member function of derived * Used by the destructor and the "shallow_clear" member function of derived
* list classes to destroy the list. * list classes to destroy the list.
@ -845,12 +844,12 @@ public:
} }
void shallow_copy( // dangerous!! void shallow_copy( // dangerous!!
CLIST *from_list) { // beware destructors!! ConsList *from_list) { // beware destructors!!
last = from_list->last; last = from_list->last;
} }
/*********************************************************************** /***********************************************************************
* CLIST::assign_to_sublist * ConsList::assign_to_sublist
* *
* The list is set to a sublist of another list. "This" list must be empty * The list is set to a sublist of another list. "This" list must be empty
* before this function is invoked. The two iterators passed must refer to * before this function is invoked. The two iterators passed must refer to
@ -862,12 +861,12 @@ public:
* end point is always the end_it position. * end point is always the end_it position.
**********************************************************************/ **********************************************************************/
void assign_to_sublist( // to this list void assign_to_sublist( // to this list
ITERATOR *start_it, // from list start Iterator *start_it, // from list start
ITERATOR *end_it) { // from list end Iterator *end_it) { // from list end
constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist"); constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist");
if (!empty()) { if (!empty()) {
LIST_NOT_EMPTY.error("CLIST.assign_to_sublist", ABORT); LIST_NOT_EMPTY.error("ConsList.assign_to_sublist", ABORT);
} }
last = start_it->extract_sublist(end_it); last = start_it->extract_sublist(end_it);
@ -885,21 +884,21 @@ public:
} }
/*********************************************************************** /***********************************************************************
* CLIST::sort * ConsList::sort
* *
* Sort elements on list * Sort elements on list
**********************************************************************/ **********************************************************************/
void sort( // sort elements void sort( // sort elements
int comparator( // comparison routine int comparator( // comparison routine
const CLASSNAME *, const CLASSNAME *)) { const T *, const T *)) {
// Allocate an array of pointers, one per list element. // Allocate an array of pointers, one per list element.
auto count = length(); auto count = length();
if (count > 0) { if (count > 0) {
// ptr array to sort // ptr array to sort
std::vector<CLASSNAME *> base; std::vector<T *> base;
base.reserve(count); base.reserve(count);
ITERATOR it(this); Iterator it(this);
// Extract all elements, putting the pointers in the array. // Extract all elements, putting the pointers in the array.
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
@ -925,10 +924,10 @@ public:
// Time is linear to add pre-sorted items to an empty list. // Time is linear to add pre-sorted items to an empty list.
// If unique, then don't add duplicate entries. // If unique, then don't add duplicate entries.
// Returns true if the element was added to the list. // Returns true if the element was added to the list.
bool add_sorted(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLASSNAME *new_data) { bool add_sorted(int comparator(const T *, const T *), bool unique, T *new_data) {
// Check for adding at the end. // Check for adding at the end.
if (last == nullptr || comparator(last->data, new_data) < 0) { if (last == nullptr || comparator(last->data, new_data) < 0) {
auto *new_element = new LINK; auto *new_element = new Link;
new_element->data = new_data; new_element->data = new_data;
if (last == nullptr) { if (last == nullptr) {
new_element->next = new_element; new_element->next = new_element;
@ -940,7 +939,7 @@ public:
return true; return true;
} else if (!unique || last->data != new_data) { } else if (!unique || last->data != new_data) {
// Need to use an iterator. // Need to use an iterator.
ITERATOR it(this); Iterator it(this);
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
auto data = it.data(); auto data = it.data();
if (data == new_data && unique) { if (data == new_data && unique) {
@ -965,16 +964,16 @@ public:
// the set difference minuend - subtrahend to this, being the elements // the set difference minuend - subtrahend to this, being the elements
// of minuend that do not compare equal to anything in subtrahend. // of minuend that do not compare equal to anything in subtrahend.
// If unique is true, any duplicates in minuend are also eliminated. // If unique is true, any duplicates in minuend are also eliminated.
void set_subtract(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLIST *minuend, void set_subtract(int comparator(const T *, const T *), bool unique, ConsList *minuend,
CLIST *subtrahend) { ConsList *subtrahend) {
shallow_clear(); shallow_clear();
ITERATOR m_it(minuend); Iterator m_it(minuend);
ITERATOR s_it(subtrahend); Iterator s_it(subtrahend);
// Since both lists are sorted, finding the subtras that are not // Since both lists are sorted, finding the subtras that are not
// minus is a case of a parallel iteration. // minus is a case of a parallel iteration.
for (m_it.mark_cycle_pt(); !m_it.cycled_list(); m_it.forward()) { for (m_it.mark_cycle_pt(); !m_it.cycled_list(); m_it.forward()) {
auto minu = m_it.data(); auto minu = m_it.data();
CLASSNAME *subtra = nullptr; T *subtra = nullptr;
if (!s_it.empty()) { if (!s_it.empty()) {
subtra = s_it.data(); subtra = s_it.data();
while (!s_it.at_last() && comparator(subtra, minu) < 0) { while (!s_it.at_last() && comparator(subtra, minu) < 0) {
@ -989,11 +988,11 @@ public:
} }
}; };
#define CLISTIZEH(CLASSNAME) \ #define CLISTIZEH(T) \
class CLASSNAME##_CLIST : public CLIST<CLASSNAME> { \ class T##_CLIST : public ConsList<T> { \
using CLIST<CLASSNAME>::CLIST; \ using ConsList<T>::ConsList; \
}; \ }; \
using CLASSNAME##_C_IT = CLIST<CLASSNAME>::ITERATOR; using T##_C_IT = ConsList<T>::Iterator;
} // namespace tesseract } // namespace tesseract

View File

@ -72,10 +72,9 @@ lists.
* Generic list class for singly linked lists with embedded links * Generic list class for singly linked lists with embedded links
**********************************************************************/ **********************************************************************/
template <typename CLASSNAME> template <typename T>
class ELIST { class IntrusiveForwardList {
public: public:
/********************************************************************** /**********************************************************************
* CLASS - ELIST_LINK * CLASS - ELIST_LINK
* *
@ -83,33 +82,33 @@ public:
*embedded links *embedded links
* *
* Note: No destructor - elements are assumed to be destroyed EITHER after * Note: No destructor - elements are assumed to be destroyed EITHER after
* they have been extracted from a list OR by the ELIST destructor which * they have been extracted from a list OR by the IntrusiveForwardList destructor which
* walks the list. * walks the list.
**********************************************************************/ **********************************************************************/
class LINK { class Link {
friend class ITERATOR; friend class Iterator;
friend class ELIST; friend class IntrusiveForwardList;
CLASSNAME *next; T *next;
public: public:
LINK() { Link() {
next = nullptr; next = nullptr;
} }
// constructor // constructor
// The special copy constructor is used by lots of classes. // The special copy constructor is used by lots of classes.
LINK(const LINK &) { Link(const Link &) {
next = nullptr; next = nullptr;
} }
// The special assignment operator is used by lots of classes. // The special assignment operator is used by lots of classes.
void operator=(const LINK &) { void operator=(const Link &) {
next = nullptr; next = nullptr;
} }
}; };
using LINK = Link; // compat
/*********************************************************************** /***********************************************************************
* CLASS - ELIST_ITERATOR * CLASS - ELIST_ITERATOR
@ -118,36 +117,36 @@ public:
*embedded links *embedded links
**********************************************************************/ **********************************************************************/
class ITERATOR { class Iterator {
friend void ELIST::assign_to_sublist(ITERATOR *, ITERATOR *); friend void IntrusiveForwardList::assign_to_sublist(Iterator *, Iterator *);
ELIST *list; // List being iterated IntrusiveForwardList *list; // List being iterated
CLASSNAME *prev; // prev element T *prev; // prev element
CLASSNAME *current; // current element T *current; // current element
CLASSNAME *next; // next element T *next; // next element
CLASSNAME *cycle_pt; // point we are cycling the list to. T *cycle_pt; // point we are cycling the list to.
bool ex_current_was_last; // current extracted was end of list bool ex_current_was_last; // current extracted was end of list
bool ex_current_was_cycle_pt; // current extracted was cycle point bool ex_current_was_cycle_pt; // current extracted was cycle point
bool started_cycling; // Have we moved off the start? bool started_cycling; // Have we moved off the start?
/*********************************************************************** /***********************************************************************
* ITERATOR::extract_sublist() * Iterator::extract_sublist()
* *
* This is a private member, used only by ELIST::assign_to_sublist. * This is a private member, used only by IntrusiveForwardList::assign_to_sublist.
* Given another iterator for the same list, extract the links from THIS to * Given another iterator for the same list, extract the links from THIS to
* OTHER inclusive, link them into a new circular list, and return a * OTHER inclusive, link them into a new circular list, and return a
* pointer to the last element. * pointer to the last element.
* (Can't inline this function because it contains a loop) * (Can't inline this function because it contains a loop)
**********************************************************************/ **********************************************************************/
CLASSNAME *extract_sublist( // from this current... T *extract_sublist( // from this current...
ITERATOR *other_it) { // to other current Iterator *other_it) { // to other current
#ifndef NDEBUG #ifndef NDEBUG
constexpr ERRCODE BAD_EXTRACTION_PTS("Can't extract sublist from points on different lists"); constexpr ERRCODE BAD_EXTRACTION_PTS("Can't extract sublist from points on different lists");
constexpr ERRCODE DONT_EXTRACT_DELETED("Can't extract a sublist marked by deleted points"); constexpr ERRCODE DONT_EXTRACT_DELETED("Can't extract a sublist marked by deleted points");
#endif #endif
constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list");
ITERATOR temp_it = *this; Iterator temp_it = *this;
CLASSNAME *end_of_new_list; T *end_of_new_list;
#ifndef NDEBUG #ifndef NDEBUG
if (!other_it) if (!other_it)
@ -170,7 +169,7 @@ public:
temp_it.mark_cycle_pt(); temp_it.mark_cycle_pt();
do { // walk sublist do { // walk sublist
if (temp_it.cycled_list()) { // can't find end pt if (temp_it.cycled_list()) { // can't find end pt
BAD_SUBLIST.error("ITERATOR.extract_sublist", ABORT); BAD_SUBLIST.error("Iterator.extract_sublist", ABORT);
} }
if (temp_it.at_last()) { if (temp_it.at_last()) {
@ -208,7 +207,7 @@ public:
} // to other current } // to other current
public: public:
ITERATOR() { // constructor Iterator() { // constructor
list = nullptr; list = nullptr;
} // unassigned list } // unassigned list
/*********************************************************************** /***********************************************************************
@ -216,7 +215,7 @@ public:
* *
* CONSTRUCTOR - set iterator to specified list; * CONSTRUCTOR - set iterator to specified list;
**********************************************************************/ **********************************************************************/
ITERATOR(ELIST *list_to_iterate) { Iterator(IntrusiveForwardList *list_to_iterate) {
set_to_list(list_to_iterate); set_to_list(list_to_iterate);
} }
/*********************************************************************** /***********************************************************************
@ -226,7 +225,7 @@ public:
* over. * over.
**********************************************************************/ **********************************************************************/
void set_to_list( // change list void set_to_list( // change list
ELIST *list_to_iterate) { IntrusiveForwardList *list_to_iterate) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list_to_iterate) { if (!list_to_iterate) {
BAD_PARAMETER.error("ELIST_ITERATOR::set_to_list", ABORT, "list_to_iterate is nullptr"); BAD_PARAMETER.error("ELIST_ITERATOR::set_to_list", ABORT, "list_to_iterate is nullptr");
@ -249,7 +248,7 @@ public:
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_after_then_move( // add after current & void add_after_then_move( // add after current &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::add_after_then_move", ABORT); NO_LIST.error("ELIST_ITERATOR::add_after_then_move", ABORT);
@ -294,7 +293,7 @@ public:
* the iterator to the new element. * the iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_after_stay_put( // add after current & void add_after_stay_put( // add after current &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::add_after_stay_put", ABORT); NO_LIST.error("ELIST_ITERATOR::add_after_stay_put", ABORT);
@ -341,7 +340,7 @@ public:
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_before_then_move( // add before current & void add_before_then_move( // add before current &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::add_before_then_move", ABORT); NO_LIST.error("ELIST_ITERATOR::add_before_then_move", ABORT);
@ -382,7 +381,7 @@ public:
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_before_stay_put( // add before current & void add_before_stay_put( // add before current &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::add_before_stay_put", ABORT); NO_LIST.error("ELIST_ITERATOR::add_before_stay_put", ABORT);
@ -425,7 +424,7 @@ public:
* iterator. * iterator.
**********************************************************************/ **********************************************************************/
void add_list_after( // add a list & void add_list_after( // add a list &
ELIST *list_to_add) { IntrusiveForwardList *list_to_add) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::add_list_after", ABORT); NO_LIST.error("ELIST_ITERATOR::add_list_after", ABORT);
@ -471,7 +470,7 @@ public:
* iterator. * iterator.
**********************************************************************/ **********************************************************************/
void add_list_before( // add a list & void add_list_before( // add a list &
ELIST *list_to_add) { IntrusiveForwardList *list_to_add) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::add_list_before", ABORT); NO_LIST.error("ELIST_ITERATOR::add_list_before", ABORT);
@ -508,7 +507,7 @@ public:
} }
} // move to it 1st item } // move to it 1st item
CLASSNAME *data() { // get current data T *data() { // get current data
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::data", ABORT); NO_LIST.error("ELIST_ITERATOR::data", ABORT);
@ -526,9 +525,9 @@ public:
* "offset" must not be less than -1. * "offset" must not be less than -1.
* (This function can't be INLINEd because it contains a loop) * (This function can't be INLINEd because it contains a loop)
**********************************************************************/ **********************************************************************/
CLASSNAME *data_relative( // get data + or - ... T *data_relative( // get data + or - ...
int8_t offset) { // offset from current int8_t offset) { // offset from current
CLASSNAME *ptr; T *ptr;
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
@ -560,7 +559,7 @@ public:
* Move the iterator to the next element of the list. * Move the iterator to the next element of the list.
* REMEMBER: ALL LISTS ARE CIRCULAR. * REMEMBER: ALL LISTS ARE CIRCULAR.
**********************************************************************/ **********************************************************************/
CLASSNAME *forward() { T *forward() {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
NO_LIST.error("ELIST_ITERATOR::forward", ABORT); NO_LIST.error("ELIST_ITERATOR::forward", ABORT);
@ -606,8 +605,8 @@ public:
* this.) The iterator's current points to nullptr. If the extracted element * this.) The iterator's current points to nullptr. If the extracted element
* is to be deleted, this is the callers responsibility. * is to be deleted, this is the callers responsibility.
**********************************************************************/ **********************************************************************/
CLASSNAME *extract() { T *extract() {
CLASSNAME *extracted_link; T *extracted_link;
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
@ -643,7 +642,7 @@ public:
* Move current so that it is set to the start of the list. * Move current so that it is set to the start of the list.
* Return data just in case anyone wants it. * Return data just in case anyone wants it.
**********************************************************************/ **********************************************************************/
CLASSNAME *move_to_first() { T *move_to_first() {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::move_to_first", ABORT); NO_LIST.error("ELIST_ITERATOR::move_to_first", ABORT);
@ -662,7 +661,7 @@ public:
* Return data just in case anyone wants it. * Return data just in case anyone wants it.
* (This function can't be INLINEd because it contains a loop) * (This function can't be INLINEd because it contains a loop)
**********************************************************************/ **********************************************************************/
CLASSNAME *move_to_last() { T *move_to_last() {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
NO_LIST.error("ELIST_ITERATOR::move_to_last", ABORT); NO_LIST.error("ELIST_ITERATOR::move_to_last", ABORT);
@ -772,7 +771,7 @@ public:
queues. queues.
**********************************************************************/ **********************************************************************/
void add_to_end( // add at end & void add_to_end( // add at end &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::add_to_end", ABORT); NO_LIST.error("ELIST_ITERATOR::add_to_end", ABORT);
@ -808,10 +807,10 @@ public:
* (This function hasn't been in-lined because its a bit big!) * (This function hasn't been in-lined because its a bit big!)
**********************************************************************/ **********************************************************************/
void exchange( // positions of 2 links void exchange( // positions of 2 links
ITERATOR *other_it) { // other iterator Iterator *other_it) { // other iterator
constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists"); constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists");
CLASSNAME *old_current; T *old_current;
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
@ -905,7 +904,7 @@ public:
**********************************************************************/ **********************************************************************/
void sort( // sort elements void sort( // sort elements
int comparator( // comparison routine int comparator( // comparison routine
const CLASSNAME *, const CLASSNAME *)) { const T *, const T *)) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST_ITERATOR::sort", ABORT); NO_LIST.error("ELIST_ITERATOR::sort", ABORT);
@ -916,17 +915,17 @@ public:
move_to_first(); move_to_first();
} }
}; };
using ITERATOR = Iterator; // compat
private: private:
CLASSNAME *last = nullptr; // End of list T *last = nullptr; // End of list
//(Points to head) //(Points to head)
CLASSNAME *First() { // return first T *First() { // return first
return last ? last->next : nullptr; return last ? last->next : nullptr;
} }
public: public:
~ELIST() { ~IntrusiveForwardList() {
clear(); clear();
} }
@ -937,16 +936,16 @@ public:
/* Become a deep copy of src_list */ /* Become a deep copy of src_list */
template <typename U> template <typename U>
void deep_copy(const U *src_list, CLASSNAME *(*copier)(const CLASSNAME *)) { void deep_copy(const U *src_list, T *(*copier)(const T *)) {
ITERATOR from_it(const_cast<U *>(src_list)); Iterator from_it(const_cast<U *>(src_list));
ITERATOR to_it(this); Iterator to_it(this);
for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward())
to_it.add_after_then_move((*copier)(from_it.data())); to_it.add_after_then_move((*copier)(from_it.data()));
} }
/*********************************************************************** /***********************************************************************
* ELIST::internal_clear * IntrusiveForwardList::internal_clear
* *
* Used by the destructor and the "clear" member function of derived list * Used by the destructor and the "clear" member function of derived list
* classes to destroy all the elements on the list. * classes to destroy all the elements on the list.
@ -959,8 +958,8 @@ public:
// destroy all links // destroy all links
void internal_clear() { void internal_clear() {
CLASSNAME *ptr; T *ptr;
CLASSNAME *next; T *next;
if (!empty()) { if (!empty()) {
ptr = last->next; // set to first ptr = last->next; // set to first
@ -983,12 +982,12 @@ public:
} }
void shallow_copy( // dangerous!! void shallow_copy( // dangerous!!
ELIST *from_list) { // beware destructors!! IntrusiveForwardList *from_list) { // beware destructors!!
last = from_list->last; last = from_list->last;
} }
/*********************************************************************** /***********************************************************************
* ELIST::assign_to_sublist * IntrusiveForwardList::assign_to_sublist
* *
* The list is set to a sublist of another list. "This" list must be empty * The list is set to a sublist of another list. "This" list must be empty
* before this function is invoked. The two iterators passed must refer to * before this function is invoked. The two iterators passed must refer to
@ -1000,12 +999,12 @@ public:
* end point is always the end_it position. * end point is always the end_it position.
**********************************************************************/ **********************************************************************/
void assign_to_sublist( // to this list void assign_to_sublist( // to this list
ITERATOR *start_it, // from list start Iterator *start_it, // from list start
ITERATOR *end_it) { // from list end Iterator *end_it) { // from list end
constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist"); constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist");
if (!empty()) { if (!empty()) {
LIST_NOT_EMPTY.error("ELIST.assign_to_sublist", ABORT); LIST_NOT_EMPTY.error("IntrusiveForwardList.assign_to_sublist", ABORT);
} }
last = start_it->extract_sublist(end_it); last = start_it->extract_sublist(end_it);
@ -1024,7 +1023,7 @@ public:
} }
/*********************************************************************** /***********************************************************************
* ELIST::sort * IntrusiveForwardList::sort
* *
* Sort elements on list * Sort elements on list
* NB If you don't like the const declarations in the comparator, coerce yours: * NB If you don't like the const declarations in the comparator, coerce yours:
@ -1032,16 +1031,16 @@ public:
**********************************************************************/ **********************************************************************/
void sort( // sort elements void sort( // sort elements
int comparator( // comparison routine int comparator( // comparison routine
const CLASSNAME *, const CLASSNAME *)) { const T *, const T *)) {
// Allocate an array of pointers, one per list element. // Allocate an array of pointers, one per list element.
auto count = length(); auto count = length();
if (count > 0) { if (count > 0) {
// ptr array to sort // ptr array to sort
std::vector<CLASSNAME *> base; std::vector<T *> base;
base.reserve(count); base.reserve(count);
ITERATOR it(this); Iterator it(this);
// Extract all elements, putting the pointers in the array. // Extract all elements, putting the pointers in the array.
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
@ -1070,8 +1069,8 @@ public:
// list) - new_link is not added to the list and the function returns the // list) - new_link is not added to the list and the function returns the
// pointer to the identical entry that already exists in the list // pointer to the identical entry that already exists in the list
// (otherwise the function returns new_link). // (otherwise the function returns new_link).
CLASSNAME *add_sorted_and_find(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, T *add_sorted_and_find(int comparator(const T *, const T *), bool unique,
CLASSNAME *new_link) { T *new_link) {
// Check for adding at the end. // Check for adding at the end.
if (last == nullptr || comparator(last, new_link) < 0) { if (last == nullptr || comparator(last, new_link) < 0) {
if (last == nullptr) { if (last == nullptr) {
@ -1083,7 +1082,7 @@ public:
last = new_link; last = new_link;
} else { } else {
// Need to use an iterator. // Need to use an iterator.
ITERATOR it(this); Iterator it(this);
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
auto *link = it.data(); auto *link = it.data();
int compare = comparator(link, new_link); int compare = comparator(link, new_link);
@ -1104,22 +1103,24 @@ public:
// Same as above, but returns true if the new entry was inserted, false // Same as above, but returns true if the new entry was inserted, false
// if the identical entry already existed in the list. // if the identical entry already existed in the list.
bool add_sorted(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLASSNAME *new_link) { bool add_sorted(int comparator(const T *, const T *), bool unique, T *new_link) {
return (add_sorted_and_find(comparator, unique, new_link) == new_link); return (add_sorted_and_find(comparator, unique, new_link) == new_link);
} }
}; };
template <typename CLASSNAME>
using ELIST = IntrusiveForwardList<CLASSNAME>;
// add TESS_API? // add TESS_API?
// move templated lists to public include dirs? // move templated lists to public include dirs?
#define ELISTIZEH(CLASSNAME) \ #define ELISTIZEH(T) \
class CLASSNAME##_LIST : public ELIST<CLASSNAME> { \ class T##_LIST : public IntrusiveForwardList<T> { \
public: \ public: \
using ELIST<CLASSNAME>::ELIST; \ using IntrusiveForwardList<T>::IntrusiveForwardList; \
}; \ }; \
class CLASSNAME##_IT : public ELIST<CLASSNAME>::ITERATOR { \ class T##_IT : public IntrusiveForwardList<T>::Iterator { \
public: \ public: \
using base = ELIST<CLASSNAME>::ITERATOR; \ using IntrusiveForwardList<T>::Iterator::Iterator; \
using base::base; \
}; };
} // namespace tesseract } // namespace tesseract

View File

@ -51,11 +51,11 @@ i) The duplication in source does not affect the run time code size - the
* Generic list class for doubly linked lists with embedded links * Generic list class for doubly linked lists with embedded links
**********************************************************************/ **********************************************************************/
template <typename CLASSNAME> template <typename T>
class ELIST2 { class IntrusiveList {
public: public:
/********************************************************************** /**********************************************************************
* CLASS - LINK * CLASS - Link
* *
* Generic link class for doubly linked lists with embedded links * Generic link class for doubly linked lists with embedded links
* *
@ -64,26 +64,26 @@ public:
* walks the list. * walks the list.
**********************************************************************/ **********************************************************************/
class LINK { class Link {
friend class ITERATOR; friend class Iterator;
friend class ELIST2; friend class IntrusiveList;
CLASSNAME *prev; T *prev;
CLASSNAME *next; T *next;
public: public:
LINK() { // constructor Link() { // constructor
prev = next = nullptr; prev = next = nullptr;
} }
LINK(const LINK &) = delete; Link(const Link &) = delete;
// The assignment operator is required for WERD. // The assignment operator is required for WERD.
void operator=(const LINK &) { void operator=(const Link &) {
prev = next = nullptr; prev = next = nullptr;
} }
}; };
using LINK = Link; // compat
/*********************************************************************** /***********************************************************************
* CLASS - ELIST2_ITERATOR * CLASS - ELIST2_ITERATOR
@ -92,36 +92,36 @@ public:
*links *links
**********************************************************************/ **********************************************************************/
class ITERATOR { class Iterator {
friend void ELIST2::assign_to_sublist(ITERATOR *, ITERATOR *); friend void IntrusiveList::assign_to_sublist(Iterator *, Iterator *);
ELIST2 *list; // List being iterated IntrusiveList *list; // List being iterated
CLASSNAME *prev; // prev element T *prev; // prev element
CLASSNAME *current; // current element T *current; // current element
CLASSNAME *next; // next element T *next; // next element
CLASSNAME *cycle_pt; // point we are cycling the list to. T *cycle_pt; // point we are cycling the list to.
bool ex_current_was_last; // current extracted was end of list bool ex_current_was_last; // current extracted was end of list
bool ex_current_was_cycle_pt; // current extracted was cycle point bool ex_current_was_cycle_pt; // current extracted was cycle point
bool started_cycling; // Have we moved off the start? bool started_cycling; // Have we moved off the start?
/*********************************************************************** /***********************************************************************
* ELIST2_ITERATOR::extract_sublist() * ELIST2_ITERATOR::extract_sublist()
* *
* This is a private member, used only by ELIST2::assign_to_sublist. * This is a private member, used only by IntrusiveList::assign_to_sublist.
* Given another iterator for the same list, extract the links from THIS to * Given another iterator for the same list, extract the links from THIS to
* OTHER inclusive, link them into a new circular list, and return a * OTHER inclusive, link them into a new circular list, and return a
* pointer to the last element. * pointer to the last element.
* (Can't inline this function because it contains a loop) * (Can't inline this function because it contains a loop)
**********************************************************************/ **********************************************************************/
CLASSNAME *extract_sublist( // from this current... T *extract_sublist( // from this current...
ITERATOR *other_it) { // to other current Iterator *other_it) { // to other current
#ifndef NDEBUG #ifndef NDEBUG
constexpr ERRCODE BAD_EXTRACTION_PTS("Can't extract sublist from points on different lists"); constexpr ERRCODE BAD_EXTRACTION_PTS("Can't extract sublist from points on different lists");
constexpr ERRCODE DONT_EXTRACT_DELETED("Can't extract a sublist marked by deleted points"); constexpr ERRCODE DONT_EXTRACT_DELETED("Can't extract a sublist marked by deleted points");
#endif #endif
constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list");
ITERATOR temp_it = *this; Iterator temp_it = *this;
CLASSNAME *end_of_new_list; T *end_of_new_list;
#ifndef NDEBUG #ifndef NDEBUG
if (!other_it) if (!other_it)
@ -193,8 +193,8 @@ public:
* *
* CONSTRUCTOR - set iterator to specified list; * CONSTRUCTOR - set iterator to specified list;
**********************************************************************/ **********************************************************************/
ITERATOR( // constructor Iterator( // constructor
ELIST2 *list_to_iterate) { IntrusiveList *list_to_iterate) {
set_to_list(list_to_iterate); set_to_list(list_to_iterate);
} }
@ -206,7 +206,7 @@ public:
**********************************************************************/ **********************************************************************/
void set_to_list( // change list void set_to_list( // change list
ELIST2 *list_to_iterate) { IntrusiveList *list_to_iterate) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list_to_iterate) { if (!list_to_iterate) {
BAD_PARAMETER.error("ELIST2_ITERATOR::set_to_list", ABORT, "list_to_iterate is nullptr"); BAD_PARAMETER.error("ELIST2_ITERATOR::set_to_list", ABORT, "list_to_iterate is nullptr");
@ -229,7 +229,7 @@ public:
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_after_then_move( // add after current & void add_after_then_move( // add after current &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::add_after_then_move", ABORT); NO_LIST.error("ELIST2_ITERATOR::add_after_then_move", ABORT);
@ -278,7 +278,7 @@ public:
* the iterator to the new element. * the iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_after_stay_put( // add after current & void add_after_stay_put( // add after current &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::add_after_stay_put", ABORT); NO_LIST.error("ELIST2_ITERATOR::add_after_stay_put", ABORT);
@ -329,7 +329,7 @@ public:
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_before_then_move( // add before current & void add_before_then_move( // add before current &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::add_before_then_move", ABORT); NO_LIST.error("ELIST2_ITERATOR::add_before_then_move", ABORT);
@ -375,7 +375,7 @@ public:
* iterator to the new element. * iterator to the new element.
**********************************************************************/ **********************************************************************/
void add_before_stay_put( // add before current & void add_before_stay_put( // add before current &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::add_before_stay_put", ABORT); NO_LIST.error("ELIST2_ITERATOR::add_before_stay_put", ABORT);
@ -423,7 +423,7 @@ public:
* iterator. * iterator.
**********************************************************************/ **********************************************************************/
void add_list_after( // add a list & void add_list_after( // add a list &
ELIST2 *list_to_add) { IntrusiveList *list_to_add) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::add_list_after", ABORT); NO_LIST.error("ELIST2_ITERATOR::add_list_after", ABORT);
@ -473,7 +473,7 @@ public:
* iterator. * iterator.
**********************************************************************/ **********************************************************************/
void add_list_before( // add a list & void add_list_before( // add a list &
ELIST2 *list_to_add) { IntrusiveList *list_to_add) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::add_list_before", ABORT); NO_LIST.error("ELIST2_ITERATOR::add_list_before", ABORT);
@ -514,7 +514,7 @@ public:
} }
} // move to it 1st item } // move to it 1st item
CLASSNAME *data() { // get current data T *data() { // get current data
#ifndef NDEBUG #ifndef NDEBUG
if (!current) { if (!current) {
NULL_DATA.error("ELIST2_ITERATOR::data", ABORT); NULL_DATA.error("ELIST2_ITERATOR::data", ABORT);
@ -531,9 +531,9 @@ public:
* Return the data pointer to the element "offset" elements from current. * Return the data pointer to the element "offset" elements from current.
* (This function can't be INLINEd because it contains a loop) * (This function can't be INLINEd because it contains a loop)
**********************************************************************/ **********************************************************************/
CLASSNAME *data_relative( // get data + or - ... T *data_relative( // get data + or - ...
int8_t offset) { // offset from current int8_t offset) { // offset from current
CLASSNAME *ptr; T *ptr;
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
@ -565,7 +565,7 @@ public:
* Move the iterator to the next element of the list. * Move the iterator to the next element of the list.
* REMEMBER: ALL LISTS ARE CIRCULAR. * REMEMBER: ALL LISTS ARE CIRCULAR.
**********************************************************************/ **********************************************************************/
CLASSNAME *forward() { T *forward() {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
NO_LIST.error("ELIST2_ITERATOR::forward", ABORT); NO_LIST.error("ELIST2_ITERATOR::forward", ABORT);
@ -611,7 +611,7 @@ public:
* Move the iterator to the previous element of the list. * Move the iterator to the previous element of the list.
* REMEMBER: ALL LISTS ARE CIRCULAR. * REMEMBER: ALL LISTS ARE CIRCULAR.
**********************************************************************/ **********************************************************************/
CLASSNAME *backward() { T *backward() {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
NO_LIST.error("ELIST2_ITERATOR::backward", ABORT); NO_LIST.error("ELIST2_ITERATOR::backward", ABORT);
@ -655,8 +655,8 @@ public:
* this.) The iterator's current points to nullptr. If the extracted element * this.) The iterator's current points to nullptr. If the extracted element
* is to be deleted, this is the callers responsibility. * is to be deleted, this is the callers responsibility.
**********************************************************************/ **********************************************************************/
CLASSNAME *extract() { T *extract() {
CLASSNAME *extracted_link; T *extracted_link;
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
@ -697,7 +697,7 @@ public:
* Return data just in case anyone wants it. * Return data just in case anyone wants it.
**********************************************************************/ **********************************************************************/
// go to start of list // go to start of list
CLASSNAME *move_to_first() { T *move_to_first() {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::move_to_first", ABORT); NO_LIST.error("ELIST2_ITERATOR::move_to_first", ABORT);
@ -715,7 +715,7 @@ public:
* Move current so that it is set to the end of the list. * Move current so that it is set to the end of the list.
* Return data just in case anyone wants it. * Return data just in case anyone wants it.
**********************************************************************/ **********************************************************************/
CLASSNAME *move_to_last() { T *move_to_last() {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::move_to_last", ABORT); NO_LIST.error("ELIST2_ITERATOR::move_to_last", ABORT);
@ -825,7 +825,7 @@ public:
queues. queues.
**********************************************************************/ **********************************************************************/
void add_to_end( // add at end & void add_to_end( // add at end &
CLASSNAME *new_element) { T *new_element) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::add_to_end", ABORT); NO_LIST.error("ELIST2_ITERATOR::add_to_end", ABORT);
@ -863,10 +863,10 @@ public:
* (This function hasn't been in-lined because its a bit big!) * (This function hasn't been in-lined because its a bit big!)
**********************************************************************/ **********************************************************************/
void exchange( // positions of 2 links void exchange( // positions of 2 links
ITERATOR *other_it) { // other iterator Iterator *other_it) { // other iterator
constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists"); constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists");
CLASSNAME *old_current; T *old_current;
#ifndef NDEBUG #ifndef NDEBUG
if (!list) if (!list)
@ -972,7 +972,7 @@ public:
**********************************************************************/ **********************************************************************/
void sort( // sort elements void sort( // sort elements
int comparator( // comparison routine int comparator( // comparison routine
const CLASSNAME *, const CLASSNAME *)) { const T *, const T *)) {
#ifndef NDEBUG #ifndef NDEBUG
if (!list) { if (!list) {
NO_LIST.error("ELIST2_ITERATOR::sort", ABORT); NO_LIST.error("ELIST2_ITERATOR::sort", ABORT);
@ -985,19 +985,19 @@ public:
private: private:
// Don't use the following constructor. // Don't use the following constructor.
ITERATOR() = delete; Iterator() = delete;
}; };
using ITERATOR = Iterator; // compat
private: private:
CLASSNAME *last = nullptr; // End of list T *last = nullptr; // End of list
//(Points to head) //(Points to head)
CLASSNAME *First() { // return first T *First() { // return first
return last ? last->next : nullptr; return last ? last->next : nullptr;
} }
public: public:
~ELIST2() { ~IntrusiveList() {
clear(); clear();
} }
@ -1008,16 +1008,16 @@ public:
/* Become a deep copy of src_list */ /* Become a deep copy of src_list */
template <typename U> template <typename U>
void deep_copy(const U *src_list, CLASSNAME *(*copier)(const CLASSNAME *)) { void deep_copy(const U *src_list, T *(*copier)(const T *)) {
ITERATOR from_it(const_cast<U *>(src_list)); Iterator from_it(const_cast<U *>(src_list));
ITERATOR to_it(this); Iterator to_it(this);
for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward())
to_it.add_after_then_move((*copier)(from_it.data())); to_it.add_after_then_move((*copier)(from_it.data()));
} }
/*********************************************************************** /***********************************************************************
* ELIST2::internal_clear * IntrusiveList::internal_clear
* *
* Used by the destructor and the "clear" member function of derived list * Used by the destructor and the "clear" member function of derived list
* classes to destroy all the elements on the list. * classes to destroy all the elements on the list.
@ -1031,8 +1031,8 @@ public:
// destroy all links // destroy all links
void internal_clear() { void internal_clear() {
// ptr to zapper functn // ptr to zapper functn
CLASSNAME *ptr; T *ptr;
CLASSNAME *next; T *next;
if (!empty()) { if (!empty()) {
ptr = last->next; // set to first ptr = last->next; // set to first
@ -1055,12 +1055,12 @@ public:
} }
void shallow_copy( // dangerous!! void shallow_copy( // dangerous!!
ELIST2 *from_list) { // beware destructors!! IntrusiveList *from_list) { // beware destructors!!
last = from_list->last; last = from_list->last;
} }
/*********************************************************************** /***********************************************************************
* ELIST2::assign_to_sublist * IntrusiveList::assign_to_sublist
* *
* The list is set to a sublist of another list. "This" list must be empty * The list is set to a sublist of another list. "This" list must be empty
* before this function is invoked. The two iterators passed must refer to * before this function is invoked. The two iterators passed must refer to
@ -1072,8 +1072,8 @@ public:
* end point is always the end_it position. * end point is always the end_it position.
**********************************************************************/ **********************************************************************/
void assign_to_sublist( // to this list void assign_to_sublist( // to this list
ITERATOR *start_it, // from list start Iterator *start_it, // from list start
ITERATOR *end_it); // from list end Iterator *end_it); // from list end
// # elements in list // # elements in list
int32_t length() const { int32_t length() const {
@ -1087,7 +1087,7 @@ public:
return count; return count;
} }
/*********************************************************************** /***********************************************************************
* ELIST2::sort * IntrusiveList::sort
* *
* Sort elements on list * Sort elements on list
* NB If you don't like the const declarations in the comparator, coerce yours: * NB If you don't like the const declarations in the comparator, coerce yours:
@ -1095,15 +1095,15 @@ public:
**********************************************************************/ **********************************************************************/
void sort( // sort elements void sort( // sort elements
int comparator( // comparison routine int comparator( // comparison routine
const CLASSNAME *, const CLASSNAME *)) { const T *, const T *)) {
// Allocate an array of pointers, one per list element. // Allocate an array of pointers, one per list element.
auto count = length(); auto count = length();
if (count > 0) { if (count > 0) {
// ptr array to sort // ptr array to sort
std::vector<CLASSNAME *> base; std::vector<T *> base;
base.reserve(count); base.reserve(count);
ITERATOR it(this); Iterator it(this);
// Extract all elements, putting the pointers in the array. // Extract all elements, putting the pointers in the array.
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
@ -1127,7 +1127,7 @@ public:
// Comparison function is the same as used by sort, i.e. uses double // Comparison function is the same as used by sort, i.e. uses double
// indirection. Time is O(1) to add to beginning or end. // indirection. Time is O(1) to add to beginning or end.
// Time is linear to add pre-sorted items to an empty list. // Time is linear to add pre-sorted items to an empty list.
void add_sorted(int comparator(const CLASSNAME *, const CLASSNAME *), CLASSNAME *new_link) { void add_sorted(int comparator(const T *, const T *), T *new_link) {
// Check for adding at the end. // Check for adding at the end.
if (last == nullptr || comparator(last, new_link) < 0) { if (last == nullptr || comparator(last, new_link) < 0) {
if (last == nullptr) { if (last == nullptr) {
@ -1142,7 +1142,7 @@ public:
last = new_link; last = new_link;
} else { } else {
// Need to use an iterator. // Need to use an iterator.
ITERATOR it(this); Iterator it(this);
for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
auto link = it.data(); auto link = it.data();
if (comparator(link, new_link) > 0) { if (comparator(link, new_link) > 0) {
@ -1158,16 +1158,19 @@ public:
} }
}; };
template <typename CLASSNAME>
using ELIST2 = IntrusiveList<CLASSNAME>;
// add TESS_API? // add TESS_API?
// move templated lists to public include dirs? // move templated lists to public include dirs?
#define ELIST2IZEH(CLASSNAME) \ #define ELIST2IZEH(T) \
class CLASSNAME##_LIST : public ELIST2<CLASSNAME> { \ class T##_LIST : public IntrusiveList<T> { \
public: \ public: \
using ELIST2<CLASSNAME>::ELIST2; \ using IntrusiveList<T>::IntrusiveList; \
}; \ }; \
class CLASSNAME##_IT : public ELIST2<CLASSNAME>::ITERATOR { \ class T##_IT : public IntrusiveList<T>::Iterator { \
public: \ public: \
using base = ELIST2<CLASSNAME>::ITERATOR; \ using base = IntrusiveList<T>::Iterator; \
using base::base; \ using base::base; \
}; };