#ifndef POLLFD_RBHASH_H #define POLLFD_RBHASH_H /* Generated by rbhash.cpppp using command cpppp -p 'max_bits=16' \ -p 'namespace=pollfd_rbhash' \ -p '@default_search_args=('\''int search_fd'\'')' \ -p 'default_search_cmp=search_fd - ((struct pollfd*)rbhash)[(int)node_id-1-(int)capacity].fd' \ rbhash.cpppp \ -o 'public=pollfd_rbhash.h' \ -o pollfd_rbhash.c */ #include #include #include #include #include #include /* MAX_TREE_HEIGHT is the maximum number of nodes from root to leaf in any * correctly balanced tree. The exact formula for the maximum height (including * root node) is floor(2*log2(N/2+1)) for a tree of N nodes. */ #define POLLFD_RBHASH_MAX_ELEMENTS_8 0x7F #define POLLFD_RBHASH_MAX_TREE_HEIGHT_8 12 #define POLLFD_RBHASH_MAX_ELEMENTS_16 0x7FFF #define POLLFD_RBHASH_MAX_TREE_HEIGHT_16 28 /* This macro tells you the word offset (treating rbhash as an array of words) * of the first hash bucket. */ #define POLLFD_RBHASH_TABLE_WORD_OFS(capacity) ( (capacity)*2 + 2 ) /* This macro selects the word size needed to index 'capacity' number of * user elements. */ #define POLLFD_RBHASH_SIZEOF_WORD(capacity) ( \ (capacity) <= POLLFD_RBHASH_MAX_ELEMENTS_8? 1 : \ 2 \ ) /* This macro defines the total size (in bytes) of the rbhash storage * for a given number of elements and buckets. This does not include * the user's elements themselves, since those are whatever size the * user wants them to be, and rbhash doesn't need to know. */ #define POLLFD_RBHASH_SIZEOF(capacity, buckets) ( \ POLLFD_RBHASH_SIZEOF_WORD(capacity) \ * ( POLLFD_RBHASH_TABLE_WORD_OFS(capacity) + buckets ) \ ) /* Several functions can operate on a "path", which is a list of * references starting at the bucket and ending at a tree node. * The path is allocated to the maximum depth that a tree of that * word-bits-size could reach. Since this drastically affects the * amount of stack used, a struct is declared for each word-bit size. * * The structs each record their length so that they can be passed * interchangably to the functions. You could even allocate custom * lengths with alloca, but that seems overcomplicated. */ struct pollfd_rbhash_path_8 { uint8_t len, lim; size_t refs[POLLFD_RBHASH_MAX_TREE_HEIGHT_8]; }; inline void pollfd_rbhash_path_8_init(struct pollfd_rbhash_path_8 *p) { p->len= 0; p->lim= POLLFD_RBHASH_MAX_TREE_HEIGHT_8; } struct pollfd_rbhash_path_16 { uint8_t len, lim; size_t refs[POLLFD_RBHASH_MAX_TREE_HEIGHT_16]; }; inline void pollfd_rbhash_path_16_init(struct pollfd_rbhash_path_16 *p) { p->len= 0; p->lim= POLLFD_RBHASH_MAX_TREE_HEIGHT_16; } // Different template output may end up with different structs claiming // the name of pollfd_rbhash_path, but that should be OK. typedef struct pollfd_rbhash_path_16 pollfd_rbhash_path; #define pollfd_rbhash_path_init(p) pollfd_rbhash_path_16_init(p) // Iterate one or more places through the R/B tree of a bucket, updating 'path' extern size_t pollfd_rbhash_path_step(void *rbhash, size_t capacity, pollfd_rbhash_path *path, int ofs); // Exchange the tree node of one node_id (at the end of 'path') for another node_id extern size_t pollfd_rbhash_path_swap(void *rbhash, size_t capacity, pollfd_rbhash_path *path, size_t new_node_id); // implementation detail used to reduce size of inline functions extern size_t pollfd_rbhash_capacity_bounds_assertion(size_t capacity); // Add a node_id at the end of 'path', and balance the tree if needed extern size_t pollfd_rbhash_path_insert_8(uint8_t *rbhash, pollfd_rbhash_path *path, size_t node_id); extern size_t pollfd_rbhash_path_insert_16(uint16_t *rbhash, pollfd_rbhash_path *path, size_t node_id); inline size_t pollfd_rbhash_path_insert(void *rbhash, size_t capacity, pollfd_rbhash_path *path, size_t node) { return (capacity <= POLLFD_RBHASH_MAX_ELEMENTS_8)? pollfd_rbhash_path_insert_8((uint8_t*) rbhash, path, node) : (capacity <= POLLFD_RBHASH_MAX_ELEMENTS_16)? pollfd_rbhash_path_insert_16((uint16_t*) rbhash, path, node) : pollfd_rbhash_capacity_bounds_assertion(capacity); } // Remove the node_id from the end of 'path', and balance the tree if needed extern size_t pollfd_rbhash_path_delete_8(uint8_t *rbhash, pollfd_rbhash_path *path); extern size_t pollfd_rbhash_path_delete_16(uint16_t *rbhash, pollfd_rbhash_path *path); inline size_t pollfd_rbhash_path_delete(void *rbhash, size_t capacity, pollfd_rbhash_path *path) { return (capacity <= POLLFD_RBHASH_MAX_ELEMENTS_8)? pollfd_rbhash_path_delete_8((uint8_t*) rbhash, path) : (capacity <= POLLFD_RBHASH_MAX_ELEMENTS_16)? pollfd_rbhash_path_delete_16((uint16_t*) rbhash, path) : pollfd_rbhash_capacity_bounds_assertion(capacity); } // Find a node_id matching the search criteria and fill in the 'path' to it, else return 0 extern size_t pollfd_rbhash_find_path(void *rbhash, size_t capacity, pollfd_rbhash_path *path, size_t bucket_idx, int search_fd); // Simplified search for node_id, without building a path extern size_t pollfd_rbhash_find(void *rbhash, size_t capacity, size_t bucket_idx, int search_fd); // Insert a node_id, unless one already matches the search criteria extern size_t pollfd_rbhash_insert(void *rbhash, size_t capacity, size_t node_id, size_t bucket_idx, int search_fd); // Delete a node matching the search criteria extern size_t pollfd_rbhash_delete(void *rbhash, size_t capacity, size_t bucket_idx, int search_fd); // Handy for gdb: // p pollfd_rbhash_print(rbhash, capacity, buckets, NULL, NULL, stdout) extern void pollfd_rbhash_print(void *rbhash, size_t capacity, size_t n_buckets, void (*print_node)(void*,size_t,FILE*), void* userdata, FILE *out); #endif