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.
 |