red_black is a pair of C libraries that provide a simple interface for
implementing (ordered) sets
and associative arrays. red_black
containers house their data in binary tree structures which are
height-balanced
by adhering to a tried-and-true set of rules--the
red-black properties.
With proper preservation of these properties, the RedBlackTree and RedBlackHMap
interfaces are able to provide the following operations:
- insert
- random access
- delete
- (ordered) traverse
- shallow copy
- various set operations
with competitive
worst-case performance guarantees.
The red-black system of balance was chosen to provide greater
responsiveness to mutations than more rigid constructs, such as
AVL balance factor,
without compromising a guaranteed logarithmic height, as is often the case with simpler
methods like treap priority or
splaying.
red_black distinguishes itself from many other balanced tree/hash table-balanced tree
hybrid implementations by preferring finer casewise granularity
over heavier abstraction throughout container accesses.
- GNU Make
- gcc >= 4.8.1 (C99 and C++11 support for benchmarking against
stdcontainers) - ruby >= 1.9 (for Unity test runner generator scripts)
$ git clone https://github.com/wrpaape/red_black.git
$ cd red_black
$ make
Will build static and shared versions of the RedBlackTree and RedBlackHMap
libraries in the static and shared directories, as well as unit tests
and example executables in the bin directory.
$ make run_tests
will run all unit tests, and
$ make clean
will delete all generated files.
- Build the
red_blacklibraries - If key order is required or memory is constrained,
use a
RedBlackTree.
If key order is not required and memory can be spared for extra speed, use aRedBlackHMap.
-
Add
#include "red_black_tree.h"to the top of your source file.
The file must be included with no path prefix since it will be including other module headers by paths relative to theincludedirectory. -
Implement an ordered set, associative array, or poor man's priority queue with the provided interface.
Keys are completely opaque to theRedBlackTreeimplementation.
Accordingly, aRedBlackComparatormust be provided at initialization to determine ordering.
Internally keys are stored asconst void *.
As a consequence, keys requiring more thansizeof(void *)bytes of storage must be accessible by pointer, and their memory must be managed externally. The same goes for keys where a access via anupdateorgetmight be useful, as only a copy of the actual stored key will be provided.
For instance, if thesizeof(int)on my machine is 4 bytes, and thesizeof(void *)is at least 8 bytes, then I could store key-value pairs ofints directly in myRedBlackTreelike so:struct KeyValue { int index; int counter; }; ... struct KeyValue mapping = { .index = 9001, .counter = 0 }; ... int status = red_black_tree_insert(&tree, *((void **) &mapping)); assert(status >= 0);
and not have to worry about their memory management. However, if I wanted to
update the counter of a certain member index, the following would have no effect
on the stored key:
```
struct KeyValue mapping;
struct KeyValue fetched;
...
mapping.index = 9001;
...
if (red_black_tree_fetch(&tree,
*((void **) &mapping),
(void **) &fetched)) {
/* key with 'index' of 9001 exists in 'tree' */
++(fetched.counter); /* tree mapping NOT updated!, operates on copy */
}
```
In order to properly update tree, I would have to follow up with a set and clobber
the old value:
```
if (red_black_tree_fetch(&tree,
*((void **) &mapping),
(void **) &fetched)) {
/* key with 'index' of 9001 exists in 'tree' */
++(fetched.counter); /* increment counter */
/* overwrite old value */
red_black_tree_set(&tree,
*((void **) &fetched));
}
```
- Compile your source code and link to either
static/libred_black_tree.{a,lib}orshared/libred_black_tree.{so,dylib,dll}.
Remember to add theincludedirectory to your preprocessor's search path either via compiler flag (i.e.-Ipath/to/include) or environment variable (i.e.CPATH=path/to/include:$CPATH).
-
Add
#include "red_black_hmap.h"to the top of your source file.
The file must be included with no path prefix since it will be including other module headers by paths relative to theincludedirectory. -
Implement an unordered set or associative array with the provided interface.
RedBlackHMapcan be thought of as a hash table ofRedBlackTrees.
Accesses are made by hashing input keys of arbitrarylengthto a fixed word, which is then used to locate the bucket tree where it should reside. Bucket trees are ordered by this hash word, falling back on an unsigned memory comparison in case of equal (colliding) values. This method of key organization imposes three restrictions on the application developer:- Keys must be input by their address and
length. - Key addresses must reference valid memory if
lengthis nonzero.
Keys of zerolengthwill emit identical hash values and will not be compared by memory. Accordingly, keys of all addresses are considered equivalent toRedBlackHMapif theirlengthis zero, and only one zero-lengthkey may reside in the container at any time. - Keys must be bytewise-distinguishable.
Keys of all types are handled as byte strings byRedBlackHMap. As a result, care must be taken withstructkeys with fields having different alignments. Suchstructs must be packed, be cleared before having their fields set, or be passed in such a way that forlengthbytes from the input address no padding will be encountered.
The following snippet:
#define NAME_LENGTH_MAX 20 #define SPECIES_CAT 0 ... struct Pet { char name[NAME_LENGTH_MAX + 1]; int species; }; ... struct Pet *charley_ptr = malloc(sizeof(*charley_ptr)); assert(charley_ptr != NULL); /* insert Charley into set of 'Pet's */ (void) strcpy(&charley_ptr->name[0], "Charley"); charley_ptr->species = SPECIES_CAT; int status = red_black_hmap_insert(&my_pets, (void *) charley_ptr, sizeof(*charley_ptr)); assert(status >= 0); /* check if Charley is a member of 'my_pets' */ struct Pet charly = { .name = "Charley", .species = SPECIES_CAT }; if (red_black_hmap_find(&my_pets, (void *) &charley, sizeof(charley))) { /* found Charley */ ... }may exhibit unexpected behavior. The
namefield will not always be completely filled for eachPetentry, and even if all my pets had 20-character (plus one for'\0'terminator) names, unused padding will still be added after thenamefield to ensure thatspeciesbegins on an address aligned tosizeof(int). As such, thefindroutine above may fail to retrieve "Charley" the cat frommy_pets.
If there is no need to distinguishPetentries byspecies, the simple solution is to pass the string length of entrynames as their keylength. But if I needed to track "Charley" the frog as well I would need to swap the order of myPetfields and passsizeof(pet.species) + strlen(pet.name)as keylengthto avoid padding bytes, or justmemset(&pet, 0, sizeof(pet))before setting fields and passsizeof(pet)as keylength. - Keys must be input by their address and
-
Compile your source code and link to either
static/libred_black_hmap.{a,lib}orshared/libred_black_hmap.{so,dylib,dll}.
Remember to add theincludedirectory to your preprocessor's search path either via compiler flag (i.e.-Ipath/to/include) or environment variable (i.e.CPATH=path/to/include:$CPATH).
- Creation
- Destruction
- Insertion
- Deletion
- Traversal
- Random Access
- Clone
- Inspection
- Set Comparison
- Set Building Operations
init
void
red_black_tree_init(RedBlackTree *const restrict tree,
const RedBlackComparator comparator);
bool
red_black_hmap_init(RedBlackHMap *const restrict map);
initialize an empty red_black container
red_black_tree_init requires a key comparator
(see RedBlackComparator) to determine ordering
and performs no heap allocations, so it need not be destroyed until
the first successful insertion is made. red_black_hmap_init will
need to allocate an array of empty buckets, and so has an opportunity to fail --
a return value of true indicates success, false indicates failure.
destroy
void
red_black_tree_destroy(RedBlackTree *const restrict tree);
void
red_black_hmap_destroy(RedBlackHMap *const restrict map);
free all internal allocations
RedBlackTree can and RedBlackHMap must store its keys by address
and not by copy in order to adequately handle data of all sizes.
Hence, a solution to maintaining persistent keys might be to allocate them
on the heap as they are inserted. If an application loses reference to
such keys, their memory will be leaked when their container is destroyed
unless a final
traversal is made to free them beforehand.
insert
int
red_black_tree_insert(RedBlackTree *const restrict tree,
const void *const key);
int
red_black_hmap_insert(RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
insert key into the container
If key is not already present in the container, an insertion is attempted.
If the insertion succeeds, 1 is returned.
If key is already present, the container is untouched and 0 is returned.
If a memory allocation failure occurs, -1 is returned.
add
bool
red_black_tree_add(RedBlackTree *const restrict tree,
const void *const key);
bool
red_black_hmap_add(RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
insert GUARANTEED UNIQUE key into the container
Some assumptions can be made if key is unique that will slightly
speed up insertion, useful for initial provisioning.
If the insertion succeeds, true is returned.
If a memory allocation failure occurs, false is returned.
This call will segfault or worse if key is already present in the container.
put
int
red_black_tree_put(RedBlackTree *const restrict tree,
const void *const key);
int
red_black_hmap_put(RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
insert key into the container--if key is already present,
overwrite the old value
If the insertion suceeds, 1 is returned.
If key overwrites a matching key, 0 is returned.
If a memory allocation failure occurs, -1 is returned.
update
int
red_black_tree_update_set(RedBlackTree *const restrict tree,
const void *const key,
void **const restrict old_ptr);
int
red_black_tree_update_get(RedBlackTree *const restrict tree,
const void *const key,
void **const restrict old_ptr);
int
red_black_hmap_update_set(RedBlackHMap *const restrict map,
const void *const key,
const size_t length,
void **const restrict old_ptr);
int
red_black_hmap_update_get(RedBlackHMap *const restrict map,
const void *const key,
const size_t length,
void **const restrict old_ptr);
insert key into the container--if key is already present,
set old_ptr to the member key's value
update_set will overwrite a matching key in the container with
key (like a put) before setting old_ptr, whereas update_get
will leave the container key untouched.
If the insertion suceeds, 1 is returned, and old_ptr is not set.
If a key matching key is present, 0 is returned, and old_ptr is set to its value.
If a memory allocation failure occurs, -1 is returned.
delete
int
red_black_tree_delete(RedBlackTree *const restrict tree,
const void *const key);
int
red_black_tree_delete_min(RedBlackTree *const restrict tree);
int
red_black_tree_delete_max(RedBlackTree *const restrict tree);
int
red_black_hmap_delete(RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
delete key from the container
If key was present in the container, it will be removed and 1 is returned.
If key was not found, the container is untouched and 0 is returned.
drop
void
red_black_tree_drop(RedBlackTree *const restrict tree,
const void *const key);
void
red_black_tree_drop_min(RedBlackTree *const restrict tree);
void
red_black_tree_drop_max(RedBlackTree *const restrict tree);
void
red_black_hmap_drop(RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
delete GUARANTEED PRESENT key from the container
Some assumptions can be made if key a known member of the container
that will slightly speed up deletion.
This call will segfault or worse if key is not present in the container.
remove
int
red_black_tree_remove(RedBlackTree *const restrict tree,
const void *const key,
void **const restrict remove_ptr);
int
red_black_tree_remove_min(RedBlackTree *const restrict tree,
void **const restrict remove_ptr);
int
red_black_tree_remove_max(RedBlackTree *const restrict tree,
void **const restrict remove_ptr);
int
red_black_hmap_remove(RedBlackHMap *const restrict map,
const void *const key,
const size_t length,
void **const restrict remove_ptr);
delete key from the container--if key was found, set remove_ptr to its value
If key was deleted from the container, 1 is returned and remove_ptr is set.
If key was not found, the container is untouched, 0 is returned, and remove_ptr is not set.
pluck
void *
red_black_tree_pluck(RedBlackTree *const restrict tree,
const void *const key);
void *
red_black_tree_pluck_min(RedBlackTree *const restrict tree);
void *
red_black_tree_pluck_max(RedBlackTree *const restrict tree);
void *
red_black_hmap_pluck(RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
delete GUARANTEED PRESENT key from the container and return its value
Some assumptions can be made if key a known member of the container
that will slightly speed up deletion.
This call will segfault or worse is key is not present in the container.
init
void
red_black_tree_asc_itor_init(RedBlackTreeItor *const restrict itor,
const RedBlackTree *const restrict tree);
void
red_black_tree_desc_itor_init(RedBlackTreeItor *const restrict itor,
const RedBlackTree *const restrict tree);
void
red_black_hmap_itor_init(RedBlackHMapItor *const restrict itor,
const RedBlackHMap *const restrict map);
initialize a traversal Itor object to vist all keys of the container
RedBlackTreeItors may be initialized to traverse in ascending (asc, from "smallest" key to "largest")
or descending (desc, from "largest" key to "smallest") fashion.
Container mutations are not allowed throughout the useful lifespan of the Itor
(see next).
Neither Itor makes internal allocations, so no destructor function need be called
when finished.
next
bool
red_black_tree_itor_next(RedBlackTreeItor *const restrict itor,
void **const restrict key_ptr);
bool
red_black_hmap_itor_next(RedBlackHMapItor *const restrict itor,
void **const restrict key_ptr,
size_t *const restrict length_ptr);
visit the next key is the container if any remain
Following an init or reset, as long as keys remain, next will return true
and fetch the next key in the container by setting key_ptr (and length_ptr).
false is returned without setting the input pointer(s) once all member keys
have been traversed.
Since a stack of unvisted keys its maintained internally, container insertions and deletions
must be avoided throughout the useful lifespan of an Itor.
reset
void
red_black_tree_itor_reset(RedBlackTreeItor *const restrict itor,
const RedBlackTree *const restrict tree);
void
red_black_hmap_itor_reset(RedBlackHMapItor *const restrict itor,
const RedBlackHMap *const restrict map);
begin iteration on a new container
Itor must be initialized at least once prior to a reset. A reset does
not change the direction of iteration for RedBlackTreeItors.
find
bool
red_black_tree_find(const RedBlackTree *const restrict tree,
const void *const key);
bool
red_black_hmap_find(const RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
check if key is a member of the container
find returns true if key was found, false if it was not.
fetch
bool
red_black_tree_fetch(const RedBlackTree *const restrict tree,
const void *const key,
void **const restrict fetch_ptr);
bool
red_black_tree_fetch_min(const RedBlackTree *const restrict tree,
void **const restrict fetch_ptr);
bool
red_black_tree_fetch_max(const RedBlackTree *const restrict tree,
void **const restrict fetch_ptr);
bool
red_black_hmap_fetch(const RedBlackHMap *const restrict map,
const void *const key,
const size_t length,
void **const restrict fetch_ptr);
attempt to retrieve member key from the container
If key is found, store it in fetch_ptr and return true.
If key is not present, leave fetch_ptr unset and return false.
RedBlackTree provides fetch_min/fetch_max to retrieve the
smallest/largest key in tree without comparisons.
get
void *
red_black_tree_get(const RedBlackTree *const restrict tree,
const void *const key);
void *
red_black_tree_get_min(const RedBlackTree *const restrict tree);
void *
red_black_tree_get_max(const RedBlackTree *const restrict tree);
void *
red_black_hmap_get(const RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
retrieve GUARANTEED PRESENT key from the container
get returns member key comparing equal to input key (and length).
RedBlackTree provides get_min/get_max to retrieve the
smallest/largest key in tree without comparisons.
This call will segfault or worse if key is not present in the container
or if tree is empty in the case of get_min/get_max.
replace
bool
red_black_tree_replace(const RedBlackTree *const restrict tree,
const void *const key);
bool
red_black_tree_replace_min(const RedBlackTree *const restrict tree,
const void *const key);
bool
red_black_tree_replace_max(const RedBlackTree *const restrict tree,
const void *const key);
bool
red_black_hmap_replace(const RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
attempt to set member key in the container
If a key is found that matches input key (and length),
it is replaced by key and true is returned.
If no matching key is found, the container is untouched
and false is returned.
RedBlackTree provides replace_min/replace_max to set the
smallest/largest key in tree without comparisons.
set
void
red_black_tree_set(const RedBlackTree *const restrict tree,
const void *const key);
void
red_black_tree_set_min(const RedBlackTree *const restrict tree,
const void *const key);
void
red_black_tree_set_max(const RedBlackTree *const restrict tree,
const void *const key);
void
red_black_hmap_set(const RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
set GUARANTEED PRESENT key from the container
The key matching input key (and length) is replaced by key.
RedBlackTree provides set_min/set_max to set the
smallest/largest key in tree without comparisons.
This call will segfault or worse if key is not present in the container
or if tree is empty in the case of set_min/set_max.
exchange
bool
red_black_tree_exchange(const RedBlackTree *const restrict tree,
const void *const key,
void **const restrict old_ptr);
bool
red_black_tree_exchange_min(const RedBlackTree *const restrict tree,
const void *const key,
void **const restrict old_ptr);
bool
red_black_tree_exchange_max(const RedBlackTree *const restrict tree,
const void *const key,
void **const restrict old_ptr);
bool
red_black_hmap_exchange(const RedBlackHMap *const restrict map,
const void *const key,
const size_t length,
void **const restrict old_ptr);
attempt to swap member key in the container
if a key is found that matches input key (and length),
it is replaced by key, old_ptr is set to its old value,
and true is returned.
If no matching key is found, the container is untouched,
old_ptr is not set, and false is returned.
RedBlackTree provides exchange_min/exchange_max to swap the
smallest/largest key in tree without comparisons.
swap
void *
red_black_tree_swap(const RedBlackTree *const restrict tree,
const void *const key);
void *
red_black_tree_swap_min(const RedBlackTree *const restrict tree,
const void *const key);
void *
red_black_tree_swap_max(const RedBlackTree *const restrict tree,
const void *const key);
void *
red_black_hmap_swap(const RedBlackHMap *const restrict map,
const void *const key,
const size_t length);
swap GUARANTEED PRESENT key in the container
swap will replace the key matching key (and length)
with key and return the old value.
RedBlackTree provides swap_min/swap_max to swap the
smallest/largest key in tree without comparisons.
This call will segfault or worse if key is not present in the container.
clone
bool
red_black_tree_clone(RedBlackTree *const restrict dst_tree,
const RedBlackTree *const restrict src_tree);
bool
red_black_hmap_clone(RedBlackHMap *const restrict dst_map,
const RedBlackHMap *const restrict src_map);
allocate a shallow copy of dst container and store it in src
If adequate memory can be allocated, the call succeeds and returns true.
If memory allocation fails, false is returned and dst container is either left
untouched or is destroyed if any intermediate allocations have been made.
A successfully cloned container is guaranteed to compare similar and
congruent (see Set Comparison) to the original copy.
dst container must be provided free of internal allocations (uninitialized
for hmap and no insertion history for tree) to avoid memory leaks.
empty
bool
red_black_tree_empty(const RedBlackTree *const restrict tree);
bool
red_black_hmap_empty(const RedBlackHMap *const restrict map);
check if a container is empty
If the input container contains no keys, true is returned.
If the input container contains at least one key, false is returned.
count
unsigned int
red_black_tree_count(const RedBlackTree *const restrict tree);
unsigned int
red_black_hmap_count(const RedBlackHMap *const restrict map);
query the count of all member keys in the container
RedBlackHMap must maintain its member count to properly manage
its buckets, so hmap_count is an O(1) operation.
RedBlackTree does not keep track of its member count, so a
full tree traversal is required for tree_count (an O(n) operation).
verify
bool
red_black_tree_verify(const RedBlackTree *const restrict tree);
bool
red_black_hmap_verify(const RedBlackHMap *const restrict map);
examine the validity of the container state
verify scans all member keys in O(n) operations, stopping if
either a pair of keys is found out of (hashed) order or another
one of the red-black properties has been violated.
If the container is in a valid state, true is returned.
If the container is not safe to query/modify, false is returned.
similar
bool
red_black_tree_similar(const RedBlackTree *const tree1,
const RedBlackTree *const tree2);
bool
red_black_hmap_similar(const RedBlackHMap *const map1,
const RedBlackHMap *const map2);
check if two containers are equal
If both containers contain exactly the same keys, return true.
If the set of keys differ at all between the two containers, return false.
congruent
bool
red_black_tree_congruent(const RedBlackTree *const tree1,
const RedBlackTree *const tree2);
bool
red_black_hmap_congruent(const RedBlackHMap *const map1,
const RedBlackHMap *const map2);
check if two containers are identical
The congruent query checks for container similarity AND equivalent
container shape. Two congruent containers will always be similar,
but the converse is not necessarily true. For instance a container and
its clone will always compare similar AND congruent, however,
two similar containers may not be congruent if their sequence of
key insertions/deletions are not perfectly aligned.
If both containers contain exactly the same keys and share the same
underlying shape, return true.
If the containers are not similar or are shaped differently, return false.
intersect
bool
red_black_tree_intersect(const RedBlackTree *const tree1,
const RedBlackTree *const tree2);
bool
red_black_hmap_intersect(const RedBlackHMap *const map1,
const RedBlackHMap *const map2);
check if two containers have an overlap in their key set
If at least one key is found in both containers, return true.
If both containers have completely disjoint key sets, return false.
subset
bool
red_black_tree_subset(const RedBlackTree *const tree1,
const RedBlackTree *const tree2);
bool
red_black_hmap_subset(const RedBlackHMap *const map1,
const RedBlackHMap *const map2);
check if container2 is a subset of container1
If all of the keys of container2 are members of container1, return true.
If at least one key in container2 is not present in container1, return false.
insert_all
int
red_black_tree_insert_all(RedBlackTree *const restrict dst_tree,
const RedBlackTree *const restrict src_tree);
int
red_black_hmap_insert_all(RedBlackHMap *const restrict dst_map,
const RedBlackHMap *const restrict src_map);
insert all of the keys of src container into dst container
On success, insert_all returns the count of additional keys inserted.
If memory allocation fails, -1 is returned and dst is left in an
indeterminate (but valid and destructible) state.
put_all
int
red_black_tree_put_all(RedBlackTree *const restrict dst_tree,
const RedBlackTree *const restrict src_tree);
int
red_black_hmap_put_all(RedBlackHMap *const restrict dst_map,
const RedBlackHMap *const restrict src_map);
insert all of the keys of src container into dst container -- matching
keys will be overwritten with those from src
On success, put_all returns the count of additional keys inserted.
If memory allocation fails, -1 is returned and dst is left in an
indeterminate (but valid and destructible) state.
add_all
bool
red_black_tree_add_all(RedBlackTree *const restrict dst_tree,
const RedBlackTree *const restrict src_tree);
bool
red_black_hmap_add_all(RedBlackHMap *const restrict dst_map,
const RedBlackHMap *const restrict src_map);
insert all GUARANTEED UNIQUE keys of src container into dst container
Some assumptions can be made if all keys of src are known to be unique
(i.e. intersect(dst, src) == false) that will slightly speed up insertion.
If add_all succeeds, true is returned.
If a memory allocation failure occurs, false is returned.
This call will segfault or worse if any src keys are present in dst container.
delete_all
int
red_black_tree_delete_all(RedBlackTree *const restrict dst_tree,
const RedBlackTree *const restrict src_tree);
int
red_black_hmap_delete_all(RedBlackHMap *const restrict dst_map,
const RedBlackHMap *const restrict src_map);
delete all of the keys of src container from dst container
delete_all returns the count of overlapping keys deleted.
drop_all
void
red_black_tree_drop_all(RedBlackTree *const restrict dst_tree,
const RedBlackTree *const restrict src_tree);
void
red_black_hmap_drop_all(RedBlackHMap *const restrict dst_map,
const RedBlackHMap *const restrict src_map);
delete all GUARANTEED PRESENT keys of src container from dst container
Some assumptions can be made if all keys of src are present in dst
(i.e. subset(dst, src) == true) that will slightly speed up deletion.
This call will segfault or worse if src contains any keys that are not present in dst.
union
int
red_black_tree_union(RedBlackTree *const restrict union_tree,
const RedBlackTree *const tree1,
const RedBlackTree *const tree2);
int
red_black_hmap_union(RedBlackHMap *const restrict union_map,
const RedBlackHMap *const map1,
const RedBlackHMap *const map2);
Build the union
set of keys from containers 1 and 2
union constructs a union container from the set of all unique keys between
other two provided containers.
If union succeeds, a non-negative count of keys in union container is
returned.
If memory allocation fails, -1 is returned and union container is either left
untouched or is destroyed if any intermediate allocations have been made.
union container must be provided free of internal allocations (uninitialized
for hmap and no insertion history for tree) to avoid memory leaks.
intersection
int
red_black_tree_intersection(RedBlackTree *const restrict intersection_tree,
const RedBlackTree *const tree1,
const RedBlackTree *const tree2);
int
red_black_hmap_intersection(RedBlackHMap *const restrict intersection_map,
const RedBlackHMap *const map1,
const RedBlackHMap *const map2);
Build the intersection
set of keys from containers 1 and 2
intersection constructs a intersection container from the set of all unique keys between
other two provided containers.
If intersection succeeds, a non-negative count of keys in intersection container is
returned.
If memory allocation fails, -1 is returned and intersection container is either left
untouched or is destroyed if any intermediate allocations have been made.
intersection container must be provided free of internal allocations (uninitialized
for hmap and no insertion history for tree) to avoid memory leaks.
difference
int
red_black_tree_difference(RedBlackTree *const restrict difference_tree,
const RedBlackTree *const tree1,
const RedBlackTree *const tree2);
int
red_black_hmap_difference(RedBlackHMap *const restrict difference_map,
const RedBlackHMap *const map1,
const RedBlackHMap *const map2);
Build the difference
set of keys from containers 1 and 2
difference constructs a difference container from the set of all keys in container 1 that
are not present in container 2
If difference succeeds, a non-negative count of keys in difference container is
returned.
If memory allocation fails, -1 is returned and difference container is either left
untouched or is destroyed if any intermediate allocations have been made.
difference container must be provided free of internal allocations (uninitialized
for hmap and no insertion history for tree) to avoid memory leaks.
sym_difference
int
red_black_tree_sym_difference(RedBlackTree *const restrict sym_difference_tree,
const RedBlackTree *const tree1,
const RedBlackTree *const tree2);
int
red_black_hmap_sym_difference(RedBlackHMap *const restrict sym_difference_map,
const RedBlackHMap *const map1,
const RedBlackHMap *const map2);
Build the symmetric difference
set of keys from containers 1 and 2
sym_difference constructs a sym_difference container from the set of all
non-overlapping keys among containers 1 and 2
If sym_difference succeeds, a non-negative count of keys in sym_difference container is
returned.
If memory allocation fails, -1 is returned and sym_difference container is either left
untouched or is destroyed if any intermediate allocations have been made.
sym_difference container must be provided free of internal allocations (uninitialized
for hmap and no insertion history for tree) to avoid memory leaks.
typedef int
(*RedBlackComparator)(const void *key1,
const void *key2);
responsibility
maintain proper sorted order of tree nodes
behaviour
should compare two keys according to the table:
return |
key1 __ key2 |
|---|---|
< 0 |
< |
0 |
== |
> 0 |
> |
without altering their values
examples
/* for trees housing integer keys */
#include <stdint.h> /* intptr_t */
int
compare_integer_keys(const void *key1,
const void *key2)
{
const int int_key1 = (int) (intptr_t) key1;
const int int_key2 = (int) (intptr_t) key2;
#if 0 /* if domain of keys is known to be small */
return int_key1 - int_key2;
#else /* if subtraction of keys may cause arithmetic overflow */
if (int_key1 < int_key2)
return -1;
return (int_key1 > int_key2); /* 1, 0 */
#endif
}
/* for trees housing string keys */
int
compare_string_keys(const void *key1,
const void *key2)
{
const unsigned char *string1;
const unsigned char *string2;
int token1;
int token2;
string1 = (const unsigned char *) key1;
string2 = (const unsigned char *) key2;
while (1) {
token1 = (int) *string1;
token2 = (int) *string2;
if (token1 != token2)
return token1 - token2;
if (token1 == 0)
return 0;
++string1;
++string2;
}
}
TODO