blob: 0b31035d734412237d78b876d07f8b45118c91cc [file] [log] [blame]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* list.c
*
* Inserting and removing elements
*
* void listDestroy()
* DLLIST *listAddToHead()
* l_int32 listAddToTail()
* l_int32 listInsertBefore()
* l_int32 listInsertAfter()
* void *listRemoveElement()
* void *listRemoveFromHead()
* void *listRemoveFromTail()
*
* Other list operations
*
* DLLIST *listFindElement()
* DLLIST *listFindTail()
* l_int32 listGetCount()
* l_int32 listReverse()
* DLLIST *listJoin()
*
* Lists are much harder to handle than arrays. There is
* more overhead for the programmer, both cognitive and
* codewise, and more likelihood that an error can be made.
* For that reason, lists should only be used when it is
* inefficient to use arrays, such as when elements are
* routinely inserted or deleted from inside arrays whose
* average size is greater than about 10.
*
* A list of data structures can be implemented in a number
* of ways. The two most popular are:
*
* (1) The list can be composed of a linked list of
* pointer cells ("cons cells"), where the data structures
* are hung off the cells. This is more difficult
* to use because you have to keep track of both
* your hanging data and the cell structures.
* It requires 3 pointers for every data structure
* that is put in a list. There is no problem
* cloning (using reference counts) for structures that
* are put in such a list. We implement lists by this
* method here.
*
* (2) The list pointers can be inserted directly into
* the data structures. This is easy to implement
* and easier to use, but it adds 2 ptrs of overhead
* to every data structure in which the ptrs are embedded.
* It also requires special care not to put the ptrs
* in any data that is cloned with a reference count;
* else your lists will break.
*
* Writing C code that uses list pointers explicitly to make
* and alter lists is difficult and prone to error.
* Consequently, a generic list utility that handles lists
* of arbitrary objects and doesn't force the programmer to
* touch the "next" and "prev" pointers, is quite useful.
* Such functions are provided here. However, the usual
* situation requires traversing a list and applying some
* function to one or more of the list elements. Macros
* for traversing the list are, in general, necessary, to
* achieve the goal of invisibly handling all "next" and "prev"
* pointers in generic lists. We provide macros for
* traversing a list in both forward and reverse directions.
*
* Because of the typing in C, implementation of a general
* list utility requires casting. If macros are used, the
* casting can be done implicitly; otherwise, using functions,
* some of the casts must be explicit. Fortunately, this
* can be implemented with void* so the programmer using
* the library will not have to make any casts! (Unless you
* compile with g++, in which case the rules on implicit
* conversion are more strict.)
*
* For example, to add an arbitrary data structure foo to the
* tail of a list, use
* listAddToTail(&head, &tail, pfoo);
* where head and tail are list cell ptrs and pfoo is
* a pointer to the foo object.
* And to remove an arbitrary data structure foo from a
* list, when you know the list cell element it is hanging from,
* use
* pfoo = listRemoveElement(&head, elem)
* where head and elem are list cell ptrs and pfoo is a pointer
* to the foo object. No casts are required for foo in
* either direction in ANSI C. (However, casts are
* required for ANSI C++).
*
* We use lists that are composed of doubly-linked
* cells with data structures hanging off the cells.
* We use doubly-linked cells to simplify insertion
* and deletion, and to allow operations to proceed in either
* direction along the list. With doubly-linked lists,
* it is tempting to make them circular, by setting head->prev
* to the tail of the list and tail->next to the head.
* The circular list costs nothing extra in storage, and
* allows operations to proceed from either end of the list
* with equal speed. However, the circular link adds
* cognitive overhead for the application programmer in
* general, and it greatly complicates list traversal when
* arbitrary list elements can be added or removed as you
* move through. It can be done, but in the spirit of
* simplicity, we avoid the temptation. The price to be paid
* is the extra cost to find the tail of a list -- a full
* traversal -- before the tail can be used. This is a
* cheap price to pay to avoid major headaches and buggy code.
*
* When you are only applying some function to each element
* in a list, you can go either forwards or backwards.
* To run through a list forwards, use:
*
* for (elem = head; elem; elem = nextelem) {
* nextelem = elem->next; (in case we destroy elem)
* <do something with elem->data>
* }
*
* To run through a list backwards, find the tail and use:
*
* for (elem = tail; elem; elem = prevelem) {
# prevelem = elem->prev; (in case we destroy elem)
* <do something with elem->data>
* }
*
* Even though these patterns are very simple, they are so common
* that we've provided macros for them in list.h. Using the
* macros, this becomes:
*
* L_BEGIN_LIST_FORWARD(head, elem)
* <do something with elem->data>
* L_END_LIST
*
* L_BEGIN_LIST_REVERSE(tail, elem)
* <do something with elem->data>
* L_END_LIST
*
* Note again that with macros, the application programmer does
* not need to refer explicitly to next and prev fields. Also,
* in the reverse case, note that we do not explicitly
* show the head of the list. However, the head of the list
* is always in scope, and functions can be called within the
* iterator that change the head.
*
* Some special cases are simpler. For example, when
* removing all items from the head of the list, you can use
*
* while (head) {
* obj = listRemoveFromHead(&head);
* <do something with obj>
* }
*
* Removing successive elements from the tail is equally simple:
*
* while (tail) {
* obj = listRemoveFromTail(&head, &tail);
* <do something with obj>
* }
*
* When removing an arbitrary element from a list, use
*
* obj = listRemoveElement(&head, elem);
*
* All the listRemove*() functions hand you the object,
* destroy the list cell to which it was attached, and
* reset the list pointers if necessary.
*
* Several other list operations, that do not involve
* inserting or removing objects, are also provided.
* The function listFindElement() locates a list pointer
* by matching the object hanging on it to a given
* object. The function listFindTail() gets a handle
* to the tail list ptr, allowing backwards traversals of
* the list. listGetCount() gives the number of elements
* in a list. Functions that reverse a list and concatenate
* two lists are also provided.
*
* These functions can be modified for efficiency in the
* situation where there is a large amount of creation and
* destruction of list cells. If millions of cells are
* made and destroyed, but a relatively small number are
* around at any time, the list cells can be stored for
* later re-use in a stack (see the generic stack functions
* in stack.c).
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
/*---------------------------------------------------------------------*
* Inserting and removing elements *
*---------------------------------------------------------------------*/
/*!
* listDestroy()
*
* Input: &head (<to be nulled> head of list)
* Return: void
*
* Notes:
* (1) This only destroys the cons cells. Before destroying
* the list, it is necessary to remove all data and set the
* data pointers in each cons cell to NULL.
* (2) listDestroy() will give a warning message for each data
* ptr that is not NULL.
*/
void
listDestroy(DLLIST **phead)
{
DLLIST *elem, *next, *head;
PROCNAME("listDestroy");
if (phead == NULL) {
L_WARNING("ptr address is null!", procName);
return;
}
if ((head = *phead) == NULL)
return;
for (elem = head; elem; elem = next) {
if (elem->data)
L_WARNING("list data ptr is not null", procName);
next = elem->next;
FREE(elem);
}
*phead = NULL;
return;
}
/*!
* listAddToHead()
*
* Input: &head (<optional> input head)
* data (void* ptr, to be added)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This makes a new cell, attaches the data, and adds the
* cell to the head of the list.
* (2) When consing from NULL, be sure to initialize head to NULL
* before calling this function.
*/
l_int32
listAddToHead(DLLIST **phead,
void *data)
{
DLLIST *cell, *head;
PROCNAME("listAddToHead");
if (!phead)
return ERROR_INT("&head not defined", procName, 1);
head = *phead;
if (!data)
return ERROR_INT("data not defined", procName, 1);
if ((cell = (DLLIST *)CALLOC(1, sizeof(DLLIST))) == NULL)
return ERROR_INT("cell not made", procName, 1);
cell->data = data;
if (!head) { /* start the list; initialize the ptrs */
cell->prev = NULL;
cell->next = NULL;
}
else {
cell->prev = NULL;
cell->next = head;
head->prev = cell;
}
*phead = cell;
return 0;
}
/*!
* listAddToTail()
*
* Input: &head (<may be updated>, head can be null)
* &tail (<updated>, tail can be null)
* data (void* ptr, to be hung on tail cons cell)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This makes a new cell, attaches the data, and adds the
* cell to the tail of the list.
* (2) &head is input to allow the list to be "cons'd" up from NULL.
* (3) &tail is input to allow the tail to be updated
* for efficient sequential operation with this function.
* (4) We assume that if *phead and/or *ptail are not NULL,
* then they are valid addresses. Therefore:
* (a) when consing from NULL, be sure to initialize both
* head and tail to NULL.
* (b) when tail == NULL for an existing list, the tail
* will be found and updated.
*/
l_int32
listAddToTail(DLLIST **phead,
DLLIST **ptail,
void *data)
{
DLLIST *cell, *head, *tail;
PROCNAME("listAddToTail");
if (!phead)
return ERROR_INT("&head not defined", procName, 1);
head = *phead;
if (!ptail)
return ERROR_INT("&tail not defined", procName, 1);
if (!data)
return ERROR_INT("data not defined", procName, 1);
if ((cell = (DLLIST *)CALLOC(1, sizeof(DLLIST))) == NULL)
return ERROR_INT("cell not made", procName, 1);
cell->data = data;
if (!head) { /* Start the list and initialize the ptrs. *ptail
* should also have been initialized to NULL */
cell->prev = NULL;
cell->next = NULL;
*phead = cell;
*ptail = cell;
}
else {
if ((tail = *ptail) == NULL)
tail = listFindTail(head);
cell->prev = tail;
cell->next = NULL;
tail->next = cell;
*ptail = cell;
}
return 0;
}
/*!
* listInsertBefore()
*
* Input: &head (<optional> input head)
* elem (list element to be inserted in front of;
* must be null if head is null)
* data (void* address, to be added)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This can be called on a null list, in which case both
* head and elem must be null.
* (2) If you are searching through a list, looking for a condition
* to add an element, you can do something like this:
* L_BEGIN_LIST_FORWARD(head, elem)
* <identify an elem to insert before>
* listInsertBefore(&head, elem, data);
* L_END_LIST
*
*/
l_int32
listInsertBefore(DLLIST **phead,
DLLIST *elem,
void *data)
{
DLLIST *cell, *head;
PROCNAME("listInsertBefore");
if (!phead)
return ERROR_INT("&head not defined", procName, 1);
head = *phead;
if (!data)
return ERROR_INT("data not defined", procName, 1);
if ((!head && elem) || (head && !elem))
return ERROR_INT("head and elem not consistent", procName, 1);
/* New cell to insert */
if ((cell = (DLLIST *)CALLOC(1, sizeof(DLLIST))) == NULL)
return ERROR_INT("cell not made", procName, 1);
cell->data = data;
if (!head) { /* start the list; initialize the ptrs */
cell->prev = NULL;
cell->next = NULL;
*phead = cell;
}
else if (head == elem) { /* insert before head of list */
cell->prev = NULL;
cell->next = head;
head->prev = cell;
*phead = cell;
}
else { /* insert before elem and after head of list */
cell->prev = elem->prev;
cell->next = elem;
elem->prev->next = cell;
elem->prev = cell;
}
return 0;
}
/*!
* listInsertAfter()
*
* Input: &head (<optional> input head)
* elem (list element to be inserted after;
* must be null if head is null)
* data (void* ptr, to be added)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This can be called on a null list, in which case both
* head and elem must be null. The head is included
* in the call to allow "consing" up from NULL.
* (2) If you are searching through a list, looking for a condition
* to add an element, you can do something like this:
* L_BEGIN_LIST_FORWARD(head, elem)
* <identify an elem to insert after>
* listInsertAfter(&head, elem, data);
* L_END_LIST
*/
l_int32
listInsertAfter(DLLIST **phead,
DLLIST *elem,
void *data)
{
DLLIST *cell, *head;
PROCNAME("listInsertAfter");
if (!phead)
return ERROR_INT("&head not defined", procName, 1);
head = *phead;
if (!data)
return ERROR_INT("data not defined", procName, 1);
if ((!head && elem) || (head && !elem))
return ERROR_INT("head and elem not consistent", procName, 1);
/* New cell to insert */
if ((cell = (DLLIST *)CALLOC(1, sizeof(DLLIST))) == NULL)
return ERROR_INT("cell not made", procName, 1);
cell->data = data;
if (!head) { /* start the list; initialize the ptrs */
cell->prev = NULL;
cell->next = NULL;
*phead = cell;
}
else if (elem->next == NULL) { /* insert after last */
cell->prev = elem;
cell->next = NULL;
elem->next = cell;
}
else { /* insert after elem and before the end */
cell->prev = elem;
cell->next = elem->next;
elem->next->prev = cell;
elem->next = cell;
}
return 0;
}
/*!
* listRemoveElement()
*
* Input: &head (<can be changed> input head)
* elem (list element to be removed)
* Return: data (void* struct on cell)
*
* Notes:
* (1) in ANSI C, it is not necessary to cast return to actual type; e.g.,
* pix = listRemoveElement(&head, elem);
* but in ANSI C++, it is necessary to do the cast:
* pix = (Pix *)listRemoveElement(&head, elem);
*/
void *
listRemoveElement(DLLIST **phead,
DLLIST *elem)
{
void *data;
DLLIST *head;
PROCNAME("listRemoveElement");
if (!phead)
return (void *)ERROR_PTR("&head not defined", procName, NULL);
head = *phead;
if (!head)
return (void *)ERROR_PTR("head not defined", procName, NULL);
if (!elem)
return (void *)ERROR_PTR("elem not defined", procName, NULL);
data = elem->data;
if (head->next == NULL) { /* only one */
if (elem != head)
return (void *)ERROR_PTR("elem must be head", procName, NULL);
*phead = NULL;
}
else if (head == elem) { /* first one */
elem->next->prev = NULL;
*phead = elem->next;
}
else if (elem->next == NULL) { /* last one */
elem->prev->next = NULL;
}
else { /* neither the first nor the last one */
elem->next->prev = elem->prev;
elem->prev->next = elem->next;
}
FREE(elem);
return data;
}
/*!
* listRemoveFromHead()
*
* Input: &head (<to be updated> head of list)
* Return: data (void* struct on cell), or null on error
*
* Notes:
* (1) in ANSI C, it is not necessary to cast return to actual type; e.g.,
* pix = listRemoveFromHead(&head);
* but in ANSI C++, it is necessary to do the cast; e.g.,
* pix = (Pix *)listRemoveFromHead(&head);
*/
void *
listRemoveFromHead(DLLIST **phead)
{
DLLIST *head;
void *data;
PROCNAME("listRemoveFromHead");
if (!phead)
return (void *)ERROR_PTR("&head not defined", procName, NULL);
if ((head = *phead) == NULL)
return (void *)ERROR_PTR("head not defined", procName, NULL);
if (head->next == NULL) /* only one */
*phead = NULL;
else {
head->next->prev = NULL;
*phead = head->next;
}
data = head->data;
FREE(head);
return data;
}
/*!
* listRemoveFromTail()
*
* Input: &head (<may be changed>, head must NOT be null)
* &tail (<always updated>, tail may be null)
* Return: data (void* struct on cell) or null on error
*
* Notes:
* (1) We include &head so that it can be set to NULL if
* if the only element in the list is removed.
* (2) The function is relying on the fact that if tail is
* not NULL, then is is a valid address. You can use
* this function with tail == NULL for an existing list, in
* which case the tail is found and updated, and the
* removed element is returned.
* (3) In ANSI C, it is not necessary to cast return to actual type; e.g.,
* pix = listRemoveFromTail(&head, &tail);
* but in ANSI C++, it is necessary to do the cast; e.g.,
* pix = (Pix *)listRemoveFromTail(&head, &tail);
*/
void *
listRemoveFromTail(DLLIST **phead,
DLLIST **ptail)
{
DLLIST *head, *tail;
void *data;
PROCNAME("listRemoveFromTail");
if (!phead)
return (void *)ERROR_PTR("&head not defined", procName, NULL);
if ((head = *phead) == NULL)
return (void *)ERROR_PTR("head not defined", procName, NULL);
if (!ptail)
return (void *)ERROR_PTR("&tail not defined", procName, NULL);
if ((tail = *ptail) == NULL)
tail = listFindTail(head);
if (head->next == NULL) { /* only one */
*phead = NULL;
*ptail = NULL;
}
else {
tail->prev->next = NULL;
*ptail = tail->prev;
}
data = tail->data;
FREE(tail);
return data;
}
/*---------------------------------------------------------------------*
* Other list operations *
*---------------------------------------------------------------------*/
/*!
* listFindElement()
*
* Input: head (list head)
* data (void* address, to be searched for)
* Return: cell (the containing cell, or null if not found or on error)
*
* Notes:
* (1) This returns a ptr to the cell, which is still embedded in
* the list.
* (2) This handle and the attached data have not been copied or
* reference counted, so they must not be destroyed. This
* violates our basic rule that every handle returned from a
* function is owned by that function and must be destroyed,
* but if rules aren't there to be broken, why have them?
*/
DLLIST *
listFindElement(DLLIST *head,
void *data)
{
DLLIST *cell;
PROCNAME("listFindElement");
if (!head)
return (DLLIST *)ERROR_PTR("head not defined", procName, NULL);
if (!data)
return (DLLIST *)ERROR_PTR("data not defined", procName, NULL);
for (cell = head; cell; cell = cell->next) {
if (cell->data == data)
return cell;
}
return NULL;
}
/*!
* listFindTail()
*
* Input: head
* Return: tail, or null on error
*/
DLLIST *
listFindTail(DLLIST *head)
{
DLLIST *cell;
PROCNAME("listFindTail");
if (!head)
return (DLLIST *)ERROR_PTR("head not defined", procName, NULL);
for (cell = head; cell; cell = cell->next) {
if (cell->next == NULL)
return cell;
}
return (DLLIST *)ERROR_PTR("tail not found !!", procName, NULL);
}
/*!
* listGetCount()
*
* Input: head (of list)
* Return: number of elements; 0 if no list or on error
*/
l_int32
listGetCount(DLLIST *head)
{
l_int32 count;
DLLIST *elem;
PROCNAME("listGetCount");
if (!head)
return ERROR_INT("head not defined", procName, 0);
count = 0;
for (elem = head; elem; elem = elem->next)
count++;
return count;
}
/*!
* listReverse()
*
* Input: &head (<may be changed> list head)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This reverses the list in-place.
*/
l_int32
listReverse(DLLIST **phead)
{
void *obj; /* whatever */
DLLIST *head, *rhead;
PROCNAME("listReverse");
if (!phead)
return ERROR_INT("&head not defined", procName, 1);
if ((head = *phead) == NULL)
return ERROR_INT("head not defined", procName, 1);
rhead = NULL;
while (head) {
obj = listRemoveFromHead(&head);
listAddToHead(&rhead, obj);
}
*phead = rhead;
return 0;
}
/*!
* listJoin()
*
* Input: &head1 (<may be changed> head of first list)
* &head2 (<to be nulled> head of second list)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) The concatenated list is returned with head1 as the new head.
* (2) Both input ptrs must exist, though either can have the value NULL.
*/
l_int32
listJoin(DLLIST **phead1,
DLLIST **phead2)
{
void *obj;
DLLIST *head1, *head2, *tail1;
PROCNAME("listJoin");
if (!phead1)
return ERROR_INT("&head1 not defined", procName, 1);
if (!phead2)
return ERROR_INT("&head2 not defined", procName, 1);
/* If no list2, just return list1 unchanged */
if ((head2 = *phead2) == NULL)
return 0;
/* If no list1, just return list2 */
if ((head1 = *phead1) == NULL) {
*phead1 = head2;
*phead2 = NULL;
return 0;
}
/* General case for concatenation into list 1 */
tail1 = listFindTail(head1);
while (head2) {
obj = listRemoveFromHead(&head2);
listAddToTail(&head1, &tail1, obj);
}
*phead2 = NULL;
return 0;
}