nginx provides advanced data structure like array, list, queue, rb tree which are specific to nginx, like array, it can grow dynamically, but not support free element back to array, list actually is list of several arrays.
array
array will increase dynamically if no more space for use. but it’s better to allocate enough element at initialization. as when increase array size, move original data from old array to new array, so that new array has whole data, can iterate it to get all elements. but the new address may be not equal old(a->elts), the original array is not freed at all, because user still points to old part and access the element that get from old array as user has that pointer, new element is created from the new array.
e1 and e2 still point to old array after double array e3 and e4 point to new array, but the first two elements still have the same info with old array
limitation
NOT support decrease array size
NOT support free element back to array
Only support free the whole array
data structure
1 2 3 4 5 6 7 8
typedefstruct { void *elts; /* address of nalloc * size */ ngx_uint_t nelts; /* in use counter */ size_t size; /* size of the element */ ngx_uint_t nalloc; /* total elements can be used */ ngx_pool_t *pool; } ngx_array_t;
API
1 2 3 4 5 6 7 8 9
/* example to use: */ typedefstruct { ngx_int_t id; } my_element_t;
nginx list is a list of array(fixed size, nalloc * size) each list part is an array(fixed size), list stores the header part and last part.
when request an element from the list, it checks if the last array is used up or not, if not, gets it, otherwise creates a new array, get one element from it.
Note
list is good for dynamic element size, but not good for free ops
The array in the list is used once only when it’s last later on even you free elements back to the array, if it’s not last anymore, never use it anymore.
Never delete element of list as it’s dangerous as it related data movement
data structure
1 2 3 4 5 6 7 8 9 10 11 12 13
structngx_list_part_s { void *elts; /* header of the continuos memory */ ngx_uint_t nelts; /* used counter of this part */ ngx_list_part_t *next; /* next part of the list */ };
typedefstruct { ngx_list_part_t *last; /* the last part */ ngx_list_part_t part; /* the first part */ size_t size; /* size of each element */ ngx_uint_t nalloc; /* a list can have several parts, each part can take nalloc continuous memory */ ngx_pool_t *pool; } ngx_list_t;
/* 4 elements for each array in the list */ ngx_list_t *mylist = ngx_list_create(pool, 4, sizeof(my_element_t)); my_element_t *node = ngx_list_push(mylist); node->id = 2;
/* the iteration through the list: */ part = &mylist.part; data = part->elts;
for (i = 0 ;; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; data = part->elts; i = 0; } ...data[i] ... }
queue
ngx_queue_t is just for linking, data area must be allocated /freed by user, ngx_queue is bidirectional list.
when delete/insert a node, the tree may be rotated to make the depth small,hence quick search
binary:
each node have at most two child left/right
search:
the left node is smaller than its parent
the right node is large than it’s parent
as it’s sorted, so each node must have a key(integer)
Note By default, key must be unique, if insert a node with same key the previous one will be replaced, BUT you can define custom ‘insert’ method to overwrite the default, hence allow two nodes can have the same key in the tree.
structngx_rbtree_s { /* root can be change after insert or delete as it may rotate ngx_rbtree_node_t *root; /* sentinel is a node with black color, if node has no left/right child, set * its left/right child with sentinel to meet rb tree required */ ngx_rbtree_node_t *sentinel; /* 'insert' method, how to insert a node in the tree * you can define your own to allow same key * actually nginx provides such way to us */ ngx_rbtree_insert_pt insert; };
Hash is for quick searching, elements are distributed at different buckets based on hash_value % bucket_count, inside each bucket, it’s an array, compared one by one, so for hash, the important are hash function and bucket counter to distribute element evenly to reduce conflict. nginx has built-in hash function and bucket count calculated by nginx itself.
typedefstruct { /* each element is <key, value> pair * key: name * value: any value */ void *value; /* as you can see each element is not fixed!! * each element size = sizeof(void*) + sizeof(u_short) + 1 + len */ u_short len; u_char name[1]; } ngx_hash_elt_t;
/* ngx_hash_combined_t is not a must for hash, but a helper for nginx to use for some case * to create different hashes for different types, like this * server_name abc.com *.example.com test.com.*; */ typedefstruct { ngx_hash_t hash; /* abc.com */ ngx_hash_wildcard_t *wc_head; /* *.example.com */ ngx_hash_wildcard_t *wc_tail; /* test.com.* */ } ngx_hash_combined_t;
typedefstruct { ngx_hash_t *hash; /* passed in if NULL, created inside init function */ ngx_hash_key_pt key; ngx_uint_t max_size; /* max bucket count, how many buckets */ /* the real bucket count is calculated based on hash keys */ ngx_uint_t bucket_size; /* how many bytes used by each bucket */
char *name; ngx_pool_t *pool; ngx_pool_t *temp_pool; /* used for wildcard hash_init to allocate memory for checking conflict */ } ngx_hash_init_t;
/* helper structure to add keys to the keys_arrays * it supports key conflict checking * then pass each key array to hash_init */ typedefstruct { ngx_uint_t hsize; /* bucket number */ ngx_pool_t *pool; ngx_pool_t *temp_pool;
/* key array of exact match like abc.com*/ ngx_array_t keys; /* key array, each of ngx_hash_key_t */ ngx_array_t *keys_hash; /* bucket array, each bucket is array of ngx_str_t temporary used for checking conflict when creating ngx_hash_key_t */ /* key array of wc_head like *.test.com */ ngx_array_t dns_wc_head; ngx_array_t *dns_wc_head_hash;
/* key array of wc_tail like abc.test.* */ ngx_array_t dns_wc_tail; ngx_array_t *dns_wc_tail_hash; } ngx_hash_keys_arrays_t;