mirror of https://github.com/openssl/openssl.git
				
				
				
			
		
			
	
	
		
			156 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
		
		
			
		
	
	
			156 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
|  | The sparse_array.c file contains an implementation of a sparse array that | ||
|  | attempts to be both space and time efficient. | ||
|  | 
 | ||
|  | The sparse array is represented using a tree structure.  Each node in the | ||
|  | tree contains a block of pointers to either the user supplied leaf values or | ||
|  | to another node. | ||
|  | 
 | ||
|  | There are a number of parameters used to define the block size: | ||
|  | 
 | ||
|  |     OPENSSL_SA_BLOCK_BITS   Specifies the number of bits covered by each block | ||
|  |     SA_BLOCK_MAX            Specifies the number of pointers in each block | ||
|  |     SA_BLOCK_MASK           Specifies a bit mask to perform modulo block size | ||
|  |     SA_BLOCK_MAX_LEVELS     Indicates the maximum possible height of the tree | ||
|  | 
 | ||
|  | These constants are inter-related: | ||
|  |     SA_BLOCK_MAX        = 2 ^ OPENSSL_SA_BLOCK_BITS | ||
|  |     SA_BLOCK_MASK       = SA_BLOCK_MAX - 1 | ||
|  |     SA_BLOCK_MAX_LEVELS = number of bits in size_t divided by | ||
|  |                           OPENSSL_SA_BLOCK_BITS rounded up to the next multiple | ||
|  |                           of OPENSSL_SA_BLOCK_BITS | ||
|  | 
 | ||
|  | OPENSSL_SA_BLOCK_BITS can be defined at compile time and this overrides the | ||
|  | built in setting. | ||
|  | 
 | ||
|  | As a space and performance optimisation, the height of the tree is usually | ||
|  | less than the maximum possible height.  Only sufficient height is allocated to | ||
|  | accommodate the largest index added to the data structure. | ||
|  | 
 | ||
|  | The largest index used to add a value to the array determines the tree height: | ||
|  | 
 | ||
|  |         +----------------------+---------------------+ | ||
|  |         | Largest Added Index  |   Height of Tree    | | ||
|  |         +----------------------+---------------------+ | ||
|  |         | SA_BLOCK_MAX     - 1 |          1          | | ||
|  |         | SA_BLOCK_MAX ^ 2 - 1 |          2          | | ||
|  |         | SA_BLOCK_MAX ^ 3 - 1 |          3          | | ||
|  |         | ...                  |          ...        | | ||
|  |         | size_t max           | SA_BLOCK_MAX_LEVELS | | ||
|  |         +----------------------+---------------------+ | ||
|  | 
 | ||
|  | The tree height is dynamically increased as needed based on additions. | ||
|  | 
 | ||
|  | An empty tree is represented by a NULL root pointer.  Inserting a value at | ||
|  | index 0 results in the allocation of a top level node full of null pointers | ||
|  | except for the single pointer to the user's data (N = SA_BLOCK_MAX for | ||
|  | breviety): | ||
|  | 
 | ||
|  |         +----+ | ||
|  |         |Root| | ||
|  |         |Node| | ||
|  |         +-+--+ | ||
|  |           | | ||
|  |           | | ||
|  |           | | ||
|  |           v | ||
|  |         +-+-+---+---+---+---+ | ||
|  |         | 0 | 1 | 2 |...|N-1| | ||
|  |         |   |nil|nil|...|nil| | ||
|  |         +-+-+---+---+---+---+ | ||
|  |           | | ||
|  |           | | ||
|  |           | | ||
|  |           v | ||
|  |         +-+--+ | ||
|  |         |User| | ||
|  |         |Data| | ||
|  |         +----+ | ||
|  |     Index 0 | ||
|  | 
 | ||
|  | 
 | ||
|  | Inserting at element 2N+1 creates a new root node and pushes down the old root | ||
|  | node.  It then creates a second second level node to hold the pointer to the | ||
|  | user's new data: | ||
|  | 
 | ||
