Line data Source code
1 : /* Generate prototypes, inlines and/or implementations for HPC doubly
2 : linked lists. A dlist can store a practically unbounded number of
3 : elements. Typical dlist operations are generally a fast O(1) time
4 : and dlist element memory overhead is a small O(1) space.
5 :
6 : This API is designed for ultra tight coupling with pools, treaps,
7 : heaps, maps, other dlists, etc. Likewise, a dlist can be persisted
8 : beyond the lifetime of the creating process, used concurrently in
9 : many common operations, used inter-process, relocated in memory,
10 : naively serialized/deserialized, moved between hosts, supports index
11 : compresson for cache and memory bandwidth efficiency, etc.
12 :
13 : Memory efficiency and flexible footprint are prioritized.
14 :
15 : Typical usage:
16 :
17 : struct myele {
18 : ulong prev; // Technically "DLIST_IDX_T DLIST_PREV" (default is ulong prev), do not modify while element is in the dlist
19 : ulong next; // Technically "DLIST_IDX_T DLIST_NEXT" (default is ulong next), do not modify while element is in the dlist
20 : ... prev and next can be located arbitrarily in the element and
21 : ... can be reused for other purposes when the element is not a in
22 : ... dlist. An element should not be moved / released while an
23 : ... element is in a dlist
24 : };
25 :
26 : typedef struct myele myele_t;
27 :
28 : #define DLIST_NAME mydlist
29 : #define DLIST_ELE_T myele_t
30 : #include "tmpl/fd_dlist.c"
31 :
32 : will declare the following APIs as a header only style library in the
33 : compilation unit:
34 :
35 : // mydlist_ele_max returns the theoretical maximum number of
36 : // elements that can be held in a mydlist.
37 :
38 : ulong mydlist_ele_max( void );
39 :
40 : // mydlist_{align,footprint} returns the alignment and footprint
41 : // needed for a memory region to be used as a mydlist. align will
42 : // be an integer power-of-two and footprint will be a multiple of
43 : // align. The values will be compile-time declaration friendly
44 : // (e.g. "mydlist_t mem[1];" will have the correct alignment and
45 : // footprint and not be larger than 4096).
46 : //
47 : // mydlist_new formats a memory region with the appropriate
48 : // alignment and footprint whose first byte in the caller's address
49 : // space is pointed to by shmem as a mydlist. Returns shmem on
50 : // success and NULL on failure (logs details). Caller is not
51 : // joined on return. The dlist will be empty.
52 : //
53 : // mydlist_join joins a mydlist. Assumes shdlist points at a
54 : // memory region formatted as a mydlist in the caller's address
55 : // space. Returns a handle to the caller's local join on success
56 : // and NULL on failure (logs details). Do not assume this is a
57 : // simple cast of shdlist!
58 : //
59 : // mydlist_leave leaves a mydlist. Assumes join points to a
60 : // current local join. Returns shdlist used on join. Do not
61 : // assume this is a simple cast of join!
62 : //
63 : // mydlist_delete unformats a memory region used as a mydlist.
64 : // Assumes shdlist points to a memory region in the caller's local
65 : // address space formatted as a mydlist, that there are no joins to
66 : // the mydlist and that any application cleanup of the entries has
67 : // already been done. Returns shdlist on success and NULL on
68 : // failure.
69 :
70 : ulong mydlist_align ( void );
71 : ulong mydlist_footprint( void );
72 : void * mydlist_new ( void * shmem );
73 : mydlist_t * mydlist_join ( void * shdlist );
74 : void * mydlist_leave ( mydlist_t * join );
75 : void * mydlist_delete ( void * shdlist );
76 :
77 : // The below APIs assume join is a current local join to a mydlist
78 : // and pool is a current local join to the element storage backing
79 : // the dlist.
80 : //
81 : // mydlist_is_empty returns 1 if the dlist is empty and 0
82 : // otherwise.
83 : //
84 : // mydlist_idx_peek_{head,tail} returns the pool index of the
85 : // dlist's {head,tail}. Assumes dlist is not empty.
86 :
87 : int mydlist_is_empty( mydlist_t const * join, myele_t const * pool );
88 :
89 : ulong mydlist_idx_peek_head( mydlist_t const * join, myele_t const * pool );
90 : ulong mydlist_idx_peek_tail( mydlist_t const * join, myele_t const * pool );
91 :
92 : // mydlist_idx_push_{head,tail} pushes the pool element whose index
93 : // is ele_idx to the dlist's {head,tail} and returns join. Assumes
94 : // ele_idx valid and not already in the dlist.
95 : /
96 : // mydlist_idx_pop_{head,tail} pops the pool element at the dlist's
97 : // {head,tail} and returns its pool index. Assumes dlist is not
98 : // empty.
99 : //
100 : // mydlist_idx_insert_{before,after} inserts the pool element whose
101 : // index is ele_idx into the dlist immediately {before,after} the
102 : // pool element whose index is {next_idx,prev_idx} and returns
103 : // join. Assumes ele_idx is valid and not already in the dlist and
104 : // {next_idx,prev_idx} are already in the dlist.
105 : //
106 : // mydlist_idx_remove removes the pool element whose index is
107 : // ele_idx from the dlist and returns join. Assumes ele_idx is in
108 : // the dlist already.
109 : //
110 : // mydlist_idx_replace replaces the pool element whose index is
111 : // old_idx with the pool element whose index is ele_idx in the
112 : // dlist and returns join. Assumes ele_idx is not in the dlist and
113 : // old_idx is in the dlist already.
114 :
115 : mydlist_t * mydlist_idx_push_head ( mydlist_t * join, ulong ele_idx, myele_t * pool );
116 : mydlist_t * mydlist_idx_push_tail ( mydlist_t * join, ulong ele_idx, myele_t * pool );
117 : ulong mydlist_idx_pop_head ( mydlist_t * join, myele_t * pool );
118 : ulong mydlist_idx_pop_tail ( mydlist_t * join, myele_t * pool );
119 : mydlist_t * mydlist_idx_insert_before( mydlist_t * join, ulong ele_idx, ulong next_idx, myele_t * pool );
120 : mydlist_t * mydlist_idx_insert_after ( mydlist_t * join, ulong ele_idx, ulong prev_idx, myele_t * pool );
121 : mydlist_t * mydlist_idx_remove ( mydlist_t * join, ulong ele_idx, myele_t * pool );
122 : mydlist_t * mydlist_idx_replace ( mydlist_t * join, ulong ele_idx, ulong old_idx, myele_t * pool );
123 :
124 : // mydlist_remove_all removes all elements from the dlist and
125 : // returns join. It is the caller's responsibility to release all
126 : // elements to the pool as might be necessary.
127 :
128 : mydlist_t * mydlist_remove_all( mydlist_t * join, myele_t * pool );
129 :
130 : // mydlist_iter_* support fast ordered forward (head to tail) and
131 : // reverse (tail to head) iteration over all the elements in a
132 : // dlist. Example usage:
133 : //
134 : // for( mydlist_iter_t iter = mydlist_iter_rev_init( join, pool );
135 : // !mydlist_iter_done( iter, join, pool );
136 : // iter = mydlist_iter_rev_next( iter, join, pool ) ) {
137 : // ulong ele_idx = mydlist_iter_idx( iter, join, pool );
138 : //
139 : // ... process element here
140 : //
141 : // ... IMPORTANT! It is generally safe to insert elements
142 : // ... here (though they might not be covered by this
143 : // ... iteration). It is also generally safe to remove any
144 : // ... element but the current element here (the removed
145 : // ... element might have already be iterated over). It is
146 : // ... straightforward to make a variant of this iterator
147 : // ... that would support removing the current element here
148 : // ... if desired.
149 : // }
150 :
151 : struct mydlist_iter_private { ... internal use only ... };
152 : typedef struct mydlist_iter_private mydlist_iter_t;
153 :
154 : mydlist_iter_t mydlist_iter_fwd_init( mydlist_t const * join, myele_t const * pool );
155 : mydlist_iter_t mydlist_iter_rev_init( mydlist_t const * join, myele_t const * pool );
156 : int mydlist_iter_done ( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool );
157 : mydlist_iter_t mydlist_iter_fwd_next( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool ); // assumes !done
158 : mydlist_iter_t mydlist_iter_rev_next( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool ); // assumes !done
159 : ulong mydlist_iter_idx ( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool ); // assumes !done
160 :
161 : // mydlist_verify returns 0 if the mydlist is not obviously corrupt
162 : // or -1 (i.e. ERR_INVAL) otherwise (logs details).
163 :
164 : int
165 : mydlist_verify( mydlist_t const * join, // Current local join to a mydlist.
166 : ulong ele_cnt, // Element storage size, in [0,mydlist_ele_max()]
167 : myele_t const * pool ); // Current local join to element storage, indexed [0,ele_cnt)
168 :
169 : // The above APIs have helpers that operate purely in the caller's
170 : // local address space when applicable. The various elements
171 : // passed to / returned from these functions should be / will be
172 : // from the dlist's underlying pool.
173 :
174 : myele_t * mydlist_ele_peek_head( mydlist_t const * join, myele_t * pool );
175 : myele_t * mydlist_ele_peek_tail( mydlist_t const * join, myele_t * pool );
176 :
177 : mydlist_t * mydlist_ele_push_head ( mydlist_t * join, myele_t * ele, myele_t * pool );
178 : mydlist_t * mydlist_ele_push_tail ( mydlist_t * join, myele_t * ele, myele_t * pool );
179 : myele_t * mydlist_ele_pop_head ( mydlist_t * join, myele_t * pool );
180 : myele_t * mydlist_ele_pop_tail ( mydlist_t * join, myele_t * pool );
181 : mydlist_t * mydlist_ele_insert_before( mydlist_t * join, myele_t * ele, myele_t * next, myele_t * pool );
182 : mydlist_t * mydlist_ele_insert_after ( mydlist_t * join, myele_t * ele, myele_t * prev, myele_t * pool );
183 : mydlist_t * mydlist_ele_replace ( mydlist_t * join, myele_t * ele, myele_t * old, myele_t * pool );
184 : mydlist_t * mydlist_ele_remove ( mydlist_t * join, myele_t * ele, myele_t * pool );
185 :
186 : myele_t * mydlist_iter_ele( mydlist_iter_t iter, mydlist_t const * join, myele_t * pool );
187 :
188 : // ... and const correct helpers when applicable
189 :
190 : myele_t const * mydlist_ele_peek_head_const( mydlist_t const * join, myele_t const * pool );
191 : myele_t const * mydlist_ele_peek_tail_const( mydlist_t const * join, myele_t const * pool );
192 :
193 : myele_t const * mydlist_iter_ele_const( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool );
194 :
195 : You can do this as often as you like in a compilation unit to get
196 : different types of dlists. Variants exist for making header
197 : prototypes only and/or implementations only if making a library for
198 : use across multiple compilation units. Further, options exist to use
199 : different hashing functions, comparison functions, etc as detailed
200 : below. */
201 :
202 : /* TODO: DOC CONCURRENCY REQUIREMENTS */
203 :
204 : /* DLIST_NAME gives the API prefix to use for a dlist */
205 :
206 : #ifndef DLIST_NAME
207 : #error "Define DLIST_NAME"
208 : #endif
209 :
210 : /* DLIST_ELE_T is the dlist element type. */
211 :
212 : #ifndef DLIST_ELE_T
213 : #error "Define DLIST_ELE_T"
214 : #endif
215 :
216 : /* DLIST_IDX_T is the type used for the prev and next fields in the
217 : DLIST_ELE_T. Should be a primitive unsigned integer type. Defaults
218 : to ulong. A dlist can't use element memory regions with more
219 : elements than the maximum value that can be represented by a
220 : DLIST_IDX_T. (E.g. if ushort, the maximum size element store
221 : supported by the dlist is 65535 elements.) */
222 :
223 : #ifndef DLIST_IDX_T
224 : #define DLIST_IDX_T ulong
225 : #endif
226 :
227 : /* DLIST_PREV is the DLIST_ELE_T prev field */
228 :
229 : #ifndef DLIST_PREV
230 0 : #define DLIST_PREV prev
231 : #endif
232 :
233 : /* DLIST_NEXT is the DLIST_ELE_T next field */
234 :
235 : #ifndef DLIST_NEXT
236 0 : #define DLIST_NEXT next
237 : #endif
238 :
239 : /* DLIST_MAGIC is the magic number to use for the structure to aid in
240 : persistent and/or IPC usage. */
241 :
242 : #ifndef DLIST_MAGIC
243 3 : #define DLIST_MAGIC (0xf17eda2c37d71570UL) /* firedancer dlist version 0 */
244 : #endif
245 :
246 : /* 0 - local use only
247 : 1 - library header declaration
248 : 2 - library implementation */
249 :
250 : #ifndef DLIST_IMPL_STYLE
251 : #define DLIST_IMPL_STYLE 0
252 : #endif
253 :
254 : /* Implementation *****************************************************/
255 :
256 : /* Constructors and verification log details on failure (rest only needs
257 : fd_bits.h, consider making logging a compile time option). */
258 :
259 : #include "../log/fd_log.h"
260 :
261 34565385909 : #define DLIST_(n) FD_EXPAND_THEN_CONCAT3(DLIST_NAME,_,n)
262 :
263 : #if DLIST_IMPL_STYLE==0 || DLIST_IMPL_STYLE==1 /* need structures and inlines */
264 :
265 : struct DLIST_(private) {
266 :
267 : /* join points here */
268 :
269 : ulong magic; /* == DLIST_MAGIC */
270 : DLIST_IDX_T head; /* index of first list element (or idx_null if empty list) */
271 : DLIST_IDX_T tail; /* index of last list element (or idx_null if empty list) */
272 : };
273 :
274 : typedef struct DLIST_(private) DLIST_(private_t);
275 :
276 : typedef DLIST_(private_t) DLIST_(t);
277 :
278 : typedef ulong DLIST_(iter_t);
279 :
280 : FD_PROTOTYPES_BEGIN
281 :
282 : /* dlist_private returns the location of the dlist header for a current
283 : local join. Assumes join is a current local join.
284 : dlist_private_const is a const correct version. */
285 :
286 : FD_FN_CONST static inline DLIST_(private_t) *
287 1331323689 : DLIST_(private)( DLIST_(t) * join ) {
288 1331323689 : return (DLIST_(private_t) *)join;
289 1331323689 : }
290 :
291 : FD_FN_CONST static inline DLIST_(private_t) const *
292 7462087899 : DLIST_(private_const)( DLIST_(t) const * join ) {
293 7462087899 : return (DLIST_(private_t) const *)join;
294 7462087899 : }
295 :
296 : /* dlist_private_{cidx,idx} compress / decompress 64-bit in-register
297 : indices to/from their in-memory representations. */
298 :
299 3393939273 : FD_FN_CONST static inline DLIST_IDX_T DLIST_(private_cidx)( ulong idx ) { return (DLIST_IDX_T)idx; }
300 12374952969 : FD_FN_CONST static inline ulong DLIST_(private_idx) ( DLIST_IDX_T cidx ) { return (ulong) cidx; }
301 :
302 : /* dlist_private_idx_null returns the element storage index that
303 : represents NULL. */
304 :
305 0 : FD_FN_CONST static inline ulong DLIST_(private_idx_null)( void ) { return (ulong)(DLIST_IDX_T)~0UL; }
306 :
307 : /* dlist_private_idx_is_null returns 1 if idx is the NULL dlist index
308 : and 0 otherwise. */
309 :
310 5812703667 : FD_FN_CONST static inline int DLIST_(private_idx_is_null)( ulong idx ) { return idx==(ulong)(DLIST_IDX_T)~0UL; }
311 :
312 0 : FD_FN_CONST static ulong DLIST_(ele_max)( void ) { return (ulong)(DLIST_IDX_T)~0UL; }
313 :
314 : FD_FN_PURE static inline int
315 : DLIST_(is_empty)( DLIST_(t) const * join,
316 1124963763 : DLIST_ELE_T const * pool ) {
317 1124963763 : (void)pool;
318 1124963763 : return DLIST_(private_idx_is_null)( DLIST_(private_idx)( DLIST_(private_const)( join )->head ) );
319 1124963763 : }
320 :
321 : FD_FN_PURE static inline ulong
322 : DLIST_(idx_peek_head)( DLIST_(t) const * join,
323 2699814870 : DLIST_ELE_T const * pool ) {
324 2699814870 : (void)pool;
325 2699814870 : return DLIST_(private_idx)( DLIST_(private_const)( join )->head );
326 2699814870 : }
327 :
328 : FD_FN_PURE static inline ulong
329 : DLIST_(idx_peek_tail)( DLIST_(t) const * join,
330 2699814870 : DLIST_ELE_T const * pool ) {
331 2699814870 : (void)pool;
332 2699814870 : return DLIST_(private_idx)( DLIST_(private_const)( join )->tail );
333 2699814870 : }
334 :
335 : static inline DLIST_(t) *
336 : DLIST_(idx_push_head)( DLIST_(t) * join,
337 : ulong ele_idx,
338 168756039 : DLIST_ELE_T * pool ) {
339 168756039 : DLIST_(private_t) * dlist = DLIST_(private)( join );
340 :
341 168756039 : ulong head_idx = DLIST_(private_idx)( dlist->head );
342 :
343 168756039 : pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
344 168756039 : pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( head_idx );
345 :
346 168756039 : *fd_ptr_if( !DLIST_(private_idx_is_null)( head_idx ), &pool[ head_idx ].DLIST_PREV, &dlist->tail ) =
347 168756039 : DLIST_(private_cidx)( ele_idx );
348 :
349 168756039 : dlist->head = DLIST_(private_cidx)( ele_idx );
350 168756039 : return join;
351 168756039 : }
352 :
353 : static inline DLIST_(t) *
354 : DLIST_(idx_push_tail)( DLIST_(t) * join,
355 : ulong ele_idx,
356 168754200 : DLIST_ELE_T * pool ) {
357 168754200 : DLIST_(private_t) * dlist = DLIST_(private)( join );
358 :
359 168754200 : ulong tail_idx = DLIST_(private_idx)( dlist->tail );
360 :
361 168754200 : pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( tail_idx );
362 168754200 : pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
363 :
364 168754200 : *fd_ptr_if( !DLIST_(private_idx_is_null)( tail_idx ), &pool[ tail_idx ].DLIST_NEXT, &dlist->head ) =
365 168754200 : DLIST_(private_cidx)( ele_idx );
366 :
367 168754200 : dlist->tail = DLIST_(private_cidx)( ele_idx );
368 168754200 : return join;
369 168754200 : }
370 :
371 : static inline ulong
372 : DLIST_(idx_pop_head)( DLIST_(t) * join,
373 150031173 : DLIST_ELE_T * pool ) {
374 150031173 : DLIST_(private_t) * dlist = DLIST_(private)( join );
375 :
376 150031173 : ulong ele_idx = DLIST_(private_idx)( dlist->head ); /* Not NULL as per contract */
377 150031173 : ulong next_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_NEXT );
378 :
379 150031173 : *fd_ptr_if( !DLIST_(private_idx_is_null)( next_idx ), &pool[ next_idx ].DLIST_PREV, &dlist->tail ) =
380 150031173 : DLIST_(private_cidx)( DLIST_(private_idx_null)() );
381 :
382 150031173 : dlist->head = DLIST_(private_cidx)( next_idx );
383 150031173 : return ele_idx;
384 150031173 : }
385 :
386 : static inline ulong
387 : DLIST_(idx_pop_tail)( DLIST_(t) * join,
388 150025710 : DLIST_ELE_T * pool ) {
389 150025710 : DLIST_(private_t) * dlist = DLIST_(private)( join );
390 :
391 150025710 : ulong ele_idx = DLIST_(private_idx)( dlist->tail ); /* Not NULL as per contract */
392 150025710 : ulong prev_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_PREV );
393 :
394 150025710 : *fd_ptr_if( !DLIST_(private_idx_is_null)( prev_idx ), &pool[ prev_idx ].DLIST_NEXT, &dlist->head ) =
395 150025710 : DLIST_(private_cidx)( DLIST_(private_idx_null)() );
396 :
397 150025710 : dlist->tail = DLIST_(private_cidx)( prev_idx );
398 150025710 : return ele_idx;
399 150025710 : }
400 :
401 : static inline DLIST_(t) *
402 : DLIST_(idx_insert_before)( DLIST_(t) * join,
403 : ulong ele_idx,
404 : ulong next_idx,
405 131251101 : DLIST_ELE_T * pool ) {
406 131251101 : ulong prev_idx = DLIST_(private_idx)( pool[ next_idx ].DLIST_PREV );
407 :
408 131251101 : pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( prev_idx );
409 131251101 : pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( next_idx );
410 :
411 131251101 : pool[ next_idx ].DLIST_PREV = DLIST_(private_cidx)( ele_idx );
412 :
413 131251101 : *fd_ptr_if( !DLIST_(private_idx_is_null)( prev_idx ), &pool[ prev_idx ].DLIST_NEXT, &DLIST_(private)( join )->head ) =
414 131251101 : DLIST_(private_cidx)( ele_idx );
415 :
416 131251101 : return join;
417 131251101 : }
418 :
419 : static inline DLIST_(t) *
420 : DLIST_(idx_insert_after)( DLIST_(t) * join,
421 : ulong ele_idx,
422 : ulong prev_idx,
423 131270532 : DLIST_ELE_T * pool ) {
424 131270532 : ulong next_idx = DLIST_(private_idx)( pool[ prev_idx ].DLIST_NEXT );
425 :
426 131270532 : pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( prev_idx );
427 131270532 : pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( next_idx );
428 :
429 131270532 : pool[ prev_idx ].DLIST_NEXT = DLIST_(private_cidx)( ele_idx );
430 :
431 131270532 : *fd_ptr_if( !DLIST_(private_idx_is_null)( next_idx ), &pool[ next_idx ].DLIST_PREV, &DLIST_(private)( join )->tail ) =
432 131270532 : DLIST_(private_cidx)( ele_idx );
433 :
434 131270532 : return join;
435 131270532 : }
436 :
437 : static inline DLIST_(t) *
438 : DLIST_(idx_remove)( DLIST_(t) * join,
439 : ulong ele_idx,
440 299974860 : DLIST_ELE_T * pool ) {
441 299974860 : DLIST_(private_t) * dlist = DLIST_(private)( join );
442 :
443 299974860 : ulong prev_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_PREV );
444 299974860 : ulong next_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_NEXT );
445 :
446 299974860 : *fd_ptr_if( !DLIST_(private_idx_is_null)( next_idx ), &pool[ next_idx ].DLIST_PREV, &dlist->tail ) =
447 299974860 : DLIST_(private_cidx)( prev_idx );
448 :
449 299974860 : *fd_ptr_if( !DLIST_(private_idx_is_null)( prev_idx ), &pool[ prev_idx ].DLIST_NEXT, &dlist->head ) =
450 299974860 : DLIST_(private_cidx)( next_idx );
451 :
452 299974860 : return join;
453 299974860 : }
454 :
455 : static inline DLIST_(t) *
456 : DLIST_(idx_replace)( DLIST_(t) * join,
457 : ulong ele_idx,
458 : ulong old_idx,
459 131260047 : DLIST_ELE_T * pool ) {
460 131260047 : DLIST_(private_t) * dlist = DLIST_(private)( join );
461 :
462 131260047 : ulong prev_idx = DLIST_(private_idx)( pool[ old_idx ].DLIST_PREV );
463 131260047 : ulong next_idx = DLIST_(private_idx)( pool[ old_idx ].DLIST_NEXT );
464 :
465 131260047 : pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( prev_idx );
466 131260047 : pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( next_idx );
467 :
468 131260047 : *fd_ptr_if( !DLIST_(private_idx_is_null)( next_idx ), &pool[ next_idx ].DLIST_PREV, &dlist->tail ) =
469 131260047 : DLIST_(private_cidx)( ele_idx );
470 :
471 131260047 : *fd_ptr_if( !DLIST_(private_idx_is_null)( prev_idx ), &pool[ prev_idx ].DLIST_NEXT, &dlist->head ) =
472 131260047 : DLIST_(private_cidx)( ele_idx );
473 :
474 131260047 : return join;
475 131260047 : }
476 :
477 : static inline DLIST_(t) *
478 : DLIST_(remove_all)( DLIST_(t) * join,
479 27 : DLIST_ELE_T * pool ) {
480 27 : (void)pool;
481 27 : DLIST_(private_t) * dlist = DLIST_(private)( join );
482 27 : dlist->head = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
483 27 : dlist->tail = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
484 27 : return join;
485 27 : }
486 :
487 : FD_FN_PURE static inline DLIST_(iter_t)
488 : DLIST_(iter_fwd_init)( DLIST_(t) const * join,
489 449975715 : DLIST_ELE_T const * pool ) {
490 449975715 : (void)pool;
491 449975715 : return DLIST_(private_idx)( DLIST_(private_const)( join )->head );
492 449975715 : }
493 :
494 : FD_FN_PURE static inline DLIST_(iter_t)
495 : DLIST_(iter_rev_init)( DLIST_(t) const * join,
496 300040953 : DLIST_ELE_T const * pool ) {
497 300040953 : (void)pool;
498 300040953 : return DLIST_(private_idx)( DLIST_(private_const)( join )->tail );
499 300040953 : }
500 :
501 : FD_FN_CONST static inline int
502 : DLIST_(iter_done)( DLIST_(iter_t) iter,
503 : DLIST_(t) const * join,
504 2062618656 : DLIST_ELE_T const * pool ) {
505 2062618656 : (void)join; (void)pool;
506 2062618656 : return DLIST_(private_idx_is_null)( iter );
507 2062618656 : }
508 :
509 : FD_FN_PURE static inline DLIST_(iter_t)
510 : DLIST_(iter_fwd_next)( DLIST_(iter_t) iter,
511 : DLIST_(t) const * join,
512 787505583 : DLIST_ELE_T const * pool ) {
513 787505583 : (void)join;
514 787505583 : return DLIST_(private_idx)( pool[ iter ].DLIST_NEXT );
515 787505583 : }
516 :
517 : FD_FN_PURE static inline DLIST_(iter_t)
518 : DLIST_(iter_rev_next)( DLIST_(iter_t) iter,
519 : DLIST_(t) const * join,
520 525096405 : DLIST_ELE_T const * pool ) {
521 525096405 : (void)join;
522 525096405 : return DLIST_(private_idx)( pool[ iter ].DLIST_PREV );
523 525096405 : }
524 :
525 : FD_FN_CONST static inline ulong
526 : DLIST_(iter_idx)( DLIST_(iter_t) iter,
527 : DLIST_(t) const * join,
528 375025248 : DLIST_ELE_T const * pool ) {
529 375025248 : (void)join; (void)pool;
530 375025248 : return iter;
531 375025248 : }
532 :
533 : FD_FN_PURE static inline DLIST_ELE_T *
534 : DLIST_(ele_peek_head)( DLIST_(t) const * join,
535 899938290 : DLIST_ELE_T * pool ) {
536 899938290 : return pool + DLIST_(idx_peek_head)( join, pool );
537 899938290 : }
538 :
539 : FD_FN_PURE static inline DLIST_ELE_T const *
540 : DLIST_(ele_peek_head_const)( DLIST_(t) const * join,
541 899938290 : DLIST_ELE_T const * pool ) {
542 899938290 : return pool + DLIST_(idx_peek_head)( join, pool );
543 899938290 : }
544 :
545 : FD_FN_PURE static inline DLIST_ELE_T *
546 : DLIST_(ele_peek_tail)( DLIST_(t) const * join,
547 899938290 : DLIST_ELE_T * pool ) {
548 899938290 : return pool + DLIST_(idx_peek_tail)( join, pool );
549 899938290 : }
550 :
551 : FD_FN_PURE static inline DLIST_ELE_T const *
552 : DLIST_(ele_peek_tail_const)( DLIST_(t) const * join,
553 899938290 : DLIST_ELE_T const * pool ) {
554 899938290 : return pool + DLIST_(idx_peek_tail)( join, pool );
555 899938290 : }
556 :
557 : static inline DLIST_(t) *
558 : DLIST_(ele_push_head)( DLIST_(t) * join,
559 : DLIST_ELE_T * ele,
560 84373425 : DLIST_ELE_T * pool ) {
561 84373425 : return DLIST_(idx_push_head)( join, (ulong)(ele-pool), pool );
562 84373425 : }
563 :
564 : static inline DLIST_(t) *
565 : DLIST_(ele_push_tail)( DLIST_(t) * join,
566 : DLIST_ELE_T * ele,
567 84381648 : DLIST_ELE_T * pool ) {
568 84381648 : return DLIST_(idx_push_tail)( join, (ulong)(ele-pool), pool );
569 84381648 : }
570 :
571 : static inline DLIST_ELE_T *
572 : DLIST_(ele_pop_head)( DLIST_(t) * join,
573 75015066 : DLIST_ELE_T * pool ) {
574 75015066 : return pool + DLIST_(idx_pop_head)( join, pool );
575 75015066 : }
576 :
577 : static inline DLIST_ELE_T*
578 : DLIST_(ele_pop_tail)( DLIST_(t) * join,
579 75016527 : DLIST_ELE_T * pool ) {
580 75016527 : return pool + DLIST_(idx_pop_tail)( join, pool );
581 75016527 : }
582 :
583 : static inline DLIST_(t) *
584 : DLIST_(ele_insert_before)( DLIST_(t) * join,
585 : DLIST_ELE_T * ele,
586 : DLIST_ELE_T * next,
587 65610312 : DLIST_ELE_T * pool ) {
588 65610312 : return DLIST_(idx_insert_before)( join, (ulong)(ele-pool), (ulong)(next-pool), pool );
589 65610312 : }
590 :
591 : static inline DLIST_(t) *
592 : DLIST_(ele_insert_after)( DLIST_(t) * join,
593 : DLIST_ELE_T * ele,
594 : DLIST_ELE_T * prev,
595 65643615 : DLIST_ELE_T * pool ) {
596 65643615 : return DLIST_(idx_insert_after)( join, (ulong)(ele-pool), (ulong)(prev-pool), pool );
597 65643615 : }
598 :
599 : static inline DLIST_(t) *
600 : DLIST_(ele_replace)( DLIST_(t) * join,
601 : DLIST_ELE_T * ele,
602 : DLIST_ELE_T * old,
603 65623485 : DLIST_ELE_T * pool ) {
604 65623485 : return DLIST_(idx_replace)( join, (ulong)(ele-pool), (ulong)(old-pool), pool );
605 65623485 : }
606 :
607 : static inline DLIST_(t) *
608 : DLIST_(ele_remove)( DLIST_(t) * join,
609 : DLIST_ELE_T * ele,
610 149987385 : DLIST_ELE_T * pool ) {
611 149987385 : return DLIST_(idx_remove)( join, (ulong)(ele-pool), pool );
612 149987385 : }
613 :
614 : FD_FN_CONST static inline DLIST_ELE_T *
615 : DLIST_(iter_ele)( DLIST_(iter_t) iter,
616 : DLIST_(t) const * join,
617 150008760 : DLIST_ELE_T * pool ) {
618 150008760 : (void)join; (void)pool;
619 150008760 : return pool + iter;
620 150008760 : }
621 :
622 : FD_FN_CONST static inline DLIST_ELE_T const *
623 : DLIST_(iter_ele_const)( DLIST_(iter_t) iter,
624 : DLIST_(t) const * join,
625 224982660 : DLIST_ELE_T const * pool ) {
626 224982660 : (void)join; (void)pool;
627 224982660 : return pool + iter;
628 224982660 : }
629 :
630 : FD_PROTOTYPES_END
631 :
632 : #endif
633 :
634 : #if DLIST_IMPL_STYLE==1 /* need prototypes */
635 :
636 : FD_PROTOTYPES_BEGIN
637 :
638 : FD_FN_CONST ulong DLIST_(align) ( void );
639 : FD_FN_CONST ulong DLIST_(footprint)( void );
640 : void * DLIST_(new) ( void * shmem );
641 : DLIST_(t) * DLIST_(join) ( void * shdlist );
642 : void * DLIST_(leave) ( DLIST_(t) * join );
643 : void * DLIST_(delete) ( void * shdlist );
644 :
645 : FD_FN_PURE int
646 : DLIST_(verify)( DLIST_(t) const * join,
647 : ulong ele_cnt,
648 : DLIST_ELE_T const * pool );
649 :
650 : FD_PROTOTYPES_END
651 :
652 : #else /* need implementations */
653 :
654 : #if DLIST_IMPL_STYLE==0 /* local only */
655 : #define DLIST_IMPL_STATIC FD_FN_UNUSED static
656 : #else
657 : #define DLIST_IMPL_STATIC
658 : #endif
659 :
660 : FD_PROTOTYPES_BEGIN
661 :
662 0 : FD_FN_CONST DLIST_IMPL_STATIC ulong DLIST_(align) ( void ) { return alignof(DLIST_(t)); }
663 0 : FD_FN_CONST DLIST_IMPL_STATIC ulong DLIST_(footprint)( void ) { return sizeof( DLIST_(t)); }
664 :
665 : DLIST_IMPL_STATIC void *
666 9 : DLIST_(new)( void * shmem ) {
667 :
668 9 : if( FD_UNLIKELY( !shmem ) ) {
669 3 : FD_LOG_WARNING(( "NULL shmem" ));
670 3 : return NULL;
671 3 : }
672 :
673 6 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, DLIST_(align)() ) ) ) {
674 3 : FD_LOG_WARNING(( "misaligned shmem" ));
675 3 : return NULL;
676 3 : }
677 :
678 : // Note: Guaranteed non-zero and not otherwise used
679 : //ulong footprint = DLIST_(footprint)();
680 : //if( FD_UNLIKELY( !footprint ) ) {
681 : // FD_LOG_WARNING(( "bad footprint" ));
682 : // return NULL;
683 : //}
684 :
685 3 : DLIST_(private_t) * dlist = (DLIST_(private_t) *)shmem;
686 :
687 3 : dlist->head = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
688 3 : dlist->tail = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
689 :
690 3 : FD_COMPILER_MFENCE();
691 3 : FD_VOLATILE( dlist->magic ) = DLIST_MAGIC;
692 3 : FD_COMPILER_MFENCE();
693 :
694 3 : return shmem;
695 6 : }
696 :
697 : DLIST_IMPL_STATIC DLIST_(t) *
698 12 : DLIST_(join)( void * shdlist ) {
699 12 : DLIST_(private_t) * dlist = (DLIST_(private_t) *)shdlist;
700 :
701 12 : if( FD_UNLIKELY( !dlist ) ) {
702 3 : FD_LOG_WARNING(( "NULL shdlist" ));
703 3 : return NULL;
704 3 : }
705 :
706 9 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)dlist, DLIST_(align)() ) ) ) {
707 3 : FD_LOG_WARNING(( "misaligned shdlist" ));
708 3 : return NULL;
709 3 : }
710 :
711 6 : if( FD_UNLIKELY( dlist->magic!=DLIST_MAGIC ) ) {
712 3 : FD_LOG_WARNING(( "bad magic" ));
713 3 : return NULL;
714 3 : }
715 :
716 3 : return (DLIST_(t) *)dlist;
717 6 : }
718 :
719 : DLIST_IMPL_STATIC void *
720 6 : DLIST_(leave)( DLIST_(t) * join ) {
721 :
722 6 : if( FD_UNLIKELY( !join ) ) {
723 3 : FD_LOG_WARNING(( "NULL join" ));
724 3 : return NULL;
725 3 : }
726 :
727 3 : return (void *)join;
728 6 : }
729 :
730 : DLIST_IMPL_STATIC void *
731 12 : DLIST_(delete)( void * shdlist ) {
732 12 : DLIST_(private_t) * dlist = (DLIST_(private_t) *)shdlist;
733 :
734 12 : if( FD_UNLIKELY( !dlist ) ) {
735 3 : FD_LOG_WARNING(( "NULL shdlist" ));
736 3 : return NULL;
737 3 : }
738 :
739 9 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)dlist, DLIST_(align)() ) ) ) {
740 3 : FD_LOG_WARNING(( "misaligned shdlist" ));
741 3 : return NULL;
742 3 : }
743 :
744 6 : if( FD_UNLIKELY( dlist->magic!=DLIST_MAGIC ) ) {
745 3 : FD_LOG_WARNING(( "bad magic" ));
746 3 : return NULL;
747 3 : }
748 :
749 3 : FD_COMPILER_MFENCE();
750 3 : FD_VOLATILE( dlist->magic ) = 0UL;
751 3 : FD_COMPILER_MFENCE();
752 :
753 3 : return shdlist;
754 6 : }
755 :
756 : FD_FN_PURE DLIST_IMPL_STATIC int
757 : DLIST_(verify)( DLIST_(t) const * join,
758 : ulong ele_cnt,
759 187477728 : DLIST_ELE_T const * pool ) {
760 :
761 2962643493 : # define DLIST_TEST(c) do { \
762 2962643493 : if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return -1; } \
763 2962643493 : } while(0)
764 :
765 : /* Validate input args */
766 :
767 187477728 : DLIST_TEST( join );
768 187477728 : DLIST_TEST( ele_cnt<=DLIST_(ele_max)() );
769 187477728 : DLIST_TEST( (!!pool) | (!ele_cnt) );
770 :
771 : /* Iterate forward through the dlist, validating as we go */
772 :
773 187477728 : DLIST_(private_t) const * dlist = DLIST_(private_const)( join );
774 :
775 187477728 : DLIST_TEST( dlist->magic==DLIST_MAGIC );
776 :
777 187477728 : ulong rem = ele_cnt;
778 187477728 : ulong prev_idx = DLIST_(private_idx_null)();
779 187477728 : ulong ele_idx = DLIST_(private_idx)( dlist->head );
780 862562679 : while( !DLIST_(private_idx_is_null)( ele_idx ) ) {
781 :
782 : /* Visit ele_idx */
783 :
784 675084951 : DLIST_TEST( rem ); rem--; /* Test for cycles */
785 675084951 : DLIST_TEST( ele_idx<ele_cnt ); /* Test valid ele_idx */
786 675084951 : DLIST_TEST( DLIST_(private_idx)( pool[ ele_idx ].DLIST_PREV )==prev_idx ); /* Test reverse link integrity */
787 :
788 : /* Advance to next element */
789 :
790 675084951 : prev_idx = ele_idx;
791 675084951 : ele_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_NEXT );
792 675084951 : }
793 :
794 187477728 : DLIST_TEST( DLIST_(private_idx)( dlist->tail )==prev_idx );
795 :
796 187477728 : # undef DLIST_TEST
797 :
798 187477728 : return 0;
799 187477728 : }
800 :
801 : FD_PROTOTYPES_END
802 :
803 : #undef DLIST_IMPL_STATIC
804 :
805 : #endif
806 :
807 : #undef DLIST_
808 :
809 : #undef DLIST_IMPL_STYLE
810 : #undef DLIST_MAGIC
811 : #undef DLIST_NEXT
812 : #undef DLIST_PREV
813 : #undef DLIST_IDX_T
814 : #undef DLIST_ELE_T
815 : #undef DLIST_NAME
|