diff --git a/chinookfile b/chinookfile index 96d5b63..d352a09 100644 --- a/chinookfile +++ b/chinookfile @@ -5,6 +5,8 @@ semv: 1.0.0 requires: - remote: git@git.erasit.com:malunal/allocators branch: v1.0.0 +- remote: git@git.erasit.com:malunal/assert + branch: v1.0.0 - remote: git@git.erasit.com:malunal/microtest branch: v1.0.0 - remote: git@git.erasit.com:malunal/types @@ -15,11 +17,12 @@ targets: type: archive deps: - malunal.allocators + - malunal.assert - malunal.types srcs: - ./sources/container.c - ./sources/vector.c - - ./sources/table.c + - ./sources/map.c tests: - name: malunal.containers.allinone @@ -29,7 +32,7 @@ tests: - malunal.microtest srcs: - ./tests/vector_container.c - - ./tests/table_container.c + - ./tests/map_container.c - ./tests/containers.c exports: diff --git a/include/malunal/container.h b/include/malunal/container.h index fd74497..5ba4c0e 100644 --- a/include/malunal/container.h +++ b/include/malunal/container.h @@ -85,6 +85,32 @@ typedef error_t malunal_size_t* capacity ); +/** + * @brief A type definition for a pointer to a function which will attempt to + * resize the provided @c container to the provided @c capacity. + * @param container A pointer to the container to resize. + * @param capacity The capacity to resize the container to. + * @returns An error code if the container could not be resized. + */ +typedef error_t +(*container_resize_pfn_t)( + malunal_mptr_t container, + malunal_size_t capacity +); + +/** + * @brief A type definition for a pointer to a function which will attempt to + * reserve the provided @c capacity of memory in the @c container. + * @param container A pointer to the container to reserve memory for. + * @param capacity The capacity of memory to reserve for the container. + * @returns An error code if the container could not reserve memory. + */ +typedef error_t +(*container_reserve_pfn_t)( + malunal_mptr_t container, + malunal_size_t capacity +); + /** * @brief A type definition for a pointer to a function which will attempt to * append the provided @c element into the provided @c container. @@ -136,12 +162,13 @@ typedef error_t ); /** + * @struct container_vtable * @brief Defines the container virtual function table. * @details Implementing classes of the container interface are expected to * provided valid pointers to these functions that are implementation * specific. */ -typedef struct { +define_struct(container_vtable) { /** * @brief A pointer for a function which will attempt to provide the * allocator for a provided @c container into the @c out parameter. @@ -166,6 +193,18 @@ typedef struct { */ container_capacity_pfn_t const capacity; + /** + * @brief A type definition for a pointer to a function which will attempt to + * resize the provided @c container to the provided @c capacity. + */ + container_resize_pfn_t const resize; + + /** + * @brief A type definition for a pointer to a function which will attempt to + * reserve the provided @c capacity of memory in the @c container. + */ + container_reserve_pfn_t const reserve; + /** * @brief A pointer to a function which will attempt to append the provided * @c element into the provided @c container. @@ -188,37 +227,24 @@ typedef struct { * @brief A pointer to a function which will clear the provided @c container. */ container_clear_pfn_t const clear; -} container_vtable_t; +}; /** + * @struct container * @brief Defines an abstract class type for containers. * @details Containers are objects which simply store data. They provide ways to * @c append, @c remove, @c find, and @c clear the container. Being an * abstract class like object, it provides some useful member variables * and a virtual function table for container specific functions. */ -typedef struct { +define_struct(container) { /** * @brief A pointer to an immutable container virtual function table. * @details Contains the function pointer to the container specific functions * that make this container function as one. */ const container_vtable_t* vtable; -} container_t; - -/** - * @brief A pointer to a mutable container. - * @details This is provided to simplify type declarations for functions - * requiring containers that are meant to be mutable. - */ -typedef container_t* container_mptr_t; - -/** - * @brief A pointer to an immutable container. - * @details This is provided to simplify type declarations for functions - * requiring containers that are meant to be immutable. - */ -typedef const container_t* container_iptr_t; +}; /** @@ -229,6 +255,7 @@ typedef const container_t* container_iptr_t; typedef enum { CONTAINER_ERROR_FAILURE, CONTAINER_ERROR_NULL_CONTAINER, + CONTAINER_ERROR_NULL_RECEIVER, CONTAINER_ERROR_OUT_OF_BOUNDS, } container_error_t; @@ -281,6 +308,30 @@ container_capacity( malunal_size_t* out ); +/** + * @brief Resizes the container to the specified capacity. + * @param container A pointer to the container to resize. + * @param capacity The amount of slots to allocate in the container. + * @returns An error if the container could not be resized. + */ +error_t +container_resize( + container_mptr_t container, + malunal_size_t capacity +); + +/** + * @brief Reserves the specified amount of space in the container. + * @param container A pointer to the container to reserve space for. + * @param capacity The amount of slots in the container to reserve. + * @returns An error if the container could not reserve the memory. + */ +error_t +container_reserve( + container_mptr_t container, + malunal_size_t capacity +); + /** * @brief Appends a new element to the container. * @param container A pointer to the container to append the element to. diff --git a/include/malunal/containers/map.h b/include/malunal/containers/map.h new file mode 100644 index 0000000..9236c34 --- /dev/null +++ b/include/malunal/containers/map.h @@ -0,0 +1,325 @@ +/** + * @file map.h + * @brief Contains the structures and functions necessary to utilize a map + * container implementation. + * @author John Christman (sorakatadzuma@gmail.com) + * @copyright Malunal Studios, LLC. + */ +#include "../container.h" + +#ifndef MALUNAL_CONTAINERS_MAP_HEADER +#define MALUNAL_CONTAINERS_MAP_HEADER + +/** + * @def MALUNAL_CONTAINERS_MAP_INIT_CAPACITY + * @brief Defines the initial capacity of maps to simplify initialization. + * @details This will tell all maps what their initial capacity should be, which + * can be overriden simply by calling the appropriate @c resize or + * @c reserve functions. + */ +#ifndef MALUNAL_CONTAINERS_MAP_INIT_CAPACITY +#define MALUNAL_CONTAINERS_MAP_INIT_CAPACITY 16 +#endif /* MALUNAL_CONTAINERS_MAP_INIT_CAPACITY */ + + +/** + * @struct map_container + * @brief Defines a class type for maps. + * @details Maps store their data contiguously in memory, but are hashed into + * their locations within the unreserved portion of the container. + * The reserved portion is one control byte per slot, which records + * whether the slot is empty, filled, or holds a pair that has been + * erased. Maps extend the functionality of containers, so they may be + * used in container functions. + */ +define_struct(map_container) { + malunal_size_t __opaque[7]; +}; + +/** + * @struct map_pair + * @brief Defines a key and its value as a single element. + * @details The container interface appends one element at a time, so a map + * needs a way to be handed both halves of a pair at once. This is + * that way, and it is only ever borrowed by the map: the key and the + * value are copied into the map itself. + */ +define_struct(map_pair) { + malunal_iptr_t key; + malunal_iptr_t value; +}; + + +/** + * @brief Initializes a map container with the following @c keystride, + * @c valstride, and @c allocator. + * @param keystride The size of the key object in bytes. + * @param valstride The size of the value object in bytes. + * @param allocator The allocator it should use to obtain memory. + * @param map A pointer to the map to initialize. + * @returns An error if the map could not be initialized. + */ +error_t +map_container_init( + malunal_size_t keystride, + malunal_size_t valstride, + allocator_mptr_t allocator, + map_container_mptr_t map +); + +/** + * @brief Frees the backing memory of the map. + * @param map A pointer to the map to free. + * @returns An error if the map could not be freed. + */ +error_t +map_container_free( + map_container_mptr_t map +); + +/** + * @brief Obtains the allocator for the given map. + * @param map A pointer to the map to obtain the allocator from. + * @param out A pointer to where to store the obtained allocator pointer. + * @returns An error if the given map could not provide the allocator. + */ +error_t +map_container_allocator( + map_container_iptr_t map, + allocator_mptr_t* out +); + +/** + * @brief Obtains the stride for the given map. + * @param map A pointer to the map to obtain the stride from. + * @param out A pointer to where to store the obtained stride. + * @returns An error if the given map could not provide the stride. + * @remarks This will get the stride of the key and value together. + */ +error_t +map_container_stride( + map_container_iptr_t map, + malunal_size_t* out +); + +/** + * @brief Obtains the key stride for the given map. + * @param map A pointer to the map to obtain the stride from. + * @param out A pointer to where to store the obtained stride. + * @returns An error if the given map could not provide the key stride. + */ +error_t +map_container_key_stride( + map_container_iptr_t map, + malunal_size_t* out +); + +/** + * @brief Obtains the value stride for the given map. + * @param map A pointer to the map to obtain the stride from. + * @param out A pointer to where to store the obtained stride. + * @returns An error if the given map could not provide the value stride. + */ +error_t +map_container_value_stride( + map_container_iptr_t map, + malunal_size_t* out +); + +/** + * @brief Obtains the count for the given map. + * @param map A pointer to the map to obtain the count from. + * @param out A pointer to where to store the obtained count. + * @returns An error if the given map could not provide the count. + */ +error_t +map_container_count( + map_container_iptr_t map, + malunal_size_t* out +); + +/** + * @brief Obtains the capacity for the given map. + * @param map A pointer to the map to obtain the capacity from. + * @param out A pointer to where to store the obtained capacity. + * @returns An error if the given map could not provide the capacity. + */ +error_t +map_container_capacity( + map_container_iptr_t map, + malunal_size_t* out +); + +/** + * @brief Obtains the keys for the given map. + * @param map A pointer to the map to obtain the keys of. + * @param container A pointer to the container to place the keys in. + * @returns An error if the given map could not provide its keys. + * @remarks The keys are appended to the container, which must therefore have a + * stride equal to the key stride of the map. They are appended in the + * order the map happens to store them in, which is not the order they + * were inserted in. + */ +error_t +map_container_keys( + map_container_iptr_t map, + container_mptr_t container +); + +/** + * @brief Obtains the values for the given map. + * @param map A pointer to the map to obtain the values of. + * @param container A pointer to the container to place the values in. + * @returns An error if the given map could not provide its values. + * @remarks The values are appended to the container, which must therefore have + * a stride equal to the value stride of the map. They are appended in + * the same order as the keys of @c map_container_keys, so the two line + * up with each other. + */ +error_t +map_container_values( + map_container_iptr_t map, + container_mptr_t container +); + +/** + * @brief Resizes the map to the specified capacity. + * @param map A pointer to the map to resize. + * @param capacity The amount of slots to allocate in the map. + * @returns An error if the map could not be resized. + * @remarks The capacity is rounded up to a power of two and the pairs already + * within the map are rehashed into the new slots. Maps only ever + * grow, so a capacity which is not greater than the current one is + * ignored. Unlike a vector, this does not change the count of the map, + * because a slot only holds a pair once one is inserted into it. + */ +error_t +map_container_resize( + map_container_mptr_t map, + malunal_size_t capacity +); + +/** + * @brief Reserves the specified amount of space in the map. + * @param map A pointer to the map to reserve space for. + * @param capacity The amount of pairs the map should be able to hold. + * @returns An error if the map could not reserve the memory. + * @remarks Maps keep slots to spare so that their probe sequences stay short, + * so this allocates more slots than the pairs asked for. After this, + * that many pairs may be inserted without the map rehashing itself. + */ +error_t +map_container_reserve( + map_container_mptr_t map, + malunal_size_t capacity +); + +/** + * @brief Appends a new key/value pair to the map. + * @param map A pointer to the map to append the pair to. + * @param pair A pointer to the key and value pointers. + * @returns An error if the map could not append the pair. + * @remarks This is @c map_container_insert given a pair instead of two + * separate pointers, and it exists so that maps satisfy the append of + * the container interface. + */ +error_t +map_container_append( + map_container_mptr_t map, + map_pair_iptr_t pair +); + +/** + * @brief Inserts a new key/value pair into the map. + * @param map A pointer to the map to insert the pair into. + * @param key A pointer to the data of the key to insert. + * @param value A pointer to the data of the value to insert. + * @returns An error if the map could not insert the pair. + * @remarks Both the key and the value are copied into the map. When the key is + * already present, its value is overwritten and the count of the map + * is left alone. + */ +error_t +map_container_insert( + map_container_mptr_t map, + malunal_iptr_t key, + malunal_iptr_t value +); + +/** + * @brief Removes the specified key, with its value, from the map. + * @param map A pointer to the map to remove the key from. + * @param key A pointer to the data of the key to remove. + * @returns An error if the map could not remove the key. + * @remarks Removing a key which is not within the map is not an error, it + * simply leaves the map as it was. + */ +error_t +map_container_remove( + map_container_mptr_t map, + malunal_iptr_t key +); + +/** + * @brief Checks the map for the given key. + * @param map A pointer to the map to find the key within. + * @param key A pointer to the data of the key to search for. + * @returns An error if the map could not find the key. + * @remarks A key which is not within the map is reported as a + * @c CONTAINER_ERROR_FAILURE, which is how the container interface + * says that it does not contain something. + */ +error_t +map_container_contains( + map_container_iptr_t map, + malunal_iptr_t key +); + +/** + * @brief Clears the map, removing all elements from it. + * @param map A pointer to the map to clear. + * @returns An error if the map could not clear all its elements. + */ +error_t +map_container_clear( + map_container_mptr_t map +); + +/** + * @brief Gets the value at the given key from the given map, copying + * the data into the memory region of the @c value address. + * @param map A pointer to the map to get the value from. + * @param key A pointer to the key of the value to obtain. + * @param value A pointer to the memory region to copy the value to. + * @returns An error if the given map can not get the value. + * @remarks A key which is not within the map is reported as a + * @c CONTAINER_ERROR_OUT_OF_BOUNDS, the same as an index which is + * past the end of a vector. + */ +error_t +map_container_get( + map_container_iptr_t map, + malunal_iptr_t key, + malunal_mptr_t value +); + +/** + * @brief Sets the value of the given key within the given map, copying the + * data from the memory region of the @c value. + * @param map A pointer to the map to set the value into. + * @param key A pointer to the key of the value to set. + * @param value A pointer to the value to set within the map. + * @returns An error if the given map can not set the value. + * @remarks This only overwrites the value of a key which is already within the + * map. A key which is not within it is reported as a + * @c CONTAINER_ERROR_OUT_OF_BOUNDS, rather than being inserted, which + * is what @c map_container_insert is for. + */ +error_t +map_container_set( + map_container_mptr_t map, + malunal_iptr_t key, + malunal_iptr_t value +); + +#endif /* MALUNAL_CONTAINERS_MAP_HEADER */ diff --git a/include/malunal/containers/vector.h b/include/malunal/containers/vector.h index 3085b9d..13f7487 100644 --- a/include/malunal/containers/vector.h +++ b/include/malunal/containers/vector.h @@ -7,43 +7,36 @@ */ #include "../container.h" -#ifndef MALUNAL_VECTOR_HEADER -#define MALUNAL_VECTOR_HEADER +#ifndef MALUNAL_CONTAINERS_VECTOR_HEADER +#define MALUNAL_CONTAINERS_VECTOR_HEADER /** - * @brief Defines an class type for vectors. + * @def MALUNAL_CONTAINERS_VECTOR_INIT_CAPACITY + * @brief Defines the initial capacity of vectors to simplify initialization. + * @details This will tell all vectors what their initial capacity should be, + * which can be overriden simply by calling the appropriate @c resize + * or @c reserve functions. + */ +#ifndef MALUNAL_CONTAINERS_VECTOR_INIT_CAPACITY +#define MALUNAL_CONTAINERS_VECTOR_INIT_CAPACITY 16 +#endif /* MALUNAL_CONTAINERS_VECTOR_INIT_CAPACITY */ + +/** + * @struct vector_container + * @brief Defines a class type for vectors. * @details Vectors store their data contiguously in memory. They extend the * functionality of containers, so they maybe used in container * functions. - * @remarks The purpose of defining a separate type definition from @c container - * is to make it physically distinct from a container. This also makes - * it so other containers cannot accidentally be used in operations - * meant for vectors. */ -typedef struct { - malunal_size_t __opaque[7]; -} vector_container_t; - -/** - * @brief A pointer to a mutable vector. - * @details This is provided to simplify type declarations for functions - * requiring vectors that are meant to be mutable. - */ -typedef vector_container_t* vector_container_mptr_t; - -/** - * @brief A pointer to an immutable vector. - * @details This is provided to simplify type declarations for functions - * requiring vectors that are meant to be immutable. - */ -typedef const vector_container_t* vector_container_iptr_t; +define_struct(vector_container) { + malunal_size_t __opaque[6]; +}; /** * @brief Initializes a vector with the given @c stride, @c capacity, and * @c allocator. * @param stride The size of the objects that it stores in bytes. - * @param capacity The initial capacity of objects that it stores. * @param allocator The allocator it should use to obtain memory. * @param vector A pointer to the vector to initialize. * @returns An error if the vector could not be initialized. @@ -51,7 +44,6 @@ typedef const vector_container_t* vector_container_iptr_t; error_t vector_container_init( malunal_size_t stride, - malunal_size_t capacity, allocator_mptr_t allocator, vector_container_mptr_t vector ); @@ -114,6 +106,30 @@ vector_container_capacity( malunal_size_t* out ); +/** + * @brief Resizes the vector to the specified capacity. + * @param vector A pointer to the vector to resize. + * @param capacity The amount of slots to allocate in the vector. + * @returns An error if the vector could not be resized. + */ +error_t +vector_container_resize( + vector_container_mptr_t vector, + malunal_size_t capacity +); + +/** + * @brief Reserves the specified amount of space in the vector. + * @param vector A pointer to the vector to reserve space for. + * @param capacity The amount of slots in the vector to reserve. + * @returns An error if the vector could not reserve the memory. + */ +error_t +vector_container_reserve( + vector_container_mptr_t vector, + malunal_size_t capacity +); + /** * @brief Appends a new element to the vector. * @param vector A pointer to the vector to append the element to. @@ -232,4 +248,4 @@ vector_container_index_of( malunal_size_t* outidx ); -#endif /* MALUNAL_VECTOR_HEADER */ +#endif /* MALUNAL_CONTAINERS_VECTOR_HEADER */ diff --git a/sources/container.c b/sources/container.c index 65d5269..2072c1b 100644 --- a/sources/container.c +++ b/sources/container.c @@ -1,3 +1,4 @@ +#include "malunal/assert.h" #include "malunal/container.h" const uuid_t UUID_CONTAINER_T = { @@ -32,17 +33,15 @@ const error_domain_t ERROR_DOMAIN_CONTAINER_T = { .name = "malunal.container.error" }; + error_t container_allocator( container_iptr_t container, allocator_mptr_t* out ) { - return container != null - ? container->vtable->allocator(container, out) - : (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; + assert(container != null); + assert(out != null); + return container->vtable->allocator(container, out); } error_t @@ -50,12 +49,9 @@ container_stride( container_iptr_t container, malunal_size_t* out ) { - return container != null - ? container->vtable->stride(container, out) - : (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; + assert(container != null); + assert(out != null); + return container->vtable->stride(container, out); } error_t @@ -63,12 +59,9 @@ container_count( container_iptr_t container, malunal_size_t* out ) { - return container != null - ? container->vtable->count(container, out) - : (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; + assert(container != null); + assert(out != null); + return container->vtable->count(container, out); } error_t @@ -76,12 +69,27 @@ container_capacity( container_iptr_t container, malunal_size_t* out ) { - return container != null - ? container->vtable->capacity(container, out) - : (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; + assert(container != null); + assert(out != null); + return container->vtable->capacity(container, out); +} + +error_t +container_resize( + container_mptr_t container, + malunal_size_t capacity +) { + assert(container != null); + return container->vtable->resize(container, capacity); +} + +error_t +container_reserve( + container_mptr_t container, + malunal_size_t capacity +) { + assert(container != null); + return container->vtable->reserve(container, capacity); } error_t @@ -89,12 +97,9 @@ container_append( container_mptr_t container, malunal_iptr_t element ) { - return container != null - ? container->vtable->append(container, element) - : (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; + assert(container != null); + assert(element != null); + return container->vtable->append(container, element); } error_t @@ -102,12 +107,9 @@ container_remove( container_mptr_t container, malunal_iptr_t element ) { - return container != null - ? container->vtable->remove(container, element) - : (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; + assert(container != null); + assert(element != null); + return container->vtable->remove(container, element); } error_t @@ -115,22 +117,15 @@ container_contains( container_iptr_t container, malunal_iptr_t element ) { - return container != null - ? container->vtable->contains(container, element) - : (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; + assert(container != null); + assert(element != null); + return container->vtable->contains(container, element); } error_t container_clear( container_mptr_t container ) { - return container != null - ? container->vtable->clear(container) - : (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; + assert(container != null); + return container->vtable->clear(container); } diff --git a/sources/map.c b/sources/map.c new file mode 100644 index 0000000..cf12610 --- /dev/null +++ b/sources/map.c @@ -0,0 +1,674 @@ +#include +#include "malunal/containers/map.h" + + +#define MAP_CONTROL_EMPTY ((malunal_uint8_t)0x00) +#define MAP_CONTROL_ERASED ((malunal_uint8_t)0x01) +#define MAP_CONTROL_FILLED ((malunal_uint8_t)0x02) +#define MAP_LOAD_FACTOR 0.75f +#define MAP_NO_SLOT ((malunal_size_t)-1) +#define MAP_HASH_BASIS 0xCBF29CE484222325ULL +#define MAP_HASH_PRIME 0x00000100000001B3ULL + + +typedef struct { + container_t container; + allocator_mptr_t allocator; + malunal_size_t keystride; + malunal_size_t valstride; + malunal_size_t count; + malunal_size_t capacity; + malunal_mptr_t buffer; +} impl_t; + +typedef impl_t* impl_mptr_t; +typedef const impl_t* impl_iptr_t; +_Static_assert( + sizeof(map_container_t) == sizeof(impl_t), + "Map container must be the size of its implementation" +); + + +static const +container_vtable_t map_container_vtable = { + .allocator = (container_allocator_pfn_t)&map_container_allocator, + .stride = (container_stride_pfn_t)&map_container_stride, + .count = (container_count_pfn_t)&map_container_count, + .capacity = (container_capacity_pfn_t)&map_container_capacity, + .resize = (container_resize_pfn_t)&map_container_resize, + .reserve = (container_reserve_pfn_t)&map_container_reserve, + .append = (container_append_pfn_t)&map_container_append, + .remove = (container_remove_pfn_t)&map_container_remove, + .contains = (container_contains_pfn_t)&map_container_contains, + .clear = (container_clear_pfn_t)&map_container_clear +}; + +static +malunal_size_t +power_2_ceil(malunal_size_t capacity) { + if (capacity <= 1) + return 1; + + capacity--; + malunal_size_t power = 2; + while (capacity >>= 1) + power <<= 1; + return power; +} + +static +malunal_size_t +map_container_bytes( + malunal_size_t keystride, + malunal_size_t valstride, + malunal_size_t capacity +) { + return capacity + (keystride + valstride) * capacity; +} + +static +malunal_uint8_t* +map_container_control(impl_iptr_t self) { + return (malunal_uint8_t*)self->buffer; +} + +static +malunal_uint8_t* +map_container_key( + impl_iptr_t self, + malunal_size_t index +) { + malunal_uint8_t* slots = + map_container_control(self) + + self->capacity; + return slots + (self->keystride + self->valstride) * index; +} + +static +malunal_uint8_t* +map_container_value( + impl_iptr_t self, + malunal_size_t index +) { + return + map_container_key(self, index) + + self->keystride; +} + +static +malunal_size_t +map_container_hash( + malunal_iptr_t key, + malunal_size_t keystride +) { + malunal_uint8_t* bytes = key; + malunal_uint64_t hash = MAP_HASH_BASIS; + for (malunal_size_t index = 0; index < keystride; index++) { + hash ^= (malunal_uint64_t)bytes[index]; + hash *= MAP_HASH_PRIME; + } + + return (malunal_size_t)hash; +} + +static +malunal_bool_t +map_container_find( + impl_iptr_t self, + malunal_iptr_t key, + malunal_size_t* outidx +) { + *outidx = MAP_NO_SLOT; + if (self->buffer == null || self->capacity == 0) + return false; + + malunal_uint8_t* control = map_container_control(self); + malunal_size_t mask = self->capacity - 1; + malunal_size_t index = map_container_hash(key, self->keystride) & mask; + malunal_size_t vacancy = MAP_NO_SLOT; + malunal_size_t probed = 0; + while (probed < self->capacity) { + if (control[index] == MAP_CONTROL_EMPTY) { + *outidx = vacancy != MAP_NO_SLOT ? vacancy : index; + return false; + } + + if (control[index] == MAP_CONTROL_ERASED) { + if (vacancy == MAP_NO_SLOT) + vacancy = index; + } else if (memcmp(map_container_key(self, index), key, self->keystride) == 0) { + *outidx = index; + return true; + } + + index = (index + 1) & mask; + probed += 1; + } + + *outidx = vacancy; + return false; +} + +static +malunal_void_t +map_container_place( + impl_mptr_t self, + malunal_size_t index, + malunal_iptr_t key, + malunal_iptr_t value +) { + map_container_control(self)[index] = MAP_CONTROL_FILLED; + memcpy(map_container_key(self, index), key, self->keystride); + if (self->valstride != 0) + memcpy(map_container_value(self, index), value, self->valstride); +} + +static +error_t +map_container_realloc( + impl_mptr_t self, + malunal_size_t capacity +) { + if (capacity <= self->capacity) + return NO_ERROR; + + malunal_mptr_t buffer; + malunal_size_t newcap = power_2_ceil(capacity); + malunal_size_t bytes = map_container_bytes( + self->keystride, + self->valstride, + newcap + ); + + error_t result = allocator_acquire(self->allocator, bytes, &buffer); + if (result.domain != null) + return result; + + memset(buffer, MAP_CONTROL_EMPTY, newcap); + + impl_t rehashed = *self; + rehashed.buffer = buffer; + rehashed.capacity = newcap; + + malunal_uint8_t* control = map_container_control(self); + for (malunal_size_t index = 0; index < self->capacity; index++) { + if (control[index] != MAP_CONTROL_FILLED) + continue; + + malunal_size_t slot; + malunal_uint8_t* key = map_container_key(self, index); + map_container_find(&rehashed, key, &slot); + map_container_place(&rehashed, slot, key, map_container_value(self, index)); + } + + if (self->buffer != null) { + bytes = map_container_bytes( + self->keystride, + self->valstride, + self->capacity + ); + + result = allocator_dispose(self->allocator, self->buffer, bytes); + } + + self->buffer = buffer; + self->capacity = newcap; + return result; +} + +error_t +map_container_init( + malunal_size_t keystride, + malunal_size_t valstride, + allocator_mptr_t allocator, + map_container_mptr_t map +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_mptr_t self = (impl_mptr_t)map; + malunal_size_t capacity = power_2_ceil(MALUNAL_CONTAINERS_MAP_INIT_CAPACITY); + malunal_size_t bytes = map_container_bytes(keystride, valstride, capacity); + error_t result = allocator_acquire(allocator, bytes, &self->buffer); + if (result.domain != null) + return result; + + memset(self->buffer, MAP_CONTROL_EMPTY, capacity); + self->container = (container_t){ &map_container_vtable }; + self->allocator = allocator; + self->keystride = keystride; + self->valstride = valstride; + self->count = 0; + self->capacity = capacity; + return NO_ERROR; +} + +error_t +map_container_free( + map_container_mptr_t map +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_mptr_t self = (impl_mptr_t)map; + error_t result = allocator_dispose( + self->allocator, + self->buffer, + map_container_bytes(self->keystride, self->valstride, self->capacity) + ); + + if (result.domain != null) + return result; + + self->keystride = 0; + self->valstride = 0; + self->count = 0; + self->capacity = 0; + self->buffer = null; + return NO_ERROR; +} + +error_t +map_container_allocator( + map_container_iptr_t map, + allocator_mptr_t* out +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_iptr_t self = (impl_iptr_t)map; + *out = self->allocator; + return NO_ERROR; +} + +error_t +map_container_stride( + map_container_iptr_t map, + malunal_size_t* out +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_iptr_t self = (impl_iptr_t)map; + *out = self->keystride + self->valstride; + return NO_ERROR; +} + +error_t +map_container_key_stride( + map_container_iptr_t map, + malunal_size_t* out +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_iptr_t self = (impl_iptr_t)map; + *out = self->keystride; + return NO_ERROR; +} + +error_t +map_container_value_stride( + map_container_iptr_t map, + malunal_size_t* out +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_iptr_t self = (impl_iptr_t)map; + *out = self->valstride; + return NO_ERROR; +} + +error_t +map_container_count( + map_container_iptr_t map, + malunal_size_t* out +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_iptr_t self = (impl_iptr_t)map; + *out = self->count; + return NO_ERROR; +} + +error_t +map_container_capacity( + map_container_iptr_t map, + malunal_size_t* out +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_iptr_t self = (impl_iptr_t)map; + *out = self->capacity; + return NO_ERROR; +} + +error_t +map_container_keys( + map_container_iptr_t map, + container_mptr_t container +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + if (container == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_RECEIVER + }; + + malunal_size_t stride = 0; + impl_iptr_t self = (impl_iptr_t)map; + error_t result = container_stride(container, &stride); + if (result.domain != null) + return result; + + if (stride != self->keystride) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_FAILURE + }; + + malunal_uint8_t* control = map_container_control(self); + for (malunal_size_t index = 0; index < self->capacity; index++) { + if (control[index] != MAP_CONTROL_FILLED) + continue; + + result = container_append(container, map_container_key(self, index)); + if (result.domain != null) + return result; + } + + return NO_ERROR; +} + +error_t +map_container_values( + map_container_iptr_t map, + container_mptr_t container +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + if (container == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_RECEIVER + }; + + malunal_size_t stride = 0; + impl_iptr_t self = (impl_iptr_t)map; + error_t result = container_stride(container, &stride); + if (result.domain != null) + return result; + + if (stride != self->valstride) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_FAILURE + }; + + malunal_uint8_t* control = map_container_control(self); + for (malunal_size_t index = 0; index < self->capacity; index++) { + if (control[index] != MAP_CONTROL_FILLED) + continue; + + result = container_append(container, map_container_value(self, index)); + if (result.domain != null) + return result; + } + + return NO_ERROR; +} + +error_t +map_container_resize( + map_container_mptr_t map, + malunal_size_t capacity +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_mptr_t self = (impl_mptr_t)map; + return map_container_realloc(self, capacity); +} + +error_t +map_container_reserve( + map_container_mptr_t map, + malunal_size_t capacity +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_mptr_t self = (impl_mptr_t)map; + return map_container_realloc(self, (capacity * 4) / 3 + 1); +} + +error_t +map_container_append( + map_container_mptr_t map, + map_pair_iptr_t pair +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + if (pair == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_FAILURE + }; + + return map_container_insert(map, pair->key, pair->value); +} + +error_t +map_container_insert( + map_container_mptr_t map, + malunal_iptr_t key, + malunal_iptr_t value +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + error_t result = NO_ERROR; + impl_mptr_t self = (impl_mptr_t)map; + if (self->capacity == 0) { + result = map_container_realloc(self, MALUNAL_CONTAINERS_MAP_INIT_CAPACITY); + if (result.domain != null) + return result; + } + + malunal_size_t slot; + if (map_container_find(self, key, &slot)) { + if (self->valstride != 0) + memcpy(map_container_value(self, slot), value, self->valstride); + return NO_ERROR; + } + + if (slot == MAP_NO_SLOT || + (float)(self->count + 1) / self->capacity >= MAP_LOAD_FACTOR) { + result = map_container_realloc(self, self->capacity * 2); + if (result.domain != null) + return result; + + map_container_find(self, key, &slot); + } + + if (slot == MAP_NO_SLOT) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_FAILURE + }; + + map_container_place(self, slot, key, value); + self->count++; + return NO_ERROR; +} + +error_t +map_container_remove( + map_container_mptr_t map, + malunal_iptr_t key +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + malunal_size_t slot; + impl_mptr_t self = (impl_mptr_t)map; + if (!map_container_find(self, key, &slot)) + return NO_ERROR; + + malunal_uint8_t* control = map_container_control(self); + malunal_size_t mask = self->capacity - 1; + control[slot] = MAP_CONTROL_ERASED; + self->count--; + + if (control[(slot + 1) & mask] != MAP_CONTROL_EMPTY) + return NO_ERROR; + + malunal_size_t index = slot; + while (control[index] == MAP_CONTROL_ERASED) { + control[index] = MAP_CONTROL_EMPTY; + index = (index - 1) & mask; + } + + return NO_ERROR; +} + +error_t +map_container_contains( + map_container_iptr_t map, + malunal_iptr_t key +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + malunal_size_t slot; + impl_iptr_t self = (impl_iptr_t)map; + return map_container_find(self, key, &slot) + ? NO_ERROR + : (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_FAILURE + }; +} + +error_t +map_container_clear( + map_container_mptr_t map +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_mptr_t self = (impl_mptr_t)map; + if (self->buffer != null) + memset(self->buffer, MAP_CONTROL_EMPTY, self->capacity); + + self->count = 0; + return NO_ERROR; +} + +error_t +map_container_get( + map_container_iptr_t map, + malunal_iptr_t key, + malunal_mptr_t value +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + malunal_size_t slot; + impl_iptr_t self = (impl_iptr_t)map; + if (!map_container_find(self, key, &slot)) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_OUT_OF_BOUNDS + }; + + if (self->valstride != 0) + memcpy(value, map_container_value(self, slot), self->valstride); + return NO_ERROR; +} + +error_t +map_container_set( + map_container_mptr_t map, + malunal_iptr_t key, + malunal_iptr_t value +) { + if (map == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + malunal_size_t slot; + impl_mptr_t self = (impl_mptr_t)map; + if (!map_container_find(self, key, &slot)) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_OUT_OF_BOUNDS + }; + + if (self->valstride != 0) + memcpy(map_container_value(self, slot), value, self->valstride); + return NO_ERROR; +} diff --git a/sources/vector.c b/sources/vector.c index 0884ae0..99f909e 100644 --- a/sources/vector.c +++ b/sources/vector.c @@ -3,9 +3,7 @@ typedef struct { - object_t object; - container_t container; - + container_t container; allocator_mptr_t allocator; malunal_size_t stride; malunal_size_t count; @@ -21,95 +19,60 @@ _Static_assert( ); -static -error_t -vector_object_cast_impl( - malunal_mptr_t object, - uuid_iptr_t uuid, - malunal_mptr_t* out -) { - if (object == null) - return (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_NULL_CONTAINER - }; - - impl_mptr_t self = (impl_mptr_t)object; - if (uuid_equals(&UUID_OBJECT_T, uuid)) { - *out = &self->object; - return NO_ERROR; - } - - if (uuid_equals(&UUID_CONTAINER_T, uuid)) { - *out = &self->container; - return NO_ERROR; - } - - *out = null; - return (error_t) { - .domain = &ERROR_DOMAIN_CONTAINER_T, - .code = CONTAINER_ERROR_FAILURE - }; -} - -static -malunal_uint32_t -vector_object_retain_impl( - malunal_mptr_t object -) { - // TODO: figure out how to handle this. -} - -static -malunal_uint32_t -vector_object_release_impl( - malunal_mptr_t object -) { - // TODO: figure out how to handle this. -} - -static const -object_vtable_t vector_object_vtable = { - .cast = &vector_object_cast_impl, - .retain = &vector_object_retain_impl, - .release = &vector_object_release_impl -}; - static const container_vtable_t vector_container_vtable = { .allocator = (container_allocator_pfn_t)&vector_container_allocator, .stride = (container_stride_pfn_t)&vector_container_stride, .count = (container_count_pfn_t)&vector_container_count, .capacity = (container_capacity_pfn_t)&vector_container_capacity, + .resize = (container_resize_pfn_t)&vector_container_resize, + .reserve = (container_reserve_pfn_t)&vector_container_reserve, .append = (container_append_pfn_t)&vector_container_append, .remove = (container_remove_pfn_t)&vector_container_remove, .contains = (container_contains_pfn_t)&vector_container_contains, .clear = (container_clear_pfn_t)&vector_container_clear }; +static +malunal_size_t +power_2_ceil(malunal_size_t capacity) { + if (capacity <= 1) + return 1; + + capacity--; + malunal_size_t power = 2; + while (capacity >>= 1) + power <<= 1; + return power; +} + static error_t -vector_container_realloc(impl_mptr_t self) { - if ((float)self->count / self->capacity < 0.75f) +vector_container_realloc( + impl_mptr_t self, + malunal_size_t capacity +) { + if (capacity <= self->capacity) return NO_ERROR; malunal_mptr_t buffer; - malunal_size_t oldcap = self->stride * self->capacity; - error_t result = allocator_acquire(self->allocator, oldcap * 2, &buffer); + malunal_size_t newcap = power_2_ceil(capacity); + malunal_size_t bytes = self->stride * newcap; + error_t result = allocator_acquire(self->allocator, bytes, &buffer); if (result.domain != null) return result; - buffer = memmove(buffer, self->buffer, oldcap); - result = allocator_dispose(self->allocator, self->buffer, oldcap); + bytes = self->stride * self->capacity; + buffer = memmove(buffer, self->buffer, bytes); + result = allocator_dispose(self->allocator, self->buffer, bytes); self->buffer = buffer; - self->capacity = self->capacity * 2; + self->capacity = newcap; return NO_ERROR; } error_t vector_container_init( malunal_size_t stride, - malunal_size_t capacity, allocator_mptr_t allocator, vector_container_mptr_t vector ) { @@ -119,22 +82,17 @@ vector_container_init( .code = CONTAINER_ERROR_NULL_CONTAINER }; - impl_mptr_t self = (impl_mptr_t)vector; - error_t result = allocator_acquire( - allocator, - stride * capacity, - &self->buffer - ); - + impl_mptr_t self = (impl_mptr_t)vector; + malunal_size_t bytes = stride * MALUNAL_CONTAINERS_VECTOR_INIT_CAPACITY; + error_t result = allocator_acquire(allocator, bytes, &self->buffer); if (result.domain != null) return result; - self->object = (object_t) { &vector_object_vtable }; self->container = (container_t){ &vector_container_vtable }; self->allocator = allocator; self->stride = stride; self->count = 0; - self->capacity = capacity; + self->capacity = MALUNAL_CONTAINERS_VECTOR_INIT_CAPACITY; return NO_ERROR; } @@ -229,6 +187,41 @@ vector_container_capacity( return NO_ERROR; } +error_t +vector_container_resize( + vector_container_mptr_t vector, + malunal_size_t capacity +) { + if (vector == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_mptr_t self = (impl_mptr_t)vector; + error_t result = vector_container_realloc(self, capacity); + if (result.domain != null) + return result; + + self->count = capacity; + return NO_ERROR; +} + +error_t +vector_container_reserve( + vector_container_mptr_t vector, + malunal_size_t capacity +) { + if (vector == null) + return (error_t) { + .domain = &ERROR_DOMAIN_CONTAINER_T, + .code = CONTAINER_ERROR_NULL_CONTAINER + }; + + impl_mptr_t self = (impl_mptr_t)vector; + return vector_container_realloc(self, capacity); +} + error_t vector_container_append( vector_container_mptr_t vector, @@ -240,10 +233,13 @@ vector_container_append( .code = CONTAINER_ERROR_NULL_CONTAINER }; + error_t result = {}; impl_mptr_t self = (impl_mptr_t)vector; - error_t result = vector_container_realloc(self); - if (result.domain != null) - return result; + if ((float)self->count / self->capacity >= 0.75f) { + result = vector_container_realloc(self, self->capacity * 2); + if (result.domain != null) + return result; + } malunal_uint8_t* buffer = self->buffer; buffer += self->stride * self->count; @@ -383,15 +379,18 @@ vector_container_insert_at( .code = CONTAINER_ERROR_OUT_OF_BOUNDS }; - error_t result = vector_container_realloc(self); - if (result.domain != null) - return result; + error_t result = {}; + if ((float)self->count / self->capacity > 0.75f) { + result = vector_container_realloc(self, self->capacity * 2); + if (result.domain != null) + return result; + } malunal_uint8_t* orig = self->buffer; orig += self->stride * index; malunal_uint8_t* dest = orig + self->stride; - memmove(dest, orig, self->stride * self->count - index); + memmove(dest, orig, self->stride * (self->count - index)); memcpy(orig, element, self->stride); self->count++; return NO_ERROR; @@ -419,7 +418,7 @@ vector_container_remove_at( dest += self->stride * index; malunal_uint8_t* orig = dest + self->stride; - memmove(dest, orig, self->stride * self->count - index); + memmove(dest, orig, self->stride * (self->count - index)); self->count--; return NO_ERROR; } diff --git a/tests/map_container.c b/tests/map_container.c new file mode 100644 index 0000000..24b55a4 --- /dev/null +++ b/tests/map_container.c @@ -0,0 +1,562 @@ +#include "malunal/containers/map.h" +#include "malunal/containers/vector.h" +#include "malunal/microtest.h" + +MICROTEST(map_container, can_init_and_free) { + error_t result = {}; + map_container_t map = {}; + malunal_size_t temporary = 0; + + result = map_container_init(4, 8, libc_allocator(), &map); + MICROTEST_EXPECT_NULL(result.domain); + + allocator_mptr_t allocator; + result = map_container_allocator(&map, &allocator); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_PTR_EQ(allocator, libc_allocator()); + + result = map_container_key_stride(&map, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 4); + + result = map_container_value_stride(&map, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 8); + + result = map_container_stride(&map, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 12); + + result = map_container_count(&map, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = map_container_capacity(&map, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, MALUNAL_CONTAINERS_MAP_INIT_CAPACITY); + + result = map_container_free(&map); + MICROTEST_EXPECT_NULL(result.domain); + + result = map_container_stride(&map, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = map_container_count(&map, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = map_container_capacity(&map, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); +} + +MICROTEST(map_container, reports_a_null_map) { + error_t result = {}; + malunal_size_t temporary = 0; + + result = map_container_count(null, &temporary); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_NULL_CONTAINER); + + malunal_int32_t key = 1; + malunal_int32_t value = 2; + result = map_container_insert(null, &key, &value); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_NULL_CONTAINER); +} + +MICROTEST(map_container, insert_then_get_round_trips_value) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t key = 42; + malunal_int32_t value = 1337; + result = map_container_insert(&map, &key, &value); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t count = 0; + result = map_container_count(&map, &count); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(count, 1); + + malunal_int32_t out = 0; + result = map_container_get(&map, &key, &out); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(out, 1337); + + map_container_free(&map); +} + +MICROTEST(map_container, insert_of_existing_key_overwrites_value) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t key = 7; + malunal_int32_t first = 10; + malunal_int32_t second = 20; + map_container_insert(&map, &key, &first); + result = map_container_insert(&map, &key, &second); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t count = 0; + map_container_count(&map, &count); + MICROTEST_EXPECT_EQ(count, 1); + + malunal_int32_t out = 0; + map_container_get(&map, &key, &out); + MICROTEST_EXPECT_EQ(out, 20); + + map_container_free(&map); +} + +MICROTEST(map_container, append_inserts_the_pair) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t key = 3; + malunal_int32_t value = 300; + map_pair_t pair = { .key = &key, .value = &value }; + result = map_container_append(&map, &pair); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_int32_t out = 0; + result = map_container_get(&map, &key, &out); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(out, 300); + + map_container_free(&map); +} + +MICROTEST(map_container, get_reports_a_missing_key) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t key = 1; + malunal_int32_t value = 100; + map_container_insert(&map, &key, &value); + + malunal_int32_t missing = 404; + malunal_int32_t out = 0; + result = map_container_get(&map, &missing, &out); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_OUT_OF_BOUNDS); + + map_container_free(&map); +} + +MICROTEST(map_container, set_overwrites_an_existing_key_only) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t key = 5; + malunal_int32_t value = 50; + map_container_insert(&map, &key, &value); + + malunal_int32_t replacement = 555; + result = map_container_set(&map, &key, &replacement); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_int32_t out = 0; + map_container_get(&map, &key, &out); + MICROTEST_EXPECT_EQ(out, 555); + + malunal_int32_t missing = 404; + result = map_container_set(&map, &missing, &replacement); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_OUT_OF_BOUNDS); + + malunal_size_t count = 0; + map_container_count(&map, &count); + MICROTEST_EXPECT_EQ(count, 1); + + map_container_free(&map); +} + +MICROTEST(map_container, contains_finds_a_present_key) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t key = 9; + malunal_int32_t value = 90; + map_container_insert(&map, &key, &value); + + result = map_container_contains(&map, &key); + MICROTEST_EXPECT_NULL(result.domain); + + map_container_free(&map); +} + +MICROTEST(map_container, contains_rejects_an_absent_key) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t key = 9; + malunal_int32_t value = 90; + map_container_insert(&map, &key, &value); + + malunal_int32_t missing = 404; + result = map_container_contains(&map, &missing); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_FAILURE); + + map_container_free(&map); +} + +MICROTEST(map_container, remove_erases_the_key) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t keys[2] = { 1, 2 }; + malunal_int32_t vals[2] = { 10, 20 }; + map_container_insert(&map, &keys[0], &vals[0]); + map_container_insert(&map, &keys[1], &vals[1]); + + result = map_container_remove(&map, &keys[0]); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t count = 0; + map_container_count(&map, &count); + MICROTEST_EXPECT_EQ(count, 1); + + result = map_container_contains(&map, &keys[0]); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + + malunal_int32_t out = 0; + result = map_container_get(&map, &keys[1], &out); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(out, 20); + + map_container_free(&map); +} + +MICROTEST(map_container, remove_of_absent_key_succeeds) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t key = 1; + malunal_int32_t value = 10; + map_container_insert(&map, &key, &value); + + malunal_int32_t missing = 404; + result = map_container_remove(&map, &missing); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t count = 0; + map_container_count(&map, &count); + MICROTEST_EXPECT_EQ(count, 1); + + map_container_free(&map); +} + +MICROTEST(map_container, remove_keeps_the_other_keys_reachable) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + for (malunal_int32_t index = 0; index < 256; index++) { + malunal_int32_t value = index * 3; + map_container_insert(&map, &index, &value); + } + + for (malunal_int32_t index = 0; index < 256; index += 2) + map_container_remove(&map, &index); + + malunal_size_t count = 0; + map_container_count(&map, &count); + MICROTEST_EXPECT_EQ(count, 128); + + for (malunal_int32_t index = 1; index < 256; index += 2) { + malunal_int32_t out = 0; + result = map_container_get(&map, &index, &out); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(out, index * 3); + } + + for (malunal_int32_t index = 0; index < 256; index += 2) { + result = map_container_contains(&map, &index); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + } + + map_container_free(&map); +} + +MICROTEST(map_container, remove_reuses_the_erased_slots) { + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_size_t oldcap = 0; + map_container_capacity(&map, &oldcap); + for (malunal_int32_t index = 0; index < 4096; index++) { + malunal_int32_t value = index * 2; + map_container_insert(&map, &index, &value); + map_container_remove(&map, &index); + } + + malunal_size_t newcap = 0; + malunal_size_t count = 0; + map_container_capacity(&map, &newcap); + map_container_count(&map, &count); + MICROTEST_EXPECT_EQ(count, 0); + MICROTEST_EXPECT_EQ(newcap, oldcap); + + map_container_free(&map); +} + +MICROTEST(map_container, clear_resets_count_but_keeps_capacity) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_int32_t keys[2] = { 1, 2 }; + malunal_int32_t vals[2] = { 10, 20 }; + map_container_insert(&map, &keys[0], &vals[0]); + map_container_insert(&map, &keys[1], &vals[1]); + + result = map_container_clear(&map); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t count = 99; + malunal_size_t capacity = 0; + map_container_count(&map, &count); + map_container_capacity(&map, &capacity); + MICROTEST_EXPECT_EQ(count, 0); + MICROTEST_EXPECT_EQ(capacity, MALUNAL_CONTAINERS_MAP_INIT_CAPACITY); + + result = map_container_contains(&map, &keys[0]); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + + map_container_free(&map); +} + +MICROTEST(map_container, insert_past_load_factor_grows_the_map) { + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + malunal_size_t capacity = 0; + for (malunal_int32_t index = 0; index < 11; index++) { + malunal_int32_t value = index * 10; + map_container_insert(&map, &index, &value); + } + + map_container_capacity(&map, &capacity); + MICROTEST_EXPECT_EQ(capacity, MALUNAL_CONTAINERS_MAP_INIT_CAPACITY); + + malunal_int32_t key = 11; + malunal_int32_t value = 110; + map_container_insert(&map, &key, &value); + map_container_capacity(&map, &capacity); + MICROTEST_EXPECT_EQ(capacity, MALUNAL_CONTAINERS_MAP_INIT_CAPACITY * 2); + + map_container_free(&map); +} + +MICROTEST(map_container, growth_preserves_every_pair) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + for (malunal_int32_t index = 0; index < 1000; index++) { + malunal_int32_t value = index * 7; + result = map_container_insert(&map, &index, &value); + MICROTEST_EXPECT_NULL(result.domain); + } + + malunal_size_t count = 0; + malunal_size_t capacity = 0; + map_container_count(&map, &count); + map_container_capacity(&map, &capacity); + MICROTEST_EXPECT_EQ(count, 1000); + MICROTEST_EXPECT_GE(capacity, 1334); + + for (malunal_int32_t index = 0; index < 1000; index++) { + malunal_int32_t out = 0; + result = map_container_get(&map, &index, &out); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(out, index * 7); + } + + map_container_free(&map); +} + +MICROTEST(map_container, resize_rounds_to_power_of_two) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + result = map_container_resize(&map, 24); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t capacity = 0; + map_container_capacity(&map, &capacity); + MICROTEST_EXPECT_EQ(capacity, 32); + + map_container_free(&map); +} + +MICROTEST(map_container, resize_preserves_pairs_and_the_count) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + for (malunal_int32_t index = 0; index < 8; index++) { + malunal_int32_t value = index * 11; + map_container_insert(&map, &index, &value); + } + + result = map_container_resize(&map, 128); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t count = 0; + map_container_count(&map, &count); + MICROTEST_EXPECT_EQ(count, 8); + + for (malunal_int32_t index = 0; index < 8; index++) { + malunal_int32_t out = 0; + result = map_container_get(&map, &index, &out); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(out, index * 11); + } + + map_container_free(&map); +} + +MICROTEST(map_container, reserve_holds_the_pairs_without_regrowing) { + error_t result = {}; + map_container_t map = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + + result = map_container_reserve(&map, 100); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t reserved = 0; + map_container_capacity(&map, &reserved); + MICROTEST_EXPECT_GE(reserved, 134); + + for (malunal_int32_t index = 0; index < 100; index++) { + malunal_int32_t value = index; + map_container_insert(&map, &index, &value); + } + + malunal_size_t capacity = 0; + map_container_capacity(&map, &capacity); + MICROTEST_EXPECT_EQ(capacity, reserved); + + map_container_free(&map); +} + +MICROTEST(map_container, keys_and_values_line_up_with_each_other) { + error_t result = {}; + map_container_t map = {}; + vector_container_t keys = {}; + vector_container_t values = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + vector_container_init(sizeof(malunal_int32_t), libc_allocator(), &keys); + vector_container_init(sizeof(malunal_int32_t), libc_allocator(), &values); + + for (malunal_int32_t index = 0; index < 50; index++) { + malunal_int32_t value = index * 5; + map_container_insert(&map, &index, &value); + } + + result = map_container_keys(&map, (container_mptr_t)&keys); + MICROTEST_EXPECT_NULL(result.domain); + + result = map_container_values(&map, (container_mptr_t)&values); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t keycount = 0; + malunal_size_t valcount = 0; + vector_container_count(&keys, &keycount); + vector_container_count(&values, &valcount); + MICROTEST_EXPECT_EQ(keycount, 50); + MICROTEST_EXPECT_EQ(valcount, 50); + + for (malunal_size_t index = 0; index < keycount; index++) { + malunal_int32_t key = 0; + malunal_int32_t value = 0; + malunal_int32_t expected = 0; + vector_container_get(&keys, index, &key); + vector_container_get(&values, index, &value); + map_container_get(&map, &key, &expected); + MICROTEST_EXPECT_EQ(value, expected); + MICROTEST_EXPECT_EQ(value, key * 5); + } + + vector_container_free(&values); + vector_container_free(&keys); + map_container_free(&map); +} + +MICROTEST(map_container, keys_rejects_a_mismatched_container) { + error_t result = {}; + map_container_t map = {}; + vector_container_t keys = {}; + map_container_init(sizeof(malunal_int32_t), sizeof(malunal_int32_t), + libc_allocator(), &map); + vector_container_init(sizeof(malunal_int64_t), libc_allocator(), &keys); + + result = map_container_keys(&map, (container_mptr_t)&keys); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_FAILURE); + + result = map_container_keys(&map, null); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_NULL_RECEIVER); + + vector_container_free(&keys); + map_container_free(&map); +} + +MICROTEST(map_container, handles_keys_which_are_not_scalars) { + error_t result = {}; + map_container_t map = {}; + map_container_init(8, sizeof(malunal_int32_t), libc_allocator(), &map); + + malunal_char_t keys[3][8] = { "alpha", "beta", "gamma" }; + for (malunal_int32_t index = 0; index < 3; index++) { + malunal_int32_t value = index + 1; + result = map_container_insert(&map, keys[index], &value); + MICROTEST_EXPECT_NULL(result.domain); + } + + for (malunal_int32_t index = 0; index < 3; index++) { + malunal_int32_t out = 0; + result = map_container_get(&map, keys[index], &out); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(out, index + 1); + } + + malunal_char_t missing[8] = "delta"; + result = map_container_contains(&map, missing); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + + map_container_free(&map); +} diff --git a/tests/vector_container.c b/tests/vector_container.c index 5352c42..966aeb4 100644 --- a/tests/vector_container.c +++ b/tests/vector_container.c @@ -6,13 +6,13 @@ MICROTEST(vector_container, can_init_and_free) { vector_container_t vector = {}; malunal_size_t temporary = 0; - result = vector_container_init(4, 32, libc_allocator(), &vector); + result = vector_container_init(4, libc_allocator(), &vector); MICROTEST_EXPECT_NULL(result.domain); allocator_mptr_t allocator; result = vector_container_allocator(&vector, &allocator); MICROTEST_EXPECT_NULL(result.domain); - MICROTEST_EXPECT_EQ(allocator, libc_allocator()); + MICROTEST_EXPECT_PTR_EQ(allocator, libc_allocator()); result = vector_container_stride(&vector, &temporary); MICROTEST_EXPECT_NULL(result.domain); @@ -24,7 +24,7 @@ MICROTEST(vector_container, can_init_and_free) { result = vector_container_capacity(&vector, &temporary); MICROTEST_EXPECT_NULL(result.domain); - MICROTEST_EXPECT_EQ(temporary, 32); + MICROTEST_EXPECT_EQ(temporary, MALUNAL_CONTAINERS_VECTOR_INIT_CAPACITY); result = vector_container_free(&vector); MICROTEST_EXPECT_NULL(result.domain); @@ -42,10 +42,49 @@ MICROTEST(vector_container, can_init_and_free) { MICROTEST_EXPECT_EQ(temporary, 0); } +MICROTEST(vector_container, resize_rounds_to_power_of_two) { + error_t result = {}; + vector_container_t vector = {}; + vector_container_init(4, libc_allocator(), &vector); + + result = vector_container_resize(&vector, 24); + MICROTEST_EXPECT_NULL(result.domain); + + malunal_size_t capacity = 0; + vector_container_capacity(&vector, &capacity); + MICROTEST_EXPECT_EQ(capacity, 32); + printf("Capacity = %d\n", capacity); + + vector_container_free(&vector); +} + +MICROTEST(vector_container, resize_perserves_elements) { + error_t result = {}; + vector_container_t vector = {}; + vector_container_init(4, libc_allocator(), &vector); + + malunal_size_t capacity = 0; + vector_container_capacity(&vector, &capacity); + for (malunal_int32_t index = 0; index < capacity; index++) { + malunal_int32_t value = (index + 1) * 10; + vector_container_append(&vector, &value); + } + + result = vector_container_resize(&vector, 24); + MICROTEST_EXPECT_NULL(result.domain); + for (malunal_int32_t index = 0; index < capacity; index++) { + malunal_int32_t out = 0; + vector_container_get(&vector, index, &out); + MICROTEST_EXPECT_EQ(out, (index + 1) * 10); + } + + vector_container_free(&vector); +} + MICROTEST(vector_container, append_then_get_round_trips_value) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t a = 42; result = vector_container_append(&vector, &a); @@ -67,24 +106,26 @@ MICROTEST(vector_container, append_then_get_round_trips_value) { MICROTEST(vector_container, append_past_capacity_grows_and_preserves) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 4, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); - malunal_int32_t vals[5] = { 10, 20, 30, 40, 50 }; - for (malunal_int32_t index = 0; index < 5; index++) { - result = vector_container_append(&vector, &vals[index]); + malunal_size_t oldcap = 0; + vector_container_capacity(&vector, &oldcap); + for (malunal_int32_t index = 0; index < oldcap + 1; index++) { + malunal_int32_t value = (index + 1) * 10; + result = vector_container_append(&vector, &value); MICROTEST_EXPECT_NULL(result.domain); } - malunal_size_t capacity = 0; - malunal_size_t count = 0; - vector_container_capacity(&vector, &capacity); + malunal_size_t newcap = 0; + malunal_size_t count = 0; + vector_container_capacity(&vector, &newcap); vector_container_count(&vector, &count); - MICROTEST_EXPECT_GT(capacity, 5); - MICROTEST_EXPECT_EQ(count, 5); - for (malunal_int32_t index = 0; index < 5; index++) { + MICROTEST_EXPECT_GE(newcap, oldcap * 2); + MICROTEST_EXPECT_EQ(count, oldcap + 1); + for (malunal_int32_t index = 0; index < oldcap; index++) { malunal_int32_t out = 0; vector_container_get(&vector, index, &out); - MICROTEST_EXPECT_EQ(out, vals[index]); + MICROTEST_EXPECT_EQ(out, (index + 1) * 10); } vector_container_free(&vector); @@ -93,7 +134,7 @@ MICROTEST(vector_container, append_past_capacity_grows_and_preserves) { MICROTEST(vector_container, remove_first_matching_element) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t vals[3] = { 10, 20, 30 }; vector_container_append(&vector, &vals[0]); @@ -117,7 +158,7 @@ MICROTEST(vector_container, remove_first_matching_element) { MICROTEST(vector_container, remove_of_absent_element_succeeds) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t a = 1; vector_container_append(&vector, &a); @@ -134,7 +175,7 @@ MICROTEST(vector_container, remove_of_absent_element_succeeds) { MICROTEST(vector_container, contains_finds_a_present_element) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t a = 1; malunal_int32_t b = 2; @@ -150,7 +191,7 @@ MICROTEST(vector_container, contains_finds_a_present_element) { MICROTEST(vector_container, contains_rejects_an_absent_element) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t a = 1; malunal_int32_t b = 2; @@ -159,8 +200,8 @@ MICROTEST(vector_container, contains_rejects_an_absent_element) { malunal_int32_t missing = 404; result = vector_container_contains(&vector, &missing); - MICROTEST_EXPECT_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); - MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_FAILURE); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_FAILURE); vector_container_free(&vector); } @@ -168,7 +209,7 @@ MICROTEST(vector_container, contains_rejects_an_absent_element) { MICROTEST(vector_container, clear_resets_count_but_keeps_capacity) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t a = 1; malunal_int32_t b = 2; @@ -182,8 +223,8 @@ MICROTEST(vector_container, clear_resets_count_but_keeps_capacity) { malunal_size_t capacity = 0; vector_container_count(&vector, &count); vector_container_capacity(&vector, &capacity); - MICROTEST_EXPECT_EQ(count, 0); - MICROTEST_EXPECT_EQ(capacity, 32); + MICROTEST_EXPECT_EQ(count, 0); + MICROTEST_EXPECT_EQ(capacity, MALUNAL_CONTAINERS_VECTOR_INIT_CAPACITY); vector_container_free(&vector); } @@ -191,7 +232,7 @@ MICROTEST(vector_container, clear_resets_count_but_keeps_capacity) { MICROTEST(vector_container, set_overwrites_existing_element) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t vals[3] = { 10, 20, 333 }; vector_container_append(&vector, &vals[0]); @@ -210,18 +251,18 @@ MICROTEST(vector_container, set_overwrites_existing_element) { MICROTEST(vector_container, get_set_reports_out_of_bounds_at_count) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t a = 10; malunal_int32_t out = 0; result = vector_container_get(&vector, 4, &out); - MICROTEST_EXPECT_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); - MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_OUT_OF_BOUNDS); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_OUT_OF_BOUNDS); result = NO_ERROR; result = vector_container_set(&vector, 4, &out); - MICROTEST_EXPECT_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); - MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_OUT_OF_BOUNDS); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_OUT_OF_BOUNDS); vector_container_free(&vector); } @@ -229,7 +270,7 @@ MICROTEST(vector_container, get_set_reports_out_of_bounds_at_count) { MICROTEST(vector_container, insert_at_end_appends_value) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t vals[3] = { 10, 20, 30 }; vector_container_append(&vector, &vals[0]); @@ -248,7 +289,7 @@ MICROTEST(vector_container, insert_at_end_appends_value) { MICROTEST(vector_container, insert_at_middle_shifts_later_elements) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t vals[3] = { 10, 20, 30 }; vector_container_append(&vector, &vals[0]); @@ -271,7 +312,7 @@ MICROTEST(vector_container, insert_at_middle_shifts_later_elements) { MICROTEST(vector_container, remove_at_last_preserves_earlier_elements) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t vals[3] = { 10, 20, 30 }; vector_container_append(&vector, &vals[0]); @@ -297,7 +338,7 @@ MICROTEST(vector_container, remove_at_last_preserves_earlier_elements) { MICROTEST(vector_container, remove_at_middle_shifts_later_elements_left) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t vals[3] = { 10, 20, 30 }; vector_container_append(&vector, &vals[0]); @@ -323,11 +364,11 @@ MICROTEST(vector_container, remove_at_middle_shifts_later_elements_left) { MICROTEST(vector_container, remove_at_on_empty_vector_is_out_of_bounds) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); result = vector_container_remove_at(&vector, 0); - MICROTEST_EXPECT_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); - MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_OUT_OF_BOUNDS); + MICROTEST_EXPECT_PTR_EQ(result.domain, &ERROR_DOMAIN_CONTAINER_T); + MICROTEST_EXPECT_EQ(result.code, CONTAINER_ERROR_OUT_OF_BOUNDS); vector_container_free(&vector); } @@ -335,7 +376,7 @@ MICROTEST(vector_container, remove_at_on_empty_vector_is_out_of_bounds) { MICROTEST(vector_container, index_of_finds_present_element) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t vals[3] = { 50, 60, 70 }; vector_container_append(&vector, &vals[0]); @@ -353,7 +394,7 @@ MICROTEST(vector_container, index_of_finds_present_element) { MICROTEST(vector_container, index_of_reports_missing_element) { error_t result = {}; vector_container_t vector = {}; - vector_container_init(4, 32, libc_allocator(), &vector); + vector_container_init(4, libc_allocator(), &vector); malunal_int32_t a = 50; vector_container_append(&vector, &a);