diff options
Diffstat (limited to 'tiler/list_utils.h')
-rw-r--r-- | tiler/list_utils.h | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/tiler/list_utils.h b/tiler/list_utils.h new file mode 100644 index 0000000..1643cf0 --- /dev/null +++ b/tiler/list_utils.h @@ -0,0 +1,511 @@ +/* + * list_utils.h + * + * Utility definitions for the Memory Interface for TI OMAP processors. + * + * Copyright (C) 2009-2011 Texas Instruments, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Texas Instruments Incorporated nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LIST_UTILS_H_ +#define _LIST_UTILS_H_ + +/* + + DLIST macros facilitate double-linked lists in a generic fashion. + + - Double-linked lists simplify list manipulations, so they are preferred to + single-linked lists. + + - Having a double-linked list with a separate header allow accessing the + last element of the list easily, so this is also preferred to a double + linked list that uses NULL pointers at its ends. + + Therefore, the following is required: a list info structure (separate from + the element structure, although the info structure can be a member of the + element structure). The info structure must contain (at least) the + following 3 members: + + *next, *last: as pointers to the info structure. These are the next and + previous elements in the list. + *me: as a pointer to the element structure. This is how we get to the + element itself, as next and last points to another info structure. + + The list element structure __MAY__ contain the info structure as a member, + or a pointer to the info structure if it is desired to be kept separately. + In such cases macros are provided to add the elements directly to the list, + and automatically set up the info structure fields correctly. You can also + iterate through the elements of the list using a pointer to the elements + itself, as the list structure can be obtained for each element. + + Otherwise, macros are provided to manipulate list info structures and + link them in any shape or form into double linked lists. This allows + having NULL values as members of such lists. + + :NOTE: If you use a macro with a field argument, you must not have NULL + elements because the field of any element must be/point to the list + info structure + + DZLIST macros + + Having the list info structure separate from the element structure also + allows to link elements into many separate lists (with separate info + structure fields/pointers). However, for cases where only a single list + is desired, a set of easy macros are also provided. + + These macros combine the element and list structures. These macros + require the following 2 members in each element structure: + + *next, *last: as pointers to the element structure. These are the next and + previous elements in the list. + + :NOTE: In case of the DZLIST-s, the head of the list must be an element + structure, where only the next and last members are used. */ + +/* + Usage: + + DLIST macros are designed for preallocating the list info structures, e.g. in + an array. This is why most macros take a list info structure, and not a + pointer to a list info structure. + + Basic linked list consists of element structure and list info structure + + struct elem { + int data; + } *elA, *elB; + struct list { + struct elem *me; + struct list *last, *next; + } head, *inA, *inB, *in; + + DLIST_INIT(head); // initialization -> () + DLIST_IS_EMPTY(head) == TRUE; // emptiness check + + // add element at beginning of list -> (1) + elA = NEW(struct elem); + elA->data = 1; + inA = NEW(struct list); + DLIST_ADD_AFTER(head, elA, *inA); + + // add before an element -> (2, 1) + elB = NEW(struct elem); + elB->data = 2; + inB = NEW(struct list); + DLIST_ADD_BEFORE(*inA, elB, *inB); + + // move an element to another position or another list -> (1, 2) + DLIST_MOVE_BEFORE(head, *inB); + + // works even if the position is the same -> (1, 2) + DLIST_MOVE_BEFORE(head, *inB); + + // get first and last elements + DLIST_FIRST(head) == elA; + DLIST_LAST(head) == elB; + + // loop through elements + DLIST_LOOP(head, in) { + P("%d", in->me->data); + } + + // remove element -> (2) + DLIST_REMOVE(*inA); + FREE(elA); + FREE(inA); + + // delete list + DLIST_SAFE_LOOP(head, in, inA) { + DLIST_REMOVE(*in); + FREE(in->me); + FREE(in); + } + + You can combine the element and list info structures to create an easy list, + but you still need to specify both element and info structure while adding + elements. + + struct elem { + int data; + struct elem *me, *last, *next; + } head, *el, *elA, *elB; + + DLIST_INIT(head); // initialization -> () + DLIST_IS_EMPTY(head) == TRUE; // emptiness check + + // add element at beginning of list -> (1) + elA = NEW(struct elem); + elA->data = 1; + DLIST_ADD_AFTER(head, elA, *elA); + + // add before an element -> (2, 1) + elB = NEW(struct elem); + elB->data = 2; + DLIST_ADD_BEFORE(*elA, elB, *elB); + + // move an element to another position or another list -> (1, 2) + DLIST_MOVE_BEFORE(head, *elB); + + // works even if the position is the same -> (1, 2) + DLIST_MOVE_BEFORE(head, *elB); + + // get first and last elements + DLIST_FIRST(head) == elA; + DLIST_LAST(head) == elB; + + // loop through elements + DLIST_LOOP(head, el) { + P("%d", el->data); + } + + // remove element -> (2) + DLIST_REMOVE(*elA); + FREE(elA); + + // delete list + DLIST_SAFE_LOOP(head, el, elA) { + DLIST_REMOVE(*el); + FREE(el); + } + + Or, you can use a DZLIST. + + struct elem { + int data; + struct elem *last, *next; + } head, *el, *elA, *elB; + + DZLIST_INIT(head); // initialization -> () + DZLIST_IS_EMPTY(head) == TRUE; // emptiness check + + // add element at beginning of list -> (1) + elA = NEW(struct elem); + elA->data = 1; + DZLIST_ADD_AFTER(head, *elA); + + // add before an element -> (2, 1) + elB = NEW(struct elem); + elB->data = 2; + DZLIST_ADD_BEFORE(*elA, *elB); + + // move an element to another position or another list -> (1, 2) + DZLIST_MOVE_BEFORE(head, *elB); + + // works even if the position is the same -> (1, 2) + DZLIST_MOVE_BEFORE(head, *elB); + + // get first and last elements + DZLIST_FIRST(head) == elA; + DZLIST_LAST(head) == elB; + + // loop through elements + DZLIST_LOOP(head, el) { + P("%d", el->data); + } + + // remove element -> (2) + DZLIST_REMOVE(*elA); + FREE(elA); + + // delete list + DZLIST_SAFE_LOOP(head, el, elA) { + DZLIST_REMOVE(*el); + FREE(el); + } + + A better way to get to the list structure from the element structure is to + enclose a pointer the list structure in the element structure. This allows + getting to the next/previous element from the element itself. + + struct elem; + struct list { + struct elem *me; + struct list *last, *next; + } head, *inA, *inB, *in; + struct elem { + int data; + struct list *list_data; + } *elA, *elB, *el; + + // or + + struct elem { + int data; + struct list { + struct elem *me; + struct list *last, *next; + } *list_data; + } *elA, *elB, *el; + struct list head, *inA, *inB, *in; + + DLIST_INIT(head); // initialization -> () + DLIST_IS_EMPTY(head) == TRUE; // emptiness check + + // add element at beginning of list -> (1) + elA = NEW(struct elem); + elA->data = 1; + inA = NEW(struct list); + DLIST_PADD_AFTER(head, elA, inA, list_data); + + // add before an element -> (2, 1) + elB = NEW(struct elem); + elB->data = 2; + inB = NEW(struct list); + DLIST_PADD_BEFORE(*elA, elB, inB, list_data); + + // move an element to another position or another list -> (1, 2) + DLIST_MOVE_BEFORE(head, *inB); + + // works even if the position is the same -> (1, 2) + DLIST_MOVE_BEFORE(head, *inB); + + // get first and last elements + DLIST_FIRST(head) == elA; + DLIST_LAST(head) == elB; + + // loop through elements + DLIST_LOOP(head, in) { + P("%d", in->me->data); + } + DLIST_PLOOP(head, el, list_data) { + P("%d", el->data); + } + + // remove element + DLIST_REMOVE(*inA); + FREE(inA); + FREE(elA); + + // delete list + DLIST_SAFE_PLOOP(head, el, elA, list_data) { + DLIST_REMOVE(*el->list_data); + FREE(el->list_data); + FREE(el); + } + + Lastly, you can include the list data in the element structure itself. + + struct elem { + int data; + struct list { + struct list *last, *next; + struct elem *me; + } list_data; + } *elA, *elB, *el; + struct list head, *in; + + DLIST_INIT(head); // initialization -> () + DLIST_IS_EMPTY(head) == TRUE; // emptiness check + + // add element at beginning of list -> (1) + elA = NEW(struct elem); + elA->data = 1; + DLIST_MADD_AFTER(head, elA, list_data); + + // add before an element -> (2, 1) + elB = NEW(struct elem); + elB->data = 2; + DLIST_PADD_BEFORE(elA->list_data, elB, list_data); + + // move an element to another position or another list -> (1, 2) + DLIST_MOVE_BEFORE(head, elB->list_data); + + // works even if the position is the same -> (1, 2) + DLIST_MOVE_BEFORE(head, elB->list_data); + + // get first and last elements + DLIST_FIRST(head) == elA; + DLIST_LAST(head) == elB; + + // loop through elements + DLIST_LOOP(head, in) { + P("%d", in->me->data); + } + DLIST_MLOOP(head, el, list_data) { + P("%d", el->data); + } + + // remove element + DLIST_REMOVE(elA->list_data); + FREE(elA); + + // delete list + DLIST_SAFE_MLOOP(head, el, elA, list_data) { + DLIST_REMOVE(el->list_data); + FREE(el); + } + + */ + +/* -- internal (generic direction) macros -- */ +#define DLIST_move__(base, info, next, last) \ + ((info).last = &base)->next = ((info).next = (base).next)->last = &(info) +#define DLIST_padd__(base, pInfo, pElem, pField, next, last) \ + ((pInfo)->last = &base)->next = ((pInfo)->next = (base).next)->last = \ + pElem->pField = pInfo +#define DLIST_loop__(head, pInfo, next) \ + for (pInfo=(head).next; pInfo != &(head); pInfo = (pInfo)->next) +#define DLIST_ploop__(head, pElem, pField, next) \ + for (pElem=(head).next->me; pElem; pElem = (pElem)->pField->next->me) +#define DLIST_mloop__(head, pElem, mField, next) \ + for (pElem=(head).next->me; pElem; pElem = (pElem)->mField.next->me) +#define DLIST_safe_loop__(head, pInfo, pInfo_safe, next) \ + for (pInfo=(head).next; pInfo != &(head); pInfo = pInfo_safe) \ + if ((pInfo_safe = (pInfo)->next) || 1) +#define DLIST_safe_ploop__(head, pElem, pElem_safe, pField, next) \ + for (pElem=(head).next->me; pElem; pElem = pElem_safe) \ + if ((pElem_safe = (pElem)->pField->next->me) || 1) +#define DLIST_safe_mloop__(head, pElem, pElem_safe, mField, next) \ + for (pElem=(head).next->me; pElem; pElem = pElem_safe) \ + if ((pElem_safe = (pElem)->mField.next->me) || 1) + +#define DLIST_IS_EMPTY(head) ((head).next == &(head)) + +/* Adds the element (referred to by the info structure) before/or after another + element (or list header) (base). */ + +#define DLIST_ADD_AFTER(base, pElem, info) \ + (DLIST_move__(base, info, next, last))->me = pElem +#define DLIST_ADD_BEFORE(base, pElem, info) \ + (DLIST_move__(base, info, last, next))->me = pElem + +/* Adds the element (referred to by pElem pointer) along with its info + structure (referred to by pInfo pointer) before/or after an element or + list header (base). It also sets up the list structure header to point to + the element as well as the element's field to point back to the list info + structure. */ +#define DLIST_PADD_BEFORE(base, pElem, pInfo, pField) \ + (DLIST_padd__(base, pInfo, pElem, pField, last, next))->me = pElem +#define DLIST_PADD_AFTER(base, pElem, pInfo, pField) \ + (DLIST_padd__(base, pInfo, pElem, pField, next, last))->me = pElem + +/* Adds the element (referred to by pElem pointer) before/or after an element or + list header (base). It also sets up the list structure header (which is a + member of the element's structure) to point to the element. */ +#define DLIST_MADD_BEFORE(base, pElem, mField) \ + (DLIST_move__(base, pElem->mField, last, next))->me = pElem +#define DLIST_MADD_AFTER(base, pElem, mField) \ + (DLIST_move__(base, pElem->mField, next, last))->me = pElem + +/* Removes the element (referred to by the info structure) from its current + list. This requires that the element is a part of a list. + + :NOTE: the info structure will still think that it belongs to the list it + used to belong to. However, the old list will not contain this element any + longer. You want to discard the info/element after this call. Otherwise, + you can use one of the MOVE macros to also add the item to another list, + or another place in the same list. */ +#define DLIST_REMOVE(info) ((info).last->next = (info).next)->last = (info).last + +/* Initializes the list header (to an empty list) */ +#define DLIST_INIT(head) \ + do { (head).me = NULL; (head).next = (head).last = &(head); } while(0) + +/* These functions move an element (referred to by the info structure) before + or after another element (or the list head). + :NOTE: This logic also works for moving an element after/before itself. */ +#define DLIST_MOVE_AFTER(base, info) \ + do { DLIST_REMOVE(info); DLIST_move__(base, info, next, last); } while(0) +#define DLIST_MOVE_BEFORE(base, info) \ + do { DLIST_REMOVE(info); DLIST_move__(base, info, last, next); } while(0) + +/* Loops behave syntactically as a for() statement. They traverse the loop + variable from the 1st to the last element (or in the opposite direction in + case of RLOOP). There are 3 flavors of loops depending on the type of the + loop variable. + + DLIST_LOOP's loop variable is a pointer to the list info structure. You can + get to the element by using the 'me' member. Nonetheless, this loop + construct allows having NULL elements in the list. + + DLIST_MLOOP's loop variable is a pointer to a list element. mField is the + field of the element containing the list info structure. Naturally, this + list cannot have NULL elements. + + DLIST_PLOOP's loop variable is also a pointer to a list element. Use this + construct if the element contains a pointer to the list info structure + instead of embedding it directly into the element structure. + +*/ +#define DLIST_LOOP(head, pInfo) DLIST_loop__(head, pInfo, next) +#define DLIST_RLOOP(head, pInfo) DLIST_loop__(head, pInfo, last) +#define DLIST_MLOOP(head, pElem, mField) \ + DLIST_mloop__(head, pElem, mField, next) +#define DLIST_RMLOOP(head, pElem, mField) \ + DLIST_mloop__(head, pElem, mField, last) +#define DLIST_PLOOP(head, pElem, pField) \ + DLIST_ploop__(head, pElem, pField, next) +#define DLIST_RPLOOP(head, pElem, pField) \ + DLIST_ploop__(head, pElem, pField, last) + +/* Safe loops are like ordinary loops, but they allow removal of the current + element from the list. They require an extra loop variable that holds the + value of the next element in case the current element is moved/removed. */ +#define DLIST_SAFE_LOOP(head, pInfo, pInfo_safe) \ + DLIST_safe_loop__(head, pInfo, pInfo_safe, next) +#define DLIST_SAFE_RLOOP(head, pInfo, pInfo_safe) \ + DLIST_safe_loop__(head, pInfo, pInfo_safe, last) +#define DLIST_SAFE_MLOOP(head, pElem, pElem_safe, mField) \ + DLIST_safe_mloop__(head, pElem, pElem_safe, mField, next) +#define DLIST_SAFE_RMLOOP(head, pElem, pElem_safe, mField) \ + DLIST_safe_mloop__(head, pElem, pElem_safe, mField, last) +#define DLIST_SAFE_PLOOP(head, pElem, pElem_safe, pField) \ + DLIST_safe_ploop__(head, pElem, pElem_safe, pField, next) +#define DLIST_SAFE_RPLOOP(head, pElem, pElem_safe, pField) \ + DLIST_safe_ploop__(head, pElem, pElem_safe, pField, last) + +/* returns the first element of a list */ +#define DLIST_FIRST(head) (head).next->me +/* returns the last element of a list */ +#define DLIST_LAST(head) (head).last->me + + +/* DZLIST equivalent API - provided so arguments are specified */ +#define DZLIST_INIT(head) do { (head).next = (head).last = &(head); } while(0) +#define DZLIST_IS_EMPTY(head) DLIST_IS_EMPTY(head) +#define DZLIST_FIRST(head) (head).next +#define DZLIST_LAST(head) (head).last + +#define DZLIST_ADD_AFTER(base, elem) DLIST_move__(base, elem, next, last) +#define DZLIST_ADD_BEFORE(base, elem) DLIST_move__(base, elem, last, next) + +#define DZLIST_REMOVE(elem) DLIST_REMOVE(elem) + +#define DZLIST_MOVE_AFTER(base, elem) DLIST_MOVE_AFTER(base, elem) +#define DZLIST_MOVE_BEFORE(base, elem) DLIST_MOVE_BEFORE(base, elem) + +#define DZLIST_LOOP(head, pElem) DLIST_LOOP(head, pElem) +#define DZLIST_RLOOP(head, pElem) DLIST_RLOOP(head, pElem) +#define DZLIST_SAFE_LOOP(head, pElem, pElem_safe) \ + DLIST_SAFE_LOOP(head, pElem, pElem_safe) +#define DZLIST_SAFE_RLOOP(head, pElem, pElem_safe) \ + DLIST_SAFE_RLOOP(head, pElem, pElem_safe) + +#endif + |