From df82c04cf1c154d23a768edfb60c1ae97aa9e250 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 03:38:07 +0300 Subject: [PATCH 01/17] Make CLIST templated. Move member methods inside the class. Move helper classes (CLIST_LINK and CLIST_ITERATOR inside the list class). This allows us to use real C++ templates for different instantiations instead of void * emulation. --- src/ccutil/clst.cpp | 444 ------------- src/ccutil/clst.h | 1437 ++++++++++++++++++++++++++----------------- 2 files changed, 863 insertions(+), 1018 deletions(-) delete mode 100644 src/ccutil/clst.cpp diff --git a/src/ccutil/clst.cpp b/src/ccutil/clst.cpp deleted file mode 100644 index c80eed8336..0000000000 --- a/src/ccutil/clst.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/********************************************************************** - * File: clst.cpp (Formerly clist.c) - * Description: CONS cell list handling code which is not in the include file. - * Author: Phil Cheatle - * - * (C) Copyright 1991, Hewlett-Packard Ltd. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - **********************************************************************/ - -#include "clst.h" -#include - -namespace tesseract { - -/*********************************************************************** - * CLIST::internal_deep_clear - * - * Used by the "deep_clear" member function of derived list - * classes to destroy all the elements on the list. - * The calling function passes a "zapper" function which can be called to - * delete each data element of the list, regardless of its class. This - * technique permits a generic clear function to destroy elements of - * different derived types correctly, without requiring virtual functions and - * the consequential memory overhead. - **********************************************************************/ - -void CLIST::internal_deep_clear( // destroy all links - void (*zapper)(void *)) { // ptr to zapper functn - if (!empty()) { - auto ptr = last->next; // set to first - last->next = nullptr; // break circle - last = nullptr; // set list empty - while (ptr) { - auto next = ptr->next; - zapper(ptr->data); - delete (ptr); - ptr = next; - } - } -} - -/*********************************************************************** - * CLIST::shallow_clear - * - * Used by the destructor and the "shallow_clear" member function of derived - * list classes to destroy the list. - * The data elements are NOT destroyed. - * - **********************************************************************/ - -void CLIST::shallow_clear() { // destroy all links - if (!empty()) { - auto ptr = last->next; // set to first - last->next = nullptr; // break circle - last = nullptr; // set list empty - while (ptr) { - auto next = ptr->next; - delete (ptr); - ptr = next; - } - } -} - -/*********************************************************************** - * CLIST::assign_to_sublist - * - * 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 - * the same list, different from "this" one. The sublist removed is the - * inclusive list from start_it's current position to end_it's current - * position. If this range passes over the end of the source list then the - * source list has its end set to the previous element of start_it. The - * extracted sublist is unaffected by the end point of the source list, its - * end point is always the end_it position. - **********************************************************************/ - -void CLIST::assign_to_sublist( // to this list - CLIST_ITERATOR *start_it, // from list start - CLIST_ITERATOR *end_it) { // from list end - constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist"); - - if (!empty()) { - LIST_NOT_EMPTY.error("CLIST.assign_to_sublist", ABORT); - } - - last = start_it->extract_sublist(end_it); -} - -/*********************************************************************** - * CLIST::sort - * - * Sort elements on list - **********************************************************************/ - -void CLIST::sort( // sort elements - int comparator( // comparison routine - const void *, const void *)) { - // Allocate an array of pointers, one per list element. - auto count = length(); - if (count > 0) { - // ptr array to sort - std::vector base; - base.reserve(count); - - CLIST_ITERATOR it(this); - - // Extract all elements, putting the pointers in the array. - for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { - base.push_back(it.extract()); - } - - // Sort the pointer array. - qsort(&base[0], count, sizeof(base[0]), comparator); - - // Rebuild the list from the sorted pointers. - for (auto current : base) { - it.add_to_end(current); - } - } -} - -// Assuming list has been sorted already, insert new_data to -// keep the list sorted according to the same comparison function. -// Comparison function is the same as used by sort, i.e. uses double -// indirection. Time is O(1) to add to beginning or end. -// Time is linear to add pre-sorted items to an empty list. -// If unique, then don't add duplicate entries. -// Returns true if the element was added to the list. -bool CLIST::add_sorted(int comparator(const void *, const void *), bool unique, void *new_data) { - // Check for adding at the end. - if (last == nullptr || comparator(&last->data, &new_data) < 0) { - auto *new_element = new CLIST_LINK; - new_element->data = new_data; - if (last == nullptr) { - new_element->next = new_element; - } else { - new_element->next = last->next; - last->next = new_element; - } - last = new_element; - return true; - } else if (!unique || last->data != new_data) { - // Need to use an iterator. - CLIST_ITERATOR it(this); - for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { - void *data = it.data(); - if (data == new_data && unique) { - return false; - } - if (comparator(&data, &new_data) > 0) { - break; - } - } - if (it.cycled_list()) { - it.add_to_end(new_data); - } else { - it.add_before_then_move(new_data); - } - return true; - } - return false; -} - -// Assuming that the minuend and subtrahend are already sorted with -// the same comparison function, shallow clears this and then copies -// the set difference minuend - subtrahend to this, being the elements -// of minuend that do not compare equal to anything in subtrahend. -// If unique is true, any duplicates in minuend are also eliminated. -void CLIST::set_subtract(int comparator(const void *, const void *), bool unique, CLIST *minuend, - CLIST *subtrahend) { - shallow_clear(); - CLIST_ITERATOR m_it(minuend); - CLIST_ITERATOR s_it(subtrahend); - // Since both lists are sorted, finding the subtras that are not - // minus is a case of a parallel iteration. - for (m_it.mark_cycle_pt(); !m_it.cycled_list(); m_it.forward()) { - void *minu = m_it.data(); - void *subtra = nullptr; - if (!s_it.empty()) { - subtra = s_it.data(); - while (!s_it.at_last() && comparator(&subtra, &minu) < 0) { - s_it.forward(); - subtra = s_it.data(); - } - } - if (subtra == nullptr || comparator(&subtra, &minu) != 0) { - add_sorted(comparator, unique, minu); - } - } -} - -/*********************************************************************** - * MEMBER FUNCTIONS OF CLASS: CLIST_ITERATOR - * ========================================= - **********************************************************************/ - -/*********************************************************************** - * CLIST_ITERATOR::forward - * - * Move the iterator to the next element of the list. - * REMEMBER: ALL LISTS ARE CIRCULAR. - **********************************************************************/ - -void *CLIST_ITERATOR::forward() { - if (list->empty()) { - return nullptr; - } - - if (current) { // not removed so - // set previous - prev = current; - started_cycling = true; - // In case next is deleted by another iterator, get next from current. - current = current->next; - } else { - if (ex_current_was_cycle_pt) { - cycle_pt = next; - } - current = next; - } - - next = current->next; - return current->data; -} - -/*********************************************************************** - * CLIST_ITERATOR::data_relative - * - * Return the data pointer to the element "offset" elements from current. - * "offset" must not be less than -1. - * (This function can't be INLINEd because it contains a loop) - **********************************************************************/ - -void *CLIST_ITERATOR::data_relative( // get data + or - ... - int8_t offset) { // offset from current - CLIST_LINK *ptr; - -#ifndef NDEBUG - if (!list) - NO_LIST.error("CLIST_ITERATOR::data_relative", ABORT); - if (list->empty()) - EMPTY_LIST.error("CLIST_ITERATOR::data_relative", ABORT); - if (offset < -1) - BAD_PARAMETER.error("CLIST_ITERATOR::data_relative", ABORT, "offset < -l"); -#endif - - if (offset == -1) { - ptr = prev; - } else { - for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next) { - ; - } - } - - return ptr->data; -} - -/*********************************************************************** - * CLIST_ITERATOR::move_to_last() - * - * Move current so that it is set to the end of the list. - * Return data just in case anyone wants it. - * (This function can't be INLINEd because it contains a loop) - **********************************************************************/ - -void *CLIST_ITERATOR::move_to_last() { - while (current != list->last) { - forward(); - } - - if (current == nullptr) { - return nullptr; - } else { - return current->data; - } -} - -/*********************************************************************** - * CLIST_ITERATOR::exchange() - * - * 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 - * elements. On return, each iterator points to the element which was the - * other iterators current on entry. - * (This function hasn't been in-lined because its a bit big!) - **********************************************************************/ - -void CLIST_ITERATOR::exchange( // positions of 2 links - CLIST_ITERATOR *other_it) { // other iterator - 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 -link */ - - if ((list->empty()) || (other_it->list->empty()) || (current == other_it->current)) { - return; - } - - /* Error if either current element is deleted */ - - if (!current || !other_it->current) { - DONT_EXCHANGE_DELETED.error("CLIST_ITERATOR.exchange", ABORT); - } - - /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements -(other before this); non-doubleton adjacent elements (this before other); -non-adjacent elements. */ - - // adjacent links - if ((next == other_it->current) || (other_it->next == current)) { - // doubleton list - if ((next == other_it->current) && (other_it->next == current)) { - prev = next = current; - other_it->prev = other_it->next = other_it->current; - } else { // non-doubleton with - // adjacent links - // other before this - if (other_it->next == current) { - other_it->prev->next = current; - other_it->current->next = next; - current->next = other_it->current; - other_it->next = other_it->current; - prev = current; - } else { // this before other - prev->next = other_it->current; - current->next = other_it->next; - other_it->current->next = current; - next = current; - other_it->prev = other_it->current; - } - } - } else { // no overlap - prev->next = other_it->current; - current->next = other_it->next; - other_it->prev->next = current; - other_it->current->next = next; - } - - /* update end of list pointer when necessary (remember that the 2 iterators - may iterate over different lists!) */ - - if (list->last == current) { - list->last = other_it->current; - } - if (other_it->list->last == other_it->current) { - other_it->list->last = current; - } - - if (current == cycle_pt) { - cycle_pt = other_it->cycle_pt; - } - if (other_it->current == other_it->cycle_pt) { - other_it->cycle_pt = cycle_pt; - } - - /* The actual exchange - in all cases*/ - - auto old_current = current; - current = other_it->current; - other_it->current = old_current; -} - -/*********************************************************************** - * CLIST_ITERATOR::extract_sublist() - * - * This is a private member, used only by CLIST::assign_to_sublist. - * 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 - * pointer to the last element. - * (Can't inline this function because it contains a loop) - **********************************************************************/ - -CLIST_LINK *CLIST_ITERATOR::extract_sublist( // from this current - CLIST_ITERATOR *other_it) { // to other current - CLIST_ITERATOR temp_it = *this; - - constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); -#ifndef NDEBUG - 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"); - - if (list != other_it->list) - BAD_EXTRACTION_PTS.error("CLIST_ITERATOR.extract_sublist", ABORT); - if (list->empty()) - EMPTY_LIST.error("CLIST_ITERATOR::extract_sublist", ABORT); - - if (!current || !other_it->current) - DONT_EXTRACT_DELETED.error("CLIST_ITERATOR.extract_sublist", ABORT); -#endif - - ex_current_was_last = other_it->ex_current_was_last = false; - ex_current_was_cycle_pt = false; - other_it->ex_current_was_cycle_pt = false; - - temp_it.mark_cycle_pt(); - do { // walk sublist - if (temp_it.cycled_list()) { // can't find end pt - BAD_SUBLIST.error("CLIST_ITERATOR.extract_sublist", ABORT); - } - - if (temp_it.at_last()) { - list->last = prev; - ex_current_was_last = other_it->ex_current_was_last = true; - } - - if (temp_it.current == cycle_pt) { - ex_current_was_cycle_pt = true; - } - - if (temp_it.current == other_it->cycle_pt) { - other_it->ex_current_was_cycle_pt = true; - } - - temp_it.forward(); - } while (temp_it.prev != other_it->current); - - // circularise sublist - other_it->current->next = current; - auto end_of_new_list = other_it->current; - - // sublist = whole list - if (prev == other_it->current) { - list->last = nullptr; - prev = current = next = nullptr; - other_it->prev = other_it->current = other_it->next = nullptr; - } else { - prev->next = other_it->next; - current = other_it->current = nullptr; - next = other_it->next; - other_it->prev = prev; - } - return end_of_new_list; -} - -} // namespace tesseract diff --git a/src/ccutil/clst.h b/src/ccutil/clst.h index a731cc55ed..dfc77b5e2e 100644 --- a/src/ccutil/clst.h +++ b/src/ccutil/clst.h @@ -27,687 +27,976 @@ namespace tesseract { -class CLIST_ITERATOR; - -/********************************************************************** - * CLASS - CLIST_LINK - * - * Generic link class for singly linked CONS cell lists - * - * Note: No destructor - elements are assumed to be destroyed EITHER after - * they have been extracted from a list OR by the CLIST destructor which - * walks the list. - **********************************************************************/ - -class CLIST_LINK { - friend class CLIST_ITERATOR; - friend class CLIST; - - CLIST_LINK *next; - void *data; - -public: - CLIST_LINK() { // constructor - data = next = nullptr; - } - - CLIST_LINK(const CLIST_LINK &) = delete; - void operator=(const CLIST_LINK &) = delete; -}; - /********************************************************************** * CLASS - CLIST * * Generic list class for singly linked CONS cell lists **********************************************************************/ +template class TESS_API CLIST { - friend class CLIST_ITERATOR; - - CLIST_LINK *last = nullptr; // End of list - - //(Points to head) - CLIST_LINK *First() { // return first - return last != nullptr ? last->next : nullptr; - } - - const CLIST_LINK *First() const { // return first - return last != nullptr ? last->next : nullptr; - } + friend class CLIST_LINK; + //friend class CLIST_ITERATOR; public: - ~CLIST() { // destructor - shallow_clear(); - } - - void internal_deep_clear( // destroy all links - void (*zapper)(void *)); // ptr to zapper functn - - void shallow_clear(); // clear list but don't - // delete data elements - - bool empty() const { // is list empty? - return !last; - } - - bool singleton() const { - return last != nullptr ? (last == last->next) : false; - } - void shallow_copy( // dangerous!! - CLIST *from_list) { // beware destructors!! - last = from_list->last; - } - - void assign_to_sublist( // to this list - CLIST_ITERATOR *start_it, // from list start - CLIST_ITERATOR *end_it); // from list end - - int32_t length() const { //# elements in list - int32_t count = 0; - if (last != nullptr) { - count = 1; - for (auto it = last->next; it != last; it = it->next) { - count++; - } + /********************************************************************** + * CLASS - CLIST_LINK + * + * Generic link class for singly linked CONS cell lists + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the CLIST destructor which + * walks the list. + **********************************************************************/ + struct CLIST_LINK { + CLIST_LINK *next; + void *data; + + public: + CLIST_LINK() { // constructor + data = next = nullptr; } - return count; - } - - void sort( // sort elements - int comparator( // comparison routine - const void *, const void *)); - - // Assuming list has been sorted already, insert new_data to - // keep the list sorted according to the same comparison function. - // Comparison function is the same as used by sort, i.e. uses double - // indirection. Time is O(1) to add to beginning or end. - // Time is linear to add pre-sorted items to an empty list. - // If unique, then don't add duplicate entries. - // Returns true if the element was added to the list. - bool add_sorted(int comparator(const void *, const void *), bool unique, void *new_data); - // Assuming that the minuend and subtrahend are already sorted with - // the same comparison function, shallow clears this and then copies - // the set difference minuend - subtrahend to this, being the elements - // of minuend that do not compare equal to anything in subtrahend. - // If unique is true, any duplicates in minuend are also eliminated. - void set_subtract(int comparator(const void *, const void *), bool unique, CLIST *minuend, - CLIST *subtrahend); -}; - -/*********************************************************************** - * CLASS - CLIST_ITERATOR - * - * Generic iterator class for singly linked lists with embedded - *links - **********************************************************************/ - -class TESS_API CLIST_ITERATOR { - friend void CLIST::assign_to_sublist(CLIST_ITERATOR *, CLIST_ITERATOR *); + CLIST_LINK(const CLIST_LINK &) = delete; + void operator=(const CLIST_LINK &) = delete; + }; - CLIST *list; // List being iterated - CLIST_LINK *prev; // prev element - CLIST_LINK *current; // current element - CLIST_LINK *next; // next element - CLIST_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_cycle_pt; // current extracted was cycle point - bool started_cycling; // Have we moved off the start? + /*********************************************************************** + * CLASS - CLIST_ITERATOR + * + * Generic iterator class for singly linked lists with embedded + *links + **********************************************************************/ + class TESS_API CLIST_ITERATOR { + CLIST *list; // List being iterated + CLIST_LINK *prev; // prev element + CLIST_LINK *current; // current element + CLIST_LINK *next; // next element + CLIST_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_cycle_pt; // current extracted was cycle point + bool started_cycling; // Have we moved off the start? + + /*********************************************************************** + * CLIST_ITERATOR::extract_sublist() + * + * This is a private member, used only by CLIST::assign_to_sublist. + * 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 + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + CLIST_LINK *extract_sublist( // from this current + CLIST_ITERATOR *other_it) { // to other current + CLIST_ITERATOR temp_it = *this; + + constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); +#ifndef NDEBUG + 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"); - CLIST_LINK *extract_sublist( // from this current... - CLIST_ITERATOR *other_it); // to other current + if (list != other_it->list) + BAD_EXTRACTION_PTS.error("CLIST_ITERATOR.extract_sublist", ABORT); + if (list->empty()) + EMPTY_LIST.error("CLIST_ITERATOR::extract_sublist", ABORT); -public: - CLIST_ITERATOR() { // constructor - list = nullptr; - } // unassigned list + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error("CLIST_ITERATOR.extract_sublist", ABORT); +#endif - CLIST_ITERATOR( // constructor - CLIST *list_to_iterate); + ex_current_was_last = other_it->ex_current_was_last = false; + ex_current_was_cycle_pt = false; + other_it->ex_current_was_cycle_pt = false; - void set_to_list( // change list - CLIST *list_to_iterate); + temp_it.mark_cycle_pt(); + do { // walk sublist + if (temp_it.cycled_list()) { // can't find end pt + BAD_SUBLIST.error("CLIST_ITERATOR.extract_sublist", ABORT); + } - void add_after_then_move( // add after current & - void *new_data); // move to new + if (temp_it.at_last()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = true; + } - void add_after_stay_put( // add after current & - void *new_data); // stay at current + if (temp_it.current == cycle_pt) { + ex_current_was_cycle_pt = true; + } - void add_before_then_move( // add before current & - void *new_data); // move to new + if (temp_it.current == other_it->cycle_pt) { + other_it->ex_current_was_cycle_pt = true; + } - void add_before_stay_put( // add before current & - void *new_data); // stay at current + temp_it.forward(); + } while (temp_it.prev != other_it->current); + + // circularise sublist + other_it->current->next = current; + auto end_of_new_list = other_it->current; + + // sublist = whole list + if (prev == other_it->current) { + list->last = nullptr; + prev = current = next = nullptr; + other_it->prev = other_it->current = other_it->next = nullptr; + } else { + prev->next = other_it->next; + current = other_it->current = nullptr; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; + } - void add_list_after( // add a list & - CLIST *list_to_add); // stay at current + public: + CLIST_ITERATOR() { // constructor + list = nullptr; + } // unassigned list + + /*********************************************************************** + * CLIST_ITERATOR::CLIST_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + CLIST_ITERATOR( // constructor + CLIST *list_to_iterate) { + set_to_list(list_to_iterate); + } - void add_list_before( // add a list & - CLIST *list_to_add); // move to it 1st item + /*********************************************************************** + * CLIST_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + void set_to_list( // change list + CLIST *list_to_iterate) { + list = list_to_iterate; + prev = list->last; + current = list->First(); + next = current != nullptr ? current->next : nullptr; + cycle_pt = nullptr; // await explicit set + started_cycling = false; + ex_current_was_last = false; + ex_current_was_cycle_pt = false; + } - void *data() { // get current data + /*********************************************************************** + * CLIST_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + void add_after_then_move( // add after current & + void *new_data) { #ifndef NDEBUG - if (!list) { - NO_LIST.error("CLIST_ITERATOR::data", ABORT); - } + if (!new_data) { + BAD_PARAMETER.error("CLIST_ITERATOR::add_after_then_move", ABORT, "new_data is nullptr"); + } #endif - return current->data; - } - - void *data_relative( // get data + or - ... - int8_t offset); // offset from current - - void *forward(); // move to next element - void *extract(); // remove from list - - void *move_to_first(); // go to start of list + auto new_element = new CLIST_LINK; + new_element->data = new_data; - void *move_to_last(); // go to end of list + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } else { + new_element->next = next; + + if (current) { // not extracted + current->next = new_element; + prev = current; + if (current == list->last) { + list->last = new_element; + } + } else { // current extracted + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + } + if (ex_current_was_cycle_pt) { + cycle_pt = new_element; + } + } + } + current = new_element; + } // move to new + + /*********************************************************************** + * CLIST_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + void add_after_stay_put( // add after current & + void *new_data) { +#ifndef NDEBUG + if (!new_data) { + BAD_PARAMETER.error("CLIST_ITERATOR::add_after_stay_put", ABORT, "new_data is nullptr"); + } +#endif - void mark_cycle_pt(); // remember current + auto new_element = new CLIST_LINK; + new_element->data = new_data; - bool empty() const { // is list empty? - return list->empty(); - } + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = false; + current = nullptr; + } else { + new_element->next = next; + + if (current) { // not extracted + current->next = new_element; + if (prev == current) { + prev = new_element; + } + if (current == list->last) { + list->last = new_element; + } + } else { // current extracted + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = false; + } + } + next = new_element; + } + } // stay at current + + /*********************************************************************** + * CLIST_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + void add_before_then_move( // add before current & + void *new_data) { +#ifndef NDEBUG + if (!new_data) { + BAD_PARAMETER.error("CLIST_ITERATOR::add_before_then_move", ABORT, "new_data is nullptr"); + } +#endif - bool current_extracted() const { // current extracted? - return !current; - } + auto new_element = new CLIST_LINK; + new_element->data = new_data; - bool at_first() const; // Current is first? + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } else { + prev->next = new_element; + if (current) { // not extracted + new_element->next = current; + next = current; + } else { // current extracted + new_element->next = next; + if (ex_current_was_last) { + list->last = new_element; + } + if (ex_current_was_cycle_pt) { + cycle_pt = new_element; + } + } + } + current = new_element; + } // move to new + + /*********************************************************************** + * CLIST_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but don't move the + * iterator to the new element. + **********************************************************************/ + void add_before_stay_put( // add before current & + void *new_data) { +#ifndef NDEBUG + if (!new_data) { + BAD_PARAMETER.error("CLIST_ITERATOR::add_before_stay_put", ABORT, "new_data is nullptr"); + } +#endif - bool at_last() const; // Current is last? + auto new_element = new CLIST_LINK; + new_element->data = new_data; - bool cycled_list() const; // Completed a cycle? + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = true; + current = nullptr; + } else { + prev->next = new_element; + if (current) { // not extracted + new_element->next = current; + if (next == current) { + next = new_element; + } + } else { // current extracted + new_element->next = next; + if (ex_current_was_last) { + list->last = new_element; + } + } + prev = new_element; + } + } // stay at current + + /*********************************************************************** + * CLIST_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but don't move + *the + * iterator. + **********************************************************************/ + void add_list_after( // add a list & + CLIST *list_to_add) { + if (!list_to_add->empty()) { + if (list->empty()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First(); + ex_current_was_last = true; + current = nullptr; + } else { + if (current) { // not extracted + current->next = list_to_add->First(); + if (current == list->last) { + list->last = list_to_add->last; + } + list_to_add->last->next = next; + next = current->next; + } else { // current extracted + prev->next = list_to_add->First(); + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = false; + } + list_to_add->last->next = next; + next = prev->next; + } + } + list_to_add->last = nullptr; + } + } // stay at current + + /*********************************************************************** + * CLIST_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + void add_list_before( // add a list & + CLIST *list_to_add) { + if (!list_to_add->empty()) { + if (list->empty()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First(); + next = current->next; + ex_current_was_last = false; + } else { + prev->next = list_to_add->First(); + if (current) { // not extracted + list_to_add->last->next = current; + } else { // current extracted + list_to_add->last->next = next; + if (ex_current_was_last) { + list->last = list_to_add->last; + } + if (ex_current_was_cycle_pt) { + cycle_pt = prev->next; + } + } + current = prev->next; + next = current->next; + } + list_to_add->last = nullptr; + } + } // move to it 1st item - void add_to_end( // add at end & - void *new_data); // don't move + void *data() { // get current data +#ifndef NDEBUG + if (!list) { + NO_LIST.error("CLIST_ITERATOR::data", ABORT); + } +#endif + return current->data; + } - void exchange( // positions of 2 links - CLIST_ITERATOR *other_it); // other iterator + /*********************************************************************** + * CLIST_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * "offset" must not be less than -1. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + void *data_relative( // get data + or - ... + int8_t offset) { // offset from current + CLIST_LINK *ptr; - int32_t length() const; //# elements in list +#ifndef NDEBUG + if (!list) + NO_LIST.error("CLIST_ITERATOR::data_relative", ABORT); + if (list->empty()) + EMPTY_LIST.error("CLIST_ITERATOR::data_relative", ABORT); + if (offset < -1) + BAD_PARAMETER.error("CLIST_ITERATOR::data_relative", ABORT, "offset < -l"); +#endif - void sort( // sort elements - int comparator( // comparison routine - const void *, const void *)); -}; + if (offset == -1) { + ptr = prev; + } else { + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next) { + ; + } + } -/*********************************************************************** - * CLIST_ITERATOR::set_to_list - * - * (Re-)initialise the iterator to point to the start of the list_to_iterate - * over. - **********************************************************************/ + return ptr->data; + } -inline void CLIST_ITERATOR::set_to_list( // change list - CLIST *list_to_iterate) { - list = list_to_iterate; - prev = list->last; - current = list->First(); - next = current != nullptr ? current->next : nullptr; - cycle_pt = nullptr; // await explicit set - started_cycling = false; - ex_current_was_last = false; - ex_current_was_cycle_pt = false; -} - -/*********************************************************************** - * CLIST_ITERATOR::CLIST_ITERATOR - * - * CONSTRUCTOR - set iterator to specified list; - **********************************************************************/ + /*********************************************************************** + * CLIST_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + void *forward() { + if (list->empty()) { + return nullptr; + } -inline CLIST_ITERATOR::CLIST_ITERATOR(CLIST *list_to_iterate) { - set_to_list(list_to_iterate); -} + if (current) { // not removed so + // set previous + prev = current; + started_cycling = true; + // In case next is deleted by another iterator, get next from current. + current = current->next; + } else { + if (ex_current_was_cycle_pt) { + cycle_pt = next; + } + current = next; + } -/*********************************************************************** - * CLIST_ITERATOR::add_after_then_move - * - * Add a new element to the list after the current element and move the - * iterator to the new element. - **********************************************************************/ + next = current->next; + return current->data; + } -inline void CLIST_ITERATOR::add_after_then_move( // element to add - void *new_data) { + /*********************************************************************** + * CLIST_ITERATOR::extract + * + * 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 + * 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. + **********************************************************************/ + void *extract() { #ifndef NDEBUG - if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_after_then_move", ABORT, "new_data is nullptr"); - } + if (!current) { // list empty or + // element extracted + NULL_CURRENT.error("CLIST_ITERATOR::extract", ABORT); + } #endif - auto new_element = new CLIST_LINK; - new_element->data = new_data; - - if (list->empty()) { - new_element->next = new_element; - list->last = new_element; - prev = next = new_element; - } else { - new_element->next = next; + if (list->singleton()) { + // Special case where we do need to change the iterator. + prev = next = list->last = nullptr; + } else { + prev->next = next; // remove from list - if (current) { // not extracted - current->next = new_element; - prev = current; - if (current == list->last) { - list->last = new_element; + if (current == list->last) { + list->last = prev; + ex_current_was_last = true; + } else { + ex_current_was_last = false; + } } - } else { // current extracted - prev->next = new_element; - if (ex_current_was_last) { - list->last = new_element; + // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. + ex_current_was_cycle_pt = (current == cycle_pt); + auto extracted_data = current->data; + delete (current); // destroy CONS cell + current = nullptr; + return extracted_data; + } // remove from list + + /*********************************************************************** + * CLIST_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + void *move_to_first() { + current = list->First(); + prev = list->last; + next = current != nullptr ? current->next : nullptr; + return current != nullptr ? current->data : nullptr; + } // go to start of list + + /*********************************************************************** + * CLIST_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + void *move_to_last() { + while (current != list->last) { + forward(); } - if (ex_current_was_cycle_pt) { - cycle_pt = new_element; + + if (current == nullptr) { + return nullptr; + } else { + return current->data; } } - } - current = new_element; -} -/*********************************************************************** - * CLIST_ITERATOR::add_after_stay_put - * - * Add a new element to the list after the current element but do not move - * the iterator to the new element. - **********************************************************************/ - -inline void CLIST_ITERATOR::add_after_stay_put( // element to add - void *new_data) { + /*********************************************************************** + * CLIST_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + void mark_cycle_pt() { #ifndef NDEBUG - if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_after_stay_put", ABORT, "new_data is nullptr"); - } + if (!list) { + NO_LIST.error("CLIST_ITERATOR::mark_cycle_pt", ABORT); + } #endif - auto new_element = new CLIST_LINK; - new_element->data = new_data; - - if (list->empty()) { - new_element->next = new_element; - list->last = new_element; - prev = next = new_element; - ex_current_was_last = false; - current = nullptr; - } else { - new_element->next = next; - - if (current) { // not extracted - current->next = new_element; - if (prev == current) { - prev = new_element; - } - if (current == list->last) { - list->last = new_element; - } - } else { // current extracted - prev->next = new_element; - if (ex_current_was_last) { - list->last = new_element; - ex_current_was_last = false; + if (current) { + cycle_pt = current; + } else { + ex_current_was_cycle_pt = true; } + started_cycling = false; + } // remember current + + bool empty() const { // is list empty? + return list->empty(); } - next = new_element; - } -} -/*********************************************************************** - * CLIST_ITERATOR::add_before_then_move - * - * Add a new element to the list before the current element and move the - * iterator to the new element. - **********************************************************************/ + bool current_extracted() const { // current extracted? + return !current; + } -inline void CLIST_ITERATOR::add_before_then_move( // element to add - void *new_data) { + /*********************************************************************** + * CLIST_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + bool at_first() const { + // we're at a deleted + return ((list->empty()) || (current == list->First()) || + ((current == nullptr) && (prev == list->last) && // NON-last pt between + !ex_current_was_last)); // first and last + } // Current is first? + + /*********************************************************************** + * CLIST_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + bool at_last() const { + // we're at a deleted + return ((list->empty()) || (current == list->last) || + ((current == nullptr) && (prev == list->last) && // last point between + ex_current_was_last)); // first and last + } // Current is last? + + /*********************************************************************** + * CLIST_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + bool cycled_list() const { // Completed a cycle? + return ((list->empty()) || ((current == cycle_pt) && started_cycling)); + } + + /*********************************************************************** + * CLIST_ITERATOR::add_to_end + * + * 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 + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. + **********************************************************************/ + void add_to_end( // element to add + void *new_data) { #ifndef NDEBUG - if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_before_then_move", ABORT, "new_data is nullptr"); - } + if (!list) { + NO_LIST.error("CLIST_ITERATOR::add_to_end", ABORT); + } + if (!new_data) { + BAD_PARAMETER.error("CLIST_ITERATOR::add_to_end", ABORT, "new_data is nullptr"); + } #endif - auto new_element = new CLIST_LINK; - new_element->data = new_data; - - if (list->empty()) { - new_element->next = new_element; - list->last = new_element; - prev = next = new_element; - } else { - prev->next = new_element; - if (current) { // not extracted - new_element->next = current; - next = current; - } else { // current extracted - new_element->next = next; - if (ex_current_was_last) { - list->last = new_element; - } - if (ex_current_was_cycle_pt) { - cycle_pt = new_element; + if (this->at_last()) { + this->add_after_stay_put(new_data); + } else { + if (this->at_first()) { + this->add_before_stay_put(new_data); + list->last = prev; + } else { // Iteratr is elsewhere + auto new_element = new CLIST_LINK; + new_element->data = new_data; + + new_element->next = list->last->next; + list->last->next = new_element; + list->last = new_element; + } } } - } - current = new_element; -} -/*********************************************************************** - * CLIST_ITERATOR::add_before_stay_put - * - * Add a new element to the list before the current element but don't move the - * iterator to the new element. - **********************************************************************/ + /*********************************************************************** + * CLIST_ITERATOR::exchange() + * + * 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 + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + void exchange( // positions of 2 links + CLIST_ITERATOR *other_it) { // other iterator + 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 + link */ + + if ((list->empty()) || (other_it->list->empty()) || (current == other_it->current)) { + return; + } -inline void CLIST_ITERATOR::add_before_stay_put( // element to add - void *new_data) { -#ifndef NDEBUG - if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_before_stay_put", ABORT, "new_data is nullptr"); - } -#endif + /* Error if either current element is deleted */ - auto new_element = new CLIST_LINK; - new_element->data = new_data; - - if (list->empty()) { - new_element->next = new_element; - list->last = new_element; - prev = next = new_element; - ex_current_was_last = true; - current = nullptr; - } else { - prev->next = new_element; - if (current) { // not extracted - new_element->next = current; - if (next == current) { - next = new_element; + if (!current || !other_it->current) { + DONT_EXCHANGE_DELETED.error("CLIST_ITERATOR.exchange", ABORT); } - } else { // current extracted - new_element->next = next; - if (ex_current_was_last) { - list->last = new_element; - } - } - prev = new_element; - } -} - -/*********************************************************************** - * CLIST_ITERATOR::add_list_after - * - * Insert another list to this list after the current element but don't move - *the - * iterator. - **********************************************************************/ -inline void CLIST_ITERATOR::add_list_after(CLIST *list_to_add) { - if (!list_to_add->empty()) { - if (list->empty()) { - list->last = list_to_add->last; - prev = list->last; - next = list->First(); - ex_current_was_last = true; - current = nullptr; - } else { - if (current) { // not extracted - current->next = list_to_add->First(); - if (current == list->last) { - list->last = list_to_add->last; - } - list_to_add->last->next = next; - next = current->next; - } else { // current extracted - prev->next = list_to_add->First(); - if (ex_current_was_last) { - list->last = list_to_add->last; - ex_current_was_last = false; + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + // adjacent links + if ((next == other_it->current) || (other_it->next == current)) { + // doubleton list + if ((next == other_it->current) && (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } else { // non-doubleton with + // adjacent links + // other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + current->next = other_it->current; + other_it->next = other_it->current; + prev = current; + } else { // this before other + prev->next = other_it->current; + current->next = other_it->next; + other_it->current->next = current; + next = current; + other_it->prev = other_it->current; + } } - list_to_add->last->next = next; - next = prev->next; + } else { // no overlap + prev->next = other_it->current; + current->next = other_it->next; + other_it->prev->next = current; + other_it->current->next = next; } - } - list_to_add->last = nullptr; - } -} -/*********************************************************************** - * CLIST_ITERATOR::add_list_before - * - * Insert another list to this list before the current element. Move the - * iterator to the start of the inserted elements - * iterator. - **********************************************************************/ + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ -inline void CLIST_ITERATOR::add_list_before(CLIST *list_to_add) { - if (!list_to_add->empty()) { - if (list->empty()) { - list->last = list_to_add->last; - prev = list->last; - current = list->First(); - next = current->next; - ex_current_was_last = false; - } else { - prev->next = list_to_add->First(); - if (current) { // not extracted - list_to_add->last->next = current; - } else { // current extracted - list_to_add->last->next = next; - if (ex_current_was_last) { - list->last = list_to_add->last; - } - if (ex_current_was_cycle_pt) { - cycle_pt = prev->next; - } + if (list->last == current) { + list->last = other_it->current; + } + if (other_it->list->last == other_it->current) { + other_it->list->last = current; } - current = prev->next; - next = current->next; - } - list_to_add->last = nullptr; - } -} -/*********************************************************************** - * CLIST_ITERATOR::extract - * - * 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 - * 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. - **********************************************************************/ + if (current == cycle_pt) { + cycle_pt = other_it->cycle_pt; + } + if (other_it->current == other_it->cycle_pt) { + other_it->cycle_pt = cycle_pt; + } -inline void *CLIST_ITERATOR::extract() { -#ifndef NDEBUG - if (!current) { // list empty or - // element extracted - NULL_CURRENT.error("CLIST_ITERATOR::extract", ABORT); - } -#endif + /* The actual exchange - in all cases*/ - if (list->singleton()) { - // Special case where we do need to change the iterator. - prev = next = list->last = nullptr; - } else { - prev->next = next; // remove from list + auto old_current = current; + current = other_it->current; + other_it->current = old_current; + } - if (current == list->last) { - list->last = prev; - ex_current_was_last = true; - } else { - ex_current_was_last = false; + /*********************************************************************** + * CLIST_ITERATOR::length() + * + * Return the length of the list + * + **********************************************************************/ + int32_t length() const { + return list->length(); } - } - // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. - ex_current_was_cycle_pt = (current == cycle_pt); - auto extracted_data = current->data; - delete (current); // destroy CONS cell - current = nullptr; - return extracted_data; -} - -/*********************************************************************** - * CLIST_ITERATOR::move_to_first() - * - * Move current so that it is set to the start of the list. - * Return data just in case anyone wants it. - **********************************************************************/ -inline void *CLIST_ITERATOR::move_to_first() { - current = list->First(); - prev = list->last; - next = current != nullptr ? current->next : nullptr; - return current != nullptr ? current->data : nullptr; -} + /*********************************************************************** + * CLIST_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + void sort( // sort elements + int comparator( // comparison routine + const void *, const void *)) { + list->sort(comparator); + move_to_first(); + } + }; -/*********************************************************************** - * CLIST_ITERATOR::mark_cycle_pt() - * - * Remember the current location so that we can tell whether we've returned - * to this point later. - * - * If the current point is deleted either now, or in the future, the cycle - * point will be set to the next item which is set to current. This could be - * by a forward, add_after_then_move or add_after_then_move. - **********************************************************************/ +private: + CLIST_LINK *last = nullptr; // End of list -inline void CLIST_ITERATOR::mark_cycle_pt() { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("CLIST_ITERATOR::mark_cycle_pt", ABORT); + //(Points to head) + CLIST_LINK *First() { // return first + return last != nullptr ? last->next : nullptr; } -#endif - if (current) { - cycle_pt = current; - } else { - ex_current_was_cycle_pt = true; + const CLIST_LINK *First() const { // return first + return last != nullptr ? last->next : nullptr; } - started_cycling = false; -} -/*********************************************************************** - * CLIST_ITERATOR::at_first() - * - * Are we at the start of the list? - * - **********************************************************************/ +public: + ~CLIST() { // destructor + shallow_clear(); + } -inline bool CLIST_ITERATOR::at_first() const { - // we're at a deleted - return ((list->empty()) || (current == list->First()) || - ((current == nullptr) && (prev == list->last) && // NON-last pt between - !ex_current_was_last)); // first and last -} + /*********************************************************************** + * CLIST::internal_deep_clear + * + * Used by the "deep_clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each data element of the list, regardless of its class. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + void internal_deep_clear() { // ptr to zapper functn + if (!empty()) { + auto ptr = last->next; // set to first + last->next = nullptr; // break circle + last = nullptr; // set list empty + while (ptr) { + auto next = ptr->next; + delete ptr->data; + delete (ptr); + ptr = next; + } + } + } + void deep_clear() { + internal_deep_clear(); + } -/*********************************************************************** - * CLIST_ITERATOR::at_last() - * - * Are we at the end of the list? - * - **********************************************************************/ + /*********************************************************************** + * CLIST::shallow_clear + * + * Used by the destructor and the "shallow_clear" member function of derived + * list classes to destroy the list. + * The data elements are NOT destroyed. + * + **********************************************************************/ + void shallow_clear() { // destroy all links + if (!empty()) { + auto ptr = last->next; // set to first + last->next = nullptr; // break circle + last = nullptr; // set list empty + while (ptr) { + auto next = ptr->next; + delete (ptr); + ptr = next; + } + } + } -inline bool CLIST_ITERATOR::at_last() const { - // we're at a deleted - return ((list->empty()) || (current == list->last) || - ((current == nullptr) && (prev == list->last) && // last point between - ex_current_was_last)); // first and last -} + bool empty() const { // is list empty? + return !last; + } -/*********************************************************************** - * CLIST_ITERATOR::cycled_list() - * - * Have we returned to the cycle_pt since it was set? - * - **********************************************************************/ + bool singleton() const { + return last != nullptr ? (last == last->next) : false; + } -inline bool CLIST_ITERATOR::cycled_list() const { - return ((list->empty()) || ((current == cycle_pt) && started_cycling)); -} + void shallow_copy( // dangerous!! + CLIST *from_list) { // beware destructors!! + last = from_list->last; + } -/*********************************************************************** - * CLIST_ITERATOR::length() - * - * Return the length of the list - * - **********************************************************************/ + /*********************************************************************** + * CLIST::assign_to_sublist + * + * 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 + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. + **********************************************************************/ + void assign_to_sublist( // to this list + CLIST_ITERATOR *start_it, // from list start + CLIST_ITERATOR *end_it) { // from list end + constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist"); + + if (!empty()) { + LIST_NOT_EMPTY.error("CLIST.assign_to_sublist", ABORT); + } -inline int32_t CLIST_ITERATOR::length() const { - return list->length(); -} + last = start_it->extract_sublist(end_it); + } -/*********************************************************************** - * CLIST_ITERATOR::sort() - * - * Sort the elements of the list, then reposition at the start. - * - **********************************************************************/ + int32_t length() const { //# elements in list + int32_t count = 0; + if (last != nullptr) { + count = 1; + for (auto it = last->next; it != last; it = it->next) { + count++; + } + } + return count; + } -inline void CLIST_ITERATOR::sort( // sort elements - int comparator( // comparison routine - const void *, const void *)) { - list->sort(comparator); - move_to_first(); -} + /*********************************************************************** + * CLIST::sort + * + * Sort elements on list + **********************************************************************/ + void sort( // sort elements + int comparator( // comparison routine + const void *, const void *)) { + // Allocate an array of pointers, one per list element. + auto count = length(); + if (count > 0) { + // ptr array to sort + std::vector base; + base.reserve(count); + + CLIST_ITERATOR it(this); + + // Extract all elements, putting the pointers in the array. + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + base.push_back(it.extract()); + } -/*********************************************************************** - * CLIST_ITERATOR::add_to_end - * - * 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 - * the iterator couldn't set its prev pointer. Adding to the end is - * essential for implementing - queues. -**********************************************************************/ - -inline void CLIST_ITERATOR::add_to_end( // element to add - void *new_data) { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("CLIST_ITERATOR::add_to_end", ABORT); - } - if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_to_end", ABORT, "new_data is nullptr"); + // Sort the pointer array. + qsort(&base[0], count, sizeof(base[0]), comparator); + + // Rebuild the list from the sorted pointers. + for (auto current : base) { + it.add_to_end(current); + } + } } -#endif - if (this->at_last()) { - this->add_after_stay_put(new_data); - } else { - if (this->at_first()) { - this->add_before_stay_put(new_data); - list->last = prev; - } else { // Iteratr is elsewhere - auto new_element = new CLIST_LINK; + // Assuming list has been sorted already, insert new_data to + // keep the list sorted according to the same comparison function. + // Comparison function is the same as used by sort, i.e. uses double + // indirection. Time is O(1) to add to beginning or end. + // Time is linear to add pre-sorted items to an empty list. + // If unique, then don't add duplicate entries. + // Returns true if the element was added to the list. + bool add_sorted(int comparator(const void *, const void *), bool unique, void *new_data) { + // Check for adding at the end. + if (last == nullptr || comparator(&last->data, &new_data) < 0) { + auto *new_element = new CLIST_LINK; new_element->data = new_data; - - new_element->next = list->last->next; - list->last->next = new_element; - list->last = new_element; + if (last == nullptr) { + new_element->next = new_element; + } else { + new_element->next = last->next; + last->next = new_element; + } + last = new_element; + return true; + } else if (!unique || last->data != new_data) { + // Need to use an iterator. + CLIST_ITERATOR it(this); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + void *data = it.data(); + if (data == new_data && unique) { + return false; + } + if (comparator(&data, &new_data) > 0) { + break; + } + } + if (it.cycled_list()) { + it.add_to_end(new_data); + } else { + it.add_before_then_move(new_data); + } + return true; } + return false; } -} -template -class X_CLIST : public CLIST { -public: - X_CLIST() = default; - X_CLIST(const X_CLIST &) = delete; - X_CLIST &operator=(const X_CLIST &) = delete; - - void deep_clear() { - internal_deep_clear([](void *link) {delete static_cast(link);}); + // Assuming that the minuend and subtrahend are already sorted with + // the same comparison function, shallow clears this and then copies + // the set difference minuend - subtrahend to this, being the elements + // of minuend that do not compare equal to anything in subtrahend. + // If unique is true, any duplicates in minuend are also eliminated. + void set_subtract(int comparator(const void *, const void *), bool unique, CLIST *minuend, + CLIST *subtrahend) { + shallow_clear(); + CLIST_ITERATOR m_it(minuend); + CLIST_ITERATOR s_it(subtrahend); + // Since both lists are sorted, finding the subtras that are not + // minus is a case of a parallel iteration. + for (m_it.mark_cycle_pt(); !m_it.cycled_list(); m_it.forward()) { + void *minu = m_it.data(); + void *subtra = nullptr; + if (!s_it.empty()) { + subtra = s_it.data(); + while (!s_it.at_last() && comparator(&subtra, &minu) < 0) { + s_it.forward(); + subtra = s_it.data(); + } + } + if (subtra == nullptr || comparator(&subtra, &minu) != 0) { + add_sorted(comparator, unique, minu); + } + } } }; #define CLISTIZEH(CLASSNAME) \ - class CLASSNAME##_CLIST : public X_CLIST { \ - using X_CLIST::X_CLIST; \ + class CLASSNAME##_CLIST : public CLIST { \ + using CLIST::CLIST; \ }; \ - struct CLASSNAME##_C_IT : X_ITER { \ - using X_ITER::X_ITER; \ + struct CLASSNAME##_C_IT : X_ITER::CLIST_ITERATOR, CLASSNAME> { \ + using X_ITER::CLIST_ITERATOR, CLASSNAME>::X_ITER; \ }; } // namespace tesseract From c6a769d50eb2a38b3bcce55875b0732a8ffd7656 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 03:49:40 +0300 Subject: [PATCH 02/17] Use real CLASSNAME type for list. Update sorting callback signatures. --- src/ccutil/clst.h | 56 ++++++++++++++++++++------------------------ src/textord/bbgrid.h | 25 ++++---------------- 2 files changed, 31 insertions(+), 50 deletions(-) diff --git a/src/ccutil/clst.h b/src/ccutil/clst.h index dfc77b5e2e..f8fd4135fb 100644 --- a/src/ccutil/clst.h +++ b/src/ccutil/clst.h @@ -50,14 +50,10 @@ class TESS_API CLIST { * walks the list. **********************************************************************/ struct CLIST_LINK { - CLIST_LINK *next; - void *data; - - public: - CLIST_LINK() { // constructor - data = next = nullptr; - } + CLIST_LINK *next{}; + CLASSNAME *data{}; + CLIST_LINK() = default; CLIST_LINK(const CLIST_LINK &) = delete; void operator=(const CLIST_LINK &) = delete; }; @@ -189,7 +185,7 @@ class TESS_API CLIST { * iterator to the new element. **********************************************************************/ void add_after_then_move( // add after current & - void *new_data) { + CLASSNAME *new_data) { #ifndef NDEBUG if (!new_data) { BAD_PARAMETER.error("CLIST_ITERATOR::add_after_then_move", ABORT, "new_data is nullptr"); @@ -232,7 +228,7 @@ class TESS_API CLIST { * the iterator to the new element. **********************************************************************/ void add_after_stay_put( // add after current & - void *new_data) { + CLASSNAME *new_data) { #ifndef NDEBUG if (!new_data) { BAD_PARAMETER.error("CLIST_ITERATOR::add_after_stay_put", ABORT, "new_data is nullptr"); @@ -277,7 +273,7 @@ class TESS_API CLIST { * iterator to the new element. **********************************************************************/ void add_before_then_move( // add before current & - void *new_data) { + CLASSNAME *new_data) { #ifndef NDEBUG if (!new_data) { BAD_PARAMETER.error("CLIST_ITERATOR::add_before_then_move", ABORT, "new_data is nullptr"); @@ -316,7 +312,7 @@ class TESS_API CLIST { * iterator to the new element. **********************************************************************/ void add_before_stay_put( // add before current & - void *new_data) { + CLASSNAME *new_data) { #ifndef NDEBUG if (!new_data) { BAD_PARAMETER.error("CLIST_ITERATOR::add_before_stay_put", ABORT, "new_data is nullptr"); @@ -423,7 +419,7 @@ class TESS_API CLIST { } } // move to it 1st item - void *data() { // get current data + CLASSNAME *data() { // get current data #ifndef NDEBUG if (!list) { NO_LIST.error("CLIST_ITERATOR::data", ABORT); @@ -439,7 +435,7 @@ class TESS_API CLIST { * "offset" must not be less than -1. * (This function can't be INLINEd because it contains a loop) **********************************************************************/ - void *data_relative( // get data + or - ... + CLASSNAME *data_relative( // get data + or - ... int8_t offset) { // offset from current CLIST_LINK *ptr; @@ -469,7 +465,7 @@ class TESS_API CLIST { * Move the iterator to the next element of the list. * REMEMBER: ALL LISTS ARE CIRCULAR. **********************************************************************/ - void *forward() { + CLASSNAME *forward() { if (list->empty()) { return nullptr; } @@ -499,7 +495,7 @@ class TESS_API CLIST { * 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. **********************************************************************/ - void *extract() { + CLASSNAME *extract() { #ifndef NDEBUG if (!current) { // list empty or // element extracted @@ -534,7 +530,7 @@ class TESS_API CLIST { * Move current so that it is set to the start of the list. * Return data just in case anyone wants it. **********************************************************************/ - void *move_to_first() { + CLASSNAME *move_to_first() { current = list->First(); prev = list->last; next = current != nullptr ? current->next : nullptr; @@ -548,7 +544,7 @@ class TESS_API CLIST { * Return data just in case anyone wants it. * (This function can't be INLINEd because it contains a loop) **********************************************************************/ - void *move_to_last() { + CLASSNAME *move_to_last() { while (current != list->last) { forward(); } @@ -639,7 +635,7 @@ class TESS_API CLIST { queues. **********************************************************************/ void add_to_end( // element to add - void *new_data) { + CLASSNAME *new_data) { #ifndef NDEBUG if (!list) { NO_LIST.error("CLIST_ITERATOR::add_to_end", ABORT); @@ -895,12 +891,12 @@ class TESS_API CLIST { **********************************************************************/ void sort( // sort elements int comparator( // comparison routine - const void *, const void *)) { + const CLASSNAME *, const CLASSNAME *)) { // Allocate an array of pointers, one per list element. auto count = length(); if (count > 0) { // ptr array to sort - std::vector base; + std::vector base; base.reserve(count); CLIST_ITERATOR it(this); @@ -911,7 +907,7 @@ class TESS_API CLIST { } // Sort the pointer array. - qsort(&base[0], count, sizeof(base[0]), comparator); + std::sort(base.begin(), base.end(), comparator); // Rebuild the list from the sorted pointers. for (auto current : base) { @@ -927,9 +923,9 @@ class TESS_API CLIST { // Time is linear to add pre-sorted items to an empty list. // If unique, then don't add duplicate entries. // Returns true if the element was added to the list. - bool add_sorted(int comparator(const void *, const void *), bool unique, void *new_data) { + bool add_sorted(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLASSNAME *new_data) { // 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 CLIST_LINK; new_element->data = new_data; if (last == nullptr) { @@ -944,11 +940,11 @@ class TESS_API CLIST { // Need to use an iterator. CLIST_ITERATOR it(this); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { - void *data = it.data(); + auto data = it.data(); if (data == new_data && unique) { return false; } - if (comparator(&data, &new_data) > 0) { + if (comparator(data, new_data) > 0) { break; } } @@ -967,7 +963,7 @@ class TESS_API CLIST { // the set difference minuend - subtrahend to this, being the elements // of minuend that do not compare equal to anything in subtrahend. // If unique is true, any duplicates in minuend are also eliminated. - void set_subtract(int comparator(const void *, const void *), bool unique, CLIST *minuend, + void set_subtract(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLIST *minuend, CLIST *subtrahend) { shallow_clear(); CLIST_ITERATOR m_it(minuend); @@ -975,16 +971,16 @@ class TESS_API CLIST { // Since both lists are sorted, finding the subtras that are not // minus is a case of a parallel iteration. for (m_it.mark_cycle_pt(); !m_it.cycled_list(); m_it.forward()) { - void *minu = m_it.data(); - void *subtra = nullptr; + auto minu = m_it.data(); + CLASSNAME *subtra = nullptr; if (!s_it.empty()) { subtra = s_it.data(); - while (!s_it.at_last() && comparator(&subtra, &minu) < 0) { + while (!s_it.at_last() && comparator(subtra, minu) < 0) { s_it.forward(); subtra = s_it.data(); } } - if (subtra == nullptr || comparator(&subtra, &minu) != 0) { + if (subtra == nullptr || comparator(subtra, minu) != 0) { add_sorted(comparator, unique, minu); } } diff --git a/src/textord/bbgrid.h b/src/textord/bbgrid.h index cced30063d..ddaf7222f8 100644 --- a/src/textord/bbgrid.h +++ b/src/textord/bbgrid.h @@ -364,10 +364,7 @@ class GridSearch { // Sort function to sort a BBC by bounding_box().left(). template -int SortByBoxLeft(const void *void1, const void *void2) { - // The void*s are actually doubly indirected, so get rid of one level. - const BBC *p1 = *static_cast(void1); - const BBC *p2 = *static_cast(void2); +int SortByBoxLeft(const BBC *p1, const BBC *p2) { int result = p1->bounding_box().left() - p2->bounding_box().left(); if (result != 0) { return result; @@ -384,10 +381,7 @@ int SortByBoxLeft(const void *void1, const void *void2) { } template -bool StdSortByBoxLeft(const void *void1, const void *void2) { - // The void*s are actually doubly indirected, so get rid of one level. - const BBC *p1 = *static_cast(void1); - const BBC *p2 = *static_cast(void2); +bool StdSortByBoxLeft(const BBC *p1, const BBC *p2) { int result = p1->bounding_box().left() - p2->bounding_box().left(); if (result != 0) { return result < 0; @@ -405,10 +399,7 @@ bool StdSortByBoxLeft(const void *void1, const void *void2) { // Sort function to sort a BBC by bounding_box().right() in right-to-left order. template -int SortRightToLeft(const void *void1, const void *void2) { - // The void*s are actually doubly indirected, so get rid of one level. - const BBC *p1 = *static_cast(void1); - const BBC *p2 = *static_cast(void2); +int SortRightToLeft(const BBC *p1, const BBC *p2) { int result = p2->bounding_box().right() - p1->bounding_box().right(); if (result != 0) { return result; @@ -425,10 +416,7 @@ int SortRightToLeft(const void *void1, const void *void2) { } template -bool StdSortRightToLeft(const void *void1, const void *void2) { - // The void*s are actually doubly indirected, so get rid of one level. - const BBC *p1 = *static_cast(void1); - const BBC *p2 = *static_cast(void2); +bool StdSortRightToLeft(const BBC *p1, const BBC *p2) { int result = p2->bounding_box().right() - p1->bounding_box().right(); if (result != 0) { return result < 0; @@ -446,10 +434,7 @@ bool StdSortRightToLeft(const void *void1, const void *void2) { // Sort function to sort a BBC by bounding_box().bottom(). template -int SortByBoxBottom(const void *void1, const void *void2) { - // The void*s are actually doubly indirected, so get rid of one level. - const BBC *p1 = *static_cast(void1); - const BBC *p2 = *static_cast(void2); +int SortByBoxBottom(const BBC *p1, const BBC *p2) { int result = p1->bounding_box().bottom() - p2->bounding_box().bottom(); if (result != 0) { return result; From 3d7d3c39d28041102fd56e24cc9a42baacfa6c4b Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 03:49:58 +0300 Subject: [PATCH 03/17] Make simple classes simpler. --- src/ccutil/list.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ccutil/list.h b/src/ccutil/list.h index d9dfe09513..e5e6e2e88e 100644 --- a/src/ccutil/list.h +++ b/src/ccutil/list.h @@ -19,8 +19,7 @@ namespace tesseract { template -class X_ITER : public ITERATOR { -public: +struct X_ITER : ITERATOR { X_ITER() = default; template X_ITER(U *list) : ITERATOR(list) {} @@ -40,8 +39,7 @@ class X_ITER : public ITERATOR { }; template -class X_LIST : public CONTAINER { -public: +struct X_LIST : CONTAINER { X_LIST() = default; X_LIST(const X_LIST &) = delete; X_LIST &operator=(const X_LIST &) = delete; From 825d6d897abaeb8f855e9a1c76a1e3bb9aaea7ff Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 04:39:32 +0300 Subject: [PATCH 04/17] Rename CLIST_ITERATOR to ITERATOR and CLIST_LINK to LINK, so they can be accessed as CLIST::ITERATOR etc. --- src/ccutil/clst.h | 154 +++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 78 deletions(-) diff --git a/src/ccutil/clst.h b/src/ccutil/clst.h index f8fd4135fb..7fab2b1b93 100644 --- a/src/ccutil/clst.h +++ b/src/ccutil/clst.h @@ -35,13 +35,13 @@ namespace tesseract { template class TESS_API CLIST { - friend class CLIST_LINK; - //friend class CLIST_ITERATOR; + friend class LINK; + //friend class ITERATOR; public: /********************************************************************** - * CLASS - CLIST_LINK + * CLASS - LINK * * Generic link class for singly linked CONS cell lists * @@ -49,33 +49,33 @@ class TESS_API CLIST { * they have been extracted from a list OR by the CLIST destructor which * walks the list. **********************************************************************/ - struct CLIST_LINK { - CLIST_LINK *next{}; + struct LINK { + LINK *next{}; CLASSNAME *data{}; - CLIST_LINK() = default; - CLIST_LINK(const CLIST_LINK &) = delete; - void operator=(const CLIST_LINK &) = delete; + LINK() = default; + LINK(const LINK &) = delete; + void operator=(const LINK &) = delete; }; /*********************************************************************** - * CLASS - CLIST_ITERATOR + * CLASS - ITERATOR * * Generic iterator class for singly linked lists with embedded *links **********************************************************************/ - class TESS_API CLIST_ITERATOR { + class TESS_API ITERATOR { CLIST *list; // List being iterated - CLIST_LINK *prev; // prev element - CLIST_LINK *current; // current element - CLIST_LINK *next; // next element - CLIST_LINK *cycle_pt; // point we are cycling the list to. + LINK *prev; // prev element + LINK *current; // current element + LINK *next; // next element + 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_cycle_pt; // current extracted was cycle point bool started_cycling; // Have we moved off the start? /*********************************************************************** - * CLIST_ITERATOR::extract_sublist() + * ITERATOR::extract_sublist() * * This is a private member, used only by CLIST::assign_to_sublist. * Given another iterator for the same list, extract the links from THIS to @@ -83,9 +83,9 @@ class TESS_API CLIST { * pointer to the last element. * (Can't inline this function because it contains a loop) **********************************************************************/ - CLIST_LINK *extract_sublist( // from this current - CLIST_ITERATOR *other_it) { // to other current - CLIST_ITERATOR temp_it = *this; + LINK *extract_sublist( // from this current + ITERATOR *other_it) { // to other current + ITERATOR temp_it = *this; constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); #ifndef NDEBUG @@ -93,12 +93,12 @@ class TESS_API CLIST { constexpr ERRCODE DONT_EXTRACT_DELETED("Can't extract a sublist marked by deleted points"); if (list != other_it->list) - BAD_EXTRACTION_PTS.error("CLIST_ITERATOR.extract_sublist", ABORT); + BAD_EXTRACTION_PTS.error("ITERATOR.extract_sublist", ABORT); if (list->empty()) - EMPTY_LIST.error("CLIST_ITERATOR::extract_sublist", ABORT); + EMPTY_LIST.error("ITERATOR::extract_sublist", ABORT); if (!current || !other_it->current) - DONT_EXTRACT_DELETED.error("CLIST_ITERATOR.extract_sublist", ABORT); + DONT_EXTRACT_DELETED.error("ITERATOR.extract_sublist", ABORT); #endif ex_current_was_last = other_it->ex_current_was_last = false; @@ -108,7 +108,7 @@ class TESS_API CLIST { temp_it.mark_cycle_pt(); do { // walk sublist if (temp_it.cycled_list()) { // can't find end pt - BAD_SUBLIST.error("CLIST_ITERATOR.extract_sublist", ABORT); + BAD_SUBLIST.error("ITERATOR.extract_sublist", ABORT); } if (temp_it.at_last()) { @@ -146,22 +146,22 @@ class TESS_API CLIST { } public: - CLIST_ITERATOR() { // constructor + ITERATOR() { // constructor list = nullptr; } // unassigned list /*********************************************************************** - * CLIST_ITERATOR::CLIST_ITERATOR + * ITERATOR::ITERATOR * * CONSTRUCTOR - set iterator to specified list; **********************************************************************/ - CLIST_ITERATOR( // constructor + ITERATOR( // constructor CLIST *list_to_iterate) { set_to_list(list_to_iterate); } /*********************************************************************** - * CLIST_ITERATOR::set_to_list + * ITERATOR::set_to_list * * (Re-)initialise the iterator to point to the start of the list_to_iterate * over. @@ -179,7 +179,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::add_after_then_move + * ITERATOR::add_after_then_move * * Add a new element to the list after the current element and move the * iterator to the new element. @@ -188,11 +188,11 @@ class TESS_API CLIST { CLASSNAME *new_data) { #ifndef NDEBUG if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_after_then_move", ABORT, "new_data is nullptr"); + BAD_PARAMETER.error("ITERATOR::add_after_then_move", ABORT, "new_data is nullptr"); } #endif - auto new_element = new CLIST_LINK; + auto new_element = new LINK; new_element->data = new_data; if (list->empty()) { @@ -222,7 +222,7 @@ class TESS_API CLIST { } // move to new /*********************************************************************** - * CLIST_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 * the iterator to the new element. @@ -231,11 +231,11 @@ class TESS_API CLIST { CLASSNAME *new_data) { #ifndef NDEBUG if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_after_stay_put", ABORT, "new_data is nullptr"); + BAD_PARAMETER.error("ITERATOR::add_after_stay_put", ABORT, "new_data is nullptr"); } #endif - auto new_element = new CLIST_LINK; + auto new_element = new LINK; new_element->data = new_data; if (list->empty()) { @@ -267,7 +267,7 @@ class TESS_API CLIST { } // stay at current /*********************************************************************** - * CLIST_ITERATOR::add_before_then_move + * ITERATOR::add_before_then_move * * Add a new element to the list before the current element and move the * iterator to the new element. @@ -276,11 +276,11 @@ class TESS_API CLIST { CLASSNAME *new_data) { #ifndef NDEBUG if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_before_then_move", ABORT, "new_data is nullptr"); + BAD_PARAMETER.error("ITERATOR::add_before_then_move", ABORT, "new_data is nullptr"); } #endif - auto new_element = new CLIST_LINK; + auto new_element = new LINK; new_element->data = new_data; if (list->empty()) { @@ -306,7 +306,7 @@ class TESS_API CLIST { } // move to new /*********************************************************************** - * CLIST_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 * iterator to the new element. @@ -315,11 +315,11 @@ class TESS_API CLIST { CLASSNAME *new_data) { #ifndef NDEBUG if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_before_stay_put", ABORT, "new_data is nullptr"); + BAD_PARAMETER.error("ITERATOR::add_before_stay_put", ABORT, "new_data is nullptr"); } #endif - auto new_element = new CLIST_LINK; + auto new_element = new LINK; new_element->data = new_data; if (list->empty()) { @@ -346,7 +346,7 @@ class TESS_API CLIST { } // stay at current /*********************************************************************** - * CLIST_ITERATOR::add_list_after + * ITERATOR::add_list_after * * Insert another list to this list after the current element but don't move *the @@ -384,7 +384,7 @@ class TESS_API CLIST { } // stay at current /*********************************************************************** - * CLIST_ITERATOR::add_list_before + * ITERATOR::add_list_before * * Insert another list to this list before the current element. Move the * iterator to the start of the inserted elements @@ -422,14 +422,14 @@ class TESS_API CLIST { CLASSNAME *data() { // get current data #ifndef NDEBUG if (!list) { - NO_LIST.error("CLIST_ITERATOR::data", ABORT); + NO_LIST.error("ITERATOR::data", ABORT); } #endif return current->data; } /*********************************************************************** - * CLIST_ITERATOR::data_relative + * ITERATOR::data_relative * * Return the data pointer to the element "offset" elements from current. * "offset" must not be less than -1. @@ -437,15 +437,15 @@ class TESS_API CLIST { **********************************************************************/ CLASSNAME *data_relative( // get data + or - ... int8_t offset) { // offset from current - CLIST_LINK *ptr; + LINK *ptr; #ifndef NDEBUG if (!list) - NO_LIST.error("CLIST_ITERATOR::data_relative", ABORT); + NO_LIST.error("ITERATOR::data_relative", ABORT); if (list->empty()) - EMPTY_LIST.error("CLIST_ITERATOR::data_relative", ABORT); + EMPTY_LIST.error("ITERATOR::data_relative", ABORT); if (offset < -1) - BAD_PARAMETER.error("CLIST_ITERATOR::data_relative", ABORT, "offset < -l"); + BAD_PARAMETER.error("ITERATOR::data_relative", ABORT, "offset < -l"); #endif if (offset == -1) { @@ -460,7 +460,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::forward + * ITERATOR::forward * * Move the iterator to the next element of the list. * REMEMBER: ALL LISTS ARE CIRCULAR. @@ -488,7 +488,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::extract + * ITERATOR::extract * * 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 @@ -499,7 +499,7 @@ class TESS_API CLIST { #ifndef NDEBUG if (!current) { // list empty or // element extracted - NULL_CURRENT.error("CLIST_ITERATOR::extract", ABORT); + NULL_CURRENT.error("ITERATOR::extract", ABORT); } #endif @@ -525,7 +525,7 @@ class TESS_API CLIST { } // remove from list /*********************************************************************** - * CLIST_ITERATOR::move_to_first() + * ITERATOR::move_to_first() * * Move current so that it is set to the start of the list. * Return data just in case anyone wants it. @@ -538,7 +538,7 @@ class TESS_API CLIST { } // go to start of list /*********************************************************************** - * CLIST_ITERATOR::move_to_last() + * ITERATOR::move_to_last() * * Move current so that it is set to the end of the list. * Return data just in case anyone wants it. @@ -557,7 +557,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::mark_cycle_pt() + * ITERATOR::mark_cycle_pt() * * Remember the current location so that we can tell whether we've returned * to this point later. @@ -569,7 +569,7 @@ class TESS_API CLIST { void mark_cycle_pt() { #ifndef NDEBUG if (!list) { - NO_LIST.error("CLIST_ITERATOR::mark_cycle_pt", ABORT); + NO_LIST.error("ITERATOR::mark_cycle_pt", ABORT); } #endif @@ -590,7 +590,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::at_first() + * ITERATOR::at_first() * * Are we at the start of the list? * @@ -603,7 +603,7 @@ class TESS_API CLIST { } // Current is first? /*********************************************************************** - * CLIST_ITERATOR::at_last() + * ITERATOR::at_last() * * Are we at the end of the list? * @@ -616,7 +616,7 @@ class TESS_API CLIST { } // Current is last? /*********************************************************************** - * CLIST_ITERATOR::cycled_list() + * ITERATOR::cycled_list() * * Have we returned to the cycle_pt since it was set? * @@ -626,7 +626,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::add_to_end + * ITERATOR::add_to_end * * 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 @@ -638,10 +638,10 @@ class TESS_API CLIST { CLASSNAME *new_data) { #ifndef NDEBUG if (!list) { - NO_LIST.error("CLIST_ITERATOR::add_to_end", ABORT); + NO_LIST.error("ITERATOR::add_to_end", ABORT); } if (!new_data) { - BAD_PARAMETER.error("CLIST_ITERATOR::add_to_end", ABORT, "new_data is nullptr"); + BAD_PARAMETER.error("ITERATOR::add_to_end", ABORT, "new_data is nullptr"); } #endif @@ -652,7 +652,7 @@ class TESS_API CLIST { this->add_before_stay_put(new_data); list->last = prev; } else { // Iteratr is elsewhere - auto new_element = new CLIST_LINK; + auto new_element = new LINK; new_element->data = new_data; new_element->next = list->last->next; @@ -663,7 +663,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::exchange() + * ITERATOR::exchange() * * 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 @@ -672,7 +672,7 @@ class TESS_API CLIST { * (This function hasn't been in-lined because its a bit big!) **********************************************************************/ void exchange( // positions of 2 links - CLIST_ITERATOR *other_it) { // other iterator + ITERATOR *other_it) { // other iterator 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 @@ -685,7 +685,7 @@ class TESS_API CLIST { /* Error if either current element is deleted */ if (!current || !other_it->current) { - DONT_EXCHANGE_DELETED.error("CLIST_ITERATOR.exchange", ABORT); + DONT_EXCHANGE_DELETED.error("ITERATOR.exchange", ABORT); } /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements @@ -747,7 +747,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::length() + * ITERATOR::length() * * Return the length of the list * @@ -757,7 +757,7 @@ class TESS_API CLIST { } /*********************************************************************** - * CLIST_ITERATOR::sort() + * ITERATOR::sort() * * Sort the elements of the list, then reposition at the start. * @@ -771,14 +771,14 @@ class TESS_API CLIST { }; private: - CLIST_LINK *last = nullptr; // End of list + LINK *last = nullptr; // End of list //(Points to head) - CLIST_LINK *First() { // return first + LINK *First() { // return first return last != nullptr ? last->next : nullptr; } - const CLIST_LINK *First() const { // return first + const LINK *First() const { // return first return last != nullptr ? last->next : nullptr; } @@ -862,8 +862,8 @@ class TESS_API CLIST { * end point is always the end_it position. **********************************************************************/ void assign_to_sublist( // to this list - CLIST_ITERATOR *start_it, // from list start - CLIST_ITERATOR *end_it) { // from list end + ITERATOR *start_it, // from list start + ITERATOR *end_it) { // from list end constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist"); if (!empty()) { @@ -899,7 +899,7 @@ class TESS_API CLIST { std::vector base; base.reserve(count); - CLIST_ITERATOR it(this); + ITERATOR it(this); // Extract all elements, putting the pointers in the array. for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { @@ -926,7 +926,7 @@ class TESS_API CLIST { bool add_sorted(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLASSNAME *new_data) { // Check for adding at the end. if (last == nullptr || comparator(last->data, new_data) < 0) { - auto *new_element = new CLIST_LINK; + auto *new_element = new LINK; new_element->data = new_data; if (last == nullptr) { new_element->next = new_element; @@ -938,7 +938,7 @@ class TESS_API CLIST { return true; } else if (!unique || last->data != new_data) { // Need to use an iterator. - CLIST_ITERATOR it(this); + ITERATOR it(this); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { auto data = it.data(); if (data == new_data && unique) { @@ -966,8 +966,8 @@ class TESS_API CLIST { void set_subtract(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLIST *minuend, CLIST *subtrahend) { shallow_clear(); - CLIST_ITERATOR m_it(minuend); - CLIST_ITERATOR s_it(subtrahend); + ITERATOR m_it(minuend); + ITERATOR s_it(subtrahend); // Since both lists are sorted, finding the subtras that are not // minus is a case of a parallel iteration. for (m_it.mark_cycle_pt(); !m_it.cycled_list(); m_it.forward()) { @@ -991,9 +991,7 @@ class TESS_API CLIST { class CLASSNAME##_CLIST : public CLIST { \ using CLIST::CLIST; \ }; \ - struct CLASSNAME##_C_IT : X_ITER::CLIST_ITERATOR, CLASSNAME> { \ - using X_ITER::CLIST_ITERATOR, CLASSNAME>::X_ITER; \ - }; + using CLASSNAME##_C_IT = CLIST::ITERATOR; } // namespace tesseract From 9baa9c9e2c99619ea308b96e1ff85fac56dda9b9 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 04:39:42 +0300 Subject: [PATCH 05/17] Update test. --- unittest/list_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittest/list_test.cc b/unittest/list_test.cc index 212994e4b0..cfdae00e01 100644 --- a/unittest/list_test.cc +++ b/unittest/list_test.cc @@ -25,7 +25,7 @@ class ListTest : public ::testing::Test { const size_t ListSize = 5; }; -class Clst : public CLIST_LINK { +class Clst { public: Clst(unsigned n) : value(n) {} unsigned value; @@ -51,7 +51,7 @@ TEST_F(ListTest, TestCLIST) { Clst_CLIST list; EXPECT_TRUE(list.empty()); EXPECT_EQ(list.length(), 0); - auto it = CLIST_ITERATOR(&list); + auto it = Clst_CLIST::ITERATOR(&list); for (unsigned i = 0; i < ListSize; i++) { auto *lst = new Clst(i); it.add_to_end(lst); From 86c803ccb446a06e31444a29a3fc1a8b9803c02d Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 17:38:17 +0300 Subject: [PATCH 06/17] [sw] Fix build. --- sw.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sw.cpp b/sw.cpp index d006126b75..e989e6e8a9 100644 --- a/sw.cpp +++ b/sw.cpp @@ -334,8 +334,7 @@ void build(Solution &s) auto &tw = add_test("tatweel"); tw += "unittest/util/.*"_rr; - tw += "unittest/third_party/.*"_rr; - tw -= "unittest/third_party/googletest/.*"_rr; + tw += "unittest/third_party/utf/.*"_rr; } } From d7dc8b58783dad090e10a70366ac135203738779 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 17:38:34 +0300 Subject: [PATCH 07/17] Fix msvc warning. --- src/classify/cluster.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/classify/cluster.cpp b/src/classify/cluster.cpp index fd88978023..4143bbbb39 100644 --- a/src/classify/cluster.cpp +++ b/src/classify/cluster.cpp @@ -3238,10 +3238,10 @@ static bool MultipleCharSamples(CLUSTERER *Clusterer, CLUSTER *Cluster, float Ma InitSampleSearch(SearchState, Cluster); while ((Sample = NextSample(&SearchState)) != nullptr) { CharID = Sample->CharID; - if (CharFlags[CharID] == false) { + if (CharFlags[CharID] == 0) { CharFlags[CharID] = true; } else { - if (CharFlags[CharID] == true) { + if (CharFlags[CharID] == 1) { NumIllegalInCluster++; CharFlags[CharID] = ILLEGAL_CHAR; } From d777e25d7b8735d78f2474ccd9b271a9678810f0 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 17:52:03 +0300 Subject: [PATCH 08/17] Fix warnings. --- unittest/linlsq_test.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/unittest/linlsq_test.cc b/unittest/linlsq_test.cc index cac5dba24e..e278c9f999 100644 --- a/unittest/linlsq_test.cc +++ b/unittest/linlsq_test.cc @@ -103,15 +103,15 @@ TEST_F(LLSQTest, Vectors) { // sqrt( sum (!nvec * (x_i - x_avg))^2 / n) TEST_F(LLSQTest, RmsOrthWorksAsIntended) { std::vector pts; - pts.emplace_back(0.56, 0.95); - pts.emplace_back(0.09, 0.09); - pts.emplace_back(0.13, 0.77); - pts.emplace_back(0.16, 0.83); - pts.emplace_back(0.45, 0.79); - VerifyRmsOrth(pts, FCOORD(1, 0)); - VerifyRmsOrth(pts, FCOORD(1, 1)); - VerifyRmsOrth(pts, FCOORD(1, 2)); - VerifyRmsOrth(pts, FCOORD(2, 1)); + pts.emplace_back(0.56f, 0.95f); + pts.emplace_back(0.09f, 0.09f); + pts.emplace_back(0.13f, 0.77f); + pts.emplace_back(0.16f, 0.83f); + pts.emplace_back(0.45f, 0.79f); + VerifyRmsOrth(pts, FCOORD(1.f, 0.f)); + VerifyRmsOrth(pts, FCOORD(1.f, 1.f)); + VerifyRmsOrth(pts, FCOORD(1.f, 2.f)); + VerifyRmsOrth(pts, FCOORD(2.f, 1.f)); } } // namespace tesseract From 36b804080f91faa13351ae2a5a070e78c20acbf6 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 17:54:40 +0300 Subject: [PATCH 09/17] Convert ELIST into template. --- src/ccmain/paramsd.h | 2 +- src/ccstruct/blobbox.h | 4 +- src/ccstruct/coutln.h | 2 +- src/ccstruct/ocrblock.cpp | 2 +- src/ccstruct/ocrblock.h | 2 +- src/ccstruct/ocrpara.h | 2 +- src/ccstruct/ocrrow.cpp | 2 +- src/ccstruct/ocrrow.h | 2 +- src/ccstruct/pageres.cpp | 2 +- src/ccstruct/pageres.h | 8 +- src/ccstruct/points.h | 2 +- src/ccstruct/ratngs.cpp | 4 +- src/ccstruct/ratngs.h | 6 +- src/ccstruct/stepblob.h | 3 +- src/ccutil/ambigs.h | 2 +- src/ccutil/clst.h | 4 +- src/ccutil/elst.cpp | 440 ---------- src/ccutil/elst.h | 1556 ++++++++++++++++++++------------- src/classify/trainingsample.h | 2 +- src/textord/blkocc.h | 2 +- src/textord/colpartitionset.h | 2 +- src/textord/fpchop.h | 2 +- src/textord/pitsync1.h | 2 +- src/textord/sortflts.h | 2 +- src/textord/tablefind.cpp | 2 +- src/textord/tablefind.h | 2 +- src/textord/tabvector.h | 2 +- src/textord/workingpartset.h | 2 +- src/wordrec/lm_state.h | 2 +- src/wordrec/wordrec.h | 2 +- unittest/list_test.cc | 4 +- 31 files changed, 971 insertions(+), 1102 deletions(-) delete mode 100644 src/ccutil/elst.cpp diff --git a/src/ccmain/paramsd.h b/src/ccmain/paramsd.h index 5e2a57e489..e1a87b93e9 100644 --- a/src/ccmain/paramsd.h +++ b/src/ccmain/paramsd.h @@ -44,7 +44,7 @@ enum ParamType { VT_INTEGER, VT_BOOLEAN, VT_STRING, VT_DOUBLE }; // comparisond or getting its value. It is used in the context of the // ParamsEditor as a bridge from the internal tesseract parameters to the // ones displayed by the ScrollView server. -class ParamContent : public ELIST_LINK { +class ParamContent : public ELIST::LINK { public: // Compare two VC objects by their name. static int Compare(const void *v1, const void *v2); diff --git a/src/ccstruct/blobbox.h b/src/ccstruct/blobbox.h index 03e73ec9d7..5d8c1edc79 100644 --- a/src/ccstruct/blobbox.h +++ b/src/ccstruct/blobbox.h @@ -138,7 +138,7 @@ class ColPartition; class BLOBNBOX; ELISTIZEH(BLOBNBOX) -class BLOBNBOX : public ELIST_LINK { +class BLOBNBOX : public ELIST::LINK { public: BLOBNBOX() { ReInit(); @@ -695,7 +695,7 @@ class TO_ROW : public ELIST2_LINK { }; ELIST2IZEH(TO_ROW) -class TESS_API TO_BLOCK : public ELIST_LINK { +class TESS_API TO_BLOCK : public ELIST::LINK { public: TO_BLOCK() : pitch_decision(PITCH_DUNNO) { clear(); diff --git a/src/ccstruct/coutln.h b/src/ccstruct/coutln.h index 3aa25519de..fd08fd6d69 100644 --- a/src/ccstruct/coutln.h +++ b/src/ccstruct/coutln.h @@ -72,7 +72,7 @@ struct EdgeOffset { class C_OUTLINE; // forward declaration ELISTIZEH(C_OUTLINE) -class C_OUTLINE : public ELIST_LINK { +class C_OUTLINE : public ELIST::LINK { public: C_OUTLINE() { stepcount = 0; diff --git a/src/ccstruct/ocrblock.cpp b/src/ccstruct/ocrblock.cpp index 1b222f6e07..9127a85953 100644 --- a/src/ccstruct/ocrblock.cpp +++ b/src/ccstruct/ocrblock.cpp @@ -222,7 +222,7 @@ void BLOCK::print( // print list of sides BLOCK &BLOCK::operator=( // assignment const BLOCK &source // from this ) { - this->ELIST_LINK::operator=(source); + this->ELIST::LINK::operator=(source); pdblk = source.pdblk; proportional = source.proportional; kerning = source.kerning; diff --git a/src/ccstruct/ocrblock.h b/src/ccstruct/ocrblock.h index 88753b4b47..38a830d706 100644 --- a/src/ccstruct/ocrblock.h +++ b/src/ccstruct/ocrblock.h @@ -29,7 +29,7 @@ class BLOCK; // forward decl ELISTIZEH(BLOCK) -class TESS_API BLOCK : public ELIST_LINK +class TESS_API BLOCK : public ELIST::LINK // page block { friend class BLOCK_RECT_IT; // block iterator diff --git a/src/ccstruct/ocrpara.h b/src/ccstruct/ocrpara.h index e0bb6ab276..473ba6c674 100644 --- a/src/ccstruct/ocrpara.h +++ b/src/ccstruct/ocrpara.h @@ -27,7 +27,7 @@ namespace tesseract { class ParagraphModel; -struct PARA : public ELIST_LINK { +struct PARA : public ELIST::LINK { public: PARA() : model(nullptr) diff --git a/src/ccstruct/ocrrow.cpp b/src/ccstruct/ocrrow.cpp index 5e6eba4010..a2c284eb0c 100644 --- a/src/ccstruct/ocrrow.cpp +++ b/src/ccstruct/ocrrow.cpp @@ -223,7 +223,7 @@ void ROW::plot( // draw it **********************************************************************/ ROW &ROW::operator=(const ROW &source) { - this->ELIST_LINK::operator=(source); + this->ELIST::LINK::operator=(source); kerning = source.kerning; spacing = source.spacing; xheight = source.xheight; diff --git a/src/ccstruct/ocrrow.h b/src/ccstruct/ocrrow.h index 4955cd283e..637cb0c5e7 100644 --- a/src/ccstruct/ocrrow.h +++ b/src/ccstruct/ocrrow.h @@ -36,7 +36,7 @@ class TO_ROW; struct PARA; -class ROW : public ELIST_LINK { +class ROW : public ELIST::LINK { friend void tweak_row_baseline(ROW *, double, double); public: diff --git a/src/ccstruct/pageres.cpp b/src/ccstruct/pageres.cpp index 65ea748ff7..1bc5e1635f 100644 --- a/src/ccstruct/pageres.cpp +++ b/src/ccstruct/pageres.cpp @@ -184,7 +184,7 @@ ROW_RES::ROW_RES(bool merge_similar_words, ROW *the_row) { } WERD_RES &WERD_RES::operator=(const WERD_RES &source) { - this->ELIST_LINK::operator=(source); + this->ELIST::LINK::operator=(source); Clear(); if (source.combination) { word = new WERD; diff --git a/src/ccstruct/pageres.h b/src/ccstruct/pageres.h index 48e70b73d4..c7176f7a6c 100644 --- a/src/ccstruct/pageres.h +++ b/src/ccstruct/pageres.h @@ -115,7 +115,7 @@ class PAGE_RES { // page result * BLOCK_RES - Block results *************************************************************************/ -class BLOCK_RES : public ELIST_LINK { +class BLOCK_RES : public ELIST::LINK { public: BLOCK *block; // real block int32_t char_count; // chars in block @@ -139,7 +139,7 @@ class BLOCK_RES : public ELIST_LINK { * ROW_RES - Row results *************************************************************************/ -class ROW_RES : public ELIST_LINK { +class ROW_RES : public ELIST::LINK { public: ROW *row; // real row int32_t char_count; // chars in block @@ -161,7 +161,7 @@ enum CRUNCH_MODE { CR_NONE, CR_KEEP_SPACE, CR_LOOSE_SPACE, CR_DELETE }; // WERD_RES is a collection of publicly accessible members that gathers // information about a word result. -class TESS_API WERD_RES : public ELIST_LINK { +class TESS_API WERD_RES : public ELIST::LINK { public: // Which word is which? // There are 3 coordinate spaces in use here: a possibly rotated pixel space, @@ -345,7 +345,7 @@ class TESS_API WERD_RES : public ELIST_LINK { } // Deep copies everything except the ratings MATRIX. // To get that use deep_copy below. - WERD_RES(const WERD_RES &source) : ELIST_LINK(source) { + WERD_RES(const WERD_RES &source) : ELIST::LINK(source) { // combination is used in function Clear which is called from operator=. combination = false; *this = source; // see operator= diff --git a/src/ccstruct/points.h b/src/ccstruct/points.h index 59793592a1..f3adfbf623 100644 --- a/src/ccstruct/points.h +++ b/src/ccstruct/points.h @@ -160,7 +160,7 @@ class ICOORD { TDimension ycoord; ///< y value }; -class ICOORDELT : public ELIST_LINK, +class ICOORDELT : public ELIST::LINK, public ICOORD // embedded coord list { diff --git a/src/ccstruct/ratngs.cpp b/src/ccstruct/ratngs.cpp index 0b3cda09af..878b439e24 100644 --- a/src/ccstruct/ratngs.cpp +++ b/src/ccstruct/ratngs.cpp @@ -110,7 +110,7 @@ BLOB_CHOICE::BLOB_CHOICE(UNICHAR_ID src_unichar_id, // character id * * Constructor to build a BLOB_CHOICE from another BLOB_CHOICE. */ -BLOB_CHOICE::BLOB_CHOICE(const BLOB_CHOICE &other) : ELIST_LINK(other) { +BLOB_CHOICE::BLOB_CHOICE(const BLOB_CHOICE &other) : ELIST::LINK(other) { unichar_id_ = other.unichar_id(); rating_ = other.rating(); certainty_ = other.certainty(); @@ -129,7 +129,7 @@ BLOB_CHOICE::BLOB_CHOICE(const BLOB_CHOICE &other) : ELIST_LINK(other) { // Copy assignment operator. BLOB_CHOICE &BLOB_CHOICE::operator=(const BLOB_CHOICE &other) { - ELIST_LINK::operator=(other); + ELIST::LINK::operator=(other); unichar_id_ = other.unichar_id(); rating_ = other.rating(); certainty_ = other.certainty(); diff --git a/src/ccstruct/ratngs.h b/src/ccstruct/ratngs.h index 2107e90342..e9371c9c18 100644 --- a/src/ccstruct/ratngs.h +++ b/src/ccstruct/ratngs.h @@ -53,7 +53,7 @@ enum BlobChoiceClassifier { BCC_FAKE, // From some other process. }; -class BLOB_CHOICE : public ELIST_LINK { +class BLOB_CHOICE : public ELIST::LINK { public: BLOB_CHOICE() { unichar_id_ = UNICHAR_SPACE; @@ -255,7 +255,7 @@ enum ScriptPos { SP_NORMAL, SP_SUBSCRIPT, SP_SUPERSCRIPT, SP_DROPCAP }; const char *ScriptPosToString(ScriptPos script_pos); -class TESS_API WERD_CHOICE : public ELIST_LINK { +class TESS_API WERD_CHOICE : public ELIST::LINK { public: static const float kBadRating; static const char *permuter_name(uint8_t permuter); @@ -272,7 +272,7 @@ class TESS_API WERD_CHOICE : public ELIST_LINK { this->init(src_string, src_lengths, src_rating, src_certainty, src_permuter); } WERD_CHOICE(const char *src_string, const UNICHARSET &unicharset); - WERD_CHOICE(const WERD_CHOICE &word) : ELIST_LINK(word), unicharset_(word.unicharset_) { + WERD_CHOICE(const WERD_CHOICE &word) : ELIST::LINK(word), unicharset_(word.unicharset_) { this->init(word.length()); this->operator=(word); } diff --git a/src/ccstruct/stepblob.h b/src/ccstruct/stepblob.h index 31d267104c..c7dbd2528e 100644 --- a/src/ccstruct/stepblob.h +++ b/src/ccstruct/stepblob.h @@ -36,8 +36,7 @@ class C_BLOB; class DENORM; ELISTIZEH(C_BLOB) - -class TESS_API C_BLOB : public ELIST_LINK { +class TESS_API C_BLOB : public ELIST::LINK { public: C_BLOB() = default; explicit C_BLOB(C_OUTLINE_LIST *outline_list); diff --git a/src/ccutil/ambigs.h b/src/ccutil/ambigs.h index fee1c067b7..2881d36595 100644 --- a/src/ccutil/ambigs.h +++ b/src/ccutil/ambigs.h @@ -109,7 +109,7 @@ class UnicharIdArrayUtils { // AMBIG_SPEC_LIST stores a list of dangerous ambigs that // start with the same unichar (e.g. r->t rn->m rr1->m). -class AmbigSpec : public ELIST_LINK { +class AmbigSpec : public ELIST::LINK { public: AmbigSpec(); ~AmbigSpec() = default; diff --git a/src/ccutil/clst.h b/src/ccutil/clst.h index 7fab2b1b93..7aa0090754 100644 --- a/src/ccutil/clst.h +++ b/src/ccutil/clst.h @@ -34,7 +34,7 @@ namespace tesseract { **********************************************************************/ template -class TESS_API CLIST { +class CLIST { friend class LINK; //friend class ITERATOR; @@ -64,7 +64,7 @@ class TESS_API CLIST { * Generic iterator class for singly linked lists with embedded *links **********************************************************************/ - class TESS_API ITERATOR { + class ITERATOR { CLIST *list; // List being iterated LINK *prev; // prev element LINK *current; // current element diff --git a/src/ccutil/elst.cpp b/src/ccutil/elst.cpp deleted file mode 100644 index 2cac5fd14d..0000000000 --- a/src/ccutil/elst.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/********************************************************************** - * File: elst.cpp (Formerly elist.c) - * Description: Embedded list handling code which is not in the include file. - * Author: Phil Cheatle - * - * (C) Copyright 1991, Hewlett-Packard Ltd. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - **********************************************************************/ - -#include "elst.h" -#include - -namespace tesseract { - -/*********************************************************************** - * ELIST::internal_clear - * - * Used by the destructor and the "clear" member function of derived list - * classes to destroy all the elements on the list. - * The calling function passes a "zapper" function which can be called to - * delete each element of the list, regardless of its derived type. This - * technique permits a generic clear function to destroy elements of - * different derived types correctly, without requiring virtual functions and - * the consequential memory overhead. - **********************************************************************/ - -void ELIST::internal_clear( // destroy all links - void (*zapper)(void *)) { - // ptr to zapper functn - ELIST_LINK *ptr; - ELIST_LINK *next; - - if (!empty()) { - ptr = last->next; // set to first - last->next = nullptr; // break circle - last = nullptr; // set list empty - while (ptr) { - next = ptr->next; - zapper(ptr); - ptr = next; - } - } -} - -/*********************************************************************** - * ELIST::assign_to_sublist - * - * 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 - * the same list, different from "this" one. The sublist removed is the - * inclusive list from start_it's current position to end_it's current - * position. If this range passes over the end of the source list then the - * source list has its end set to the previous element of start_it. The - * extracted sublist is unaffected by the end point of the source list, its - * end point is always the end_it position. - **********************************************************************/ - -void ELIST::assign_to_sublist( // to this list - ELIST_ITERATOR *start_it, // from list start - ELIST_ITERATOR *end_it) { // from list end - constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist"); - - if (!empty()) { - LIST_NOT_EMPTY.error("ELIST.assign_to_sublist", ABORT); - } - - last = start_it->extract_sublist(end_it); -} - -/*********************************************************************** - * ELIST::sort - * - * Sort elements on list - * NB If you don't like the const declarations in the comparator, coerce yours: - * ( int (*)(const void *, const void *) - **********************************************************************/ - -void ELIST::sort( // sort elements - int comparator( // comparison routine - const void *, const void *)) { - // Allocate an array of pointers, one per list element. - auto count = length(); - - if (count > 0) { - // ptr array to sort - std::vector base; - base.reserve(count); - - ELIST_ITERATOR it(this); - - // Extract all elements, putting the pointers in the array. - for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { - base.push_back(it.extract()); - } - - // Sort the pointer array. - qsort(&base[0], count, sizeof(base[0]), comparator); - - // Rebuild the list from the sorted pointers. - for (auto current : base) { - it.add_to_end(current); - } - } -} - -// Assuming list has been sorted already, insert new_link to -// keep the list sorted according to the same comparison function. -// Comparison function is the same as used by sort, i.e. uses double -// indirection. Time is O(1) to add to beginning or end. -// Time is linear to add pre-sorted items to an empty list. -// If unique is set to true and comparator() returns 0 (an entry with the -// same information as the one contained in new_link is already in 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 -// (otherwise the function returns new_link). -ELIST_LINK *ELIST::add_sorted_and_find(int comparator(const void *, const void *), bool unique, - ELIST_LINK *new_link) { - // Check for adding at the end. - if (last == nullptr || comparator(&last, &new_link) < 0) { - if (last == nullptr) { - new_link->next = new_link; - } else { - new_link->next = last->next; - last->next = new_link; - } - last = new_link; - } else { - // Need to use an iterator. - ELIST_ITERATOR it(this); - for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { - ELIST_LINK *link = it.data(); - int compare = comparator(&link, &new_link); - if (compare > 0) { - break; - } else if (unique && compare == 0) { - return link; - } - } - if (it.cycled_list()) { - it.add_to_end(new_link); - } else { - it.add_before_then_move(new_link); - } - } - return new_link; -} - -/*********************************************************************** - * MEMBER FUNCTIONS OF CLASS: ELIST_ITERATOR - * ========================================= - **********************************************************************/ - -/*********************************************************************** - * ELIST_ITERATOR::forward - * - * Move the iterator to the next element of the list. - * REMEMBER: ALL LISTS ARE CIRCULAR. - **********************************************************************/ - -ELIST_LINK *ELIST_ITERATOR::forward() { -#ifndef NDEBUG - if (!list) - NO_LIST.error("ELIST_ITERATOR::forward", ABORT); -#endif - if (list->empty()) { - return nullptr; - } - - if (current) { // not removed so - // set previous - prev = current; - started_cycling = true; - // In case next is deleted by another iterator, get next from current. - current = current->next; - } else { - if (ex_current_was_cycle_pt) { - cycle_pt = next; - } - current = next; - } -#ifndef NDEBUG - if (!current) - NULL_DATA.error("ELIST_ITERATOR::forward", ABORT); -#endif - next = current->next; - -#ifndef NDEBUG - if (!next) { - NULL_NEXT.error("ELIST_ITERATOR::forward", ABORT, - "This is: %p Current is: %p", - static_cast(this), - static_cast(current)); - } -#endif - return current; -} - -/*********************************************************************** - * ELIST_ITERATOR::data_relative - * - * Return the data pointer to the element "offset" elements from current. - * "offset" must not be less than -1. - * (This function can't be INLINEd because it contains a loop) - **********************************************************************/ - -ELIST_LINK *ELIST_ITERATOR::data_relative( // get data + or - ... - int8_t offset) { // offset from current - ELIST_LINK *ptr; - -#ifndef NDEBUG - if (!list) - NO_LIST.error("ELIST_ITERATOR::data_relative", ABORT); - if (list->empty()) - EMPTY_LIST.error("ELIST_ITERATOR::data_relative", ABORT); - if (offset < -1) - BAD_PARAMETER.error("ELIST_ITERATOR::data_relative", ABORT, "offset < -l"); -#endif - - if (offset == -1) { - ptr = prev; - } else { - for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next) { - ; - } - } - -#ifndef NDEBUG - if (!ptr) - NULL_DATA.error("ELIST_ITERATOR::data_relative", ABORT); -#endif - - return ptr; -} - -/*********************************************************************** - * ELIST_ITERATOR::move_to_last() - * - * Move current so that it is set to the end of the list. - * Return data just in case anyone wants it. - * (This function can't be INLINEd because it contains a loop) - **********************************************************************/ - -ELIST_LINK *ELIST_ITERATOR::move_to_last() { -#ifndef NDEBUG - if (!list) - NO_LIST.error("ELIST_ITERATOR::move_to_last", ABORT); -#endif - - while (current != list->last) { - forward(); - } - - return current; -} - -/*********************************************************************** - * ELIST_ITERATOR::exchange() - * - * 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 - * elements. On return, each iterator points to the element which was the - * other iterators current on entry. - * (This function hasn't been in-lined because its a bit big!) - **********************************************************************/ - -void ELIST_ITERATOR::exchange( // positions of 2 links - ELIST_ITERATOR *other_it) { // other iterator - constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists"); - - ELIST_LINK *old_current; - -#ifndef NDEBUG - if (!list) - NO_LIST.error("ELIST_ITERATOR::exchange", ABORT); - if (!other_it) - BAD_PARAMETER.error("ELIST_ITERATOR::exchange", ABORT, "other_it nullptr"); - if (!(other_it->list)) - NO_LIST.error("ELIST_ITERATOR::exchange", ABORT, "other_it"); -#endif - - /* Do nothing if either list is empty or if both iterators reference the same -link */ - - if ((list->empty()) || (other_it->list->empty()) || (current == other_it->current)) { - return; - } - - /* Error if either current element is deleted */ - - if (!current || !other_it->current) { - DONT_EXCHANGE_DELETED.error("ELIST_ITERATOR.exchange", ABORT); - } - - /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements -(other before this); non-doubleton adjacent elements (this before other); -non-adjacent elements. */ - - // adjacent links - if ((next == other_it->current) || (other_it->next == current)) { - // doubleton list - if ((next == other_it->current) && (other_it->next == current)) { - prev = next = current; - other_it->prev = other_it->next = other_it->current; - } else { // non-doubleton with - // adjacent links - // other before this - if (other_it->next == current) { - other_it->prev->next = current; - other_it->current->next = next; - current->next = other_it->current; - other_it->next = other_it->current; - prev = current; - } else { // this before other - prev->next = other_it->current; - current->next = other_it->next; - other_it->current->next = current; - next = current; - other_it->prev = other_it->current; - } - } - } else { // no overlap - prev->next = other_it->current; - current->next = other_it->next; - other_it->prev->next = current; - other_it->current->next = next; - } - - /* update end of list pointer when necessary (remember that the 2 iterators - may iterate over different lists!) */ - - if (list->last == current) { - list->last = other_it->current; - } - if (other_it->list->last == other_it->current) { - other_it->list->last = current; - } - - if (current == cycle_pt) { - cycle_pt = other_it->cycle_pt; - } - if (other_it->current == other_it->cycle_pt) { - other_it->cycle_pt = cycle_pt; - } - - /* The actual exchange - in all cases*/ - - old_current = current; - current = other_it->current; - other_it->current = old_current; -} - -/*********************************************************************** - * ELIST_ITERATOR::extract_sublist() - * - * This is a private member, used only by ELIST::assign_to_sublist. - * 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 - * pointer to the last element. - * (Can't inline this function because it contains a loop) - **********************************************************************/ - -ELIST_LINK *ELIST_ITERATOR::extract_sublist( // from this current - ELIST_ITERATOR *other_it) { // to other current -#ifndef NDEBUG - 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"); -#endif - constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); - - ELIST_ITERATOR temp_it = *this; - ELIST_LINK *end_of_new_list; - -#ifndef NDEBUG - if (!other_it) - BAD_PARAMETER.error("ELIST_ITERATOR::extract_sublist", ABORT, "other_it nullptr"); - if (!list) - NO_LIST.error("ELIST_ITERATOR::extract_sublist", ABORT); - if (list != other_it->list) - BAD_EXTRACTION_PTS.error("ELIST_ITERATOR.extract_sublist", ABORT); - if (list->empty()) - EMPTY_LIST.error("ELIST_ITERATOR::extract_sublist", ABORT); - - if (!current || !other_it->current) - DONT_EXTRACT_DELETED.error("ELIST_ITERATOR.extract_sublist", ABORT); -#endif - - ex_current_was_last = other_it->ex_current_was_last = false; - ex_current_was_cycle_pt = false; - other_it->ex_current_was_cycle_pt = false; - - temp_it.mark_cycle_pt(); - do { // walk sublist - if (temp_it.cycled_list()) { // can't find end pt - BAD_SUBLIST.error("ELIST_ITERATOR.extract_sublist", ABORT); - } - - if (temp_it.at_last()) { - list->last = prev; - ex_current_was_last = other_it->ex_current_was_last = true; - } - - if (temp_it.current == cycle_pt) { - ex_current_was_cycle_pt = true; - } - - if (temp_it.current == other_it->cycle_pt) { - other_it->ex_current_was_cycle_pt = true; - } - - temp_it.forward(); - } while (temp_it.prev != other_it->current); - - // circularise sublist - other_it->current->next = current; - end_of_new_list = other_it->current; - - // sublist = whole list - if (prev == other_it->current) { - list->last = nullptr; - prev = current = next = nullptr; - other_it->prev = other_it->current = other_it->next = nullptr; - } else { - prev->next = other_it->next; - current = other_it->current = nullptr; - next = other_it->next; - other_it->prev = prev; - } - return end_of_new_list; -} - -} // namespace tesseract diff --git a/src/ccutil/elst.h b/src/ccutil/elst.h index 040ce2a488..b264b64716 100644 --- a/src/ccutil/elst.h +++ b/src/ccutil/elst.h @@ -27,8 +27,6 @@ namespace tesseract { -class ELIST_ITERATOR; - /********************************************************************** This module implements list classes and iterators. The following list types and iterators are provided: @@ -68,744 +66,1056 @@ list class - though macros can generate these. It also prevents heterogeneous lists. **********************************************************************/ -/********************************************************************** - * CLASS - ELIST_LINK - * - * Generic link class for singly linked lists with - *embedded links - * - * Note: No destructor - elements are assumed to be destroyed EITHER after - * they have been extracted from a list OR by the ELIST destructor which - * walks the list. - **********************************************************************/ - -class ELIST_LINK { - friend class ELIST_ITERATOR; - friend class ELIST; - - ELIST_LINK *next; - -public: - ELIST_LINK() { - next = nullptr; - } - // constructor - - // The special copy constructor is used by lots of classes. - ELIST_LINK(const ELIST_LINK &) { - next = nullptr; - } - - // The special assignment operator is used by lots of classes. - void operator=(const ELIST_LINK &) { - next = nullptr; - } -}; - /********************************************************************** * CLASS - ELIST * * Generic list class for singly linked lists with embedded links **********************************************************************/ -class TESS_API ELIST { - friend class ELIST_ITERATOR; - - ELIST_LINK *last = nullptr; // End of list - //(Points to head) - ELIST_LINK *First() { // return first - return last ? last->next : nullptr; - } - +template +class ELIST { public: - // destroy all links - void internal_clear(void (*zapper)(void *)); - - bool empty() const { - return !last; - } - bool singleton() const { - return last ? (last == last->next) : false; - } - - void shallow_copy( // dangerous!! - ELIST *from_list) { // beware destructors!! - last = from_list->last; - } - - // ptr to copier functn - void internal_deep_copy(ELIST_LINK *(*copier)(ELIST_LINK *), - const ELIST *list); // list being copied - - void assign_to_sublist( // to this list - ELIST_ITERATOR *start_it, // from list start - ELIST_ITERATOR *end_it); // from list end - - // # elements in list - int32_t length() const { - int32_t count = 0; - if (last != nullptr) { - count = 1; - for (auto it = last->next; it != last; it = it->next) { - count++; - } + /********************************************************************** + * CLASS - ELIST_LINK + * + * Generic link class for singly linked lists with + *embedded links + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the ELIST destructor which + * walks the list. + **********************************************************************/ + + class LINK { + friend class ITERATOR; + friend class ELIST; + + CLASSNAME *next; + + public: + LINK() { + next = nullptr; } - return count; - } - - void sort( // sort elements - int comparator( // comparison routine - const void *, const void *)); + // constructor - // Assuming list has been sorted already, insert new_link to - // keep the list sorted according to the same comparison function. - // Comparison function is the same as used by sort, i.e. uses double - // indirection. Time is O(1) to add to beginning or end. - // Time is linear to add pre-sorted items to an empty list. - // If unique is set to true and comparator() returns 0 (an entry with the - // same information as the one contained in new_link is already in 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 - // (otherwise the function returns new_link). - ELIST_LINK *add_sorted_and_find(int comparator(const void *, const void *), bool unique, - ELIST_LINK *new_link); - - // Same as above, but returns true if the new entry was inserted, false - // if the identical entry already existed in the list. - bool add_sorted(int comparator(const void *, const void *), bool unique, ELIST_LINK *new_link) { - return (add_sorted_and_find(comparator, unique, new_link) == new_link); - } -}; - -/*********************************************************************** - * CLASS - ELIST_ITERATOR - * - * Generic iterator class for singly linked lists with - *embedded links - **********************************************************************/ - -class TESS_API ELIST_ITERATOR { - friend void ELIST::assign_to_sublist(ELIST_ITERATOR *, ELIST_ITERATOR *); - - ELIST *list; // List being iterated - ELIST_LINK *prev; // prev element - ELIST_LINK *current; // current element - ELIST_LINK *next; // next element - ELIST_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_cycle_pt; // current extracted was cycle point - bool started_cycling; // Have we moved off the start? + // The special copy constructor is used by lots of classes. + LINK(const LINK &) { + next = nullptr; + } - ELIST_LINK *extract_sublist( // from this current... - ELIST_ITERATOR *other_it); // to other current + // The special assignment operator is used by lots of classes. + void operator=(const LINK &) { + next = nullptr; + } + }; -public: - ELIST_ITERATOR() { // constructor - list = nullptr; - } // unassigned list - explicit ELIST_ITERATOR(ELIST *list_to_iterate); + /*********************************************************************** + * CLASS - ELIST_ITERATOR + * + * Generic iterator class for singly linked lists with + *embedded links + **********************************************************************/ + + class ITERATOR { + friend void ELIST::assign_to_sublist(ITERATOR *, ITERATOR *); + + ELIST *list; // List being iterated + CLASSNAME *prev; // prev element + CLASSNAME *current; // current element + CLASSNAME *next; // next element + CLASSNAME *cycle_pt; // point we are cycling the list to. + bool ex_current_was_last; // current extracted was end of list + bool ex_current_was_cycle_pt; // current extracted was cycle point + bool started_cycling; // Have we moved off the start? + /*********************************************************************** + * ITERATOR::extract_sublist() + * + * This is a private member, used only by ELIST::assign_to_sublist. + * 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 + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + CLASSNAME *extract_sublist( // from this current... + ITERATOR *other_it) { // to other current +#ifndef NDEBUG + 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"); +#endif + constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); - void set_to_list( // change list - ELIST *list_to_iterate); + ITERATOR temp_it = *this; + CLASSNAME *end_of_new_list; - void add_after_then_move( // add after current & - ELIST_LINK *new_link); // move to new +#ifndef NDEBUG + if (!other_it) + BAD_PARAMETER.error("ELIST_ITERATOR::extract_sublist", ABORT, "other_it nullptr"); + if (!list) + NO_LIST.error("ELIST_ITERATOR::extract_sublist", ABORT); + if (list != other_it->list) + BAD_EXTRACTION_PTS.error("ELIST_ITERATOR.extract_sublist", ABORT); + if (list->empty()) + EMPTY_LIST.error("ELIST_ITERATOR::extract_sublist", ABORT); + + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error("ELIST_ITERATOR.extract_sublist", ABORT); +#endif - void add_after_stay_put( // add after current & - ELIST_LINK *new_link); // stay at current + ex_current_was_last = other_it->ex_current_was_last = false; + ex_current_was_cycle_pt = false; + other_it->ex_current_was_cycle_pt = false; - void add_before_then_move( // add before current & - ELIST_LINK *new_link); // move to new + temp_it.mark_cycle_pt(); + do { // walk sublist + if (temp_it.cycled_list()) { // can't find end pt + BAD_SUBLIST.error("ITERATOR.extract_sublist", ABORT); + } - void add_before_stay_put( // add before current & - ELIST_LINK *new_link); // stay at current + if (temp_it.at_last()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = true; + } - void add_list_after( // add a list & - ELIST *list_to_add); // stay at current + if (temp_it.current == cycle_pt) { + ex_current_was_cycle_pt = true; + } - void add_list_before( // add a list & - ELIST *list_to_add); // move to it 1st item + if (temp_it.current == other_it->cycle_pt) { + other_it->ex_current_was_cycle_pt = true; + } - ELIST_LINK *data() { // get current data -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::data", ABORT); + temp_it.forward(); + } while (temp_it.prev != other_it->current); + + // circularise sublist + other_it->current->next = current; + end_of_new_list = other_it->current; + + // sublist = whole list + if (prev == other_it->current) { + list->last = nullptr; + prev = current = next = nullptr; + other_it->prev = other_it->current = other_it->next = nullptr; + } else { + prev->next = other_it->next; + current = other_it->current = nullptr; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; + } // to other current + + public: + ITERATOR() { // constructor + list = nullptr; + } // unassigned list + /*********************************************************************** + * ELIST_ITERATOR::ELIST_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + ITERATOR(ELIST *list_to_iterate) { + set_to_list(list_to_iterate); } - if (!current) { - NULL_DATA.error("ELIST_ITERATOR::data", ABORT); + /*********************************************************************** + * ELIST_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + void set_to_list( // change list + ELIST *list_to_iterate) { +#ifndef NDEBUG + if (!list_to_iterate) { + BAD_PARAMETER.error("ELIST_ITERATOR::set_to_list", ABORT, "list_to_iterate is nullptr"); + } +#endif + + list = list_to_iterate; + prev = list->last; + current = list->First(); + next = current ? current->next : nullptr; + cycle_pt = nullptr; // await explicit set + started_cycling = false; + ex_current_was_last = false; + ex_current_was_cycle_pt = false; } + /*********************************************************************** + * ELIST_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + void add_after_then_move( // add after current & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::add_after_then_move", ABORT); + } + if (!new_element) { + BAD_PARAMETER.error("ELIST_ITERATOR::add_after_then_move", ABORT, "new_element is nullptr"); + } + if (new_element->next) { + STILL_LINKED.error("ELIST_ITERATOR::add_after_then_move", ABORT); + } #endif - return current; - } - ELIST_LINK *data_relative( // get data + or - ... - int8_t offset); // offset from current + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } else { + new_element->next = next; + + if (current) { // not extracted + current->next = new_element; + prev = current; + if (current == list->last) { + list->last = new_element; + } + } else { // current extracted + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + } + if (ex_current_was_cycle_pt) { + cycle_pt = new_element; + } + } + } + current = new_element; + } // move to new + /*********************************************************************** + * ELIST_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + void add_after_stay_put( // add after current & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::add_after_stay_put", ABORT); + } + if (!new_element) { + BAD_PARAMETER.error("ELIST_ITERATOR::add_after_stay_put", ABORT, "new_element is nullptr"); + } + if (new_element->next) { + STILL_LINKED.error("ELIST_ITERATOR::add_after_stay_put", ABORT); + } +#endif - ELIST_LINK *forward(); // move to next element + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = false; + current = nullptr; + } else { + new_element->next = next; + + if (current) { // not extracted + current->next = new_element; + if (prev == current) { + prev = new_element; + } + if (current == list->last) { + list->last = new_element; + } + } else { // current extracted + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = false; + } + } + next = new_element; + } + } // stay at current + /*********************************************************************** + * ELIST_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + void add_before_then_move( // add before current & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::add_before_then_move", ABORT); + } + if (!new_element) { + BAD_PARAMETER.error("ELIST_ITERATOR::add_before_then_move", ABORT, "new_element is nullptr"); + } + if (new_element->next) { + STILL_LINKED.error("ELIST_ITERATOR::add_before_then_move", ABORT); + } +#endif - ELIST_LINK *extract(); // remove from list + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + } else { + prev->next = new_element; + if (current) { // not extracted + new_element->next = current; + next = current; + } else { // current extracted + new_element->next = next; + if (ex_current_was_last) { + list->last = new_element; + } + if (ex_current_was_cycle_pt) { + cycle_pt = new_element; + } + } + } + current = new_element; + } // move to new + /*********************************************************************** + * ELIST_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but don't move the + * iterator to the new element. + **********************************************************************/ + void add_before_stay_put( // add before current & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::add_before_stay_put", ABORT); + } + if (!new_element) { + BAD_PARAMETER.error("ELIST_ITERATOR::add_before_stay_put", ABORT, "new_element is nullptr"); + } + if (new_element->next) { + STILL_LINKED.error("ELIST_ITERATOR::add_before_stay_put", ABORT); + } +#endif - ELIST_LINK *move_to_first(); // go to start of list + if (list->empty()) { + new_element->next = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = true; + current = nullptr; + } else { + prev->next = new_element; + if (current) { // not extracted + new_element->next = current; + if (next == current) { + next = new_element; + } + } else { // current extracted + new_element->next = next; + if (ex_current_was_last) { + list->last = new_element; + } + } + prev = new_element; + } + } // stay at current + /*********************************************************************** + * ELIST_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but don't move + *the + * iterator. + **********************************************************************/ + void add_list_after( // add a list & + ELIST *list_to_add) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::add_list_after", ABORT); + } + if (!list_to_add) { + BAD_PARAMETER.error("ELIST_ITERATOR::add_list_after", ABORT, "list_to_add is nullptr"); + } +#endif - ELIST_LINK *move_to_last(); // go to end of list + if (!list_to_add->empty()) { + if (list->empty()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First(); + ex_current_was_last = true; + current = nullptr; + } else { + if (current) { // not extracted + current->next = list_to_add->First(); + if (current == list->last) { + list->last = list_to_add->last; + } + list_to_add->last->next = next; + next = current->next; + } else { // current extracted + prev->next = list_to_add->First(); + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = false; + } + list_to_add->last->next = next; + next = prev->next; + } + } + list_to_add->last = nullptr; + } + } // stay at current + /*********************************************************************** + * ELIST_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + void add_list_before( // add a list & + ELIST *list_to_add) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::add_list_before", ABORT); + } + if (!list_to_add) { + BAD_PARAMETER.error("ELIST_ITERATOR::add_list_before", ABORT, "list_to_add is nullptr"); + } +#endif - void mark_cycle_pt(); // remember current + if (!list_to_add->empty()) { + if (list->empty()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First(); + next = current->next; + ex_current_was_last = false; + } else { + prev->next = list_to_add->First(); + if (current) { // not extracted + list_to_add->last->next = current; + } else { // current extracted + list_to_add->last->next = next; + if (ex_current_was_last) { + list->last = list_to_add->last; + } + if (ex_current_was_cycle_pt) { + cycle_pt = prev->next; + } + } + current = prev->next; + next = current->next; + } + list_to_add->last = nullptr; + } + } // move to it 1st item - bool empty() const { // is list empty? + CLASSNAME *data() { // get current data #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::empty", ABORT); - } + if (!list) { + NO_LIST.error("ELIST_ITERATOR::data", ABORT); + } + if (!current) { + NULL_DATA.error("ELIST_ITERATOR::data", ABORT); + } #endif - return list->empty(); - } - - bool current_extracted() const { // current extracted? - return !current; - } + return current; + } + /*********************************************************************** + * ELIST_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * "offset" must not be less than -1. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + CLASSNAME *data_relative( // get data + or - ... + int8_t offset) { // offset from current + CLASSNAME *ptr; - bool at_first() const; // Current is first? +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::data_relative", ABORT); + if (list->empty()) + EMPTY_LIST.error("ELIST_ITERATOR::data_relative", ABORT); + if (offset < -1) + BAD_PARAMETER.error("ELIST_ITERATOR::data_relative", ABORT, "offset < -l"); +#endif - bool at_last() const; // Current is last? + if (offset == -1) { + ptr = prev; + } else { + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next) { + ; + } + } - bool cycled_list() const; // Completed a cycle? +#ifndef NDEBUG + if (!ptr) + NULL_DATA.error("ELIST_ITERATOR::data_relative", ABORT); +#endif - void add_to_end( // add at end & - ELIST_LINK *new_link); // don't move + return ptr; + } // offset from current + /*********************************************************************** + * ELIST_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + CLASSNAME *forward() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::forward", ABORT); +#endif + if (list->empty()) { + return nullptr; + } - void exchange( // positions of 2 links - ELIST_ITERATOR *other_it); // other iterator + if (current) { // not removed so + // set previous + prev = current; + started_cycling = true; + // In case next is deleted by another iterator, get next from current. + current = current->next; + } else { + if (ex_current_was_cycle_pt) { + cycle_pt = next; + } + current = next; + } +#ifndef NDEBUG + if (!current) + NULL_DATA.error("ELIST_ITERATOR::forward", ABORT); +#endif + next = current->next; - //# elements in list - int32_t length() const { - return list->length(); - } +#ifndef NDEBUG + if (!next) { + NULL_NEXT.error("ELIST_ITERATOR::forward", ABORT, + "This is: %p Current is: %p", + static_cast(this), + static_cast(current)); + } +#endif + return current; + } // move to next element + + /*********************************************************************** + * ELIST_ITERATOR::extract + * + * Do extraction by removing current from the list, returning it to the + * caller, but NOT updating the iterator. (So that any calling loop can do + * this.) The iterator's current points to nullptr. If the extracted element + * is to be deleted, this is the callers responsibility. + **********************************************************************/ + CLASSNAME *extract() { + CLASSNAME *extracted_link; - void sort( // sort elements - int comparator( // comparison routine - const void *, const void *)); -}; +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::extract", ABORT); + } + if (!current) { // list empty or + // element extracted + NULL_CURRENT.error("ELIST_ITERATOR::extract", ABORT); + } +#endif -/*********************************************************************** - * ELIST_ITERATOR::set_to_list - * - * (Re-)initialise the iterator to point to the start of the list_to_iterate - * over. - **********************************************************************/ + if (list->singleton()) { + // Special case where we do need to change the iterator. + prev = next = list->last = nullptr; + } else { + prev->next = next; // remove from list -inline void ELIST_ITERATOR::set_to_list( // change list - ELIST *list_to_iterate) { + ex_current_was_last = (current == list->last); + if (ex_current_was_last) { + list->last = prev; + } + } + // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. + ex_current_was_cycle_pt = (current == cycle_pt); + extracted_link = current; + extracted_link->next = nullptr; // for safety + current = nullptr; + return extracted_link; + } // remove from list + /*********************************************************************** + * ELIST_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + CLASSNAME *move_to_first() { #ifndef NDEBUG - if (!list_to_iterate) { - BAD_PARAMETER.error("ELIST_ITERATOR::set_to_list", ABORT, "list_to_iterate is nullptr"); - } + if (!list) { + NO_LIST.error("ELIST_ITERATOR::move_to_first", ABORT); + } #endif - list = list_to_iterate; - prev = list->last; - current = list->First(); - next = current ? current->next : nullptr; - cycle_pt = nullptr; // await explicit set - started_cycling = false; - ex_current_was_last = false; - ex_current_was_cycle_pt = false; -} - -/*********************************************************************** - * ELIST_ITERATOR::ELIST_ITERATOR - * - * CONSTRUCTOR - set iterator to specified list; - **********************************************************************/ - -inline ELIST_ITERATOR::ELIST_ITERATOR(ELIST *list_to_iterate) { - set_to_list(list_to_iterate); -} + current = list->First(); + prev = list->last; + next = current ? current->next : nullptr; + return current; + } // go to start of list + /*********************************************************************** + * ELIST_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + CLASSNAME *move_to_last() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST_ITERATOR::move_to_last", ABORT); +#endif -/*********************************************************************** - * ELIST_ITERATOR::add_after_then_move - * - * Add a new element to the list after the current element and move the - * iterator to the new element. - **********************************************************************/ + while (current != list->last) { + forward(); + } -inline void ELIST_ITERATOR::add_after_then_move( // element to add - ELIST_LINK *new_element) { + return current; + } // go to end of list + /*********************************************************************** + * ELIST_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + void mark_cycle_pt() { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::add_after_then_move", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST_ITERATOR::add_after_then_move", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST_ITERATOR::add_after_then_move", ABORT); - } + if (!list) { + NO_LIST.error("ELIST_ITERATOR::mark_cycle_pt", ABORT); + } #endif - if (list->empty()) { - new_element->next = new_element; - list->last = new_element; - prev = next = new_element; - } else { - new_element->next = next; - - if (current) { // not extracted - current->next = new_element; - prev = current; - if (current == list->last) { - list->last = new_element; + if (current) { + cycle_pt = current; + } else { + ex_current_was_cycle_pt = true; } - } else { // current extracted - prev->next = new_element; - if (ex_current_was_last) { - list->last = new_element; - } - if (ex_current_was_cycle_pt) { - cycle_pt = new_element; + started_cycling = false; + } // remember current + + bool empty() const { // is list empty? +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::empty", ABORT); } +#endif + return list->empty(); } - } - current = new_element; -} - -/*********************************************************************** - * ELIST_ITERATOR::add_after_stay_put - * - * Add a new element to the list after the current element but do not move - * the iterator to the new element. - **********************************************************************/ -inline void ELIST_ITERATOR::add_after_stay_put( // element to add - ELIST_LINK *new_element) { + bool current_extracted() const { // current extracted? + return !current; + } + /*********************************************************************** + * ELIST_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + bool at_first() const { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::add_after_stay_put", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST_ITERATOR::add_after_stay_put", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST_ITERATOR::add_after_stay_put", ABORT); - } + if (!list) { + NO_LIST.error("ELIST_ITERATOR::at_first", ABORT); + } #endif - if (list->empty()) { - new_element->next = new_element; - list->last = new_element; - prev = next = new_element; - ex_current_was_last = false; - current = nullptr; - } else { - new_element->next = next; - - if (current) { // not extracted - current->next = new_element; - if (prev == current) { - prev = new_element; - } - if (current == list->last) { - list->last = new_element; - } - } else { // current extracted - prev->next = new_element; - if (ex_current_was_last) { - list->last = new_element; - ex_current_was_last = false; + // we're at a deleted + return ((list->empty()) || (current == list->First()) || + ((current == nullptr) && (prev == list->last) && // NON-last pt between + !ex_current_was_last)); // first and last + } // Current is first? + /*********************************************************************** + * ELIST_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + bool at_last() const { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::at_last", ABORT); } - } - next = new_element; - } -} - -/*********************************************************************** - * ELIST_ITERATOR::add_before_then_move - * - * Add a new element to the list before the current element and move the - * iterator to the new element. - **********************************************************************/ +#endif -inline void ELIST_ITERATOR::add_before_then_move( // element to add - ELIST_LINK *new_element) { + // we're at a deleted + return ((list->empty()) || (current == list->last) || + ((current == nullptr) && (prev == list->last) && // last point between + ex_current_was_last)); // first and last + } // Current is last? + /*********************************************************************** + * ELIST_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + bool cycled_list() const { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::add_before_then_move", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST_ITERATOR::add_before_then_move", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST_ITERATOR::add_before_then_move", ABORT); - } + if (!list) { + NO_LIST.error("ELIST_ITERATOR::cycled_list", ABORT); + } #endif - if (list->empty()) { - new_element->next = new_element; - list->last = new_element; - prev = next = new_element; - } else { - prev->next = new_element; - if (current) { // not extracted - new_element->next = current; - next = current; - } else { // current extracted - new_element->next = next; - if (ex_current_was_last) { - list->last = new_element; + return ((list->empty()) || ((current == cycle_pt) && started_cycling)); + } // Completed a cycle? + /*********************************************************************** + * ELIST_ITERATOR::add_to_end + * + * 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 + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. + **********************************************************************/ + void add_to_end( // add at end & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST_ITERATOR::add_to_end", ABORT); } - if (ex_current_was_cycle_pt) { - cycle_pt = new_element; + if (!new_element) { + BAD_PARAMETER.error("ELIST_ITERATOR::add_to_end", ABORT, "new_element is nullptr"); } - } - } - current = new_element; -} + if (new_element->next) { + STILL_LINKED.error("ELIST_ITERATOR::add_to_end", ABORT); + } +#endif -/*********************************************************************** - * ELIST_ITERATOR::add_before_stay_put - * - * Add a new element to the list before the current element but don't move the - * iterator to the new element. - **********************************************************************/ + if (this->at_last()) { + this->add_after_stay_put(new_element); + } else { + if (this->at_first()) { + this->add_before_stay_put(new_element); + list->last = new_element; + } else { // Iteratr is elsewhere + new_element->next = list->last->next; + list->last->next = new_element; + list->last = new_element; + } + } + } // don't move + /*********************************************************************** + * ELIST_ITERATOR::exchange() + * + * 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 + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + void exchange( // positions of 2 links + ITERATOR *other_it) { // other iterator + constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists"); + + CLASSNAME *old_current; -inline void ELIST_ITERATOR::add_before_stay_put( // element to add - ELIST_LINK *new_element) { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::add_before_stay_put", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST_ITERATOR::add_before_stay_put", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST_ITERATOR::add_before_stay_put", ABORT); - } + if (!list) + NO_LIST.error("ELIST_ITERATOR::exchange", ABORT); + if (!other_it) + BAD_PARAMETER.error("ELIST_ITERATOR::exchange", ABORT, "other_it nullptr"); + if (!(other_it->list)) + NO_LIST.error("ELIST_ITERATOR::exchange", ABORT, "other_it"); #endif - if (list->empty()) { - new_element->next = new_element; - list->last = new_element; - prev = next = new_element; - ex_current_was_last = true; - current = nullptr; - } else { - prev->next = new_element; - if (current) { // not extracted - new_element->next = current; - if (next == current) { - next = new_element; - } - } else { // current extracted - new_element->next = next; - if (ex_current_was_last) { - list->last = new_element; + /* Do nothing if either list is empty or if both iterators reference the same + link */ + + if ((list->empty()) || (other_it->list->empty()) || (current == other_it->current)) { + return; } - } - prev = new_element; - } -} -/*********************************************************************** - * ELIST_ITERATOR::add_list_after - * - * Insert another list to this list after the current element but don't move - *the - * iterator. - **********************************************************************/ + /* Error if either current element is deleted */ -inline void ELIST_ITERATOR::add_list_after(ELIST *list_to_add) { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::add_list_after", ABORT); - } - if (!list_to_add) { - BAD_PARAMETER.error("ELIST_ITERATOR::add_list_after", ABORT, "list_to_add is nullptr"); - } -#endif + if (!current || !other_it->current) { + DONT_EXCHANGE_DELETED.error("ELIST_ITERATOR.exchange", ABORT); + } - if (!list_to_add->empty()) { - if (list->empty()) { - list->last = list_to_add->last; - prev = list->last; - next = list->First(); - ex_current_was_last = true; - current = nullptr; - } else { - if (current) { // not extracted - current->next = list_to_add->First(); - if (current == list->last) { - list->last = list_to_add->last; - } - list_to_add->last->next = next; - next = current->next; - } else { // current extracted - prev->next = list_to_add->First(); - if (ex_current_was_last) { - list->last = list_to_add->last; - ex_current_was_last = false; + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + // adjacent links + if ((next == other_it->current) || (other_it->next == current)) { + // doubleton list + if ((next == other_it->current) && (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } else { // non-doubleton with + // adjacent links + // other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + current->next = other_it->current; + other_it->next = other_it->current; + prev = current; + } else { // this before other + prev->next = other_it->current; + current->next = other_it->next; + other_it->current->next = current; + next = current; + other_it->prev = other_it->current; + } } - list_to_add->last->next = next; - next = prev->next; + } else { // no overlap + prev->next = other_it->current; + current->next = other_it->next; + other_it->prev->next = current; + other_it->current->next = next; } - } - list_to_add->last = nullptr; - } -} -/*********************************************************************** - * ELIST_ITERATOR::add_list_before - * - * Insert another list to this list before the current element. Move the - * iterator to the start of the inserted elements - * iterator. - **********************************************************************/ + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ -inline void ELIST_ITERATOR::add_list_before(ELIST *list_to_add) { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::add_list_before", ABORT); - } - if (!list_to_add) { - BAD_PARAMETER.error("ELIST_ITERATOR::add_list_before", ABORT, "list_to_add is nullptr"); - } -#endif + if (list->last == current) { + list->last = other_it->current; + } + if (other_it->list->last == other_it->current) { + other_it->list->last = current; + } - if (!list_to_add->empty()) { - if (list->empty()) { - list->last = list_to_add->last; - prev = list->last; - current = list->First(); - next = current->next; - ex_current_was_last = false; - } else { - prev->next = list_to_add->First(); - if (current) { // not extracted - list_to_add->last->next = current; - } else { // current extracted - list_to_add->last->next = next; - if (ex_current_was_last) { - list->last = list_to_add->last; - } - if (ex_current_was_cycle_pt) { - cycle_pt = prev->next; - } + if (current == cycle_pt) { + cycle_pt = other_it->cycle_pt; + } + if (other_it->current == other_it->cycle_pt) { + other_it->cycle_pt = cycle_pt; } - current = prev->next; - next = current->next; - } - list_to_add->last = nullptr; - } -} -/*********************************************************************** - * ELIST_ITERATOR::extract - * - * Do extraction by removing current from the list, returning it to the - * caller, but NOT updating the iterator. (So that any calling loop can do - * this.) The iterator's current points to nullptr. If the extracted element - * is to be deleted, this is the callers responsibility. - **********************************************************************/ + /* The actual exchange - in all cases*/ -inline ELIST_LINK *ELIST_ITERATOR::extract() { - ELIST_LINK *extracted_link; + old_current = current; + current = other_it->current; + other_it->current = old_current; + } // other iterator + //# elements in list + int32_t length() const { + return list->length(); + } + /*********************************************************************** + * ELIST_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + void sort( // sort elements + int comparator( // comparison routine + const void *, const void *)) { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::extract", ABORT); - } - if (!current) { // list empty or - // element extracted - NULL_CURRENT.error("ELIST_ITERATOR::extract", ABORT); - } + if (!list) { + NO_LIST.error("ELIST_ITERATOR::sort", ABORT); + } #endif - if (list->singleton()) { - // Special case where we do need to change the iterator. - prev = next = list->last = nullptr; - } else { - prev->next = next; // remove from list - - ex_current_was_last = (current == list->last); - if (ex_current_was_last) { - list->last = prev; + list->sort(comparator); + move_to_first(); } + }; + + +private: + CLASSNAME *last = nullptr; // End of list + //(Points to head) + CLASSNAME *First() { // return first + return last ? last->next : nullptr; } - // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. - ex_current_was_cycle_pt = (current == cycle_pt); - extracted_link = current; - extracted_link->next = nullptr; // for safety - current = nullptr; - return extracted_link; -} - -/*********************************************************************** - * ELIST_ITERATOR::move_to_first() - * - * Move current so that it is set to the start of the list. - * Return data just in case anyone wants it. - **********************************************************************/ -inline ELIST_LINK *ELIST_ITERATOR::move_to_first() { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::move_to_first", ABORT); +public: + ~ELIST() { + clear(); } -#endif - current = list->First(); - prev = list->last; - next = current ? current->next : nullptr; - return current; -} + /* delete elements */ + void clear() { + internal_clear(); + } -/*********************************************************************** - * ELIST_ITERATOR::mark_cycle_pt() - * - * Remember the current location so that we can tell whether we've returned - * to this point later. - * - * If the current point is deleted either now, or in the future, the cycle - * point will be set to the next item which is set to current. This could be - * by a forward, add_after_then_move or add_after_then_move. - **********************************************************************/ + /* Become a deep copy of src_list */ + template + void deep_copy(const U *src_list, CLASSNAME *(*copier)(const CLASSNAME *)) { + ITERATOR from_it(const_cast(src_list)); + ITERATOR to_it(this); -inline void ELIST_ITERATOR::mark_cycle_pt() { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::mark_cycle_pt", ABORT); + for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) + to_it.add_after_then_move((*copier)(from_it.data())); } -#endif - if (current) { - cycle_pt = current; - } else { - ex_current_was_cycle_pt = true; + /*********************************************************************** + * ELIST::internal_clear + * + * Used by the destructor and the "clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each element of the list, regardless of its derived type. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + + // destroy all links + void internal_clear() { + CLASSNAME *ptr; + CLASSNAME *next; + + if (!empty()) { + ptr = last->next; // set to first + last->next = nullptr; // break circle + last = nullptr; // set list empty + while (ptr) { + next = ptr->next; + delete ptr; + ptr = next; + } + } } - started_cycling = false; -} -/*********************************************************************** - * ELIST_ITERATOR::at_first() - * - * Are we at the start of the list? - * - **********************************************************************/ + bool empty() const { + return !last; + } -inline bool ELIST_ITERATOR::at_first() const { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::at_first", ABORT); + bool singleton() const { + return last ? (last == last->next) : false; } -#endif - // we're at a deleted - return ((list->empty()) || (current == list->First()) || - ((current == nullptr) && (prev == list->last) && // NON-last pt between - !ex_current_was_last)); // first and last -} + void shallow_copy( // dangerous!! + ELIST *from_list) { // beware destructors!! + last = from_list->last; + } -/*********************************************************************** - * ELIST_ITERATOR::at_last() - * - * Are we at the end of the list? + /*********************************************************************** + * ELIST::assign_to_sublist * + * 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 + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. **********************************************************************/ + void assign_to_sublist( // to this list + ITERATOR *start_it, // from list start + ITERATOR *end_it) { // from list end + constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist"); -inline bool ELIST_ITERATOR::at_last() const { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::at_last", ABORT); - } -#endif - - // we're at a deleted - return ((list->empty()) || (current == list->last) || - ((current == nullptr) && (prev == list->last) && // last point between - ex_current_was_last)); // first and last -} + if (!empty()) { + LIST_NOT_EMPTY.error("ELIST.assign_to_sublist", ABORT); + } -/*********************************************************************** - * ELIST_ITERATOR::cycled_list() - * - * Have we returned to the cycle_pt since it was set? - * - **********************************************************************/ + last = start_it->extract_sublist(end_it); + } // from list end -inline bool ELIST_ITERATOR::cycled_list() const { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::cycled_list", ABORT); + // # elements in list + int32_t length() const { + int32_t count = 0; + if (last != nullptr) { + count = 1; + for (auto it = last->next; it != last; it = it->next) { + count++; + } + } + return count; } -#endif - return ((list->empty()) || ((current == cycle_pt) && started_cycling)); -} - -/*********************************************************************** - * ELIST_ITERATOR::sort() - * - * Sort the elements of the list, then reposition at the start. + /*********************************************************************** + * ELIST::sort * + * Sort elements on list + * NB If you don't like the const declarations in the comparator, coerce yours: + * ( int (*)(const void *, const void *) **********************************************************************/ + void sort( // sort elements + int comparator( // comparison routine + const void *, const void *)) { + // Allocate an array of pointers, one per list element. + auto count = length(); -inline void ELIST_ITERATOR::sort( // sort elements - int comparator( // comparison routine - const void *, const void *)) { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::sort", ABORT); - } -#endif + if (count > 0) { + // ptr array to sort + std::vector base; + base.reserve(count); - list->sort(comparator); - move_to_first(); -} + ITERATOR it(this); -/*********************************************************************** - * ELIST_ITERATOR::add_to_end - * - * 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 - * the iterator couldn't set its prev pointer. Adding to the end is - * essential for implementing - queues. -**********************************************************************/ + // Extract all elements, putting the pointers in the array. + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + base.push_back(it.extract()); + } -inline void ELIST_ITERATOR::add_to_end( // element to add - ELIST_LINK *new_element) { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST_ITERATOR::add_to_end", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST_ITERATOR::add_to_end", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST_ITERATOR::add_to_end", ABORT); + // Sort the pointer array. + qsort(&base[0], count, sizeof(base[0]), comparator); + + // Rebuild the list from the sorted pointers. + for (auto current : base) { + it.add_to_end(current); + } + } } -#endif - if (this->at_last()) { - this->add_after_stay_put(new_element); - } else { - if (this->at_first()) { - this->add_before_stay_put(new_element); - list->last = new_element; - } else { // Iteratr is elsewhere - new_element->next = list->last->next; - list->last->next = new_element; - list->last = new_element; + // Assuming list has been sorted already, insert new_link to + // keep the list sorted according to the same comparison function. + // Comparison function is the same as used by sort, i.e. uses double + // indirection. Time is O(1) to add to beginning or end. + // Time is linear to add pre-sorted items to an empty list. + // If unique is set to true and comparator() returns 0 (an entry with the + // same information as the one contained in new_link is already in 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 + // (otherwise the function returns new_link). + CLASSNAME *add_sorted_and_find(int comparator(const void *, const void *), bool unique, + CLASSNAME *new_link) { + // Check for adding at the end. + if (last == nullptr || comparator(&last, &new_link) < 0) { + if (last == nullptr) { + new_link->next = new_link; + } else { + new_link->next = last->next; + last->next = new_link; + } + last = new_link; + } else { + // Need to use an iterator. + ITERATOR it(this); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + auto *link = it.data(); + int compare = comparator(&link, &new_link); + if (compare > 0) { + break; + } else if (unique && compare == 0) { + return link; + } + } + if (it.cycled_list()) { + it.add_to_end(new_link); + } else { + it.add_before_then_move(new_link); + } } + return new_link; + } + + // Same as above, but returns true if the new entry was inserted, false + // if the identical entry already existed in the list. + bool add_sorted(int comparator(const void *, const void *), bool unique, CLASSNAME *new_link) { + return (add_sorted_and_find(comparator, unique, new_link) == new_link); } -} - -#define ELISTIZEH(CLASSNAME) \ - class CLASSNAME##_LIST : public X_LIST { \ - using X_LIST::X_LIST; \ - }; \ - class CLASSNAME##_IT : public X_ITER { \ - using X_ITER::X_ITER; \ +}; + +// add TESS_API? +// move templated lists to public include dirs? +#define ELISTIZEH(CLASSNAME) \ + struct CLASSNAME##_LIST : ELIST { \ + using ELIST::ELIST; \ + }; \ + struct CLASSNAME##_IT : ELIST::ITERATOR { \ + using base = ELIST::ITERATOR; \ + using base::base; \ }; } // namespace tesseract diff --git a/src/classify/trainingsample.h b/src/classify/trainingsample.h index 5e4a943af9..211ab66994 100644 --- a/src/classify/trainingsample.h +++ b/src/classify/trainingsample.h @@ -51,7 +51,7 @@ static const int kSampleScaleSize = 3; static const int kSampleRandomSize = kSampleYShiftSize * kSampleScaleSize - 2; // ASSERT_IS_PRIME(kSampleRandomSize) !! -class TESS_API TrainingSample : public ELIST_LINK { +class TESS_API TrainingSample : public ELIST::LINK { public: TrainingSample() : class_id_(INVALID_UNICHAR_ID) diff --git a/src/textord/blkocc.h b/src/textord/blkocc.h index 449cddbb2f..e8d5a1bb41 100644 --- a/src/textord/blkocc.h +++ b/src/textord/blkocc.h @@ -44,7 +44,7 @@ CLASS REGION_OCC ****************************************************************************/ -class REGION_OCC : public ELIST_LINK { +class REGION_OCC : public ELIST::LINK { public: float min_x; // Lowest x in region float max_x; // Highest x in region diff --git a/src/textord/colpartitionset.h b/src/textord/colpartitionset.h index 60a78297fa..53b04177fb 100644 --- a/src/textord/colpartitionset.h +++ b/src/textord/colpartitionset.h @@ -35,7 +35,7 @@ using PartSetVector = std::vector; // Its main use is in holding a candidate partitioning of the width of the // image into columns, where each member ColPartition is a single column. // ColPartitionSets are used in building the column layout of a page. -class ColPartitionSet : public ELIST_LINK { +class ColPartitionSet : public ELIST::LINK { public: ColPartitionSet() = default; explicit ColPartitionSet(ColPartition_LIST *partitions); diff --git a/src/textord/fpchop.h b/src/textord/fpchop.h index 13f4c10aaa..2e8ec39f77 100644 --- a/src/textord/fpchop.h +++ b/src/textord/fpchop.h @@ -24,7 +24,7 @@ namespace tesseract { -class C_OUTLINE_FRAG : public ELIST_LINK { +class C_OUTLINE_FRAG : public ELIST::LINK { public: C_OUTLINE_FRAG() { // empty constructor steps = nullptr; diff --git a/src/textord/pitsync1.h b/src/textord/pitsync1.h index 5df7ec4889..d81512d660 100644 --- a/src/textord/pitsync1.h +++ b/src/textord/pitsync1.h @@ -31,7 +31,7 @@ namespace tesseract { class FPSEGPT_LIST; -class FPSEGPT : public ELIST_LINK { +class FPSEGPT : public ELIST::LINK { public: FPSEGPT() = default; FPSEGPT( // constructor diff --git a/src/textord/sortflts.h b/src/textord/sortflts.h index 278d8f9074..8c1755b10b 100644 --- a/src/textord/sortflts.h +++ b/src/textord/sortflts.h @@ -23,7 +23,7 @@ namespace tesseract { -class SORTED_FLOAT : public ELIST_LINK { +class SORTED_FLOAT : public ELIST::LINK { friend class SORTED_FLOATS; public: diff --git a/src/textord/tablefind.cpp b/src/textord/tablefind.cpp index 537bec9cb0..46ce4fa433 100644 --- a/src/textord/tablefind.cpp +++ b/src/textord/tablefind.cpp @@ -2099,7 +2099,7 @@ void TableFinder::MakeTableBlocks(ColPartitionGrid *grid, //////// ColSegment code //////// ColSegment::ColSegment() - : ELIST_LINK(), + : ELIST::LINK(), num_table_cells_(0), num_text_cells_(0), type_(COL_UNKNOWN) {} diff --git a/src/textord/tablefind.h b/src/textord/tablefind.h index 2554afdbc1..378b71a255 100644 --- a/src/textord/tablefind.h +++ b/src/textord/tablefind.h @@ -36,7 +36,7 @@ class ColSegment; ELISTIZEH(ColSegment) CLISTIZEH(ColSegment) -class ColSegment : public ELIST_LINK { +class ColSegment : public ELIST::LINK { public: ColSegment(); ~ColSegment() = default; diff --git a/src/textord/tabvector.h b/src/textord/tabvector.h index 2c48d72196..f23fd60272 100644 --- a/src/textord/tabvector.h +++ b/src/textord/tabvector.h @@ -64,7 +64,7 @@ ELISTIZEH(TabConstraint) // on a list of constraints. The list itself is cooperatively owned // by the TabVectors of the constraints on the list and managed // by implicit reference counting via the elements of the list. -class TabConstraint : public ELIST_LINK { +class TabConstraint : public ELIST::LINK { public: // This empty constructor is here only so that the class can be ELISTIZED. // TODO(rays) change deep_copy in elst.h line 955 to take a callback copier diff --git a/src/textord/workingpartset.h b/src/textord/workingpartset.h index f02926967a..ee092decdb 100644 --- a/src/textord/workingpartset.h +++ b/src/textord/workingpartset.h @@ -29,7 +29,7 @@ namespace tesseract { // WorkingPartSet holds a working set of ColPartitions during transformation // from the grid-based storage to regions in logical reading order, and is // therefore only used during construction of the regions. -class WorkingPartSet : public ELIST_LINK { +class WorkingPartSet : public ELIST::LINK { public: explicit WorkingPartSet(ColPartition *column) : column_(column), latest_part_(nullptr), part_it_(&part_set_) {} diff --git a/src/wordrec/lm_state.h b/src/wordrec/lm_state.h index 14f343e4be..0176a253f3 100644 --- a/src/wordrec/lm_state.h +++ b/src/wordrec/lm_state.h @@ -89,7 +89,7 @@ struct LanguageModelNgramInfo { /// Struct for storing the information about a path in the segmentation graph /// explored by Viterbi search. -struct ViterbiStateEntry : public ELIST_LINK { +struct ViterbiStateEntry : public ELIST::LINK { ViterbiStateEntry(ViterbiStateEntry *pe, BLOB_CHOICE *b, float c, float ol, const LMConsistencyInfo &ci, const AssociateStats &as, LanguageModelFlagsType tcf, LanguageModelDawgInfo *d, LanguageModelNgramInfo *n, diff --git a/src/wordrec/wordrec.h b/src/wordrec/wordrec.h index 20bdbcf9e4..f57d6fe985 100644 --- a/src/wordrec/wordrec.h +++ b/src/wordrec/wordrec.h @@ -169,7 +169,7 @@ class SegSearchPending { }; /* ccmain/tstruct.cpp *********************************************************/ -class FRAGMENT : public ELIST_LINK { +class FRAGMENT : public ELIST::LINK { public: FRAGMENT() { // constructor } diff --git a/unittest/list_test.cc b/unittest/list_test.cc index cfdae00e01..0ba8c3afbf 100644 --- a/unittest/list_test.cc +++ b/unittest/list_test.cc @@ -31,7 +31,7 @@ class Clst { unsigned value; }; -class Elst : public ELIST_LINK { +class Elst : public ELIST::LINK { public: Elst(unsigned n) : value(n) {} unsigned value; @@ -82,7 +82,7 @@ TEST_F(ListTest, TestELIST) { Elst_LIST list; EXPECT_TRUE(list.empty()); EXPECT_EQ(list.length(), 0); - auto it = ELIST_ITERATOR(&list); + auto it = ELIST::ITERATOR(&list); for (unsigned i = 0; i < ListSize; i++) { auto *elst = new Elst(i); it.add_to_end(elst); From 34bbf961e588dad952634337d88a315eb9803a04 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 18:22:53 +0300 Subject: [PATCH 10/17] Convert ELIST2 into template. --- src/ccstruct/blobbox.h | 2 +- src/ccstruct/werd.cpp | 2 +- src/ccstruct/werd.h | 2 +- src/ccutil/elst2.cpp | 476 ----------- src/ccutil/elst2.h | 1657 ++++++++++++++++++++++-------------- src/textord/colpartition.h | 2 +- src/textord/tabvector.h | 2 +- unittest/list_test.cc | 4 +- 8 files changed, 1004 insertions(+), 1143 deletions(-) delete mode 100644 src/ccutil/elst2.cpp diff --git a/src/ccstruct/blobbox.h b/src/ccstruct/blobbox.h index 5d8c1edc79..f25d00fe13 100644 --- a/src/ccstruct/blobbox.h +++ b/src/ccstruct/blobbox.h @@ -552,7 +552,7 @@ class BLOBNBOX : public ELIST::LINK { bool owns_cblob_ = false; }; -class TO_ROW : public ELIST2_LINK { +class TO_ROW : public ELIST2::LINK { public: static const int kErrorWeight = 3; diff --git a/src/ccstruct/werd.cpp b/src/ccstruct/werd.cpp index 22f9cda710..cef414db5a 100644 --- a/src/ccstruct/werd.cpp +++ b/src/ccstruct/werd.cpp @@ -355,7 +355,7 @@ WERD *WERD::shallow_copy() { */ WERD &WERD::operator=(const WERD &source) { - this->ELIST2_LINK::operator=(source); + this->ELIST2::LINK::operator=(source); blanks = source.blanks; flags = source.flags; script_id_ = source.script_id_; diff --git a/src/ccstruct/werd.h b/src/ccstruct/werd.h index db1b0ee4e4..07fec6150f 100644 --- a/src/ccstruct/werd.h +++ b/src/ccstruct/werd.h @@ -55,7 +55,7 @@ enum DISPLAY_FLAGS { class ROW; // forward decl -class TESS_API WERD : public ELIST2_LINK { +class TESS_API WERD : public ELIST2::LINK { public: WERD() = default; // WERD constructed with: diff --git a/src/ccutil/elst2.cpp b/src/ccutil/elst2.cpp deleted file mode 100644 index 64d22fdb65..0000000000 --- a/src/ccutil/elst2.cpp +++ /dev/null @@ -1,476 +0,0 @@ -/********************************************************************** - * File: elst2.cpp (Formerly elist2.c) - * Description: Doubly linked embedded list code not in the include file. - * Author: Phil Cheatle - * - * (C) Copyright 1991, Hewlett-Packard Ltd. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - **********************************************************************/ - -#include "elst2.h" - -#include - -namespace tesseract { - -/*********************************************************************** - * ELIST2::internal_clear - * - * Used by the destructor and the "clear" member function of derived list - * classes to destroy all the elements on the list. - * The calling function passes a "zapper" function which can be called to - * delete each element of the list, regardless of its derived type. This - * technique permits a generic clear function to destroy elements of - * different derived types correctly, without requiring virtual functions and - * the consequential memory overhead. - **********************************************************************/ - -void ELIST2::internal_clear( // destroy all links - void (*zapper)(void *)) { - // ptr to zapper functn - ELIST2_LINK *ptr; - ELIST2_LINK *next; - - if (!empty()) { - ptr = last->next; // set to first - last->next = nullptr; // break circle - last = nullptr; // set list empty - while (ptr) { - next = ptr->next; - zapper(ptr); - ptr = next; - } - } -} - -/*********************************************************************** - * ELIST2::assign_to_sublist - * - * 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 - * the same list, different from "this" one. The sublist removed is the - * inclusive list from start_it's current position to end_it's current - * position. If this range passes over the end of the source list then the - * source list has its end set to the previous element of start_it. The - * extracted sublist is unaffected by the end point of the source list, its - * end point is always the end_it position. - **********************************************************************/ - -void ELIST2::assign_to_sublist( // to this list - ELIST2_ITERATOR *start_it, // from list start - ELIST2_ITERATOR *end_it) { // from list end - constexpr ERRCODE LIST_NOT_EMPTY("Destination list must be empty before extracting a sublist"); - - if (!empty()) { - LIST_NOT_EMPTY.error("ELIST2.assign_to_sublist", ABORT); - } - - last = start_it->extract_sublist(end_it); -} - -/*********************************************************************** - * ELIST2::sort - * - * Sort elements on list - * NB If you don't like the const declarations in the comparator, coerce yours: - * (int (*)(const void *, const void *) - **********************************************************************/ - -void ELIST2::sort( // sort elements - int comparator( // comparison routine - const void *, const void *)) { - // Allocate an array of pointers, one per list element. - auto count = length(); - if (count > 0) { - // ptr array to sort - std::vector base; - base.reserve(count); - - ELIST2_ITERATOR it(this); - - // Extract all elements, putting the pointers in the array. - for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { - base.push_back(it.extract()); - } - - // Sort the pointer array. - qsort(&base[0], count, sizeof(base[0]), comparator); - - // Rebuild the list from the sorted pointers. - for (auto current : base) { - it.add_to_end(current); - } - } -} - -// Assuming list has been sorted already, insert new_link to -// keep the list sorted according to the same comparison function. -// Comparison function is the same as used by sort, i.e. uses double -// indirection. Time is O(1) to add to beginning or end. -// Time is linear to add pre-sorted items to an empty list. -void ELIST2::add_sorted(int comparator(const void *, const void *), ELIST2_LINK *new_link) { - // Check for adding at the end. - if (last == nullptr || comparator(&last, &new_link) < 0) { - if (last == nullptr) { - new_link->next = new_link; - new_link->prev = new_link; - } else { - new_link->next = last->next; - new_link->prev = last; - last->next = new_link; - new_link->next->prev = new_link; - } - last = new_link; - } else { - // Need to use an iterator. - ELIST2_ITERATOR it(this); - for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { - ELIST2_LINK *link = it.data(); - if (comparator(&link, &new_link) > 0) { - break; - } - } - if (it.cycled_list()) { - it.add_to_end(new_link); - } else { - it.add_before_then_move(new_link); - } - } -} - -/*********************************************************************** - * MEMBER FUNCTIONS OF CLASS: ELIST2_ITERATOR - * ========================================== - **********************************************************************/ - -/*********************************************************************** - * ELIST2_ITERATOR::forward - * - * Move the iterator to the next element of the list. - * REMEMBER: ALL LISTS ARE CIRCULAR. - **********************************************************************/ - -ELIST2_LINK *ELIST2_ITERATOR::forward() { -#ifndef NDEBUG - if (!list) - NO_LIST.error("ELIST2_ITERATOR::forward", ABORT); -#endif - if (list->empty()) { - return nullptr; - } - - if (current) { // not removed so - // set previous - prev = current; - started_cycling = true; - // In case next is deleted by another iterator, get it from the current. - current = current->next; - } else { - if (ex_current_was_cycle_pt) { - cycle_pt = next; - } - current = next; - } - -#ifndef NDEBUG - if (!current) - NULL_DATA.error("ELIST2_ITERATOR::forward", ABORT); -#endif - - next = current->next; - -#ifndef NDEBUG - if (!next) { - NULL_NEXT.error("ELIST2_ITERATOR::forward", ABORT, - "This is: %p Current is: %p", - static_cast(this), - static_cast(current)); - } -#endif - - return current; -} - -/*********************************************************************** - * ELIST2_ITERATOR::backward - * - * Move the iterator to the previous element of the list. - * REMEMBER: ALL LISTS ARE CIRCULAR. - **********************************************************************/ - -ELIST2_LINK *ELIST2_ITERATOR::backward() { -#ifndef NDEBUG - if (!list) - NO_LIST.error("ELIST2_ITERATOR::backward", ABORT); -#endif - if (list->empty()) { - return nullptr; - } - - if (current) { // not removed so - // set previous - next = current; - started_cycling = true; - // In case prev is deleted by another iterator, get it from current. - current = current->prev; - } else { - if (ex_current_was_cycle_pt) { - cycle_pt = prev; - } - current = prev; - } - -#ifndef NDEBUG - if (!current) - NULL_DATA.error("ELIST2_ITERATOR::backward", ABORT); - if (!prev) { - NULL_PREV.error("ELIST2_ITERATOR::backward", ABORT, - "This is: %p Current is: %p", - static_cast(this), - static_cast(current)); - } -#endif - - prev = current->prev; - return current; -} - -/*********************************************************************** - * ELIST2_ITERATOR::data_relative - * - * Return the data pointer to the element "offset" elements from current. - * (This function can't be INLINEd because it contains a loop) - **********************************************************************/ - -ELIST2_LINK *ELIST2_ITERATOR::data_relative( // get data + or - .. - int8_t offset) { // offset from current - ELIST2_LINK *ptr; - -#ifndef NDEBUG - if (!list) - NO_LIST.error("ELIST2_ITERATOR::data_relative", ABORT); - if (list->empty()) - EMPTY_LIST.error("ELIST2_ITERATOR::data_relative", ABORT); -#endif - - if (offset < 0) { - for (ptr = current ? current : next; offset++ < 0; ptr = ptr->prev) { - ; - } - } else { - for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next) { - ; - } - } - -#ifndef NDEBUG - if (!ptr) - NULL_DATA.error("ELIST2_ITERATOR::data_relative", ABORT); -#endif - - return ptr; -} - -/*********************************************************************** - * ELIST2_ITERATOR::exchange() - * - * 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 - * elements. On return, each iterator points to the element which was the - * other iterators current on entry. - * (This function hasn't been in-lined because its a bit big!) - **********************************************************************/ - -void ELIST2_ITERATOR::exchange( // positions of 2 links - ELIST2_ITERATOR *other_it) { // other iterator - constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists"); - - ELIST2_LINK *old_current; - -#ifndef NDEBUG - if (!list) - NO_LIST.error("ELIST2_ITERATOR::exchange", ABORT); - if (!other_it) - BAD_PARAMETER.error("ELIST2_ITERATOR::exchange", ABORT, "other_it nullptr"); - if (!(other_it->list)) - NO_LIST.error("ELIST2_ITERATOR::exchange", ABORT, "other_it"); -#endif - - /* Do nothing if either list is empty or if both iterators reference the same -link */ - - if ((list->empty()) || (other_it->list->empty()) || (current == other_it->current)) { - return; - } - - /* Error if either current element is deleted */ - - if (!current || !other_it->current) { - DONT_EXCHANGE_DELETED.error("ELIST2_ITERATOR.exchange", ABORT); - } - - /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements -(other before this); non-doubleton adjacent elements (this before other); -non-adjacent elements. */ - - // adjacent links - if ((next == other_it->current) || (other_it->next == current)) { - // doubleton list - if ((next == other_it->current) && (other_it->next == current)) { - prev = next = current; - other_it->prev = other_it->next = other_it->current; - } else { // non-doubleton with - // adjacent links - // other before this - if (other_it->next == current) { - other_it->prev->next = current; - other_it->current->next = next; - other_it->current->prev = current; - current->next = other_it->current; - current->prev = other_it->prev; - next->prev = other_it->current; - - other_it->next = other_it->current; - prev = current; - } else { // this before other - prev->next = other_it->current; - current->next = other_it->next; - current->prev = other_it->current; - other_it->current->next = current; - other_it->current->prev = prev; - other_it->next->prev = current; - - next = current; - other_it->prev = other_it->current; - } - } - } else { // no overlap - prev->next = other_it->current; - current->next = other_it->next; - current->prev = other_it->prev; - next->prev = other_it->current; - other_it->prev->next = current; - other_it->current->next = next; - other_it->current->prev = prev; - other_it->next->prev = current; - } - - /* update end of list pointer when necessary (remember that the 2 iterators - may iterate over different lists!) */ - - if (list->last == current) { - list->last = other_it->current; - } - if (other_it->list->last == other_it->current) { - other_it->list->last = current; - } - - if (current == cycle_pt) { - cycle_pt = other_it->cycle_pt; - } - if (other_it->current == other_it->cycle_pt) { - other_it->cycle_pt = cycle_pt; - } - - /* The actual exchange - in all cases*/ - - old_current = current; - current = other_it->current; - other_it->current = old_current; -} - -/*********************************************************************** - * ELIST2_ITERATOR::extract_sublist() - * - * This is a private member, used only by ELIST2::assign_to_sublist. - * 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 - * pointer to the last element. - * (Can't inline this function because it contains a loop) - **********************************************************************/ - -ELIST2_LINK *ELIST2_ITERATOR::extract_sublist( // from this current - ELIST2_ITERATOR *other_it) { // to other current -#ifndef NDEBUG - 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"); -#endif - constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); - - ELIST2_ITERATOR temp_it = *this; - ELIST2_LINK *end_of_new_list; - -#ifndef NDEBUG - if (!other_it) - BAD_PARAMETER.error("ELIST2_ITERATOR::extract_sublist", ABORT, "other_it nullptr"); - if (!list) - NO_LIST.error("ELIST2_ITERATOR::extract_sublist", ABORT); - if (list != other_it->list) - BAD_EXTRACTION_PTS.error("ELIST2_ITERATOR.extract_sublist", ABORT); - if (list->empty()) - EMPTY_LIST.error("ELIST2_ITERATOR::extract_sublist", ABORT); - - if (!current || !other_it->current) - DONT_EXTRACT_DELETED.error("ELIST2_ITERATOR.extract_sublist", ABORT); -#endif - - ex_current_was_last = other_it->ex_current_was_last = false; - ex_current_was_cycle_pt = false; - other_it->ex_current_was_cycle_pt = false; - - temp_it.mark_cycle_pt(); - do { // walk sublist - if (temp_it.cycled_list()) { // can't find end pt - BAD_SUBLIST.error("ELIST2_ITERATOR.extract_sublist", ABORT); - } - - if (temp_it.at_last()) { - list->last = prev; - ex_current_was_last = other_it->ex_current_was_last = true; - } - - if (temp_it.current == cycle_pt) { - ex_current_was_cycle_pt = true; - } - - if (temp_it.current == other_it->cycle_pt) { - other_it->ex_current_was_cycle_pt = true; - } - - temp_it.forward(); - } - // do INCLUSIVE list - while (temp_it.prev != other_it->current); - - // circularise sublist - other_it->current->next = current; - // circularise sublist - current->prev = other_it->current; - end_of_new_list = other_it->current; - - // sublist = whole list - if (prev == other_it->current) { - list->last = nullptr; - prev = current = next = nullptr; - other_it->prev = other_it->current = other_it->next = nullptr; - } else { - prev->next = other_it->next; - other_it->next->prev = prev; - - current = other_it->current = nullptr; - next = other_it->next; - other_it->prev = prev; - } - return end_of_new_list; -} - -} // namespace tesseract diff --git a/src/ccutil/elst2.h b/src/ccutil/elst2.h index a54738a3cb..fb6a4d1611 100644 --- a/src/ccutil/elst2.h +++ b/src/ccutil/elst2.h @@ -27,8 +27,6 @@ namespace tesseract { -class ELIST2_ITERATOR; - /********************************************************************** DESIGN NOTE =========== @@ -47,787 +45,1126 @@ i) The duplication in source does not affect the run time code size - the ii) The compiler should have a bit less work to do! **********************************************************************/ -/********************************************************************** - * CLASS - ELIST2_LINK - * - * Generic link class for doubly linked lists with embedded links - * - * Note: No destructor - elements are assumed to be destroyed EITHER after - * they have been extracted from a list OR by the ELIST2 destructor which - * walks the list. - **********************************************************************/ - -class ELIST2_LINK { - friend class ELIST2_ITERATOR; - friend class ELIST2; - - ELIST2_LINK *prev; - ELIST2_LINK *next; - -public: - ELIST2_LINK() { // constructor - prev = next = nullptr; - } - - ELIST2_LINK(const ELIST2_LINK &) = delete; - - // The assignment operator is required for WERD. - void operator=(const ELIST2_LINK &) { - prev = next = nullptr; - } -}; - /********************************************************************** * CLASS - ELIST2 * * Generic list class for doubly linked lists with embedded links **********************************************************************/ -class TESS_API ELIST2 { - friend class ELIST2_ITERATOR; - - ELIST2_LINK *last = nullptr; // End of list - //(Points to head) - ELIST2_LINK *First() { // return first - return last ? last->next : nullptr; - } - +template +class ELIST2 { public: - // destroy all links - void internal_clear(void (*zapper)(void *)); - - bool empty() const { // is list empty? - return !last; - } - - bool singleton() const { - return last ? (last == last->next) : false; - } - - void shallow_copy( // dangerous!! - ELIST2 *from_list) { // beware destructors!! - last = from_list->last; - } - - // ptr to copier functn - void internal_deep_copy(ELIST2_LINK *(*copier)(ELIST2_LINK *), - const ELIST2 *list); // list being copied - - void assign_to_sublist( // to this list - ELIST2_ITERATOR *start_it, // from list start - ELIST2_ITERATOR *end_it); // from list end - - // # elements in list - int32_t length() const { - int32_t count = 0; - if (last != nullptr) { - count = 1; - for (auto it = last->next; it != last; it = it->next) { - count++; - } + /********************************************************************** + * CLASS - LINK + * + * Generic link class for doubly linked lists with embedded links + * + * Note: No destructor - elements are assumed to be destroyed EITHER after + * they have been extracted from a list OR by the ELIST2 destructor which + * walks the list. + **********************************************************************/ + + class LINK { + friend class ITERATOR; + friend class ELIST2; + + CLASSNAME *prev; + CLASSNAME *next; + + public: + LINK() { // constructor + prev = next = nullptr; } - return count; - } - - void sort( // sort elements - int comparator( // comparison routine - const void *, const void *)); - // Assuming list has been sorted already, insert new_link to - // keep the list sorted according to the same comparison function. - // Comparison function is the same as used by sort, i.e. uses double - // indirection. Time is O(1) to add to beginning or end. - // Time is linear to add pre-sorted items to an empty list. - void add_sorted(int comparator(const void *, const void *), ELIST2_LINK *new_link); -}; + LINK(const LINK &) = delete; -/*********************************************************************** - * CLASS - ELIST2_ITERATOR - * - * Generic iterator class for doubly linked lists with embedded - *links - **********************************************************************/ + // The assignment operator is required for WERD. + void operator=(const LINK &) { + prev = next = nullptr; + } + }; -class TESS_API ELIST2_ITERATOR { - friend void ELIST2::assign_to_sublist(ELIST2_ITERATOR *, ELIST2_ITERATOR *); - ELIST2 *list; // List being iterated - ELIST2_LINK *prev; // prev element - ELIST2_LINK *current; // current element - ELIST2_LINK *next; // next element - ELIST2_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_cycle_pt; // current extracted was cycle point - bool started_cycling; // Have we moved off the start? + /*********************************************************************** + * CLASS - ELIST2_ITERATOR + * + * Generic iterator class for doubly linked lists with embedded + *links + **********************************************************************/ + + class ITERATOR { + friend void ELIST2::assign_to_sublist(ITERATOR *, ITERATOR *); + + ELIST2 *list; // List being iterated + CLASSNAME *prev; // prev element + CLASSNAME *current; // current element + CLASSNAME *next; // next element + CLASSNAME *cycle_pt; // point we are cycling the list to. + bool ex_current_was_last; // current extracted was end of list + bool ex_current_was_cycle_pt; // current extracted was cycle point + bool started_cycling; // Have we moved off the start? + /*********************************************************************** + * ELIST2_ITERATOR::extract_sublist() + * + * This is a private member, used only by ELIST2::assign_to_sublist. + * 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 + * pointer to the last element. + * (Can't inline this function because it contains a loop) + **********************************************************************/ + CLASSNAME *extract_sublist( // from this current... + ITERATOR *other_it) { // to other current +#ifndef NDEBUG + 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"); +#endif + constexpr ERRCODE BAD_SUBLIST("Can't find sublist end point in original list"); - ELIST2_LINK *extract_sublist( // from this current... - ELIST2_ITERATOR *other_it); // to other current + ITERATOR temp_it = *this; + CLASSNAME *end_of_new_list; -public: - ELIST2_ITERATOR( // constructor - ELIST2 *list_to_iterate); +#ifndef NDEBUG + if (!other_it) + BAD_PARAMETER.error("ELIST2_ITERATOR::extract_sublist", ABORT, "other_it nullptr"); + if (!list) + NO_LIST.error("ELIST2_ITERATOR::extract_sublist", ABORT); + if (list != other_it->list) + BAD_EXTRACTION_PTS.error("ELIST2_ITERATOR.extract_sublist", ABORT); + if (list->empty()) + EMPTY_LIST.error("ELIST2_ITERATOR::extract_sublist", ABORT); + + if (!current || !other_it->current) + DONT_EXTRACT_DELETED.error("ELIST2_ITERATOR.extract_sublist", ABORT); +#endif - void set_to_list( // change list - ELIST2 *list_to_iterate); + ex_current_was_last = other_it->ex_current_was_last = false; + ex_current_was_cycle_pt = false; + other_it->ex_current_was_cycle_pt = false; - void add_after_then_move( // add after current & - ELIST2_LINK *new_link); // move to new + temp_it.mark_cycle_pt(); + do { // walk sublist + if (temp_it.cycled_list()) { // can't find end pt + BAD_SUBLIST.error("ELIST2_ITERATOR.extract_sublist", ABORT); + } - void add_after_stay_put( // add after current & - ELIST2_LINK *new_link); // stay at current + if (temp_it.at_last()) { + list->last = prev; + ex_current_was_last = other_it->ex_current_was_last = true; + } - void add_before_then_move( // add before current & - ELIST2_LINK *new_link); // move to new + if (temp_it.current == cycle_pt) { + ex_current_was_cycle_pt = true; + } - void add_before_stay_put( // add before current & - ELIST2_LINK *new_link); // stay at current + if (temp_it.current == other_it->cycle_pt) { + other_it->ex_current_was_cycle_pt = true; + } - void add_list_after( // add a list & - ELIST2 *list_to_add); // stay at current + temp_it.forward(); + } + // do INCLUSIVE list + while (temp_it.prev != other_it->current); + + // circularise sublist + other_it->current->next = current; + // circularise sublist + current->prev = other_it->current; + end_of_new_list = other_it->current; + + // sublist = whole list + if (prev == other_it->current) { + list->last = nullptr; + prev = current = next = nullptr; + other_it->prev = other_it->current = other_it->next = nullptr; + } else { + prev->next = other_it->next; + other_it->next->prev = prev; + + current = other_it->current = nullptr; + next = other_it->next; + other_it->prev = prev; + } + return end_of_new_list; + } // to other current + + public: + /*********************************************************************** + * ELIST2_ITERATOR::ELIST2_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + ITERATOR( // constructor + ELIST2 *list_to_iterate) { + set_to_list(list_to_iterate); + } - void add_list_before( // add a list & - ELIST2 *list_to_add); // move to it 1st item + /*********************************************************************** + * ELIST2_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ - ELIST2_LINK *data() { // get current data + void set_to_list( // change list + ELIST2 *list_to_iterate) { #ifndef NDEBUG - if (!current) { - NULL_DATA.error("ELIST2_ITERATOR::data", ABORT); - } - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::data", ABORT); - } + if (!list_to_iterate) { + BAD_PARAMETER.error("ELIST2_ITERATOR::set_to_list", ABORT, "list_to_iterate is nullptr"); + } #endif - return current; - } - ELIST2_LINK *data_relative( // get data + or - ... - int8_t offset); // offset from current - - ELIST2_LINK *forward(); // move to next element - - ELIST2_LINK *backward(); // move to prev element + list = list_to_iterate; + prev = list->last; + current = list->First(); + next = current ? current->next : nullptr; + cycle_pt = nullptr; // await explicit set + started_cycling = false; + ex_current_was_last = false; + ex_current_was_cycle_pt = false; + } + /*********************************************************************** + * ELIST2_ITERATOR::add_after_then_move + * + * Add a new element to the list after the current element and move the + * iterator to the new element. + **********************************************************************/ + void add_after_then_move( // add after current & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::add_after_then_move", ABORT); + } + if (!new_element) { + BAD_PARAMETER.error("ELIST2_ITERATOR::add_after_then_move", ABORT, "new_element is nullptr"); + } + if (new_element->next) { + STILL_LINKED.error("ELIST2_ITERATOR::add_after_then_move", ABORT); + } +#endif - ELIST2_LINK *extract(); // remove from list + if (list->empty()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + } else { + new_element->next = next; + next->prev = new_element; + + if (current) { // not extracted + new_element->prev = current; + current->next = new_element; + prev = current; + if (current == list->last) { + list->last = new_element; + } + } else { // current extracted + new_element->prev = prev; + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + } + if (ex_current_was_cycle_pt) { + cycle_pt = new_element; + } + } + } + current = new_element; + } // move to new + /*********************************************************************** + * ELIST2_ITERATOR::add_after_stay_put + * + * Add a new element to the list after the current element but do not move + * the iterator to the new element. + **********************************************************************/ + void add_after_stay_put( // add after current & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::add_after_stay_put", ABORT); + } + if (!new_element) { + BAD_PARAMETER.error("ELIST2_ITERATOR::add_after_stay_put", ABORT, "new_element is nullptr"); + } + if (new_element->next) { + STILL_LINKED.error("ELIST2_ITERATOR::add_after_stay_put", ABORT); + } +#endif - // go to start of list - ELIST2_LINK *move_to_first(); + if (list->empty()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = false; + current = nullptr; + } else { + new_element->next = next; + next->prev = new_element; + + if (current) { // not extracted + new_element->prev = current; + current->next = new_element; + if (prev == current) { + prev = new_element; + } + if (current == list->last) { + list->last = new_element; + } + } else { // current extracted + new_element->prev = prev; + prev->next = new_element; + if (ex_current_was_last) { + list->last = new_element; + ex_current_was_last = false; + } + } + next = new_element; + } + } // stay at current + /*********************************************************************** + * ELIST2_ITERATOR::add_before_then_move + * + * Add a new element to the list before the current element and move the + * iterator to the new element. + **********************************************************************/ + void add_before_then_move( // add before current & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::add_before_then_move", ABORT); + } + if (!new_element) { + BAD_PARAMETER.error("ELIST2_ITERATOR::add_before_then_move", ABORT, "new_element is nullptr"); + } + if (new_element->next) { + STILL_LINKED.error("ELIST2_ITERATOR::add_before_then_move", ABORT); + } +#endif - ELIST2_LINK *move_to_last(); // go to end of list + if (list->empty()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + } else { + prev->next = new_element; + new_element->prev = prev; + + if (current) { // not extracted + new_element->next = current; + current->prev = new_element; + next = current; + } else { // current extracted + new_element->next = next; + next->prev = new_element; + if (ex_current_was_last) { + list->last = new_element; + } + if (ex_current_was_cycle_pt) { + cycle_pt = new_element; + } + } + } + current = new_element; + } // move to new + /*********************************************************************** + * ELIST2_ITERATOR::add_before_stay_put + * + * Add a new element to the list before the current element but don't move the + * iterator to the new element. + **********************************************************************/ + void add_before_stay_put( // add before current & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::add_before_stay_put", ABORT); + } + if (!new_element) { + BAD_PARAMETER.error("ELIST2_ITERATOR::add_before_stay_put", ABORT, "new_element is nullptr"); + } + if (new_element->next) { + STILL_LINKED.error("ELIST2_ITERATOR::add_before_stay_put", ABORT); + } +#endif - void mark_cycle_pt(); // remember current + if (list->empty()) { + new_element->next = new_element; + new_element->prev = new_element; + list->last = new_element; + prev = next = new_element; + ex_current_was_last = true; + current = nullptr; + } else { + prev->next = new_element; + new_element->prev = prev; + + if (current) { // not extracted + new_element->next = current; + current->prev = new_element; + if (next == current) { + next = new_element; + } + } else { // current extracted + new_element->next = next; + next->prev = new_element; + if (ex_current_was_last) { + list->last = new_element; + } + } + prev = new_element; + } + } // stay at current + /*********************************************************************** + * ELIST2_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but don't move + *the + * iterator. + **********************************************************************/ + void add_list_after( // add a list & + ELIST2 *list_to_add) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::add_list_after", ABORT); + } + if (!list_to_add) { + BAD_PARAMETER.error("ELIST2_ITERATOR::add_list_after", ABORT, "list_to_add is nullptr"); + } +#endif - bool empty() const { // is list empty? + if (!list_to_add->empty()) { + if (list->empty()) { + list->last = list_to_add->last; + prev = list->last; + next = list->First(); + ex_current_was_last = true; + current = nullptr; + } else { + if (current) { // not extracted + current->next = list_to_add->First(); + current->next->prev = current; + if (current == list->last) { + list->last = list_to_add->last; + } + list_to_add->last->next = next; + next->prev = list_to_add->last; + next = current->next; + } else { // current extracted + prev->next = list_to_add->First(); + prev->next->prev = prev; + if (ex_current_was_last) { + list->last = list_to_add->last; + ex_current_was_last = false; + } + list_to_add->last->next = next; + next->prev = list_to_add->last; + next = prev->next; + } + } + list_to_add->last = nullptr; + } + } // stay at current + /*********************************************************************** + * ELIST2_ITERATOR::add_list_before + * + * Insert another list to this list before the current element. Move the + * iterator to the start of the inserted elements + * iterator. + **********************************************************************/ + void add_list_before( // add a list & + ELIST2 *list_to_add) { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::empty", ABORT); - } + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::add_list_before", ABORT); + } + if (!list_to_add) { + BAD_PARAMETER.error("ELIST2_ITERATOR::add_list_before", ABORT, "list_to_add is nullptr"); + } #endif - return list->empty(); - } - bool current_extracted() const { // current extracted? - return !current; - } + if (!list_to_add->empty()) { + if (list->empty()) { + list->last = list_to_add->last; + prev = list->last; + current = list->First(); + next = current->next; + ex_current_was_last = false; + } else { + prev->next = list_to_add->First(); + prev->next->prev = prev; + + if (current) { // not extracted + list_to_add->last->next = current; + current->prev = list_to_add->last; + } else { // current extracted + list_to_add->last->next = next; + next->prev = list_to_add->last; + if (ex_current_was_last) { + list->last = list_to_add->last; + } + if (ex_current_was_cycle_pt) { + cycle_pt = prev->next; + } + } + current = prev->next; + next = current->next; + } + list_to_add->last = nullptr; + } + } // move to it 1st item - bool at_first() const; // Current is first? + CLASSNAME *data() { // get current data +#ifndef NDEBUG + if (!current) { + NULL_DATA.error("ELIST2_ITERATOR::data", ABORT); + } + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::data", ABORT); + } +#endif + return current; + } + /*********************************************************************** + * ELIST2_ITERATOR::data_relative + * + * Return the data pointer to the element "offset" elements from current. + * (This function can't be INLINEd because it contains a loop) + **********************************************************************/ + CLASSNAME *data_relative( // get data + or - ... + int8_t offset) { // offset from current + CLASSNAME *ptr; - bool at_last() const; // Current is last? +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST2_ITERATOR::data_relative", ABORT); + if (list->empty()) + EMPTY_LIST.error("ELIST2_ITERATOR::data_relative", ABORT); +#endif - bool cycled_list() const; // Completed a cycle? + if (offset < 0) { + for (ptr = current ? current : next; offset++ < 0; ptr = ptr->prev) { + ; + } + } else { + for (ptr = current ? current : prev; offset-- > 0; ptr = ptr->next) { + ; + } + } - void add_to_end( // add at end & - ELIST2_LINK *new_link); // don't move +#ifndef NDEBUG + if (!ptr) + NULL_DATA.error("ELIST2_ITERATOR::data_relative", ABORT); +#endif - void exchange( // positions of 2 links - ELIST2_ITERATOR *other_it); // other iterator + return ptr; + } // offset from current + /*********************************************************************** + * ELIST2_ITERATOR::forward + * + * Move the iterator to the next element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + CLASSNAME *forward() { +#ifndef NDEBUG + if (!list) + NO_LIST.error("ELIST2_ITERATOR::forward", ABORT); +#endif + if (list->empty()) { + return nullptr; + } - //# elements in list - int32_t length() const { - return list->length(); - } + if (current) { // not removed so + // set previous + prev = current; + started_cycling = true; + // In case next is deleted by another iterator, get it from the current. + current = current->next; + } else { + if (ex_current_was_cycle_pt) { + cycle_pt = next; + } + current = next; + } - void sort( // sort elements - int comparator( // comparison routine - const void *, const void *)); +#ifndef NDEBUG + if (!current) + NULL_DATA.error("ELIST2_ITERATOR::forward", ABORT); +#endif -private: - // Don't use the following constructor. - ELIST2_ITERATOR() = delete; -}; + next = current->next; -/*********************************************************************** - * ELIST2_ITERATOR::set_to_list - * - * (Re-)initialise the iterator to point to the start of the list_to_iterate - * over. - **********************************************************************/ +#ifndef NDEBUG + if (!next) { + NULL_NEXT.error("ELIST2_ITERATOR::forward", ABORT, + "This is: %p Current is: %p", + static_cast(this), + static_cast(current)); + } +#endif -inline void ELIST2_ITERATOR::set_to_list( // change list - ELIST2 *list_to_iterate) { + return current; + } // move to next element + /*********************************************************************** + * ELIST2_ITERATOR::backward + * + * Move the iterator to the previous element of the list. + * REMEMBER: ALL LISTS ARE CIRCULAR. + **********************************************************************/ + CLASSNAME *backward() { #ifndef NDEBUG - if (!list_to_iterate) { - BAD_PARAMETER.error("ELIST2_ITERATOR::set_to_list", ABORT, "list_to_iterate is nullptr"); - } + if (!list) + NO_LIST.error("ELIST2_ITERATOR::backward", ABORT); #endif + if (list->empty()) { + return nullptr; + } - list = list_to_iterate; - prev = list->last; - current = list->First(); - next = current ? current->next : nullptr; - cycle_pt = nullptr; // await explicit set - started_cycling = false; - ex_current_was_last = false; - ex_current_was_cycle_pt = false; -} - -/*********************************************************************** - * ELIST2_ITERATOR::ELIST2_ITERATOR - * - * CONSTRUCTOR - set iterator to specified list; - **********************************************************************/ + if (current) { // not removed so + // set previous + next = current; + started_cycling = true; + // In case prev is deleted by another iterator, get it from current. + current = current->prev; + } else { + if (ex_current_was_cycle_pt) { + cycle_pt = prev; + } + current = prev; + } -inline ELIST2_ITERATOR::ELIST2_ITERATOR(ELIST2 *list_to_iterate) { - set_to_list(list_to_iterate); -} +#ifndef NDEBUG + if (!current) + NULL_DATA.error("ELIST2_ITERATOR::backward", ABORT); + if (!prev) { + NULL_PREV.error("ELIST2_ITERATOR::backward", ABORT, + "This is: %p Current is: %p", + static_cast(this), + static_cast(current)); + } +#endif -/*********************************************************************** - * ELIST2_ITERATOR::add_after_then_move - * - * Add a new element to the list after the current element and move the - * iterator to the new element. - **********************************************************************/ + prev = current->prev; + return current; + } // move to prev element + /*********************************************************************** + * ELIST2_ITERATOR::extract + * + * Do extraction by removing current from the list, returning it to the + * caller, but NOT updating the iterator. (So that any calling loop can do + * this.) The iterator's current points to nullptr. If the extracted element + * is to be deleted, this is the callers responsibility. + **********************************************************************/ + CLASSNAME *extract() { + CLASSNAME *extracted_link; -inline void ELIST2_ITERATOR::add_after_then_move( // element to add - ELIST2_LINK *new_element) { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::add_after_then_move", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST2_ITERATOR::add_after_then_move", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST2_ITERATOR::add_after_then_move", ABORT); - } + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::extract", ABORT); + } + if (!current) { // list empty or + // element extracted + NULL_CURRENT.error("ELIST2_ITERATOR::extract", ABORT); + } #endif - if (list->empty()) { - new_element->next = new_element; - new_element->prev = new_element; - list->last = new_element; - prev = next = new_element; - } else { - new_element->next = next; - next->prev = new_element; - - if (current) { // not extracted - new_element->prev = current; - current->next = new_element; - prev = current; - if (current == list->last) { - list->last = new_element; - } - } else { // current extracted - new_element->prev = prev; - prev->next = new_element; - if (ex_current_was_last) { - list->last = new_element; + if (list->singleton()) { + // Special case where we do need to change the iterator. + prev = next = list->last = nullptr; + } else { + prev->next = next; // remove from list + next->prev = prev; + + if (current == list->last) { + list->last = prev; + ex_current_was_last = true; + } else { + ex_current_was_last = false; + } } - if (ex_current_was_cycle_pt) { - cycle_pt = new_element; + // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. + ex_current_was_cycle_pt = (current == cycle_pt); + extracted_link = current; + extracted_link->next = nullptr; // for safety + extracted_link->prev = nullptr; // for safety + current = nullptr; + return extracted_link; + } // remove from list + /*********************************************************************** + * ELIST2_ITERATOR::move_to_first() + * + * Move current so that it is set to the start of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + // go to start of list + CLASSNAME *move_to_first() { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::move_to_first", ABORT); } - } - } - current = new_element; -} - -/*********************************************************************** - * ELIST2_ITERATOR::add_after_stay_put - * - * Add a new element to the list after the current element but do not move - * the iterator to the new element. - **********************************************************************/ +#endif -inline void ELIST2_ITERATOR::add_after_stay_put( // element to add - ELIST2_LINK *new_element) { + current = list->First(); + prev = list->last; + next = current ? current->next : nullptr; + return current; + } + /*********************************************************************** + * ELIST2_ITERATOR::move_to_last() + * + * Move current so that it is set to the end of the list. + * Return data just in case anyone wants it. + **********************************************************************/ + CLASSNAME *move_to_last() { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::add_after_stay_put", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST2_ITERATOR::add_after_stay_put", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST2_ITERATOR::add_after_stay_put", ABORT); - } + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::move_to_last", ABORT); + } #endif - if (list->empty()) { - new_element->next = new_element; - new_element->prev = new_element; - list->last = new_element; - prev = next = new_element; - ex_current_was_last = false; - current = nullptr; - } else { - new_element->next = next; - next->prev = new_element; - - if (current) { // not extracted - new_element->prev = current; - current->next = new_element; - if (prev == current) { - prev = new_element; + current = list->last; + prev = current ? current->prev : nullptr; + next = current ? current->next : nullptr; + return current; + } // go to end of list + /*********************************************************************** + * ELIST2_ITERATOR::mark_cycle_pt() + * + * Remember the current location so that we can tell whether we've returned + * to this point later. + * + * If the current point is deleted either now, or in the future, the cycle + * point will be set to the next item which is set to current. This could be + * by a forward, add_after_then_move or add_after_then_move. + **********************************************************************/ + void mark_cycle_pt() { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::mark_cycle_pt", ABORT); } - if (current == list->last) { - list->last = new_element; +#endif + + if (current) { + cycle_pt = current; + } else { + ex_current_was_cycle_pt = true; } - } else { // current extracted - new_element->prev = prev; - prev->next = new_element; - if (ex_current_was_last) { - list->last = new_element; - ex_current_was_last = false; + started_cycling = false; + } // remember current + + bool empty() const { // is list empty? +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::empty", ABORT); } +#endif + return list->empty(); } - next = new_element; - } -} - -/*********************************************************************** - * ELIST2_ITERATOR::add_before_then_move - * - * Add a new element to the list before the current element and move the - * iterator to the new element. - **********************************************************************/ -inline void ELIST2_ITERATOR::add_before_then_move( // element to add - ELIST2_LINK *new_element) { + bool current_extracted() const { // current extracted? + return !current; + } + /*********************************************************************** + * ELIST2_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + bool at_first() const { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::add_before_then_move", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST2_ITERATOR::add_before_then_move", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST2_ITERATOR::add_before_then_move", ABORT); - } + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::at_first", ABORT); + } #endif - if (list->empty()) { - new_element->next = new_element; - new_element->prev = new_element; - list->last = new_element; - prev = next = new_element; - } else { - prev->next = new_element; - new_element->prev = prev; - - if (current) { // not extracted - new_element->next = current; - current->prev = new_element; - next = current; - } else { // current extracted - new_element->next = next; - next->prev = new_element; - if (ex_current_was_last) { - list->last = new_element; - } - if (ex_current_was_cycle_pt) { - cycle_pt = new_element; + // we're at a deleted + return ((list->empty()) || (current == list->First()) || + ((current == nullptr) && (prev == list->last) && // NON-last pt between + !ex_current_was_last)); // first and last + } // Current is first? + /*********************************************************************** + * ELIST2_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + bool at_last() const { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::at_last", ABORT); } - } - } - current = new_element; -} - -/*********************************************************************** - * ELIST2_ITERATOR::add_before_stay_put - * - * Add a new element to the list before the current element but don't move the - * iterator to the new element. - **********************************************************************/ +#endif -inline void ELIST2_ITERATOR::add_before_stay_put( // element to add - ELIST2_LINK *new_element) { + // we're at a deleted + return ((list->empty()) || (current == list->last) || + ((current == nullptr) && (prev == list->last) && // last point between + ex_current_was_last)); // first and last + } // Current is last? + /*********************************************************************** + * ELIST2_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + bool cycled_list() const { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::add_before_stay_put", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST2_ITERATOR::add_before_stay_put", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST2_ITERATOR::add_before_stay_put", ABORT); - } + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::cycled_list", ABORT); + } #endif - if (list->empty()) { - new_element->next = new_element; - new_element->prev = new_element; - list->last = new_element; - prev = next = new_element; - ex_current_was_last = true; - current = nullptr; - } else { - prev->next = new_element; - new_element->prev = prev; - - if (current) { // not extracted - new_element->next = current; - current->prev = new_element; - if (next == current) { - next = new_element; + return ((list->empty()) || ((current == cycle_pt) && started_cycling)); + } // Completed a cycle? + /*********************************************************************** + * ELIST2_ITERATOR::add_to_end + * + * 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 + * the iterator couldn't set its prev pointer. Adding to the end is + * essential for implementing + queues. + **********************************************************************/ + void add_to_end( // add at end & + CLASSNAME *new_element) { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::add_to_end", ABORT); } - } else { // current extracted - new_element->next = next; - next->prev = new_element; - if (ex_current_was_last) { - list->last = new_element; + if (!new_element) { + BAD_PARAMETER.error("ELIST2_ITERATOR::add_to_end", ABORT, "new_element is nullptr"); } - } - prev = new_element; - } -} + if (new_element->next) { + STILL_LINKED.error("ELIST2_ITERATOR::add_to_end", ABORT); + } +#endif -/*********************************************************************** - * ELIST2_ITERATOR::add_list_after - * - * Insert another list to this list after the current element but don't move - *the - * iterator. - **********************************************************************/ + if (this->at_last()) { + this->add_after_stay_put(new_element); + } else { + if (this->at_first()) { + this->add_before_stay_put(new_element); + list->last = new_element; + } else { // Iteratr is elsewhere + new_element->next = list->last->next; + new_element->prev = list->last; + list->last->next->prev = new_element; + list->last->next = new_element; + list->last = new_element; + } + } + } // don't move + /*********************************************************************** + * ELIST2_ITERATOR::exchange() + * + * 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 + * elements. On return, each iterator points to the element which was the + * other iterators current on entry. + * (This function hasn't been in-lined because its a bit big!) + **********************************************************************/ + void exchange( // positions of 2 links + ITERATOR *other_it) { // other iterator + constexpr ERRCODE DONT_EXCHANGE_DELETED("Can't exchange deleted elements of lists"); + + CLASSNAME *old_current; -inline void ELIST2_ITERATOR::add_list_after(ELIST2 *list_to_add) { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::add_list_after", ABORT); - } - if (!list_to_add) { - BAD_PARAMETER.error("ELIST2_ITERATOR::add_list_after", ABORT, "list_to_add is nullptr"); - } + if (!list) + NO_LIST.error("ELIST2_ITERATOR::exchange", ABORT); + if (!other_it) + BAD_PARAMETER.error("ELIST2_ITERATOR::exchange", ABORT, "other_it nullptr"); + if (!(other_it->list)) + NO_LIST.error("ELIST2_ITERATOR::exchange", ABORT, "other_it"); #endif - if (!list_to_add->empty()) { - if (list->empty()) { - list->last = list_to_add->last; - prev = list->last; - next = list->First(); - ex_current_was_last = true; - current = nullptr; - } else { - if (current) { // not extracted - current->next = list_to_add->First(); - current->next->prev = current; - if (current == list->last) { - list->last = list_to_add->last; - } - list_to_add->last->next = next; - next->prev = list_to_add->last; - next = current->next; - } else { // current extracted - prev->next = list_to_add->First(); - prev->next->prev = prev; - if (ex_current_was_last) { - list->last = list_to_add->last; - ex_current_was_last = false; - } - list_to_add->last->next = next; - next->prev = list_to_add->last; - next = prev->next; + /* Do nothing if either list is empty or if both iterators reference the same + link */ + + if ((list->empty()) || (other_it->list->empty()) || (current == other_it->current)) { + return; } - } - list_to_add->last = nullptr; - } -} -/*********************************************************************** - * ELIST2_ITERATOR::add_list_before - * - * Insert another list to this list before the current element. Move the - * iterator to the start of the inserted elements - * iterator. - **********************************************************************/ + /* Error if either current element is deleted */ -inline void ELIST2_ITERATOR::add_list_before(ELIST2 *list_to_add) { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::add_list_before", ABORT); - } - if (!list_to_add) { - BAD_PARAMETER.error("ELIST2_ITERATOR::add_list_before", ABORT, "list_to_add is nullptr"); - } -#endif + if (!current || !other_it->current) { + DONT_EXCHANGE_DELETED.error("ELIST2_ITERATOR.exchange", ABORT); + } - if (!list_to_add->empty()) { - if (list->empty()) { - list->last = list_to_add->last; - prev = list->last; - current = list->First(); - next = current->next; - ex_current_was_last = false; - } else { - prev->next = list_to_add->First(); - prev->next->prev = prev; - - if (current) { // not extracted - list_to_add->last->next = current; - current->prev = list_to_add->last; - } else { // current extracted - list_to_add->last->next = next; - next->prev = list_to_add->last; - if (ex_current_was_last) { - list->last = list_to_add->last; - } - if (ex_current_was_cycle_pt) { - cycle_pt = prev->next; + /* Now handle the 4 cases: doubleton list; non-doubleton adjacent elements + (other before this); non-doubleton adjacent elements (this before other); + non-adjacent elements. */ + + // adjacent links + if ((next == other_it->current) || (other_it->next == current)) { + // doubleton list + if ((next == other_it->current) && (other_it->next == current)) { + prev = next = current; + other_it->prev = other_it->next = other_it->current; + } else { // non-doubleton with + // adjacent links + // other before this + if (other_it->next == current) { + other_it->prev->next = current; + other_it->current->next = next; + other_it->current->prev = current; + current->next = other_it->current; + current->prev = other_it->prev; + next->prev = other_it->current; + + other_it->next = other_it->current; + prev = current; + } else { // this before other + prev->next = other_it->current; + current->next = other_it->next; + current->prev = other_it->current; + other_it->current->next = current; + other_it->current->prev = prev; + other_it->next->prev = current; + + next = current; + other_it->prev = other_it->current; + } } + } else { // no overlap + prev->next = other_it->current; + current->next = other_it->next; + current->prev = other_it->prev; + next->prev = other_it->current; + other_it->prev->next = current; + other_it->current->next = next; + other_it->current->prev = prev; + other_it->next->prev = current; } - current = prev->next; - next = current->next; - } - list_to_add->last = nullptr; - } -} -/*********************************************************************** - * ELIST2_ITERATOR::extract - * - * Do extraction by removing current from the list, returning it to the - * caller, but NOT updating the iterator. (So that any calling loop can do - * this.) The iterator's current points to nullptr. If the extracted element - * is to be deleted, this is the callers responsibility. - **********************************************************************/ + /* update end of list pointer when necessary (remember that the 2 iterators + may iterate over different lists!) */ -inline ELIST2_LINK *ELIST2_ITERATOR::extract() { - ELIST2_LINK *extracted_link; + if (list->last == current) { + list->last = other_it->current; + } + if (other_it->list->last == other_it->current) { + other_it->list->last = current; + } -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::extract", ABORT); - } - if (!current) { // list empty or - // element extracted - NULL_CURRENT.error("ELIST2_ITERATOR::extract", ABORT); - } -#endif + if (current == cycle_pt) { + cycle_pt = other_it->cycle_pt; + } + if (other_it->current == other_it->cycle_pt) { + other_it->cycle_pt = cycle_pt; + } - if (list->singleton()) { - // Special case where we do need to change the iterator. - prev = next = list->last = nullptr; - } else { - prev->next = next; // remove from list - next->prev = prev; + /* The actual exchange - in all cases*/ - if (current == list->last) { - list->last = prev; - ex_current_was_last = true; - } else { - ex_current_was_last = false; - } - } - // Always set ex_current_was_cycle_pt so an add/forward will work in a loop. - ex_current_was_cycle_pt = (current == cycle_pt); - extracted_link = current; - extracted_link->next = nullptr; // for safety - extracted_link->prev = nullptr; // for safety - current = nullptr; - return extracted_link; -} - -/*********************************************************************** - * ELIST2_ITERATOR::move_to_first() - * - * Move current so that it is set to the start of the list. - * Return data just in case anyone wants it. - **********************************************************************/ + old_current = current; + current = other_it->current; + other_it->current = old_current; + } // other iterator -inline ELIST2_LINK *ELIST2_ITERATOR::move_to_first() { + //# elements in list + int32_t length() const { + return list->length(); + } + /*********************************************************************** + * ELIST2_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + void sort( // sort elements + int comparator( // comparison routine + const void *, const void *)) { #ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::move_to_first", ABORT); - } + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::sort", ABORT); + } #endif - current = list->First(); - prev = list->last; - next = current ? current->next : nullptr; - return current; -} - -/*********************************************************************** - * ELIST2_ITERATOR::move_to_last() - * - * Move current so that it is set to the end of the list. - * Return data just in case anyone wants it. - **********************************************************************/ + list->sort(comparator); + move_to_first(); + } -inline ELIST2_LINK *ELIST2_ITERATOR::move_to_last() { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::move_to_last", ABORT); - } -#endif + private: + // Don't use the following constructor. + ITERATOR() = delete; + }; - current = list->last; - prev = current ? current->prev : nullptr; - next = current ? current->next : nullptr; - return current; -} -/*********************************************************************** - * ELIST2_ITERATOR::mark_cycle_pt() - * - * Remember the current location so that we can tell whether we've returned - * to this point later. - * - * If the current point is deleted either now, or in the future, the cycle - * point will be set to the next item which is set to current. This could be - * by a forward, add_after_then_move or add_after_then_move. - **********************************************************************/ +private: + CLASSNAME *last = nullptr; // End of list + //(Points to head) + CLASSNAME *First() { // return first + return last ? last->next : nullptr; + } -inline void ELIST2_ITERATOR::mark_cycle_pt() { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::mark_cycle_pt", ABORT); +public: + ~ELIST2() { + clear(); } -#endif - if (current) { - cycle_pt = current; - } else { - ex_current_was_cycle_pt = true; + /* delete elements */ + void clear() { + internal_clear(); } - started_cycling = false; -} -/*********************************************************************** - * ELIST2_ITERATOR::at_first() - * - * Are we at the start of the list? - * - **********************************************************************/ + /* Become a deep copy of src_list */ + template + void deep_copy(const U *src_list, CLASSNAME *(*copier)(const CLASSNAME *)) { + ITERATOR from_it(const_cast(src_list)); + ITERATOR to_it(this); -inline bool ELIST2_ITERATOR::at_first() const { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::at_first", ABORT); + for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) + to_it.add_after_then_move((*copier)(from_it.data())); } -#endif - // we're at a deleted - return ((list->empty()) || (current == list->First()) || - ((current == nullptr) && (prev == list->last) && // NON-last pt between - !ex_current_was_last)); // first and last -} + /*********************************************************************** + * ELIST2::internal_clear + * + * Used by the destructor and the "clear" member function of derived list + * classes to destroy all the elements on the list. + * The calling function passes a "zapper" function which can be called to + * delete each element of the list, regardless of its derived type. This + * technique permits a generic clear function to destroy elements of + * different derived types correctly, without requiring virtual functions and + * the consequential memory overhead. + **********************************************************************/ + + // destroy all links + void internal_clear() { + // ptr to zapper functn + CLASSNAME *ptr; + CLASSNAME *next; + + if (!empty()) { + ptr = last->next; // set to first + last->next = nullptr; // break circle + last = nullptr; // set list empty + while (ptr) { + next = ptr->next; + delete ptr; + ptr = next; + } + } + } -/*********************************************************************** - * ELIST2_ITERATOR::at_last() - * - * Are we at the end of the list? - * - **********************************************************************/ + bool empty() const { // is list empty? + return !last; + } -inline bool ELIST2_ITERATOR::at_last() const { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::at_last", ABORT); + bool singleton() const { + return last ? (last == last->next) : false; } -#endif - // we're at a deleted - return ((list->empty()) || (current == list->last) || - ((current == nullptr) && (prev == list->last) && // last point between - ex_current_was_last)); // first and last -} + void shallow_copy( // dangerous!! + ELIST2 *from_list) { // beware destructors!! + last = from_list->last; + } -/*********************************************************************** - * ELIST2_ITERATOR::cycled_list() - * - * Have we returned to the cycle_pt since it was set? + /*********************************************************************** + * ELIST2::assign_to_sublist * + * 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 + * the same list, different from "this" one. The sublist removed is the + * inclusive list from start_it's current position to end_it's current + * position. If this range passes over the end of the source list then the + * source list has its end set to the previous element of start_it. The + * extracted sublist is unaffected by the end point of the source list, its + * end point is always the end_it position. **********************************************************************/ + void assign_to_sublist( // to this list + ITERATOR *start_it, // from list start + ITERATOR *end_it); // from list end -inline bool ELIST2_ITERATOR::cycled_list() const { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::cycled_list", ABORT); + // # elements in list + int32_t length() const { + int32_t count = 0; + if (last != nullptr) { + count = 1; + for (auto it = last->next; it != last; it = it->next) { + count++; + } + } + return count; } -#endif - - return ((list->empty()) || ((current == cycle_pt) && started_cycling)); -} - -/*********************************************************************** - * ELIST2_ITERATOR::sort() - * - * Sort the elements of the list, then reposition at the start. + /*********************************************************************** + * ELIST2::sort * + * Sort elements on list + * NB If you don't like the const declarations in the comparator, coerce yours: + * (int (*)(const void *, const void *) **********************************************************************/ + void sort( // sort elements + int comparator( // comparison routine + const void *, const void *)) { + // Allocate an array of pointers, one per list element. + auto count = length(); + if (count > 0) { + // ptr array to sort + std::vector base; + base.reserve(count); + + ITERATOR it(this); + + // Extract all elements, putting the pointers in the array. + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + base.push_back(it.extract()); + } -inline void ELIST2_ITERATOR::sort( // sort elements - int comparator( // comparison routine - const void *, const void *)) { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::sort", ABORT); - } -#endif - - list->sort(comparator); - move_to_first(); -} - -/*********************************************************************** - * ELIST2_ITERATOR::add_to_end - * - * 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 - * the iterator couldn't set its prev pointer. Adding to the end is - * essential for implementing - queues. -**********************************************************************/ + // Sort the pointer array. + qsort(&base[0], count, sizeof(base[0]), comparator); -inline void ELIST2_ITERATOR::add_to_end( // element to add - ELIST2_LINK *new_element) { -#ifndef NDEBUG - if (!list) { - NO_LIST.error("ELIST2_ITERATOR::add_to_end", ABORT); - } - if (!new_element) { - BAD_PARAMETER.error("ELIST2_ITERATOR::add_to_end", ABORT, "new_element is nullptr"); - } - if (new_element->next) { - STILL_LINKED.error("ELIST2_ITERATOR::add_to_end", ABORT); + // Rebuild the list from the sorted pointers. + for (auto current : base) { + it.add_to_end(current); + } + } } -#endif - if (this->at_last()) { - this->add_after_stay_put(new_element); - } else { - if (this->at_first()) { - this->add_before_stay_put(new_element); - list->last = new_element; - } else { // Iteratr is elsewhere - new_element->next = list->last->next; - new_element->prev = list->last; - list->last->next->prev = new_element; - list->last->next = new_element; - list->last = new_element; + // Assuming list has been sorted already, insert new_link to + // keep the list sorted according to the same comparison function. + // Comparison function is the same as used by sort, i.e. uses double + // indirection. Time is O(1) to add to beginning or end. + // Time is linear to add pre-sorted items to an empty list. + void add_sorted(int comparator(const void *, const void *), CLASSNAME *new_link) { + // Check for adding at the end. + if (last == nullptr || comparator(&last, &new_link) < 0) { + if (last == nullptr) { + new_link->next = new_link; + new_link->prev = new_link; + } else { + new_link->next = last->next; + new_link->prev = last; + last->next = new_link; + new_link->next->prev = new_link; + } + last = new_link; + } else { + // Need to use an iterator. + ITERATOR it(this); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + auto link = it.data(); + if (comparator(&link, &new_link) > 0) { + break; + } + } + if (it.cycled_list()) { + it.add_to_end(new_link); + } else { + it.add_before_then_move(new_link); + } } } -} +}; +// add TESS_API? +// move templated lists to public include dirs? #define ELIST2IZEH(CLASSNAME) \ - class CLASSNAME##_LIST : public X_LIST { \ - using X_LIST::X_LIST; \ - }; \ - struct CLASSNAME##_IT : X_ITER { \ - using X_ITER::X_ITER; \ - CLASSNAME *backward() { \ - return reinterpret_cast(ELIST2_ITERATOR::backward()); \ - } \ + struct CLASSNAME##_LIST : ELIST2 { \ + using ELIST2::ELIST2; \ + }; \ + struct CLASSNAME##_IT : ELIST2::ITERATOR { \ + using base = ELIST2::ITERATOR; \ + using base::base; \ }; } // namespace tesseract diff --git a/src/textord/colpartition.h b/src/textord/colpartition.h index 74a6a38ce2..0116922b0b 100644 --- a/src/textord/colpartition.h +++ b/src/textord/colpartition.h @@ -64,7 +64,7 @@ CLISTIZEH(ColPartition) * to a given y-coordinate range, eventually, a ColPartitionSet of ColPartitions * emerges, which represents the columns over a wide y-coordinate range. */ -class TESS_API ColPartition : public ELIST2_LINK { +class TESS_API ColPartition : public ELIST2::LINK { public: // This empty constructor is here only so that the class can be ELISTIZED. // TODO(rays) change deep_copy in elst.h line 955 to take a callback copier diff --git a/src/textord/tabvector.h b/src/textord/tabvector.h index f23fd60272..8b82577baa 100644 --- a/src/textord/tabvector.h +++ b/src/textord/tabvector.h @@ -102,7 +102,7 @@ class TabConstraint : public ELIST::LINK { // Class to hold information about a single vector // that represents a tab stop or a rule line. -class TabVector : public ELIST2_LINK { +class TabVector : public ELIST2::LINK { public: // TODO(rays) fix this in elst.h line 1076, where it should use the // copy constructor instead of operator=. diff --git a/unittest/list_test.cc b/unittest/list_test.cc index 0ba8c3afbf..a35ae27e21 100644 --- a/unittest/list_test.cc +++ b/unittest/list_test.cc @@ -37,7 +37,7 @@ class Elst : public ELIST::LINK { unsigned value; }; -class Elst2 : public ELIST2_LINK { +class Elst2 : public ELIST2::LINK { public: Elst2(unsigned n) : value(n) {} unsigned value; @@ -113,7 +113,7 @@ TEST_F(ListTest, TestELIST2) { Elst2_LIST list; EXPECT_TRUE(list.empty()); EXPECT_EQ(list.length(), 0); - auto it = ELIST2_ITERATOR(&list); + auto it = ELIST2::ITERATOR(&list); for (unsigned i = 0; i < ListSize; i++) { auto *lst = new Elst2(i); it.add_to_end(lst); From 17905571774f7fe44c1e8c9caec0d7b9dabe8bf2 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 18:49:27 +0300 Subject: [PATCH 11/17] Fix msvc warning. --- src/ccutil/universalambigs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ccutil/universalambigs.h b/src/ccutil/universalambigs.h index 83b0b2987e..d2e3c3a446 100644 --- a/src/ccutil/universalambigs.h +++ b/src/ccutil/universalambigs.h @@ -22,8 +22,10 @@ namespace tesseract { +#ifndef _MSC_VER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverlength-strings" +#endif inline const char kUniversalAmbigsFile[] = { "v2\n" "'' \" 1\n" @@ -19035,7 +19037,9 @@ inline const char kUniversalAmbigsFile[] = { "iXl in 1\n" #endif }; +#ifndef _MSC_VER #pragma GCC diagnostic pop +#endif inline const int ksizeofUniversalAmbigsFile = sizeof(kUniversalAmbigsFile); From 32e7268d004443c189feab76e620952e4a07fcb2 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 19:08:49 +0300 Subject: [PATCH 12/17] Update Makefile.am. --- Makefile.am | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 85ff31a081..25e9a2dd89 100644 --- a/Makefile.am +++ b/Makefile.am @@ -394,9 +394,6 @@ endif noinst_LTLIBRARIES += libtesseract_ccutil.la libtesseract_ccutil_la_SOURCES = src/ccutil/ccutil.cpp -libtesseract_ccutil_la_SOURCES += src/ccutil/clst.cpp -libtesseract_ccutil_la_SOURCES += src/ccutil/elst2.cpp -libtesseract_ccutil_la_SOURCES += src/ccutil/elst.cpp libtesseract_ccutil_la_SOURCES += src/ccutil/errcode.cpp libtesseract_ccutil_la_SOURCES += src/ccutil/serialis.cpp libtesseract_ccutil_la_SOURCES += src/ccutil/scanutils.cpp From 3129f1b695b62d3348384fddfd6b9f25916b4335 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 19:09:34 +0300 Subject: [PATCH 13/17] list.h is now unnecessary and removed. --- src/ccutil/list.h | 68 ----------------------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 src/ccutil/list.h diff --git a/src/ccutil/list.h b/src/ccutil/list.h deleted file mode 100644 index e5e6e2e88e..0000000000 --- a/src/ccutil/list.h +++ /dev/null @@ -1,68 +0,0 @@ -/********************************************************************** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - **********************************************************************/ - -#ifndef LIST_ITERATOR_H -#define LIST_ITERATOR_H - -#include - -namespace tesseract { - -template -struct X_ITER : ITERATOR { - X_ITER() = default; - template - X_ITER(U *list) : ITERATOR(list) {} - - CLASSNAME *data() { - return static_cast(ITERATOR::data()); - } - CLASSNAME *data_relative(int8_t offset) { - return static_cast(ITERATOR::data_relative(offset)); - } - CLASSNAME *forward() { - return static_cast(ITERATOR::forward()); - } - CLASSNAME *extract() { - return static_cast(ITERATOR::extract()); - } -}; - -template -struct X_LIST : CONTAINER { - X_LIST() = default; - X_LIST(const X_LIST &) = delete; - X_LIST &operator=(const X_LIST &) = delete; - ~X_LIST() { - clear(); - } - - /* delete elements */ - void clear() { - CONTAINER::internal_clear([](void *link) {delete reinterpret_cast(link);}); - } - - /* Become a deep copy of src_list */ - template - void deep_copy(const U *src_list, CLASSNAME *(*copier)(const CLASSNAME *)) { - X_ITER from_it(const_cast(src_list)); - X_ITER to_it(this); - - for (from_it.mark_cycle_pt(); !from_it.cycled_list(); from_it.forward()) - to_it.add_after_then_move((*copier)(from_it.data())); - } -}; - -} // namespace tesseract - -#endif From c3bf6a29ca61090aa42b2ced608c8977eb410149 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 19:10:18 +0300 Subject: [PATCH 14/17] Update sorting routines for lists. --- src/ccmain/fixspace.cpp | 7 ++----- src/ccmain/paramsd.cpp | 4 +--- src/ccmain/paramsd.h | 2 +- src/ccstruct/ocrblock.cpp | 6 +++--- src/ccstruct/polyblk.cpp | 7 ++----- src/ccstruct/stepblob.h | 4 +--- src/ccstruct/werd.cpp | 4 +--- src/ccstruct/werd.h | 2 +- src/ccutil/ambigs.h | 4 +--- src/ccutil/clst.h | 7 ++++--- src/ccutil/elst.h | 17 +++++++++-------- src/ccutil/elst2.h | 15 ++++++++------- src/textord/colpartition.h | 4 +--- src/textord/makerow.cpp | 18 ++++-------------- src/textord/makerow.h | 4 ++-- src/textord/tabvector.h | 4 +--- src/wordrec/lm_state.h | 4 +--- 17 files changed, 43 insertions(+), 70 deletions(-) diff --git a/src/ccmain/fixspace.cpp b/src/ccmain/fixspace.cpp index 7f03ff03ea..dd9e3663fe 100644 --- a/src/ccmain/fixspace.cpp +++ b/src/ccmain/fixspace.cpp @@ -55,12 +55,9 @@ class ROW; **********************************************************************/ static int c_blob_comparator( // sort blobs - const void *blob1p, // ptr to ptr to blob1 - const void *blob2p // ptr to ptr to blob2 + const C_BLOB *blob1, + const C_BLOB *blob2 ) { - const C_BLOB *blob1 = *reinterpret_cast(blob1p); - const C_BLOB *blob2 = *reinterpret_cast(blob2p); - return blob1->bounding_box().left() - blob2->bounding_box().left(); } diff --git a/src/ccmain/paramsd.cpp b/src/ccmain/paramsd.cpp index 85e596d100..14f220f8e6 100644 --- a/src/ccmain/paramsd.cpp +++ b/src/ccmain/paramsd.cpp @@ -191,9 +191,7 @@ void ParamsEditor::GetPrefixes(const char *s, std::string *level_one, std::strin } // Compare two VC objects by their name. -int ParamContent::Compare(const void *v1, const void *v2) { - const ParamContent *one = *static_cast(v1); - const ParamContent *two = *static_cast(v2); +int ParamContent::Compare(const ParamContent *one, const ParamContent *two) { return strcmp(one->GetName(), two->GetName()); } diff --git a/src/ccmain/paramsd.h b/src/ccmain/paramsd.h index e1a87b93e9..f898d51bf7 100644 --- a/src/ccmain/paramsd.h +++ b/src/ccmain/paramsd.h @@ -47,7 +47,7 @@ enum ParamType { VT_INTEGER, VT_BOOLEAN, VT_STRING, VT_DOUBLE }; class ParamContent : public ELIST::LINK { public: // Compare two VC objects by their name. - static int Compare(const void *v1, const void *v2); + static int Compare(const ParamContent *v1, const ParamContent *v2); // Gets a VC object identified by its ID. static ParamContent *GetParamContentById(int id); diff --git a/src/ccstruct/ocrblock.cpp b/src/ccstruct/ocrblock.cpp index 9127a85953..7281e0c97e 100644 --- a/src/ccstruct/ocrblock.cpp +++ b/src/ccstruct/ocrblock.cpp @@ -68,9 +68,9 @@ BLOCK::BLOCK(const char *name, ///< filename * Sort Comparator: Return <0 if row1 top < row2 top */ -static int decreasing_top_order(const void *row1, const void *row2) { - return (*reinterpret_cast(row2))->bounding_box().top() - - (*reinterpret_cast(row1))->bounding_box().top(); +static int decreasing_top_order(const ROW *row1, const ROW *row2) { + return row2->bounding_box().top() - + row1->bounding_box().top(); } /** diff --git a/src/ccstruct/polyblk.cpp b/src/ccstruct/polyblk.cpp index 8c21fd8dc0..bf2bc17193 100644 --- a/src/ccstruct/polyblk.cpp +++ b/src/ccstruct/polyblk.cpp @@ -34,7 +34,7 @@ namespace tesseract { #define INTERSECTING INT16_MAX -int lessthan(const void *first, const void *second); +int lessthan(const ICOORDELT *first, const ICOORDELT *second); POLY_BLOCK::POLY_BLOCK(ICOORDELT_LIST *points, PolyBlockType t) { ICOORDELT_IT v = &vertices; @@ -371,10 +371,7 @@ ICOORDELT_LIST *PB_LINE_IT::get_line(TDimension y) { return result; } -int lessthan(const void *first, const void *second) { - const ICOORDELT *p1 = *reinterpret_cast(first); - const ICOORDELT *p2 = *reinterpret_cast(second); - +int lessthan(const ICOORDELT *p1, const ICOORDELT *p2) { if (p1->x() < p2->x()) { return (-1); } else if (p1->x() > p2->x()) { diff --git a/src/ccstruct/stepblob.h b/src/ccstruct/stepblob.h index c7dbd2528e..b7d90fec0e 100644 --- a/src/ccstruct/stepblob.h +++ b/src/ccstruct/stepblob.h @@ -120,9 +120,7 @@ class TESS_API C_BLOB : public ELIST::LINK { return blob; } - static int SortByXMiddle(const void *v1, const void *v2) { - const C_BLOB *blob1 = *static_cast(v1); - const C_BLOB *blob2 = *static_cast(v2); + static int SortByXMiddle(const C_BLOB *blob1, const C_BLOB *blob2) { return blob1->bounding_box().x_middle() - blob2->bounding_box().x_middle(); } diff --git a/src/ccstruct/werd.cpp b/src/ccstruct/werd.cpp index cef414db5a..36aa0b605f 100644 --- a/src/ccstruct/werd.cpp +++ b/src/ccstruct/werd.cpp @@ -374,9 +374,7 @@ WERD &WERD::operator=(const WERD &source) { * order of left edge. */ -int word_comparator(const void *word1p, const void *word2p) { - const WERD *word1 = *reinterpret_cast(word1p); - const WERD *word2 = *reinterpret_cast(word2p); +int word_comparator(const WERD *word1, const WERD *word2) { return word1->bounding_box().left() - word2->bounding_box().left(); } diff --git a/src/ccstruct/werd.h b/src/ccstruct/werd.h index 07fec6150f..2c19ef1eb0 100644 --- a/src/ccstruct/werd.h +++ b/src/ccstruct/werd.h @@ -205,7 +205,7 @@ ELIST2IZEH(WERD) namespace tesseract { // compare words by increasing order of left edge, suitable for qsort(3) -int word_comparator(const void *word1p, const void *word2p); +int word_comparator(const WERD *word1, const WERD *word2); } // namespace tesseract diff --git a/src/ccutil/ambigs.h b/src/ccutil/ambigs.h index 2881d36595..effedbf7f6 100644 --- a/src/ccutil/ambigs.h +++ b/src/ccutil/ambigs.h @@ -117,9 +117,7 @@ class AmbigSpec : public ELIST::LINK { // Comparator function for sorting AmbigSpec_LISTs. The lists will // be sorted by their wrong_ngram arrays. Example of wrong_ngram vectors // in a sorted AmbigSpec_LIST: [9 1 3], [9 3 4], [9 8], [9, 8 1]. - static int compare_ambig_specs(const void *spec1, const void *spec2) { - const AmbigSpec *s1 = *static_cast(spec1); - const AmbigSpec *s2 = *static_cast(spec2); + static int compare_ambig_specs(const AmbigSpec *s1, const AmbigSpec *s2) { int result = UnicharIdArrayUtils::compare(s1->wrong_ngram, s2->wrong_ngram); if (result != 0) { return result; diff --git a/src/ccutil/clst.h b/src/ccutil/clst.h index 7aa0090754..54f1919cc1 100644 --- a/src/ccutil/clst.h +++ b/src/ccutil/clst.h @@ -19,7 +19,6 @@ #ifndef CLST_H #define CLST_H -#include "list.h" #include "lsterr.h" #include "serialis.h" @@ -764,7 +763,7 @@ class CLIST { **********************************************************************/ void sort( // sort elements int comparator( // comparison routine - const void *, const void *)) { + const CLASSNAME *, const CLASSNAME *)) { list->sort(comparator); move_to_first(); } @@ -907,7 +906,9 @@ class CLIST { } // Sort the pointer array. - std::sort(base.begin(), base.end(), comparator); + std::sort(base.begin(), base.end(), + // all current comparators return -1,0,1, so we handle this correctly for std::sort + [&](auto &&l, auto &&r) {return comparator(l, r) < 0; }); // Rebuild the list from the sorted pointers. for (auto current : base) { diff --git a/src/ccutil/elst.h b/src/ccutil/elst.h index b264b64716..e7a49095ed 100644 --- a/src/ccutil/elst.h +++ b/src/ccutil/elst.h @@ -19,7 +19,6 @@ #ifndef ELST_H #define ELST_H -#include "list.h" #include "lsterr.h" #include "serialis.h" @@ -905,7 +904,7 @@ class ELIST { **********************************************************************/ void sort( // sort elements int comparator( // comparison routine - const void *, const void *)) { + const CLASSNAME *, const CLASSNAME *)) { #ifndef NDEBUG if (!list) { NO_LIST.error("ELIST_ITERATOR::sort", ABORT); @@ -1032,7 +1031,7 @@ class ELIST { **********************************************************************/ void sort( // sort elements int comparator( // comparison routine - const void *, const void *)) { + const CLASSNAME *, const CLASSNAME *)) { // Allocate an array of pointers, one per list element. auto count = length(); @@ -1049,7 +1048,9 @@ class ELIST { } // Sort the pointer array. - qsort(&base[0], count, sizeof(base[0]), comparator); + std::sort(base.begin(), base.end(), + // all current comparators return -1,0,1, so we handle this correctly for std::sort + [&](auto &&l, auto &&r) {return comparator(l, r) < 0; }); // Rebuild the list from the sorted pointers. for (auto current : base) { @@ -1068,10 +1069,10 @@ class ELIST { // 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 // (otherwise the function returns new_link). - CLASSNAME *add_sorted_and_find(int comparator(const void *, const void *), bool unique, + CLASSNAME *add_sorted_and_find(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLASSNAME *new_link) { // 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) { new_link->next = new_link; } else { @@ -1084,7 +1085,7 @@ class ELIST { ITERATOR it(this); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { auto *link = it.data(); - int compare = comparator(&link, &new_link); + int compare = comparator(link, new_link); if (compare > 0) { break; } else if (unique && compare == 0) { @@ -1102,7 +1103,7 @@ class ELIST { // Same as above, but returns true if the new entry was inserted, false // if the identical entry already existed in the list. - bool add_sorted(int comparator(const void *, const void *), bool unique, CLASSNAME *new_link) { + bool add_sorted(int comparator(const CLASSNAME *, const CLASSNAME *), bool unique, CLASSNAME *new_link) { return (add_sorted_and_find(comparator, unique, new_link) == new_link); } }; diff --git a/src/ccutil/elst2.h b/src/ccutil/elst2.h index fb6a4d1611..e9dd35d774 100644 --- a/src/ccutil/elst2.h +++ b/src/ccutil/elst2.h @@ -19,7 +19,6 @@ #ifndef ELST2_H #define ELST2_H -#include "list.h" #include "lsterr.h" #include "serialis.h" @@ -972,7 +971,7 @@ class ELIST2 { **********************************************************************/ void sort( // sort elements int comparator( // comparison routine - const void *, const void *)) { + const CLASSNAME *, const CLASSNAME *)) { #ifndef NDEBUG if (!list) { NO_LIST.error("ELIST2_ITERATOR::sort", ABORT); @@ -1095,7 +1094,7 @@ class ELIST2 { **********************************************************************/ void sort( // sort elements int comparator( // comparison routine - const void *, const void *)) { + const CLASSNAME *, const CLASSNAME *)) { // Allocate an array of pointers, one per list element. auto count = length(); if (count > 0) { @@ -1111,7 +1110,9 @@ class ELIST2 { } // Sort the pointer array. - qsort(&base[0], count, sizeof(base[0]), comparator); + std::sort(base.begin(), base.end(), + // all current comparators return -1,0,1, so we handle this correctly for std::sort + [&](auto &&l, auto &&r) {return comparator(l, r) < 0; }); // Rebuild the list from the sorted pointers. for (auto current : base) { @@ -1125,9 +1126,9 @@ class ELIST2 { // Comparison function is the same as used by sort, i.e. uses double // indirection. Time is O(1) to add to beginning or end. // Time is linear to add pre-sorted items to an empty list. - void add_sorted(int comparator(const void *, const void *), CLASSNAME *new_link) { + void add_sorted(int comparator(const CLASSNAME *, const CLASSNAME *), CLASSNAME *new_link) { // 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) { new_link->next = new_link; new_link->prev = new_link; @@ -1143,7 +1144,7 @@ class ELIST2 { ITERATOR it(this); for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { auto link = it.data(); - if (comparator(&link, &new_link) > 0) { + if (comparator(link, new_link) > 0) { break; } } diff --git a/src/textord/colpartition.h b/src/textord/colpartition.h index 0116922b0b..438277542d 100644 --- a/src/textord/colpartition.h +++ b/src/textord/colpartition.h @@ -709,9 +709,7 @@ class TESS_API ColPartition : public ELIST2::LINK { bool IsInSameColumnAs(const ColPartition &part) const; // Sort function to sort by bounding box. - static int SortByBBox(const void *p1, const void *p2) { - const ColPartition *part1 = *static_cast(p1); - const ColPartition *part2 = *static_cast(p2); + static int SortByBBox(const ColPartition *part1, const ColPartition *part2) { int mid_y1 = part1->bounding_box_.y_middle(); int mid_y2 = part2->bounding_box_.y_middle(); if ((part2->bounding_box_.bottom() <= mid_y1 && diff --git a/src/textord/makerow.cpp b/src/textord/makerow.cpp index 837a3e3104..3d89b711b6 100644 --- a/src/textord/makerow.cpp +++ b/src/textord/makerow.cpp @@ -105,13 +105,8 @@ const int kMinLeaderCount = 5; * Sort function to sort rows in y from page top. */ static int row_y_order( // sort function - const void *item1, // items to compare - const void *item2) { - // converted ptr - const TO_ROW *row1 = *reinterpret_cast(item1); - // converted ptr - const TO_ROW *row2 = *reinterpret_cast(item2); - + const TO_ROW *row1, // items to compare + const TO_ROW *row2) { if (row1->parallel_c() > row2->parallel_c()) { return -1; } else if (row1->parallel_c() < row2->parallel_c()) { @@ -2540,13 +2535,8 @@ OVERLAP_STATE most_overlapping_row( // find best row * Sort function to sort blobs in x from page left. */ int blob_x_order( // sort function - const void *item1, // items to compare - const void *item2) { - // converted ptr - const BLOBNBOX *blob1 = *reinterpret_cast(item1); - // converted ptr - const BLOBNBOX *blob2 = *reinterpret_cast(item2); - + const BLOBNBOX *blob1, // items to compare + const BLOBNBOX *blob2) { if (blob1->bounding_box().left() < blob2->bounding_box().left()) { return -1; } else if (blob1->bounding_box().left() > blob2->bounding_box().left()) { diff --git a/src/textord/makerow.h b/src/textord/makerow.h index 91668dfabd..cfee8ec667 100644 --- a/src/textord/makerow.h +++ b/src/textord/makerow.h @@ -243,8 +243,8 @@ OVERLAP_STATE most_overlapping_row(TO_ROW_IT *row_it, // iterator bool testing_blob // test stuff ); int blob_x_order( // sort function - const void *item1, // items to compare - const void *item2); + const BLOBNBOX *item1, // items to compare + const BLOBNBOX *item2); void mark_repeated_chars(TO_ROW *row); diff --git a/src/textord/tabvector.h b/src/textord/tabvector.h index 8b82577baa..9762009c0b 100644 --- a/src/textord/tabvector.h +++ b/src/textord/tabvector.h @@ -286,9 +286,7 @@ class TabVector : public ELIST2::LINK { } // Sort function for E2LIST::sort to sort by sort_key_. - static int SortVectorsByKey(const void *v1, const void *v2) { - const TabVector *tv1 = *static_cast(v1); - const TabVector *tv2 = *static_cast(v2); + static int SortVectorsByKey(const TabVector *tv1, const TabVector *tv2) { return tv1->sort_key_ - tv2->sort_key_; } diff --git a/src/wordrec/lm_state.h b/src/wordrec/lm_state.h index 0176a253f3..a7ba725753 100644 --- a/src/wordrec/lm_state.h +++ b/src/wordrec/lm_state.h @@ -133,9 +133,7 @@ struct ViterbiStateEntry : public ELIST::LINK { } /// Comparator function for sorting ViterbiStateEntry_LISTs in /// non-increasing order of costs. - static int Compare(const void *e1, const void *e2) { - const ViterbiStateEntry *ve1 = *static_cast(e1); - const ViterbiStateEntry *ve2 = *static_cast(e2); + static int Compare(const ViterbiStateEntry *ve1, const ViterbiStateEntry *ve2) { return (ve1->cost < ve2->cost) ? -1 : 1; } inline bool Consistent() const { From da3561a561779af3d0b33575a368b5a056896f6b Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Fri, 22 Nov 2024 21:52:51 +0300 Subject: [PATCH 15/17] Make lists classes again. --- src/ccutil/elst.h | 16 +++++++++------- src/ccutil/elst2.h | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/ccutil/elst.h b/src/ccutil/elst.h index e7a49095ed..f87e59eed6 100644 --- a/src/ccutil/elst.h +++ b/src/ccutil/elst.h @@ -1110,13 +1110,15 @@ class ELIST { // add TESS_API? // move templated lists to public include dirs? -#define ELISTIZEH(CLASSNAME) \ - struct CLASSNAME##_LIST : ELIST { \ - using ELIST::ELIST; \ - }; \ - struct CLASSNAME##_IT : ELIST::ITERATOR { \ - using base = ELIST::ITERATOR; \ - using base::base; \ +#define ELISTIZEH(CLASSNAME) \ + class CLASSNAME##_LIST : public ELIST { \ + public: \ + using ELIST::ELIST; \ + }; \ + class CLASSNAME##_IT : public ELIST::ITERATOR { \ + public: \ + using base = ELIST::ITERATOR; \ + using base::base; \ }; } // namespace tesseract diff --git a/src/ccutil/elst2.h b/src/ccutil/elst2.h index e9dd35d774..ad7167a402 100644 --- a/src/ccutil/elst2.h +++ b/src/ccutil/elst2.h @@ -1159,13 +1159,15 @@ class ELIST2 { // add TESS_API? // move templated lists to public include dirs? -#define ELIST2IZEH(CLASSNAME) \ - struct CLASSNAME##_LIST : ELIST2 { \ - using ELIST2::ELIST2; \ - }; \ - struct CLASSNAME##_IT : ELIST2::ITERATOR { \ - using base = ELIST2::ITERATOR; \ - using base::base; \ +#define ELIST2IZEH(CLASSNAME) \ + class CLASSNAME##_LIST : public ELIST2 { \ + public: \ + using ELIST2::ELIST2; \ + }; \ + class CLASSNAME##_IT : public ELIST2::ITERATOR { \ + public: \ + using base = ELIST2::ITERATOR; \ + using base::base; \ }; } // namespace tesseract From 264773872c609bf3a0c6a2db963e05877e9e5654 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Sat, 23 Nov 2024 00:03:42 +0300 Subject: [PATCH 16/17] Add missing header for std::sort(). --- src/ccutil/clst.h | 1 + src/ccutil/elst.h | 1 + src/ccutil/elst2.h | 1 + 3 files changed, 3 insertions(+) diff --git a/src/ccutil/clst.h b/src/ccutil/clst.h index 54f1919cc1..e384097d88 100644 --- a/src/ccutil/clst.h +++ b/src/ccutil/clst.h @@ -22,6 +22,7 @@ #include "lsterr.h" #include "serialis.h" +#include #include namespace tesseract { diff --git a/src/ccutil/elst.h b/src/ccutil/elst.h index f87e59eed6..f732846af7 100644 --- a/src/ccutil/elst.h +++ b/src/ccutil/elst.h @@ -22,6 +22,7 @@ #include "lsterr.h" #include "serialis.h" +#include #include namespace tesseract { diff --git a/src/ccutil/elst2.h b/src/ccutil/elst2.h index ad7167a402..007818f57e 100644 --- a/src/ccutil/elst2.h +++ b/src/ccutil/elst2.h @@ -22,6 +22,7 @@ #include "lsterr.h" #include "serialis.h" +#include #include namespace tesseract { From 1a8a0a3bdca901da022b9e6263aed24c8aab32f4 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Sat, 23 Nov 2024 00:11:16 +0300 Subject: [PATCH 17/17] Make one-time comparator a lambda. --- src/ccstruct/polyblk.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ccstruct/polyblk.cpp b/src/ccstruct/polyblk.cpp index bf2bc17193..993e10b683 100644 --- a/src/ccstruct/polyblk.cpp +++ b/src/ccstruct/polyblk.cpp @@ -34,8 +34,6 @@ namespace tesseract { #define INTERSECTING INT16_MAX -int lessthan(const ICOORDELT *first, const ICOORDELT *second); - POLY_BLOCK::POLY_BLOCK(ICOORDELT_LIST *points, PolyBlockType t) { ICOORDELT_IT v = &vertices; @@ -357,7 +355,15 @@ ICOORDELT_LIST *PB_LINE_IT::get_line(TDimension y) { } if (!r.empty()) { - r.sort(lessthan); + r.sort([](const ICOORDELT *p1, const ICOORDELT *p2) { + if (p1->x() < p2->x()) { + return (-1); + } else if (p1->x() > p2->x()) { + return (1); + } else { + return (0); + } + }); for (r.mark_cycle_pt(); !r.cycled_list(); r.forward()) { x = r.data(); } @@ -371,16 +377,6 @@ ICOORDELT_LIST *PB_LINE_IT::get_line(TDimension y) { return result; } -int lessthan(const ICOORDELT *p1, const ICOORDELT *p2) { - if (p1->x() < p2->x()) { - return (-1); - } else if (p1->x() > p2->x()) { - return (1); - } else { - return (0); - } -} - #ifndef GRAPHICS_DISABLED /// Returns a color to draw the given type. ScrollView::Color POLY_BLOCK::ColorForPolyBlockType(PolyBlockType type) {