|  |         +----+ | ||
|  |         |Root| | ||
|  |         |Node| | ||
|  |         +-+--+ | ||
|  |           | | ||
|  |           | | ||
|  |           | | ||
|  |           v | ||
|  |         +-+-+---+---+---+---+ | ||
|  |         | 0 | 1 | 2 |...|N-1| | ||
|  |         |   |nil|   |...|nil| | ||
|  |         +-+-+---+-+-+---+---+ | ||
|  |           |       | | ||
|  |           |       +------------------+ | ||
|  |           |                          | | ||
|  |           v                          v | ||
|  |         +-+-+---+---+---+---+      +-+-+---+---+---+---+ | ||
|  |         | 0 | 1 | 2 |...|N-1|      | 0 | 1 | 2 |...|N-1| | ||
|  |         |nil|   |nil|...|nil|      |nil|   |nil|...|nil| | ||
|  |         +-+-+---+---+---+---+      +---+-+-+---+---+---+ | ||
|  |           |                              | | ||
|  |           |                              | | ||
|  |           |                              | | ||
|  |           v                              v | ||
|  |         +-+--+                         +-+--+ | ||
|  |         |User|                         |User| | ||
|  |         |Data|                         |Data| | ||
|  |         +----+                         +----+ | ||
|  |     Index 0                       Index 2N+1 | ||
|  | 
 | ||
|  | 
 | ||
|  | The nodes themselves are allocated in a sparse manner.  Only nodes which exist | ||
|  | along a path from the root of the tree to an added leaf will be allocated. | ||
|  | The complexity is hidden and nodes are allocated on an as needed basis. | ||
|  | Because the data is expected to be sparse this doesn't result in a large waste | ||
|  | of space. | ||
|  | 
 | ||
|  | Values can be removed from the sparse array by setting their index position to | ||
|  | NULL.  The data structure does not attempt to reclaim nodes or reduce the | ||
|  | height of the tree on removal.  For example, now setting index 0 to NULL would | ||
|  | result in: | ||
|  | 
 | ||
|  |         +----+ | ||
|  |         |Root| | ||
|  |         |Node| | ||
|  |         +-+--+ | ||
|  |           | | ||
|  |           | | ||
|  |           | | ||
|  |           v | ||
|  |         +-+-+---+---+---+---+ | ||
|  |         | 0 | 1 | 2 |...|N-1| | ||
|  |         |   |nil|   |...|nil| | ||
|  |         +-+-+---+-+-+---+---+ | ||
|  |           |       | | ||
|  |           |       +------------------+ | ||
|  |           |                          | | ||
|  |           v                          v | ||
|  |         +-+-+---+---+---+---+      +-+-+---+---+---+---+ | ||
|  |         | 0 | 1 | 2 |...|N-1|      | 0 | 1 | 2 |...|N-1| | ||
|  |         |nil|nil|nil|...|nil|      |nil|   |nil|...|nil| | ||
|  |         +---+---+---+---+---+      +---+-+-+---+---+---+ | ||
|  |                                          | | ||
|  |                                          | | ||
|  |                                          | | ||
|  |                                          v | ||
|  |                                        +-+--+ | ||
|  |                                        |User| | ||
|  |                                        |Data| | ||
|  |                                        +----+ | ||
|  |                                   Index 2N+1 | ||
|  | 
 | ||
|  | 
 | ||
|  | Accesses to elements in the sparse array take O(log n) time where n is the | ||
|  | largest element.  The base of the logarithm is SA_BLOCK_MAX, so for moderately | ||
|  | small indices (e.g. NIDs), single level (constant time) access is achievable. | ||
|  | Space usage is O(minimum(m, n log(n)) where m is the number of elements in the | ||
|  | array. | ||
|  | 
 | ||
|  | Note: sparse arrays only include pointers to types.  Thus, SPARSE_ARRAY_OF(char) | ||
|  | can be used to store a string. |