334 lines
15 KiB
C
334 lines
15 KiB
C
/* Copyright (C) 2001-2017 Peter Selinger.
|
|
* This file is part of Potrace. It is free software and it is covered
|
|
* by the GNU General Public License. See the file COPYING for details. */
|
|
|
|
#ifndef _PS_LISTS_H
|
|
#define _PS_LISTS_H
|
|
|
|
/* here we define some general list macros. Because they are macros,
|
|
* they should work on any datatype with a "->next" component. Some of
|
|
* them use a "hook". If elt and list are of type t* then hook is of
|
|
* type t**. A hook stands for an insertion point in the list, i.e.,
|
|
* either before the first element, or between two elements, or after
|
|
* the last element. If an operation "sets the hook" for an element,
|
|
* then the hook is set to just before the element. One can insert
|
|
* something at a hook. One can also unlink at a hook: this means,
|
|
* unlink the element just after the hook. By "to unlink", we mean the
|
|
* element is removed from the list, but not deleted. Thus, it and its
|
|
* components still need to be freed. */
|
|
|
|
/* Note: these macros are somewhat experimental. Only the ones that
|
|
* are actually *used* have been tested. So be careful to test any
|
|
* that you use. Looking at the output of the preprocessor, "gcc -E"
|
|
* (possibly piped though "indent"), might help too. Also: these
|
|
* macros define some internal (local) variables that start with
|
|
* "_". */
|
|
|
|
/* we enclose macro definitions whose body consists of more than one
|
|
* statement in MACRO_BEGIN and MACRO_END, rather than '{' and '}'. The
|
|
* reason is that we want to be able to use the macro in a context
|
|
* such as "if (...) macro(...); else ...". If we didn't use this obscure
|
|
* trick, we'd have to omit the ";" in such cases. */
|
|
|
|
#define MACRO_BEGIN \
|
|
do \
|
|
{
|
|
#define MACRO_END \
|
|
} \
|
|
while( 0 )
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* macros for singly-linked lists */
|
|
|
|
/* traverse list. At the end, elt is set to NULL. */
|
|
#define list_forall( elt, list ) for( elt = list; elt != NULL; elt = elt->next )
|
|
|
|
/* set elt to the first element of list satisfying boolean condition
|
|
* c, or NULL if not found */
|
|
#define list_find( elt, list, c ) \
|
|
MACRO_BEGIN list_forall( elt, list ) if( c ) \
|
|
break; \
|
|
MACRO_END
|
|
|
|
/* like forall, except also set hook for elt. */
|
|
#define list_forall2( elt, list, hook ) \
|
|
for( elt = list, hook = &list; elt != NULL; hook = &elt->next, elt = elt->next )
|
|
|
|
/* same as list_find, except also set hook for elt. */
|
|
#define list_find2( elt, list, c, hook ) \
|
|
MACRO_BEGIN list_forall2( elt, list, hook ) if( c ) \
|
|
break; \
|
|
MACRO_END
|
|
|
|
/* same, except only use hook. */
|
|
#define _list_forall_hook( list, hook ) for( hook = &list; *hook != NULL; hook = &( *hook )->next )
|
|
|
|
/* same, except only use hook. Note: c may only refer to *hook, not elt. */
|
|
#define _list_find_hook( list, c, hook ) \
|
|
MACRO_BEGIN _list_forall_hook( list, hook ) if( c ) \
|
|
break; \
|
|
MACRO_END
|
|
|
|
/* insert element after hook */
|
|
#define list_insert_athook( elt, hook ) \
|
|
MACRO_BEGIN elt->next = *hook; \
|
|
*hook = elt; \
|
|
MACRO_END
|
|
|
|
/* insert element before hook */
|
|
#define list_insert_beforehook( elt, hook ) \
|
|
MACRO_BEGIN elt->next = *hook; \
|
|
*hook = elt; \
|
|
hook = &elt->next; \
|
|
MACRO_END
|
|
|
|
/* unlink element after hook, let elt be unlinked element, or NULL.
|
|
* hook remains. */
|
|
#define list_unlink_athook( list, elt, hook ) \
|
|
MACRO_BEGIN \
|
|
elt = hook ? *hook : NULL; \
|
|
if( elt ) \
|
|
{ \
|
|
*hook = elt->next; \
|
|
elt->next = NULL; \
|
|
} \
|
|
MACRO_END
|
|
|
|
/* unlink the specific element, if it is in the list. Otherwise, set
|
|
* elt to NULL */
|
|
#define list_unlink( listtype, list, elt ) \
|
|
MACRO_BEGIN \
|
|
listtype * *_hook; \
|
|
_list_find_hook( list, *_hook == elt, _hook ); \
|
|
list_unlink_athook( list, elt, _hook ); \
|
|
MACRO_END
|
|
|
|
/* prepend elt to list */
|
|
#define list_prepend( list, elt ) \
|
|
MACRO_BEGIN elt->next = list; \
|
|
list = elt; \
|
|
MACRO_END
|
|
|
|
/* append elt to list. */
|
|
#define list_append( listtype, list, elt ) \
|
|
MACRO_BEGIN \
|
|
listtype * *_hook; \
|
|
_list_forall_hook( list, _hook ) \
|
|
{ \
|
|
} \
|
|
list_insert_athook( elt, _hook ); \
|
|
MACRO_END
|
|
|
|
/* unlink the first element that satisfies the condition. */
|
|
#define list_unlink_cond( listtype, list, elt, c ) \
|
|
MACRO_BEGIN \
|
|
listtype * *_hook; \
|
|
list_find2( elt, list, c, _hook ); \
|
|
list_unlink_athook( list, elt, _hook ); \
|
|
MACRO_END
|
|
|
|
/* let elt be the nth element of the list, starting to count from 0.
|
|
* Return NULL if out of bounds. */
|
|
#define list_nth( elt, list, n ) \
|
|
MACRO_BEGIN \
|
|
int _x; /* only evaluate n once */ \
|
|
for( _x = ( n ), elt = list; _x && elt; _x--, elt = elt->next ) \
|
|
{ \
|
|
} \
|
|
MACRO_END
|
|
|
|
/* let elt be the nth element of the list, starting to count from 0.
|
|
* Return NULL if out of bounds. */
|
|
#define list_nth_hook( elt, list, n, hook ) \
|
|
MACRO_BEGIN \
|
|
int _x; /* only evaluate n once */ \
|
|
for( _x = ( n ), elt = list, hook = &list; _x && elt; \
|
|
_x--, hook = &elt->next, elt = elt->next ) \
|
|
{ \
|
|
} \
|
|
MACRO_END
|
|
|
|
/* set n to the length of the list */
|
|
#define list_length( listtype, list, n ) \
|
|
MACRO_BEGIN \
|
|
listtype * _elt; \
|
|
n = 0; \
|
|
list_forall( _elt, list ) n++; \
|
|
MACRO_END
|
|
|
|
/* set n to the index of the first element satisfying cond, or -1 if
|
|
* none found. Also set elt to the element, or NULL if none found. */
|
|
#define list_index( list, n, elt, c ) \
|
|
MACRO_BEGIN \
|
|
n = 0; \
|
|
list_forall( elt, list ) \
|
|
{ \
|
|
if( c ) \
|
|
break; \
|
|
n++; \
|
|
} \
|
|
if( !elt ) \
|
|
n = -1; \
|
|
MACRO_END
|
|
|
|
/* set n to the number of elements in the list that satisfy condition c */
|
|
#define list_count( list, n, elt, c ) \
|
|
MACRO_BEGIN \
|
|
n = 0; \
|
|
list_forall( elt, list ) \
|
|
{ \
|
|
if( c ) \
|
|
n++; \
|
|
} \
|
|
MACRO_END
|
|
|
|
/* let elt be each element of the list, unlinked. At the end, set list=NULL. */
|
|
#define list_forall_unlink( elt, list ) \
|
|
for( elt = list; elt ? ( list = elt->next, elt->next = NULL ), 1 : 0; elt = list )
|
|
|
|
/* reverse a list (efficient) */
|
|
#define list_reverse( listtype, list ) \
|
|
MACRO_BEGIN \
|
|
listtype * _list1 = NULL, *elt; \
|
|
list_forall_unlink( elt, list ) list_prepend( _list1, elt ); \
|
|
list = _list1; \
|
|
MACRO_END
|
|
|
|
/* insert the element ELT just before the first element TMP of the
|
|
* list for which COND holds. Here COND must be a condition of ELT and
|
|
* TMP. Typical usage is to insert an element into an ordered list:
|
|
* for instance, list_insert_ordered(listtype, list, elt, tmp,
|
|
* elt->size <= tmp->size). Note: if we give a "less than or equal"
|
|
* condition, the new element will be inserted just before a sequence
|
|
* of equal elements. If we give a "less than" condition, the new
|
|
* element will be inserted just after a list of equal elements.
|
|
* Note: it is much more efficient to construct a list with
|
|
* list_prepend and then order it with list_merge_sort, than to
|
|
* construct it with list_insert_ordered. */
|
|
#define list_insert_ordered( listtype, list, elt, tmp, cond ) \
|
|
MACRO_BEGIN \
|
|
listtype * *_hook; \
|
|
_list_find_hook( list, ( tmp = *_hook, ( cond ) ), _hook ); \
|
|
list_insert_athook( elt, _hook ); \
|
|
MACRO_END
|
|
|
|
/* sort the given list, according to the comparison condition.
|
|
* Typical usage is list_sort(listtype, list, a, b, a->size <
|
|
* b->size). Note: if we give "less than or equal" condition, each
|
|
* segment of equal elements will be reversed in order. If we give a
|
|
* "less than" condition, each segment of equal elements will retain
|
|
* the original order. The latter is slower but sometimes
|
|
* prettier. Average running time: n*n/2. */
|
|
#define list_sort( listtype, list, a, b, cond ) \
|
|
MACRO_BEGIN \
|
|
listtype * _newlist = NULL; \
|
|
list_forall_unlink( a, list ) list_insert_ordered( listtype, _newlist, a, b, cond ); \
|
|
list = _newlist; \
|
|
MACRO_END
|
|
|
|
/* a much faster sort algorithm (merge sort, n log n worst case). It
|
|
* is required that the list type has an additional, unused next1
|
|
* component. Note there is no curious reversal of order of equal
|
|
* elements as for list_sort. */
|
|
|
|
#define list_mergesort( listtype, list, a, b, cond ) \
|
|
MACRO_BEGIN \
|
|
listtype * _elt, **_hook1; \
|
|
\
|
|
for( _elt = list; _elt; _elt = _elt->next1 ) \
|
|
{ \
|
|
_elt->next1 = _elt->next; \
|
|
_elt->next = NULL; \
|
|
} \
|
|
do \
|
|
{ \
|
|
_hook1 = &( list ); \
|
|
while( ( a = *_hook1 ) != NULL && ( b = a->next1 ) != NULL ) \
|
|
{ \
|
|
_elt = b->next1; \
|
|
_list_merge_cond( listtype, a, b, cond, *_hook1 ); \
|
|
_hook1 = &( ( *_hook1 )->next1 ); \
|
|
*_hook1 = _elt; \
|
|
} \
|
|
} \
|
|
while( _hook1 != &( list ) ); \
|
|
MACRO_END
|
|
|
|
/* merge two sorted lists. Store result at &result */
|
|
#define _list_merge_cond( listtype, a, b, cond, result ) \
|
|
MACRO_BEGIN \
|
|
listtype * *_hook; \
|
|
_hook = &( result ); \
|
|
while( 1 ) \
|
|
{ \
|
|
if( a == NULL ) \
|
|
{ \
|
|
*_hook = b; \
|
|
break; \
|
|
} \
|
|
else if( b == NULL ) \
|
|
{ \
|
|
*_hook = a; \
|
|
break; \
|
|
} \
|
|
else if( cond ) \
|
|
{ \
|
|
*_hook = a; \
|
|
_hook = &( a->next ); \
|
|
a = a->next; \
|
|
} \
|
|
else \
|
|
{ \
|
|
*_hook = b; \
|
|
_hook = &( b->next ); \
|
|
b = b->next; \
|
|
} \
|
|
} \
|
|
MACRO_END
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* macros for doubly-linked lists */
|
|
|
|
#define dlist_append( head, end, elt ) \
|
|
MACRO_BEGIN \
|
|
elt->prev = end; \
|
|
elt->next = NULL; \
|
|
if( end ) \
|
|
{ \
|
|
end->next = elt; \
|
|
} \
|
|
else \
|
|
{ \
|
|
head = elt; \
|
|
} \
|
|
end = elt; \
|
|
MACRO_END
|
|
|
|
/* let elt be each element of the list, unlinked. At the end, set list=NULL. */
|
|
#define dlist_forall_unlink( elt, head, end ) \
|
|
for( elt = head; \
|
|
elt ? ( head = elt->next, elt->next = NULL, elt->prev = NULL ), 1 : ( end = NULL, 0 ); \
|
|
elt = head )
|
|
|
|
/* unlink the first element of the list */
|
|
#define dlist_unlink_first( head, end, elt ) \
|
|
MACRO_BEGIN \
|
|
elt = head; \
|
|
if( head ) \
|
|
{ \
|
|
head = head->next; \
|
|
if( head ) \
|
|
{ \
|
|
head->prev = NULL; \
|
|
} \
|
|
else \
|
|
{ \
|
|
end = NULL; \
|
|
} \
|
|
elt->prev = NULL; \
|
|
elt->next = NULL; \
|
|
} \
|
|
MACRO_END
|
|
|
|
#endif /* _PS_LISTS_H */
|