ldb: sync DLIST_DEMOTE_SHORT() changes to include/dlinklist.h
[metze/samba/wip.git] / lib / ldb / include / dlinklist.h
1 /*
2    Unix SMB/CIFS implementation.
3    some simple double linked list macros
4
5    Copyright (C) Andrew Tridgell 1998-2010
6
7      ** NOTE! The following LGPL license applies to this file (*dlinklist.h).
8      ** This does NOT imply that all of Samba is released under the LGPL
9
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /* To use these macros you must have a structure containing a next and
25    prev pointer */
26
27 #ifndef _DLINKLIST_H
28 #define _DLINKLIST_H
29
30 /*
31   February 2010 - changed list format to have a prev pointer from the
32   list head. This makes DLIST_ADD_END() O(1) even though we only have
33   one list pointer.
34
35   The scheme is as follows:
36
37      1) with no entries in the list:
38           list_head == NULL
39
40      2) with 1 entry in the list:
41           list_head->next == NULL
42           list_head->prev == list_head
43
44      3) with 2 entries in the list:
45           list_head->next == element2
46           list_head->prev == element2
47           element2->prev == list_head
48           element2->next == NULL
49
50      4) with N entries in the list:
51           list_head->next == element2
52           list_head->prev == elementN
53           elementN->prev == element{N-1}
54           elementN->next == NULL
55
56   This allows us to find the tail of the list by using
57   list_head->prev, which means we can add to the end of the list in
58   O(1) time
59  */
60
61
62 /*
63    add an element at the front of a list
64 */
65 #define DLIST_ADD(list, p) \
66 do { \
67         if (!(list)) { \
68                 (p)->prev = (list) = (p);  \
69                 (p)->next = NULL; \
70         } else { \
71                 (p)->prev = (list)->prev; \
72                 (list)->prev = (p); \
73                 (p)->next = (list); \
74                 (list) = (p); \
75         } \
76 } while (0)
77
78 /*
79    remove an element from a list
80    Note that the element doesn't have to be in the list. If it
81    isn't then this is a no-op
82 */
83 #define DLIST_REMOVE(list, p) \
84 do { \
85         if ((p) == (list)) { \
86                 if ((p)->next) (p)->next->prev = (p)->prev; \
87                 (list) = (p)->next; \
88         } else if ((p)->prev && (list) && (p) == (list)->prev) {        \
89                 (p)->prev->next = NULL; \
90                 (list)->prev = (p)->prev; \
91         } else { \
92                 if ((p)->prev) (p)->prev->next = (p)->next; \
93                 if ((p)->next) (p)->next->prev = (p)->prev; \
94         } \
95         if ((p) != (list)) (p)->next = (p)->prev = NULL;        \
96 } while (0)
97
98 /*
99    find the head of the list given any element in it.
100    Note that this costs O(N), so you should avoid this macro
101    if at all possible!
102 */
103 #define DLIST_HEAD(p, result_head) \
104 do { \
105        (result_head) = (p); \
106        while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \
107 } while(0)
108
109 /* return the last element in the list */
110 #define DLIST_TAIL(list) ((list)?(list)->prev:NULL)
111
112 /* return the previous element in the list. */
113 #define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL)
114
115 /* insert 'p' after the given element 'el' in a list. If el is NULL then
116    this is the same as a DLIST_ADD() */
117 #define DLIST_ADD_AFTER(list, p, el) \
118 do { \
119         if (!(list) || !(el)) { \
120                 DLIST_ADD(list, p); \
121         } else { \
122                 (p)->prev = (el);   \
123                 (p)->next = (el)->next;         \
124                 (el)->next = (p);               \
125                 if ((p)->next) (p)->next->prev = (p);   \
126                 if ((list)->prev == (el)) (list)->prev = (p); \
127         }\
128 } while (0)
129
130
131 /*
132    add to the end of a list.
133 */
134 #define DLIST_ADD_END(list, p) \
135 do { \
136         if (!(list)) { \
137                 DLIST_ADD(list, p); \
138         } else { \
139                 DLIST_ADD_AFTER(list, p, (list)->prev); \
140         } \
141 } while (0)
142
143 /* promote an element to the front of a list */
144 #define DLIST_PROMOTE(list, p) \
145 do { \
146           DLIST_REMOVE(list, p); \
147           DLIST_ADD(list, p); \
148 } while (0)
149
150 /*
151    demote an element to the end of a list.
152 */
153 #define DLIST_DEMOTE(list, p) \
154 do { \
155         DLIST_REMOVE(list, p); \
156         DLIST_ADD_END(list, p); \
157 } while (0)
158
159 /*
160  * like DLIST_DEMOTE(), but optimized
161  * for short lists with 0, 1 or 2 elements
162  */
163 #define DLIST_DEMOTE_SHORT(list, p) \
164 do { \
165         if ((list) == NULL) { \
166                 /* no reason to demote, just add */ \
167                 DLIST_ADD(list, p); \
168         } else if ((list)->prev == (p)) { \
169                 /* optimize if p is last */ \
170         } else if ((list) == (p)) { \
171                 /* optimize if p is first */ \
172                 (list)->prev->next = (p); \
173                 (list) = (p)->next; \
174                 (p)->next = NULL; \
175         } else { \
176                 DLIST_DEMOTE(list, p); \
177         } \
178 } while (0)
179
180 /*
181    concatenate two lists - putting all elements of the 2nd list at the
182    end of the first list.
183 */
184 #define DLIST_CONCATENATE(list1, list2) \
185 do { \
186         if (!(list1)) { \
187                 (list1) = (list2); \
188         } else { \
189                 (list1)->prev->next = (list2); \
190                 if (list2) { \
191                         void *_tmplist = (void *)(list1)->prev; \
192                         (list1)->prev = (list2)->prev; \
193                         (list2)->prev = _tmplist; \
194                 } \
195         } \
196 } while (0)
197
198 #endif /* _DLINKLIST_H */