Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/tesseract/src/ccutil/elst2.h @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/tesseract/src/ccutil/elst2.h Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,835 @@ +/********************************************************************** + * File: elst2.h (Formerly elist2.h) + * Description: Double linked embedded list module 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. + * + **********************************************************************/ + +#ifndef ELST2_H +#define ELST2_H + +#include "list.h" +#include "lsterr.h" +#include "serialis.h" + +#include <cstdio> + +namespace tesseract { + +class ELIST2_ITERATOR; + +/********************************************************************** +DESIGN NOTE +=========== + +It would probably be possible to implement the ELIST2 classes as derived +classes from ELIST. I haven't done this because: + +a) I think it would be harder to understand the code +(Though the problem with not inheriting is that changes to ELIST must be + reflected in ELIST2 and vice versa) + +b) Most of the code is inline so: +i) The duplication in source does not affect the run time code size - the + code is copied inline anyway! + + 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; + } + +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++; + } + } + 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); +}; + +/*********************************************************************** + * CLASS - ELIST2_ITERATOR + * + * Generic iterator class for doubly linked lists with embedded + *links + **********************************************************************/ + +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? + + ELIST2_LINK *extract_sublist( // from this current... + ELIST2_ITERATOR *other_it); // to other current + +public: + ELIST2_ITERATOR( // constructor + ELIST2 *list_to_iterate); + + void set_to_list( // change list + ELIST2 *list_to_iterate); + + void add_after_then_move( // add after current & + ELIST2_LINK *new_link); // move to new + + void add_after_stay_put( // add after current & + ELIST2_LINK *new_link); // stay at current + + void add_before_then_move( // add before current & + ELIST2_LINK *new_link); // move to new + + void add_before_stay_put( // add before current & + ELIST2_LINK *new_link); // stay at current + + void add_list_after( // add a list & + ELIST2 *list_to_add); // stay at current + + void add_list_before( // add a list & + ELIST2 *list_to_add); // move to it 1st item + + ELIST2_LINK *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_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 + + ELIST2_LINK *extract(); // remove from list + + // go to start of list + ELIST2_LINK *move_to_first(); + + ELIST2_LINK *move_to_last(); // go to end of list + + void mark_cycle_pt(); // remember current + + bool empty() const { // is list empty? +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::empty", ABORT); + } +#endif + return list->empty(); + } + + bool current_extracted() const { // current extracted? + return !current; + } + + bool at_first() const; // Current is first? + + bool at_last() const; // Current is last? + + bool cycled_list() const; // Completed a cycle? + + void add_to_end( // add at end & + ELIST2_LINK *new_link); // don't move + + void exchange( // positions of 2 links + ELIST2_ITERATOR *other_it); // other iterator + + //# elements in list + int32_t length() const { + return list->length(); + } + + void sort( // sort elements + int comparator( // comparison routine + const void *, const void *)); + +private: + // Don't use the following constructor. + ELIST2_ITERATOR() = delete; +}; + +/*********************************************************************** + * ELIST2_ITERATOR::set_to_list + * + * (Re-)initialise the iterator to point to the start of the list_to_iterate + * over. + **********************************************************************/ + +inline void ELIST2_ITERATOR::set_to_list( // change list + ELIST2 *list_to_iterate) { +#ifndef NDEBUG + if (!list_to_iterate) { + BAD_PARAMETER.error("ELIST2_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; +} + +/*********************************************************************** + * ELIST2_ITERATOR::ELIST2_ITERATOR + * + * CONSTRUCTOR - set iterator to specified list; + **********************************************************************/ + +inline ELIST2_ITERATOR::ELIST2_ITERATOR(ELIST2 *list_to_iterate) { + set_to_list(list_to_iterate); +} + +/*********************************************************************** + * 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. + **********************************************************************/ + +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); + } +#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 (ex_current_was_cycle_pt) { + cycle_pt = new_element; + } + } + } + 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. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_after_stay_put( // element to add + ELIST2_LINK *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 + + 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; + } +} + +/*********************************************************************** + * 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) { +#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 + + 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; +} + +/*********************************************************************** + * 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. + **********************************************************************/ + +inline void ELIST2_ITERATOR::add_before_stay_put( // element to add + ELIST2_LINK *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 + + 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; + } +} + +/*********************************************************************** + * ELIST2_ITERATOR::add_list_after + * + * Insert another list to this list after the current element but don't move + *the + * iterator. + **********************************************************************/ + +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"); + } +#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; + } + } + 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. + **********************************************************************/ + +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 (!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; + } +} + +/*********************************************************************** + * 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. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::extract() { + ELIST2_LINK *extracted_link; + +#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 (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; + } + } + // 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. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::move_to_first() { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::move_to_first", 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. + **********************************************************************/ + +inline ELIST2_LINK *ELIST2_ITERATOR::move_to_last() { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::move_to_last", ABORT); + } +#endif + + 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. + **********************************************************************/ + +inline void ELIST2_ITERATOR::mark_cycle_pt() { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::mark_cycle_pt", ABORT); + } +#endif + + if (current) { + cycle_pt = current; + } else { + ex_current_was_cycle_pt = true; + } + started_cycling = false; +} + +/*********************************************************************** + * ELIST2_ITERATOR::at_first() + * + * Are we at the start of the list? + * + **********************************************************************/ + +inline bool ELIST2_ITERATOR::at_first() const { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::at_first", ABORT); + } +#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_ITERATOR::at_last() + * + * Are we at the end of the list? + * + **********************************************************************/ + +inline bool ELIST2_ITERATOR::at_last() const { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_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 +} + +/*********************************************************************** + * ELIST2_ITERATOR::cycled_list() + * + * Have we returned to the cycle_pt since it was set? + * + **********************************************************************/ + +inline bool ELIST2_ITERATOR::cycled_list() const { +#ifndef NDEBUG + if (!list) { + NO_LIST.error("ELIST2_ITERATOR::cycled_list", ABORT); + } +#endif + + return ((list->empty()) || ((current == cycle_pt) && started_cycling)); +} + +/*********************************************************************** + * ELIST2_ITERATOR::sort() + * + * Sort the elements of the list, then reposition at the start. + * + **********************************************************************/ + +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. +**********************************************************************/ + +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); + } +#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; + } + } +} + +#define ELIST2IZEH(CLASSNAME) \ + class CLASSNAME##_LIST : public X_LIST<ELIST2, ELIST2_ITERATOR, CLASSNAME> { \ + using X_LIST<ELIST2, ELIST2_ITERATOR, CLASSNAME>::X_LIST; \ + }; \ + struct CLASSNAME##_IT : X_ITER<ELIST2_ITERATOR, CLASSNAME> { \ + using X_ITER<ELIST2_ITERATOR, CLASSNAME>::X_ITER; \ + CLASSNAME *backward() { \ + return reinterpret_cast<CLASSNAME *>(ELIST2_ITERATOR::backward()); \ + } \ + }; + +} // namespace tesseract + +#endif
