Rework, no version bump
This commit is contained in:
+7
-1
@@ -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/microtest
|
||||
branch: v1.0.0
|
||||
- remote: git@git.erasit.com:malunal/types
|
||||
branch: v1.0.0
|
||||
|
||||
@@ -17,14 +19,18 @@ targets:
|
||||
srcs:
|
||||
- ./sources/container.c
|
||||
- ./sources/vector.c
|
||||
- ./sources/table.c
|
||||
|
||||
tests:
|
||||
- name: vector_tests
|
||||
- name: malunal.containers.allinone
|
||||
type: program
|
||||
deps:
|
||||
- malunal.containers
|
||||
- malunal.microtest
|
||||
srcs:
|
||||
- ./tests/vector_container.c
|
||||
- ./tests/table_container.c
|
||||
- ./tests/containers.c
|
||||
|
||||
exports:
|
||||
- malunal.containers
|
||||
|
||||
+212
-94
@@ -5,28 +5,190 @@
|
||||
* @author John Christman (sorakatadzuma@gmail.com)
|
||||
* @copyright Malunal Studios, LLC.
|
||||
*/
|
||||
#include "malunal/allocators.h"
|
||||
#include "malunal/types.h"
|
||||
#include "malunal/allocator.h"
|
||||
#include "malunal/types/object.h"
|
||||
|
||||
#ifndef MALUNAL_CONTAINERS_HEADER
|
||||
#define MALUNAL_CONTAINERS_HEADER
|
||||
|
||||
|
||||
#ifdef CONTAINER_SIZE
|
||||
#undef CONTAINER_SIZE
|
||||
#endif /* CONTAINER_SIZE */
|
||||
#ifndef MALUNAL_CONTAINER_HEADER
|
||||
#define MALUNAL_CONTAINER_HEADER
|
||||
|
||||
/**
|
||||
* @def CONTAINER_SIZE
|
||||
* @brief Defines the size of containers such that it can exist on the stack
|
||||
* without the need to heap allocate it.
|
||||
* @details This is defined based on the number of bytes of a pointer for the
|
||||
* system and the number of members within the internal container
|
||||
* implementation. It is statically asserted during compile to assure
|
||||
* it is not wrong.
|
||||
* @brief Imports an external constant for the UUID of a @c container_t.
|
||||
* @details This is needed to properly be able to cast @c container_t compliant
|
||||
* types to a @c container_t.
|
||||
*/
|
||||
#define CONTAINER_SIZE sizeof(malunal_size_t) * 6
|
||||
extern
|
||||
const uuid_t
|
||||
UUID_CONTAINER_T;
|
||||
|
||||
/**
|
||||
* @brief Imports an external constant for the error domain of @c container_t.
|
||||
* @details This is needed to detect from what domain an error has come from,
|
||||
* so that error codes can mean different things based on its domain.
|
||||
*/
|
||||
extern
|
||||
const error_domain_t
|
||||
ERROR_DOMAIN_CONTAINER_T;
|
||||
|
||||
/**
|
||||
* @brief A type definition for a pointer for a function which will attempt to
|
||||
* provided the allocator for a provided @c container into the @c out
|
||||
* parameter.
|
||||
* @param container A pointer to the container to get the allocator of.
|
||||
* @param out A pointer to where the allocator reference will be stored.
|
||||
* @returns An error code if the container could not provide the allocator.
|
||||
*/
|
||||
typedef error_t
|
||||
(*container_allocator_pfn_t)(
|
||||
malunal_mptr_t container,
|
||||
allocator_mptr_t* out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief A type definition for a pointer for a function which will attempt to
|
||||
* provide the stride for a provided @c container into the @c out
|
||||
* parameter.
|
||||
* @param container A pointer to the container to get the stride of.
|
||||
* @param out A pointer to where the stride will be stored.
|
||||
* @returns An error code if the container could not provide the stride.
|
||||
*/
|
||||
typedef error_t
|
||||
(*container_stride_pfn_t)(
|
||||
malunal_mptr_t container,
|
||||
malunal_size_t* out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief A type definition for a pointer for a function which will attempt to
|
||||
* provide the count for a provided @c container into the @c out
|
||||
* parameter.
|
||||
* @param container A pointer to the container to get the count of.
|
||||
* @param out A pointer to where the count will be stored.
|
||||
* @returns An error code if the container could not provide the count.
|
||||
*/
|
||||
typedef error_t
|
||||
(*container_count_pfn_t)(
|
||||
malunal_mptr_t container,
|
||||
malunal_size_t* out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief A type definition for a pointer for a function which will attempt to
|
||||
* provide the capacity for a provided @c container into the @c out
|
||||
* parameter.
|
||||
* @param container A pointer to the container to get the capacity of.
|
||||
* @param out A pointer to where the capacity will be stored.
|
||||
* @returns An error code if the container could not provide the capacity.
|
||||
*/
|
||||
typedef error_t
|
||||
(*container_capacity_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.
|
||||
* @param container A pointer to the container to append the element into.
|
||||
* @param element A pointer to the element to append into the container.
|
||||
* @returns An error code if the container could not append the element.
|
||||
*/
|
||||
typedef error_t
|
||||
(*container_append_pfn_t)(
|
||||
malunal_mptr_t container,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief A type definition for a pointer to a function which will attempt to
|
||||
* remove the provided @c element from the provided @c container.
|
||||
* @param container A pointer to the container to remove the element from.
|
||||
* @param element A pointer to the element to append from the container.
|
||||
* @returns An error code if the container could not remove the element.
|
||||
*/
|
||||
typedef error_t
|
||||
(*container_remove_pfn_t)(
|
||||
malunal_mptr_t container,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief A type definition for a pointer to a function which will check the
|
||||
* provided @c container for the provided @c element.
|
||||
* @param container A pointer to the container to search for the element of.
|
||||
* @param element A pointer to the element to search for in the container.
|
||||
* @returns An error code if the container could not find the element.
|
||||
*/
|
||||
typedef error_t
|
||||
(*container_contains_pfn_t)(
|
||||
malunal_iptr_t container,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief A type definition for a pointer to a function which will clear the
|
||||
* provided @c container.
|
||||
* @param container A pointer to the container to clear of all its elements.
|
||||
* @returns An error code if the container could not clear all its elements.
|
||||
*/
|
||||
typedef error_t
|
||||
(*container_clear_pfn_t)(
|
||||
malunal_mptr_t container
|
||||
);
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
/**
|
||||
* @brief A pointer for a function which will attempt to provide the
|
||||
* allocator for a provided @c container into the @c out parameter.
|
||||
*/
|
||||
container_allocator_pfn_t const allocator;
|
||||
|
||||
/**
|
||||
* @brief A pointer for a function which will attempt to provide the stride
|
||||
* for a provided @c container into the @c out parameter.
|
||||
*/
|
||||
container_stride_pfn_t const stride;
|
||||
|
||||
/**
|
||||
* @brief A pointer for a function which will attempt to provide the count
|
||||
* for a provided @c container into the @c out parameter.
|
||||
*/
|
||||
container_count_pfn_t const count;
|
||||
|
||||
/**
|
||||
* @brief A pointer for a function which will attempt to provide the
|
||||
* capacity for a provided @c container into the @c out parameter.
|
||||
*/
|
||||
container_capacity_pfn_t const capacity;
|
||||
|
||||
/**
|
||||
* @brief A pointer to a function which will attempt to append the provided
|
||||
* @c element into the provided @c container.
|
||||
*/
|
||||
container_append_pfn_t const append;
|
||||
|
||||
/**
|
||||
* @brief A pointer to a function which will attempt to remove the provided
|
||||
* @c element from the provided @c container.
|
||||
*/
|
||||
container_remove_pfn_t const remove;
|
||||
|
||||
/**
|
||||
* @brief A pointer to a function which will check the provided @c container
|
||||
* for the provided @c element.
|
||||
*/
|
||||
container_contains_pfn_t const contains;
|
||||
|
||||
/**
|
||||
* @brief A pointer to a function which will clear the provided @c container.
|
||||
*/
|
||||
container_clear_pfn_t const clear;
|
||||
} container_vtable_t;
|
||||
|
||||
/**
|
||||
* @brief Defines an abstract class type for containers.
|
||||
@@ -36,7 +198,12 @@
|
||||
* and a virtual function table for container specific functions.
|
||||
*/
|
||||
typedef struct {
|
||||
malunal_uint8_t __opaque[CONTAINER_SIZE];
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
@@ -55,29 +222,24 @@ typedef const container_t* container_iptr_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines a set of exception that a container may throw.
|
||||
* @brief Defines a set of errors that a container may throw.
|
||||
* @details These are extremely useful for debugging an container or catching
|
||||
* runtime issues that can be fixed.
|
||||
*/
|
||||
typedef enum {
|
||||
CONTAINER_ERROR_SUCCESS,
|
||||
CONTAINER_ERROR_FAILURE,
|
||||
CONTAINER_ERROR_NULL_CONTAINER,
|
||||
CONTAINER_ERROR_NULL_ALLOCATOR,
|
||||
CONTAINER_ERROR_NULL_CONTEXT,
|
||||
CONTAINER_ERROR_OUT_OF_BOUNDS,
|
||||
CONTAINER_ERROR_OUT_OF_MEMORY,
|
||||
} container_exception_t;
|
||||
} container_error_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Obtains the allocator for the given container.
|
||||
* @param container A pointer to the container to obtain the allocator from.
|
||||
* @param out A pointer to where to store the obtained allocator pointer.
|
||||
* @returns An exception if the given container could not provide the allocator
|
||||
* pointer; otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the given container could not provide the allocator.
|
||||
*/
|
||||
container_exception_t
|
||||
error_t
|
||||
container_allocator(
|
||||
container_iptr_t container,
|
||||
allocator_mptr_t* out
|
||||
@@ -87,10 +249,9 @@ container_allocator(
|
||||
* @brief Obtains the stride for the given container.
|
||||
* @param container A pointer to the container to obtain the stride from.
|
||||
* @param out A pointer to where to store the obtained stride.
|
||||
* @returns An exception if the given container could not provide the stride;
|
||||
* otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the given container could not provide the stride.
|
||||
*/
|
||||
container_exception_t
|
||||
error_t
|
||||
container_stride(
|
||||
container_iptr_t container,
|
||||
malunal_size_t* out
|
||||
@@ -100,10 +261,9 @@ container_stride(
|
||||
* @brief Obtains the count for the given container.
|
||||
* @param container A pointer to the container to obtain the count from.
|
||||
* @param out A pointer to where to store the obtained count.
|
||||
* @returns An exception if the given container could not provide the count;
|
||||
* otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the given container could not provide the count.
|
||||
*/
|
||||
container_exception_t
|
||||
error_t
|
||||
container_count(
|
||||
container_iptr_t container,
|
||||
malunal_size_t* out
|
||||
@@ -113,68 +273,22 @@ container_count(
|
||||
* @brief Obtains the capacity for the given container.
|
||||
* @param container A pointer to the container to obtain the capacity from.
|
||||
* @param out A pointer to where to store the obtained capacity.
|
||||
* @returns An exception if the given container could not provide the capacity;
|
||||
* otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the given container could not provide the capacity.
|
||||
*/
|
||||
container_exception_t
|
||||
error_t
|
||||
container_capacity(
|
||||
container_iptr_t container,
|
||||
malunal_size_t* out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Initializes a container, by setting some necessary variables and
|
||||
* acquiring its memory.
|
||||
* @param container A pointer to the container to initialize.
|
||||
* @param allocator A pointer to the allocator to use for initialization.
|
||||
* @param stride The size of the elements that will be stored in the memory
|
||||
* region of the container.
|
||||
* @param capacity The maximum number of elements the container can store in
|
||||
* the memory region it has acquired.
|
||||
* @returns An exception if the given container could not be initialized;
|
||||
* otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
*/
|
||||
container_exception_t
|
||||
container_initialize(
|
||||
container_mptr_t container,
|
||||
allocator_mptr_t allocator,
|
||||
malunal_size_t stride,
|
||||
malunal_size_t capacity
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Finalizes a container, by releasing its acquired memory.
|
||||
* @param container A pointer to the container to finalize.
|
||||
* @returns An exception if the given container could not be finalized;
|
||||
* otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
*/
|
||||
container_exception_t
|
||||
container_finalize(
|
||||
container_mptr_t container
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Clears the container, removing all elements from it.
|
||||
* @param container A pointer to the container to clear.
|
||||
* @returns An exception describing if the container could be cleared or an
|
||||
* exact error for why the container could not be cleared.
|
||||
* @remarks Generally speaking, if the container can be cleared successfully,
|
||||
* The most common response should be @c CONTAINER_ERROR_SUCCESS.
|
||||
*/
|
||||
container_exception_t
|
||||
container_clear(
|
||||
container_mptr_t container
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Appends a new element to the container.
|
||||
* @param container A pointer to the container to append the element to.
|
||||
* @param element A pointer to the data of the element to append to the
|
||||
* given container.
|
||||
* @returns An exception describing if the element was appended to the container
|
||||
* or an exact error for why it could not be appended.
|
||||
* @returns An error if the container could not append the element.
|
||||
*/
|
||||
container_exception_t
|
||||
error_t
|
||||
container_append(
|
||||
container_mptr_t container,
|
||||
malunal_iptr_t element
|
||||
@@ -185,12 +299,9 @@ container_append(
|
||||
* @param container A pointer to the container to remove the element from.
|
||||
* @param element A pointer to the data of the element to remove from the
|
||||
* given container.
|
||||
* @returns An exception describing if the element was removed from the container
|
||||
* or an exact error for why it could not be removed.
|
||||
* @remarks Generally speaking, if an element does not exist in the container,
|
||||
* the most common response should be @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the container could not remove the element.
|
||||
*/
|
||||
container_exception_t
|
||||
error_t
|
||||
container_remove(
|
||||
container_mptr_t container,
|
||||
malunal_iptr_t element
|
||||
@@ -201,15 +312,22 @@ container_remove(
|
||||
* @param container A pointer to the container to find the element within.
|
||||
* @param element A pointer to the data of the element to find within the
|
||||
* given container.
|
||||
* @returns An exception describing if the element is contained within the
|
||||
* container or an exact error for why it could not find the element.
|
||||
* @remarks Generally speaking, if the element does exist in the container, the
|
||||
* most common response should be @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the container could not find the element.
|
||||
*/
|
||||
container_exception_t
|
||||
error_t
|
||||
container_contains(
|
||||
container_iptr_t container,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
#endif /* MALUNAL_CONTAINERS_HEADER */
|
||||
/**
|
||||
* @brief Clears the container, removing all elements from it.
|
||||
* @param container A pointer to the container to clear.
|
||||
* @returns An error if the container could not clear all of its elements.
|
||||
*/
|
||||
error_t
|
||||
container_clear(
|
||||
container_mptr_t container
|
||||
);
|
||||
|
||||
#endif /* MALUNAL_CONTAINER_HEADER */
|
||||
|
||||
@@ -21,38 +21,158 @@
|
||||
* meant for vectors.
|
||||
*/
|
||||
typedef struct {
|
||||
malunal_uint8_t __opaque[sizeof(container_t)];
|
||||
} vector_t;
|
||||
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_t* vector_mptr_t;
|
||||
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_t* vector_iptr_t;
|
||||
typedef const vector_container_t* vector_container_iptr_t;
|
||||
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
error_t
|
||||
vector_container_init(
|
||||
malunal_size_t stride,
|
||||
malunal_size_t capacity,
|
||||
allocator_mptr_t allocator,
|
||||
vector_container_mptr_t vector
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Frees the backing memory of the vector.
|
||||
* @param vector A pointer to the vector to free.
|
||||
* @returns An error if the vector could not be freed.
|
||||
*/
|
||||
error_t
|
||||
vector_container_free(
|
||||
vector_container_mptr_t vector
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Obtains the allocator for the given vector.
|
||||
* @param vector A pointer to the vector to obtain the allocator from.
|
||||
* @param out A pointer to where to store the obtained allocator pointer.
|
||||
* @returns An error if the given vector could not provide the allocator.
|
||||
*/
|
||||
error_t
|
||||
vector_container_allocator(
|
||||
vector_container_iptr_t vector,
|
||||
allocator_mptr_t* out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Obtains the stride for the given vector.
|
||||
* @param vector A pointer to the vector to obtain the stride from.
|
||||
* @param out A pointer to where to store the obtained stride.
|
||||
* @returns An error if the given vector could not provide the stride.
|
||||
*/
|
||||
error_t
|
||||
vector_container_stride(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_size_t* out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Obtains the count for the given vector.
|
||||
* @param vector A pointer to the vector to obtain the count from.
|
||||
* @param out A pointer to where to store the obtained count.
|
||||
* @returns An error if the given vector could not provide the count.
|
||||
*/
|
||||
error_t
|
||||
vector_container_count(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_size_t* out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Obtains the capacity for the given vector.
|
||||
* @param vector A pointer to the vector to obtain the capacity from.
|
||||
* @param out A pointer to where to store the obtained capacity.
|
||||
* @returns An error if the given vector could not provide the capacity.
|
||||
*/
|
||||
error_t
|
||||
vector_container_capacity(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_size_t* out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Appends a new element to the vector.
|
||||
* @param vector A pointer to the vector to append the element to.
|
||||
* @param element A pointer to the data of the element to append.
|
||||
* @returns An error if the vector could not append the element.
|
||||
*/
|
||||
error_t
|
||||
vector_container_append(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Removes the specified element from the vector.
|
||||
* @param vector A pointer to the vector to remove the element from.
|
||||
* @param element A pointer to the data of the element to remove.
|
||||
* @returns An error if the vector could not remove the element.
|
||||
*/
|
||||
error_t
|
||||
vector_container_remove(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Checks the vector for the given element.
|
||||
* @param vector A pointer to the vector to find the element within.
|
||||
* @param element A pointer to the data of the element to search for.
|
||||
* @returns An error if the vector could not find the element.
|
||||
*/
|
||||
error_t
|
||||
vector_container_contains(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Clears the vector, removing all elements from it.
|
||||
* @param vector A pointer to the vector to clear.
|
||||
* @returns An error if the vector could not clear all its elements.
|
||||
*/
|
||||
error_t
|
||||
vector_container_clear(
|
||||
vector_container_mptr_t vector
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Gets the element at the given index from the given vector, copying
|
||||
* the data into the memory region of the @c outptr address.
|
||||
* @param vector A pointer to the vector to get the element from.
|
||||
* @param index The index of the element to get from the given vector.
|
||||
* @param outptr A pointer to the memory region to copy the element to.
|
||||
* @returns An exception if the given vector can not provide the element at the
|
||||
* given index; otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @param element A pointer to the memory region to copy the element to.
|
||||
* @returns An error if the given vector can not get the element.
|
||||
*/
|
||||
container_exception_t
|
||||
vector_get(
|
||||
vector_iptr_t vector,
|
||||
error_t
|
||||
vector_container_get(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_mptr_t outptr
|
||||
malunal_mptr_t element
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -61,12 +181,11 @@ vector_get(
|
||||
* @param vector A pointer to the vector to set the element into.
|
||||
* @param index The index of the element to set in the given vector.
|
||||
* @param element A pointer to the data of the element to set in the vector.
|
||||
* @returns An exception if the given vector can not set the element at the
|
||||
* given index; otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the given vector can not set the element.
|
||||
*/
|
||||
container_exception_t
|
||||
vector_set(
|
||||
vector_mptr_t vector,
|
||||
error_t
|
||||
vector_container_set(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
@@ -76,14 +195,12 @@ vector_set(
|
||||
* copying the data from the memory region of the @c element.
|
||||
* @param vector A pointer to the vector to insert the element into.
|
||||
* @param index The index within the vector to insert the element.
|
||||
* @param element A pointer to the data of the element to insert into the
|
||||
* vector at the given index; otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An exception if the given vector can not insert the element at the
|
||||
* given index; otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @param element A pointer to the data of the element to insert.
|
||||
* @returns An error if the given vector can not insert the element.
|
||||
*/
|
||||
container_exception_t
|
||||
vector_insert_at(
|
||||
vector_mptr_t vector,
|
||||
error_t
|
||||
vector_container_insert_at(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
@@ -92,38 +209,27 @@ vector_insert_at(
|
||||
* @brief Removes the element at the given index from the given vector.
|
||||
* @param vector A pointer to the vector to remove the element from.
|
||||
* @param index The index of the element to remove from the given vector.
|
||||
* @returns An exception if the given vector can not remove the element at the
|
||||
* given index; otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the given vector can not remove the element.
|
||||
*/
|
||||
container_exception_t
|
||||
vector_remove_at(
|
||||
vector_mptr_t vector,
|
||||
error_t
|
||||
vector_container_remove_at(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_size_t index
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Finds the index of the given element within the given vector.
|
||||
* @param vector A pointer to the vector to find the index of the element.
|
||||
* @param element A pointer to the data of the element to find the index of
|
||||
* within the vector.
|
||||
* @param element A pointer to the data of the element to find the index of.
|
||||
* @param outidx A pointer to where the index should be stored.
|
||||
* @returns An exception if the given vector can not provide the index of the
|
||||
* element within it; otherwise, @c CONTAINER_ERROR_SUCCESS.
|
||||
* @returns An error if the given vector can not provide the index of the
|
||||
* element within it.
|
||||
*/
|
||||
container_exception_t
|
||||
vector_index_of(
|
||||
vector_mptr_t vector,
|
||||
error_t
|
||||
vector_container_index_of(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_iptr_t element,
|
||||
malunal_size_t* outidx
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Provides an vector container.
|
||||
* @details This will provide appropriate vector container functions when the
|
||||
* instance is created.
|
||||
* @returns An container instance that is populated to be an vector.
|
||||
*/
|
||||
vector_t
|
||||
vector_container();
|
||||
|
||||
#endif /* MALUNAL_VECTOR_HEADER */
|
||||
|
||||
+89
-135
@@ -1,182 +1,136 @@
|
||||
#include "malunal/container.h"
|
||||
|
||||
typedef container_exception_t
|
||||
(*container_initialize_pfn_t)(
|
||||
container_mptr_t container,
|
||||
allocator_mptr_t allocator,
|
||||
malunal_size_t stride,
|
||||
malunal_size_t capacity
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*container_finalize_pfn_t)(
|
||||
container_mptr_t container
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*container_clear_pfn_t)(
|
||||
container_mptr_t container
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*container_append_pfn_t)(
|
||||
container_mptr_t container,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*container_remove_pfn_t)(
|
||||
container_mptr_t container,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*container_contains_pfn_t)(
|
||||
container_iptr_t container,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
const uuid_t UUID_CONTAINER_T = {
|
||||
.tl = 0xF4784CF5,
|
||||
.tm = 0xFE95,
|
||||
.thv = 0x4529,
|
||||
.csr = 0xBD,
|
||||
.csl = 0x6D,
|
||||
.nbs = {
|
||||
0x82, 0x53, 0x70,
|
||||
0x85, 0xC4, 0xAE
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
container_initialize_pfn_t const initialize;
|
||||
container_finalize_pfn_t const finalize;
|
||||
container_clear_pfn_t const clear;
|
||||
container_append_pfn_t const append;
|
||||
container_remove_pfn_t const remove;
|
||||
container_contains_pfn_t const contains;
|
||||
} virtual_table_t;
|
||||
static
|
||||
malunal_cstr_t
|
||||
describe(malunal_int32_t code) {
|
||||
switch (code) {
|
||||
case CONTAINER_ERROR_FAILURE:
|
||||
return "Generic container error";
|
||||
case CONTAINER_ERROR_NULL_CONTAINER:
|
||||
return "Container provided was null";
|
||||
case CONTAINER_ERROR_OUT_OF_BOUNDS:
|
||||
return "Index into container was out of bounds";
|
||||
}
|
||||
return "Unknown container error";
|
||||
}
|
||||
|
||||
typedef virtual_table_t* base_vtable_mptr_t;
|
||||
typedef const virtual_table_t* vtable_iptr_t;
|
||||
const error_domain_t ERROR_DOMAIN_CONTAINER_T = {
|
||||
.describe = &describe,
|
||||
.name = "malunal.container.error"
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
vtable_iptr_t const vtable;
|
||||
|
||||
allocator_mptr_t allocator;
|
||||
malunal_mptr_t context;
|
||||
malunal_size_t stride;
|
||||
malunal_size_t count;
|
||||
malunal_size_t capacity;
|
||||
} implementation_t;
|
||||
|
||||
typedef implementation_t* impl_mptr_t;
|
||||
typedef const implementation_t* impl_iptr_t;
|
||||
|
||||
|
||||
// Verify these are the same.
|
||||
_Static_assert(
|
||||
CONTAINER_SIZE == sizeof(implementation_t),
|
||||
"container_t must be the same size as its implementation"
|
||||
);
|
||||
|
||||
|
||||
container_exception_t
|
||||
error_t
|
||||
container_allocator(
|
||||
container_iptr_t container,
|
||||
allocator_mptr_t* out
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)container;
|
||||
if (implementation == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTAINER;
|
||||
*out = implementation->allocator;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
return container != null
|
||||
? container->vtable->allocator(container, out)
|
||||
: (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
error_t
|
||||
container_stride(
|
||||
container_iptr_t container,
|
||||
malunal_size_t* out
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)container;
|
||||
if (implementation == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTAINER;
|
||||
*out = implementation->stride;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
return container != null
|
||||
? container->vtable->stride(container, out)
|
||||
: (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
error_t
|
||||
container_count(
|
||||
container_iptr_t container,
|
||||
malunal_size_t* out
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)container;
|
||||
if (implementation == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTAINER;
|
||||
*out = implementation->count;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
return container != null
|
||||
? container->vtable->count(container, out)
|
||||
: (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
error_t
|
||||
container_capacity(
|
||||
container_iptr_t container,
|
||||
malunal_size_t* out
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)container;
|
||||
if (implementation == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTAINER;
|
||||
*out = implementation->capacity;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
return container != null
|
||||
? container->vtable->capacity(container, out)
|
||||
: (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
container_initialize(
|
||||
container_mptr_t container,
|
||||
allocator_mptr_t allocator,
|
||||
malunal_size_t stride,
|
||||
malunal_size_t capacity
|
||||
) {
|
||||
impl_mptr_t implementation = (impl_mptr_t)container;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->initialize(container, allocator, stride, capacity)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
container_finalize(container_mptr_t container) {
|
||||
impl_mptr_t implementation = (impl_mptr_t)container;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->finalize(container)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
container_clear(container_mptr_t container) {
|
||||
impl_mptr_t implementation = (impl_mptr_t)container;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->clear(container)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
error_t
|
||||
container_append(
|
||||
container_mptr_t container,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
impl_mptr_t implementation = (impl_mptr_t)container;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->append(container, element)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
return container != null
|
||||
? container->vtable->append(container, element)
|
||||
: (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
error_t
|
||||
container_remove(
|
||||
container_mptr_t container,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
impl_mptr_t implementation = (impl_mptr_t)container;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->remove(container, element)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
return container != null
|
||||
? container->vtable->remove(container, element)
|
||||
: (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
error_t
|
||||
container_contains(
|
||||
container_iptr_t container,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)container;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->contains(container, element)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
return container != null
|
||||
? container->vtable->contains(container, element)
|
||||
: (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
+367
-413
@@ -1,500 +1,454 @@
|
||||
#include <memory.h>
|
||||
#include "malunal/containers/vector.h"
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_initialize_pfn_t)(
|
||||
vector_mptr_t vector,
|
||||
allocator_mptr_t allocator,
|
||||
malunal_size_t stride,
|
||||
malunal_size_t capacity
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_finalize_pfn_t)(
|
||||
vector_mptr_t vector
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_clear_pfn_t)(
|
||||
vector_mptr_t vector
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_append_pfn_t)(
|
||||
vector_mptr_t vector,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_remove_pfn_t)(
|
||||
vector_mptr_t vector,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_contains_pfn_t)(
|
||||
vector_iptr_t vector,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_get_pfn_t)(
|
||||
vector_iptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_mptr_t outptr
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_set_pfn_t)(
|
||||
vector_mptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_insert_at_pfn_t)(
|
||||
vector_mptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_iptr_t element
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_remove_at_pfn_t)(
|
||||
vector_mptr_t vector,
|
||||
malunal_size_t index
|
||||
);
|
||||
|
||||
typedef container_exception_t
|
||||
(*vector_index_of_pfn_t)(
|
||||
vector_iptr_t vector,
|
||||
malunal_iptr_t element,
|
||||
malunal_size_t* outidx
|
||||
);
|
||||
|
||||
|
||||
typedef struct VectorVTable {
|
||||
// Container generic.
|
||||
vector_initialize_pfn_t const initialize;
|
||||
vector_finalize_pfn_t const finalize;
|
||||
vector_clear_pfn_t const clear;
|
||||
vector_append_pfn_t const append;
|
||||
vector_remove_pfn_t const remove;
|
||||
vector_contains_pfn_t const contains;
|
||||
|
||||
// Vector specific.
|
||||
vector_get_pfn_t const get;
|
||||
vector_set_pfn_t const set;
|
||||
vector_insert_at_pfn_t const insert_at;
|
||||
vector_remove_at_pfn_t const remove_at;
|
||||
vector_index_of_pfn_t const index_of;
|
||||
} virtual_table_t;
|
||||
|
||||
typedef virtual_table_t* vtable_mptr_t;
|
||||
typedef const virtual_table_t* vtable_iptr_t;
|
||||
|
||||
|
||||
// Identical to container.
|
||||
typedef struct {
|
||||
vtable_iptr_t const vtable;
|
||||
object_t object;
|
||||
container_t container;
|
||||
|
||||
allocator_mptr_t allocator;
|
||||
malunal_mptr_t context;
|
||||
malunal_size_t stride;
|
||||
malunal_size_t count;
|
||||
malunal_size_t capacity;
|
||||
} implementation_t;
|
||||
malunal_mptr_t buffer;
|
||||
} impl_t;
|
||||
|
||||
typedef implementation_t* impl_mptr_t;
|
||||
typedef const implementation_t* impl_iptr_t;
|
||||
typedef impl_t* impl_mptr_t;
|
||||
typedef const impl_t* impl_iptr_t;
|
||||
_Static_assert(
|
||||
sizeof(vector_container_t) == sizeof(impl_t),
|
||||
"Vector container must be the size of its implementation"
|
||||
);
|
||||
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_initialize_impl(
|
||||
impl_mptr_t vector,
|
||||
allocator_mptr_t allocator,
|
||||
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,
|
||||
.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
|
||||
error_t
|
||||
vector_container_realloc(impl_mptr_t self) {
|
||||
if ((float)self->count / self->capacity < 0.75f)
|
||||
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);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
buffer = memmove(buffer, self->buffer, oldcap);
|
||||
result = allocator_dispose(self->allocator, self->buffer, oldcap);
|
||||
self->buffer = buffer;
|
||||
self->capacity = self->capacity * 2;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
vector_container_init(
|
||||
malunal_size_t stride,
|
||||
malunal_size_t capacity
|
||||
malunal_size_t capacity,
|
||||
allocator_mptr_t allocator,
|
||||
vector_container_mptr_t vector
|
||||
) {
|
||||
malunal_size_t bytes = capacity * stride;
|
||||
allocation_result_t result = allocator_acquire(allocator, bytes);
|
||||
if (!result.threw) {
|
||||
vector->allocator = allocator;
|
||||
vector->context = result.address;
|
||||
vector->stride = stride;
|
||||
vector->capacity = capacity;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
}
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
switch (result.error) {
|
||||
case ALLOCATION_ERROR_NULL_ALLOCATOR:
|
||||
case ALLOCATION_ERROR_NULL_CONTEXT:
|
||||
case ALLOCATION_ERROR_NULL_UPSTREAM:
|
||||
return CONTAINER_ERROR_NULL_ALLOCATOR;
|
||||
|
||||
case ALLOCATION_ERROR_OUT_OF_MEMORY:
|
||||
return CONTAINER_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_finalize_impl(impl_mptr_t vector) {
|
||||
malunal_size_t bytes = vector->capacity * vector->stride;
|
||||
allocation_error_t error = allocator_release(
|
||||
vector->allocator,
|
||||
vector->context,
|
||||
bytes
|
||||
impl_mptr_t self = (impl_mptr_t)vector;
|
||||
error_t result = allocator_acquire(
|
||||
allocator,
|
||||
stride * capacity,
|
||||
&self->buffer
|
||||
);
|
||||
|
||||
switch (error) {
|
||||
case ALLOCATION_ERROR_SUCCESS:
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
case ALLOCATION_ERROR_NULL_ALLOCATOR:
|
||||
case ALLOCATION_ERROR_NULL_CONTEXT:
|
||||
case ALLOCATION_ERROR_NULL_UPSTREAM:
|
||||
return CONTAINER_ERROR_NULL_ALLOCATOR;
|
||||
|
||||
case ALLOCATION_ERROR_LEAKY_MEMORY:
|
||||
case ALLOCATION_ERROR_NOT_MY_ADDRESS:
|
||||
case ALLOCATION_ERROR_FAILURE:
|
||||
return CONTAINER_ERROR_FAILURE;
|
||||
}
|
||||
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;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_clear_impl(impl_mptr_t vector) {
|
||||
vector->count = 0;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_append_impl(
|
||||
impl_mptr_t vector,
|
||||
malunal_iptr_t element
|
||||
error_t
|
||||
vector_container_free(
|
||||
vector_container_mptr_t vector
|
||||
) {
|
||||
if (vector->context == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTEXT;
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
if (vector->count < vector->capacity)
|
||||
goto copy_element;
|
||||
|
||||
malunal_size_t doubled = vector->capacity * 2;
|
||||
allocation_result_t result = allocator_reacquire(
|
||||
vector->allocator,
|
||||
vector->context,
|
||||
vector->capacity * vector->stride,
|
||||
doubled * vector->stride
|
||||
impl_mptr_t self = (impl_mptr_t)vector;
|
||||
error_t result = allocator_dispose(
|
||||
self->allocator,
|
||||
self->buffer,
|
||||
self->stride * self->capacity
|
||||
);
|
||||
|
||||
if (!result.threw) {
|
||||
vector->context = result.address;
|
||||
vector->capacity = doubled;
|
||||
goto copy_element;
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
self->stride = 0;
|
||||
self->count = 0;
|
||||
self->capacity = 0;
|
||||
self->buffer = null;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
switch (result.error) {
|
||||
case ALLOCATION_ERROR_NULL_ALLOCATOR:
|
||||
case ALLOCATION_ERROR_NULL_UPSTREAM:
|
||||
case ALLOCATION_ERROR_NULL_CONTEXT:
|
||||
return CONTAINER_ERROR_NULL_ALLOCATOR;
|
||||
error_t
|
||||
vector_container_allocator(
|
||||
vector_container_iptr_t vector,
|
||||
allocator_mptr_t* out
|
||||
) {
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
case ALLOCATION_ERROR_LEAKY_MEMORY:
|
||||
case ALLOCATION_ERROR_NOT_MY_ADDRESS:
|
||||
case ALLOCATION_ERROR_FAILURE:
|
||||
return CONTAINER_ERROR_FAILURE;
|
||||
impl_iptr_t self = (impl_iptr_t)vector;
|
||||
*out = self->allocator;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
copy_element:
|
||||
malunal_uint8_t* buffer = vector->context;
|
||||
buffer = buffer + vector->stride * vector->count;
|
||||
memcpy(buffer, element, vector->stride);
|
||||
error_t
|
||||
vector_container_stride(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_size_t* out
|
||||
) {
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
vector->count++;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
impl_iptr_t self = (impl_iptr_t)vector;
|
||||
*out = self->stride;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_remove_impl(
|
||||
impl_mptr_t vector,
|
||||
error_t
|
||||
vector_container_count(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_size_t* out
|
||||
) {
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
impl_iptr_t self = (impl_iptr_t)vector;
|
||||
*out = self->count;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
vector_container_capacity(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_size_t* out
|
||||
) {
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
impl_iptr_t self = (impl_iptr_t)vector;
|
||||
*out = self->capacity;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
vector_container_append(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
if (vector->context == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTEXT;
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
malunal_size_t index = 0;
|
||||
vector_mptr_t erased = (vector_mptr_t)vector;
|
||||
container_exception_t error = vector_index_of(erased, element, &index);
|
||||
if (error != CONTAINER_ERROR_SUCCESS)
|
||||
return error;
|
||||
impl_mptr_t self = (impl_mptr_t)vector;
|
||||
error_t result = vector_container_realloc(self);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
return index != -1
|
||||
? vector_remove_at(erased, index)
|
||||
: CONTAINER_ERROR_SUCCESS;
|
||||
malunal_uint8_t* buffer = self->buffer;
|
||||
buffer += self->stride * self->count;
|
||||
memcpy(buffer, element, self->stride);
|
||||
self->count++;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_contains_impl(
|
||||
impl_iptr_t vector,
|
||||
error_t
|
||||
vector_container_remove(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
if (vector->context == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTEXT;
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
malunal_size_t index = 0;
|
||||
vector_mptr_t erased = (vector_mptr_t)vector;
|
||||
container_exception_t error = vector_index_of(erased, element, &index);
|
||||
if (error != CONTAINER_ERROR_SUCCESS)
|
||||
return error;
|
||||
malunal_size_t index;
|
||||
impl_mptr_t self = (impl_mptr_t)vector;
|
||||
error_t result = vector_container_index_of(vector, element, &index);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
return index != -1
|
||||
? CONTAINER_ERROR_SUCCESS
|
||||
: CONTAINER_ERROR_FAILURE;
|
||||
return index != (malunal_size_t)-1
|
||||
? vector_container_remove_at(vector, index)
|
||||
: NO_ERROR;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_get_def_impl(
|
||||
impl_iptr_t vector,
|
||||
error_t
|
||||
vector_container_contains(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
malunal_size_t index;
|
||||
impl_iptr_t self = (impl_iptr_t)vector;
|
||||
error_t result = vector_container_index_of(vector, element, &index);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
return index == (malunal_size_t)-1
|
||||
? (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_FAILURE
|
||||
}
|
||||
: NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
vector_container_clear(
|
||||
vector_container_mptr_t vector
|
||||
) {
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
impl_mptr_t self = (impl_mptr_t)vector;
|
||||
self->count = 0;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
vector_container_get(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_mptr_t outptr
|
||||
malunal_mptr_t element
|
||||
) {
|
||||
if (vector->context == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTEXT;
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
if (index < 0 || index >= vector->count)
|
||||
return CONTAINER_ERROR_OUT_OF_BOUNDS;
|
||||
impl_iptr_t self = (impl_iptr_t)vector;
|
||||
if (index >= self->count)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_OUT_OF_BOUNDS
|
||||
};
|
||||
|
||||
malunal_uint8_t* buffer = vector->context;
|
||||
buffer = buffer + vector->stride * index;
|
||||
memcpy(outptr, buffer, vector->stride);
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
malunal_uint8_t* buffer = self->buffer;
|
||||
buffer += self->stride * index;
|
||||
memcpy(element, buffer, self->stride);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_set_def_impl(
|
||||
impl_mptr_t vector,
|
||||
error_t
|
||||
vector_container_set(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
if (vector->context == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTEXT;
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
if (index < 0 || index >= vector->count)
|
||||
return CONTAINER_ERROR_OUT_OF_BOUNDS;
|
||||
impl_mptr_t self = (impl_mptr_t)vector;
|
||||
if (index >= self->count)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_OUT_OF_BOUNDS
|
||||
};
|
||||
|
||||
malunal_uint8_t* buffer = vector->context;
|
||||
buffer = buffer + vector->stride * index;
|
||||
memcpy(buffer, element, vector->stride);
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
malunal_uint8_t* buffer = self->buffer;
|
||||
buffer += self->stride * index;
|
||||
memcpy(buffer, element, self->stride);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_insert_at_def_impl(
|
||||
impl_mptr_t vector,
|
||||
error_t
|
||||
vector_container_insert_at(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
if (vector->context == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTEXT;
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
if (index < 0 || index > vector->count)
|
||||
return CONTAINER_ERROR_OUT_OF_BOUNDS;
|
||||
impl_mptr_t self = (impl_mptr_t)vector;
|
||||
if (index > self->count)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_OUT_OF_BOUNDS
|
||||
};
|
||||
|
||||
if (vector->count < vector->capacity)
|
||||
goto insert_element;
|
||||
error_t result = vector_container_realloc(self);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
malunal_size_t doubled = vector->capacity * 2;
|
||||
allocation_result_t result = allocator_reacquire(
|
||||
vector->allocator,
|
||||
vector->context,
|
||||
vector->capacity * vector->stride,
|
||||
doubled * vector->stride
|
||||
);
|
||||
malunal_uint8_t* orig = self->buffer;
|
||||
orig += self->stride * index;
|
||||
|
||||
if (!result.threw) {
|
||||
vector->context = result.address;
|
||||
vector->capacity = doubled;
|
||||
goto insert_element;
|
||||
malunal_uint8_t* dest = orig + self->stride;
|
||||
memmove(dest, orig, self->stride * self->count - index);
|
||||
memcpy(orig, element, self->stride);
|
||||
self->count++;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
switch (result.error) {
|
||||
case ALLOCATION_ERROR_NULL_ALLOCATOR:
|
||||
case ALLOCATION_ERROR_NULL_UPSTREAM:
|
||||
case ALLOCATION_ERROR_NULL_CONTEXT:
|
||||
return CONTAINER_ERROR_NULL_ALLOCATOR;
|
||||
|
||||
case ALLOCATION_ERROR_LEAKY_MEMORY:
|
||||
case ALLOCATION_ERROR_NOT_MY_ADDRESS:
|
||||
case ALLOCATION_ERROR_FAILURE:
|
||||
return CONTAINER_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
insert_element:
|
||||
malunal_size_t offset = vector->stride * index;
|
||||
malunal_uint8_t* orig = vector->context;
|
||||
orig = orig + offset;
|
||||
|
||||
malunal_size_t window = vector->count - index;
|
||||
malunal_size_t bytes = vector->stride * window;
|
||||
malunal_uint8_t* dest = orig + vector->stride;
|
||||
memmove(dest, orig, bytes);
|
||||
memcpy(orig, element, vector->stride);
|
||||
vector->count++;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_remove_at_def_impl(
|
||||
impl_mptr_t vector,
|
||||
error_t
|
||||
vector_container_remove_at(
|
||||
vector_container_mptr_t vector,
|
||||
malunal_size_t index
|
||||
) {
|
||||
if (vector->context == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTEXT;
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
if (index < 0 || index >= vector->count)
|
||||
return CONTAINER_ERROR_OUT_OF_BOUNDS;
|
||||
impl_mptr_t self = (impl_mptr_t)vector;
|
||||
if (self->count == 0 || index >= self->count)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_OUT_OF_BOUNDS
|
||||
};
|
||||
|
||||
malunal_size_t offset = vector->stride * index;
|
||||
malunal_uint8_t* dest = vector->context;
|
||||
dest = dest + offset;
|
||||
malunal_uint8_t* dest = self->buffer;
|
||||
dest += self->stride * index;
|
||||
|
||||
malunal_size_t window = vector->count - index;
|
||||
malunal_size_t bytes = vector->stride * window;
|
||||
malunal_uint8_t* orig = dest + vector->stride;
|
||||
memmove(dest, orig, bytes);
|
||||
vector->count--;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
malunal_uint8_t* orig = dest + self->stride;
|
||||
memmove(dest, orig, self->stride * self->count - index);
|
||||
self->count--;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static
|
||||
container_exception_t
|
||||
vector_index_of_def_impl(
|
||||
impl_mptr_t vector,
|
||||
error_t
|
||||
vector_container_index_of(
|
||||
vector_container_iptr_t vector,
|
||||
malunal_iptr_t element,
|
||||
malunal_size_t* outidx
|
||||
) {
|
||||
if (vector->context == NULL_ADDRESS)
|
||||
return CONTAINER_ERROR_NULL_CONTEXT;
|
||||
if (vector == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
||||
.code = CONTAINER_ERROR_NULL_CONTAINER
|
||||
};
|
||||
|
||||
malunal_size_t index = 0;
|
||||
malunal_uint8_t* buffer = vector->context;
|
||||
while (index < vector->count) {
|
||||
if (memcmp(buffer, element, vector->stride) == 0) {
|
||||
impl_iptr_t self = (impl_iptr_t)vector;
|
||||
malunal_uint8_t* buffer = self->buffer;
|
||||
while (index < self->count) {
|
||||
if (memcmp(buffer, element, self->stride) == 0) {
|
||||
*outidx = index;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
buffer += vector->stride;
|
||||
buffer += self->stride;
|
||||
}
|
||||
|
||||
*outidx = -1;
|
||||
return CONTAINER_ERROR_SUCCESS;
|
||||
*outidx = (malunal_size_t)-1;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
container_exception_t
|
||||
vector_get(
|
||||
vector_iptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_mptr_t outptr
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)vector;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->get(vector, index, outptr)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
vector_set(
|
||||
vector_mptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)vector;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->set(vector, index, element)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
vector_insert_at(
|
||||
vector_mptr_t vector,
|
||||
malunal_size_t index,
|
||||
malunal_iptr_t element
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)vector;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->insert_at(vector, index, element)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
vector_remove_at(
|
||||
vector_mptr_t vector,
|
||||
malunal_size_t index
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)vector;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->remove_at(vector, index)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
}
|
||||
|
||||
container_exception_t
|
||||
vector_index_of(
|
||||
vector_mptr_t vector,
|
||||
malunal_iptr_t element,
|
||||
malunal_size_t* outidx
|
||||
) {
|
||||
impl_iptr_t implementation = (impl_iptr_t)vector;
|
||||
return implementation != NULL_ADDRESS
|
||||
? implementation->vtable->index_of(vector, element, outidx)
|
||||
: CONTAINER_ERROR_NULL_CONTAINER;
|
||||
}
|
||||
|
||||
|
||||
static const
|
||||
virtual_table_t vector_vtable = {
|
||||
.initialize = (vector_initialize_pfn_t)&vector_initialize_impl,
|
||||
.finalize = (vector_finalize_pfn_t)&vector_finalize_impl,
|
||||
.clear = (vector_clear_pfn_t)&vector_clear_impl,
|
||||
.append = (vector_append_pfn_t)&vector_append_impl,
|
||||
.remove = (vector_remove_pfn_t)&vector_remove_impl,
|
||||
.contains = (vector_contains_pfn_t)&vector_contains_impl,
|
||||
.get = (vector_get_pfn_t)&vector_get_def_impl,
|
||||
.set = (vector_set_pfn_t)&vector_set_def_impl,
|
||||
.insert_at = (vector_insert_at_pfn_t)&vector_insert_at_def_impl,
|
||||
.remove_at = (vector_remove_at_pfn_t)&vector_remove_at_def_impl,
|
||||
.index_of = (vector_index_of_pfn_t)&vector_index_of_def_impl
|
||||
};
|
||||
|
||||
vector_t
|
||||
vector_container() {
|
||||
implementation_t implementation = {
|
||||
.vtable = &vector_vtable,
|
||||
.allocator = NULL_ADDRESS,
|
||||
.context = NULL_ADDRESS,
|
||||
.stride = 0,
|
||||
.count = 0,
|
||||
.capacity = 0,
|
||||
};
|
||||
|
||||
vector_t result;
|
||||
memcpy(
|
||||
&result,
|
||||
&implementation,
|
||||
sizeof(implementation_t)
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+2
-7
@@ -1,7 +1,2 @@
|
||||
#include <stdio.h>
|
||||
#include "malunal/vector.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
#include "malunal/microtest.h"
|
||||
MICROTEST_MAIN()
|
||||
|
||||
+307
-573
@@ -1,634 +1,368 @@
|
||||
#define _DEFAULT_SOURCE /* strsignal() is only declared under this with -std=c11 */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "malunal/container.h"
|
||||
#include "malunal/containers/vector.h"
|
||||
#include "malunal/allocators.h"
|
||||
#include "malunal/microtest.h"
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Minimal assertion + test-registration harness (no framework) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
MICROTEST(vector_container, can_init_and_free) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
malunal_size_t temporary = 0;
|
||||
|
||||
#define CHECK(cond, msg) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
fprintf(stderr, " assertion failed: %s (%s:%d)\n", \
|
||||
(msg), __FILE__, __LINE__); \
|
||||
_exit(1); /* fail fast within the child: state is unreliable */ \
|
||||
} \
|
||||
} while (0)
|
||||
result = vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
#define CHECK_EQ_SZ(actual, expected, msg) \
|
||||
do { \
|
||||
malunal_size_t _a = (malunal_size_t)(actual); \
|
||||
malunal_size_t _e = (malunal_size_t)(expected); \
|
||||
if (_a != _e) { \
|
||||
fprintf(stderr, \
|
||||
" assertion failed: %s (%s:%d) got=%llu want=%llu\n", \
|
||||
(msg), __FILE__, __LINE__, \
|
||||
(unsigned long long)_a, (unsigned long long)_e); \
|
||||
_exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
allocator_mptr_t allocator;
|
||||
result = vector_container_allocator(&vector, &allocator);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(allocator, libc_allocator());
|
||||
|
||||
#define CHECK_EQ_INT(actual, expected, msg) \
|
||||
do { \
|
||||
int _a = (int)(actual); \
|
||||
int _e = (int)(expected); \
|
||||
if (_a != _e) { \
|
||||
fprintf(stderr, " assertion failed: %s (%s:%d) got=%d want=%d\n", \
|
||||
(msg), __FILE__, __LINE__, _a, _e); \
|
||||
_exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
result = vector_container_stride(&vector, &temporary);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(temporary, 4);
|
||||
|
||||
typedef void (*test_fn_t)(void);
|
||||
result = vector_container_count(&vector, &temporary);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(temporary, 0);
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
test_fn_t fn;
|
||||
} test_case_t;
|
||||
result = vector_container_capacity(&vector, &temporary);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(temporary, 32);
|
||||
|
||||
#define MAX_TESTS 64
|
||||
static test_case_t g_tests[MAX_TESTS];
|
||||
static int g_test_count = 0;
|
||||
result = vector_container_free(&vector);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
static void register_test(const char* name, test_fn_t fn) {
|
||||
g_tests[g_test_count].name = name;
|
||||
g_tests[g_test_count].fn = fn;
|
||||
g_test_count++;
|
||||
result = vector_container_stride(&vector, &temporary);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(temporary, 0);
|
||||
|
||||
result = vector_container_count(&vector, &temporary);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(temporary, 0);
|
||||
|
||||
result = vector_container_capacity(&vector, &temporary);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(temporary, 0);
|
||||
}
|
||||
|
||||
#define TEST(name) \
|
||||
static void name(void); \
|
||||
static void name##_register(void) __attribute__((constructor)); \
|
||||
static void name##_register(void) { register_test(#name, name); } \
|
||||
static void name(void)
|
||||
MICROTEST(vector_container, append_then_get_round_trips_value) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
/* Runs a single test in a forked child so a crash can't take out the
|
||||
* rest of the suite. Returns 1 for pass, 0 for fail/crash. */
|
||||
static int run_isolated(test_fn_t fn) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* child */
|
||||
fn();
|
||||
_exit(0); /* reached only if no CHECK failed */
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
||||
return 1;
|
||||
|
||||
if (WIFSIGNALED(status)) {
|
||||
int sig = WTERMSIG(status);
|
||||
fprintf(stderr, " CRASHED: terminated by signal %d (%s)\n", sig, strsignal(sig));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Shared setup helper */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* Initializes a fresh vector-as-container with a libc-backed allocator.
|
||||
* `allocator_out` is filled in because callers need to keep the
|
||||
* allocator alive (by value) for the container's lifetime. */
|
||||
static container_mptr_t setup_container(
|
||||
vector_t* vector,
|
||||
allocator_t* allocator_out,
|
||||
malunal_size_t stride,
|
||||
malunal_size_t capacity
|
||||
) {
|
||||
*vector = vector_container();
|
||||
*allocator_out = libc_allocator();
|
||||
|
||||
container_mptr_t container = (container_mptr_t)vector;
|
||||
container_exception_t rc = container_initialize(
|
||||
container, allocator_out, stride, capacity);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "container_initialize should succeed");
|
||||
return container;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* vector_container() -- construction */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
TEST(construct_then_uninitialized_accessors_are_zero) {
|
||||
vector_t vector = vector_container();
|
||||
container_iptr_t container = (container_iptr_t)&vector;
|
||||
|
||||
malunal_size_t value = 123; /* sentinel so we know it was actually written */
|
||||
container_exception_t rc = container_stride(container, &value);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "container_stride should succeed pre-initialize");
|
||||
CHECK_EQ_SZ(value, 0, "stride should start at 0");
|
||||
|
||||
rc = container_count(container, &value);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "container_count should succeed pre-initialize");
|
||||
CHECK_EQ_SZ(value, 0, "count should start at 0");
|
||||
|
||||
rc = container_capacity(container, &value);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "container_capacity should succeed pre-initialize");
|
||||
CHECK_EQ_SZ(value, 0, "capacity should start at 0");
|
||||
}
|
||||
|
||||
TEST(vector_t_and_container_t_are_same_size) {
|
||||
CHECK_EQ_SZ(sizeof(vector_t), sizeof(container_t), "vector_t must match container_t size");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* container_initialize / container_finalize */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
TEST(initialize_allocates_backing_storage) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
allocator_mptr_t allocator = &libc_alloc;
|
||||
malunal_size_t value = 0;
|
||||
|
||||
container_mptr_t container = (container_mptr_t)&vector;
|
||||
container_exception_t error = container_initialize(
|
||||
container, allocator, sizeof(int), 4);
|
||||
|
||||
CHECK(error == CONTAINER_ERROR_SUCCESS, "Container failed to initialize");
|
||||
|
||||
error = container_stride(container, &value);
|
||||
CHECK(error == CONTAINER_ERROR_SUCCESS, "Container failed to provide stride");
|
||||
CHECK(value == sizeof(int), "Stride should match requested element size");
|
||||
|
||||
error = container_count(container, &value);
|
||||
CHECK(error == CONTAINER_ERROR_SUCCESS, "Container failed to provide count");
|
||||
CHECK(value == 0, "Count should be 0 right after initialize");
|
||||
|
||||
error = container_capacity(container, &value);
|
||||
CHECK(error == CONTAINER_ERROR_SUCCESS, "Container failed to provide capacity");
|
||||
CHECK(value == 4, "Capacity should match requested capacity");
|
||||
|
||||
error = container_finalize(container);
|
||||
CHECK(error == CONTAINER_ERROR_SUCCESS, "Container failed to finalize");
|
||||
}
|
||||
|
||||
TEST(allocator_accessor_returns_the_allocator_used) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = (container_mptr_t)&vector;
|
||||
container_initialize(container, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
allocator_mptr_t got = NULL;
|
||||
container_exception_t rc = container_allocator(container, &got);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "container_allocator should succeed");
|
||||
CHECK(got == &libc_alloc, "container_allocator should return the allocator passed to initialize");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* container_append + vector_get */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
TEST(append_then_get_round_trips_value) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int value = 42;
|
||||
container_exception_t rc = container_append(container, &value);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "container_append should succeed");
|
||||
|
||||
int out = 0;
|
||||
rc = vector_get(&vector, 0, &out);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "vector_get should succeed for freshly appended element");
|
||||
CHECK_EQ_INT(out, 42, "vector_get should return the value that was appended");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
TEST(append_increments_count) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int a = 1, b = 2;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
malunal_int32_t a = 42;
|
||||
result = vector_container_append(&vector, &a);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
malunal_size_t count = 0;
|
||||
container_count(container, &count);
|
||||
CHECK_EQ_SZ(count, 2, "count should track number of appends");
|
||||
result = vector_container_count(&vector, &count);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(count, 1);
|
||||
|
||||
int out0 = 0, out1 = 0;
|
||||
vector_get(&vector, 0, &out0);
|
||||
vector_get(&vector, 1, &out1);
|
||||
CHECK_EQ_INT(out0, 1, "first appended element preserved");
|
||||
CHECK_EQ_INT(out1, 2, "second appended element preserved");
|
||||
malunal_int32_t out = 0;
|
||||
result = vector_container_get(&vector, 0, &out);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(out, 42);
|
||||
|
||||
container_finalize(container);
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
TEST(append_past_capacity_grows_and_preserves_data) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 2);
|
||||
MICROTEST(vector_container, append_past_capacity_grows_and_preserves) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 4, libc_allocator(), &vector);
|
||||
|
||||
int vals[5] = { 10, 20, 30, 40, 50 };
|
||||
for (int i = 0; i < 5; i++) {
|
||||
container_exception_t rc = container_append(container, &vals[i]);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "append should succeed while growing");
|
||||
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]);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
}
|
||||
|
||||
malunal_size_t capacity = 0, count = 0;
|
||||
container_capacity(container, &capacity);
|
||||
container_count(container, &count);
|
||||
CHECK(capacity >= 5, "capacity should have grown to fit 5 elements");
|
||||
CHECK_EQ_SZ(count, 5, "count should be 5 after 5 appends");
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int out = 0;
|
||||
vector_get(&vector, i, &out);
|
||||
CHECK_EQ_INT(out, vals[i], "element should survive a growth reallocation");
|
||||
malunal_size_t capacity = 0;
|
||||
malunal_size_t count = 0;
|
||||
vector_container_capacity(&vector, &capacity);
|
||||
vector_container_count(&vector, &count);
|
||||
MICROTEST_EXPECT_GT(capacity, 5);
|
||||
MICROTEST_EXPECT_EQ(count, 5);
|
||||
for (malunal_int32_t index = 0; index < 5; index++) {
|
||||
malunal_int32_t out = 0;
|
||||
vector_container_get(&vector, index, &out);
|
||||
MICROTEST_EXPECT_EQ(out, vals[index]);
|
||||
}
|
||||
|
||||
container_finalize(container);
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* container_clear */
|
||||
/* ------------------------------------------------------------------ */
|
||||
MICROTEST(vector_container, remove_first_matching_element) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
TEST(clear_resets_count_but_keeps_capacity) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
malunal_int32_t vals[3] = { 10, 20, 30 };
|
||||
vector_container_append(&vector, &vals[0]);
|
||||
vector_container_append(&vector, &vals[1]);
|
||||
vector_container_append(&vector, &vals[2]);
|
||||
|
||||
int a = 1, b = 2;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
|
||||
container_exception_t rc = container_clear(container);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "container_clear should succeed");
|
||||
|
||||
malunal_size_t count = 99, capacity = 0;
|
||||
container_count(container, &count);
|
||||
container_capacity(container, &capacity);
|
||||
CHECK_EQ_SZ(count, 0, "clear should reset count to 0");
|
||||
CHECK_EQ_SZ(capacity, 4, "clear should not release/shrink capacity");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* vector_set */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
TEST(set_overwrites_existing_element) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int a = 1, b = 2, replacement = 99;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
|
||||
container_exception_t rc = vector_set(&vector, 0, &replacement);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "vector_set should succeed for an in-range index");
|
||||
|
||||
int out = 0;
|
||||
vector_get(&vector, 0, &out);
|
||||
CHECK_EQ_INT(out, 99, "vector_set should overwrite the element at the given index");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
TEST(get_set_report_out_of_bounds_at_count) {
|
||||
/* Per container.h/vector.h, get/set should fail once the index
|
||||
* reaches or exceeds the number of *live* elements. Capacity may
|
||||
* still have room, but reading/writing an unset slot is out of
|
||||
* bounds. */
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int a = 1;
|
||||
container_append(container, &a); /* count == 1, capacity == 4 */
|
||||
|
||||
int out = 0;
|
||||
container_exception_t rc = vector_get(&vector, 1, &out);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_OUT_OF_BOUNDS,
|
||||
"get(index == count) should be out of bounds, not read uninitialized capacity");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
TEST(get_set_report_out_of_bounds_at_capacity) {
|
||||
/* index == capacity is one past the last valid slot. */
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int a = 1;
|
||||
container_append(container, &a);
|
||||
|
||||
int out = 0;
|
||||
container_exception_t rc = vector_get(&vector, 4, &out);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_OUT_OF_BOUNDS,
|
||||
"get(index == capacity) should be out of bounds");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* vector_index_of */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
TEST(index_of_finds_present_element) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int a = 5, b = 6, c = 7;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
container_append(container, &c);
|
||||
|
||||
malunal_size_t idx = (malunal_size_t)-1;
|
||||
int needle = 6;
|
||||
container_exception_t rc = vector_index_of(&vector, &needle, &idx);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "index_of should succeed when element is present");
|
||||
CHECK_EQ_SZ(idx, 1, "index_of should report the correct index of the matching element");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
TEST(index_of_reports_missing_element) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int a = 5;
|
||||
container_append(container, &a);
|
||||
|
||||
malunal_size_t idx = 0;
|
||||
int needle = 999;
|
||||
vector_index_of(&vector, &needle, &idx);
|
||||
/* Documented contract: outidx should be set to a not-found sentinel
|
||||
* (-1) when the element isn't found. */
|
||||
CHECK_EQ_SZ(idx, (malunal_size_t)-1, "index_of should report a not-found sentinel");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
TEST(index_of_actually_compares_against_the_search_element) {
|
||||
/* vector_index_of_def_impl's loop does
|
||||
* memcmp(buffer, buffer + stride, stride)
|
||||
* i.e. it compares each element to its *neighbor*, and never touches
|
||||
* the `element` argument at all. So it doesn't search for the given
|
||||
* value -- it finds the first position holding two equal adjacent
|
||||
* elements, whatever the caller was actually looking for. This test
|
||||
* makes that failure mode concrete and unambiguous: [1, 1, 2],
|
||||
* searching for 2, should report index 2 -- not index 0. */
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int a = 1, b = 1, c = 2;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
container_append(container, &c); /* [1, 1, 2] */
|
||||
|
||||
malunal_size_t idx = 0;
|
||||
int needle = 2;
|
||||
vector_index_of(&vector, &needle, &idx);
|
||||
CHECK_EQ_SZ(idx, 2, "index_of should find the index of the *searched* value, not an adjacent duplicate pair");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* vector_insert_at */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
TEST(insert_at_end_appends_value) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
int a = 1, b = 2, c = 3;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
|
||||
container_exception_t rc = vector_insert_at(&vector, 2, &c);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "insert_at(count) should succeed");
|
||||
|
||||
int out = 0;
|
||||
vector_get(&vector, 2, &out);
|
||||
CHECK_EQ_INT(out, 3, "inserted value should be readable back at the target index");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
TEST(insert_at_middle_shifts_later_elements) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 8);
|
||||
|
||||
int a = 1, b = 3, mid = 2;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b); /* [1, 3] */
|
||||
|
||||
container_exception_t rc = vector_insert_at(&vector, 1, &mid); /* -> [1, 2, 3] */
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "insert_at(1) should succeed");
|
||||
|
||||
int out0 = 0, out1 = 0, out2 = 0;
|
||||
vector_get(&vector, 0, &out0);
|
||||
vector_get(&vector, 1, &out1);
|
||||
vector_get(&vector, 2, &out2);
|
||||
CHECK_EQ_INT(out0, 1, "element before insertion point unchanged");
|
||||
CHECK_EQ_INT(out1, 2, "inserted element lands at requested index");
|
||||
CHECK_EQ_INT(out2, 3, "element after insertion point shifted right");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* vector_remove_at */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
TEST(remove_at_last_leaves_earlier_elements_untouched) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 8);
|
||||
|
||||
int a = 1, b = 2, c = 3;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
container_append(container, &c); /* [1, 2, 3] */
|
||||
|
||||
container_exception_t rc = vector_remove_at(&vector, 2); /* remove the '3' */
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "remove_at(last) should succeed");
|
||||
result = vector_container_remove(&vector, &vals[0]);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
malunal_size_t count = 0;
|
||||
container_count(container, &count);
|
||||
CHECK_EQ_SZ(count, 2, "count should drop by one after removal");
|
||||
vector_container_count(&vector, &count);
|
||||
MICROTEST_EXPECT_EQ(count, 2);
|
||||
|
||||
int out0 = 0, out1 = 0;
|
||||
vector_get(&vector, 0, &out0);
|
||||
vector_get(&vector, 1, &out1);
|
||||
CHECK_EQ_INT(out0, 1, "element 0 should be unaffected by removing the last element");
|
||||
CHECK_EQ_INT(out1, 2, "element 1 should be unaffected by removing the last element");
|
||||
malunal_int32_t value = 0;
|
||||
vector_container_get(&vector, 0, &value);
|
||||
MICROTEST_EXPECT_EQ(value, 20);
|
||||
|
||||
container_finalize(container);
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
TEST(remove_at_middle_shifts_later_elements_left) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 8);
|
||||
MICROTEST(vector_container, remove_of_absent_element_succeeds) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
int a = 1, b = 2, c = 3;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
container_append(container, &c); /* [1, 2, 3] */
|
||||
malunal_int32_t a = 1;
|
||||
vector_container_append(&vector, &a);
|
||||
|
||||
container_exception_t rc = vector_remove_at(&vector, 0); /* -> [2, 3] */
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "remove_at(0) should succeed");
|
||||
|
||||
int out0 = 0, out1 = 0;
|
||||
vector_get(&vector, 0, &out0);
|
||||
vector_get(&vector, 1, &out1);
|
||||
CHECK_EQ_INT(out0, 2, "elements after the removed index should shift left");
|
||||
CHECK_EQ_INT(out1, 3, "elements after the removed index should shift left");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
TEST(remove_at_on_empty_vector_is_out_of_bounds) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
|
||||
container_exception_t rc = vector_remove_at(&vector, 0);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_OUT_OF_BOUNDS,
|
||||
"remove_at on an empty vector should be rejected as out of bounds");
|
||||
|
||||
container_finalize(container);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* container_remove */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
TEST(container_remove_removes_the_first_matching_element) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 8);
|
||||
|
||||
int a = 10, b = 20, c = 30;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
container_append(container, &c); /* [10, 20, 30] */
|
||||
|
||||
container_exception_t rc = container_remove(container, &a); /* remove the leading '10' */
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "container_remove should succeed for a present element");
|
||||
malunal_int32_t missing = 404;
|
||||
result = vector_container_remove(&vector, &missing);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
malunal_size_t count = 0;
|
||||
container_count(container, &count);
|
||||
CHECK_EQ_SZ(count, 2, "count should drop by one after container_remove");
|
||||
|
||||
int out0 = 0;
|
||||
vector_get(&vector, 0, &out0);
|
||||
CHECK_EQ_INT(out0, 20, "removing the first element should shift the rest left");
|
||||
|
||||
container_finalize(container);
|
||||
vector_container_count(&vector, &count);
|
||||
MICROTEST_EXPECT_EQ(count, 1);
|
||||
}
|
||||
|
||||
TEST(container_remove_of_absent_element_is_success) {
|
||||
/* container.h: "if an element does not exist in the container, the
|
||||
* most common response should be CONTAINER_ERROR_SUCCESS." */
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
MICROTEST(vector_container, contains_finds_a_present_element) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
int a = 1;
|
||||
container_append(container, &a);
|
||||
malunal_int32_t a = 1;
|
||||
malunal_int32_t b = 2;
|
||||
vector_container_append(&vector, &a);
|
||||
vector_container_append(&vector, &b);
|
||||
|
||||
int missing = 404;
|
||||
container_exception_t rc = container_remove(container, &missing);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS,
|
||||
"removing an absent element should report success, per the documented contract");
|
||||
result = vector_container_contains(&vector, &b);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
container_finalize(container);
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* container_contains */
|
||||
/* ------------------------------------------------------------------ */
|
||||
MICROTEST(vector_container, contains_rejects_an_absent_element) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
TEST(container_contains_finds_a_present_element) {
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
malunal_int32_t a = 1;
|
||||
malunal_int32_t b = 2;
|
||||
vector_container_append(&vector, &a);
|
||||
vector_container_append(&vector, &b);
|
||||
|
||||
int a = 1, b = 2;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b);
|
||||
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);
|
||||
|
||||
container_exception_t rc = container_contains(container, &b);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_SUCCESS, "contains should succeed for a present element");
|
||||
|
||||
container_finalize(container);
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
TEST(container_contains_rejects_an_absent_element) {
|
||||
/* vector_contains_impl does `index > 0 ? SUCCESS : FAILURE`. But
|
||||
* vector_index_of's not-found sentinel is (malunal_size_t)-1, i.e.
|
||||
* SIZE_MAX, which is also > 0 -- so an absent element is reported as
|
||||
* *found*. (Combined with index_of's separate bug of comparing
|
||||
* adjacent elements instead of the search value, this vector has no
|
||||
* adjacent duplicates, so index_of legitimately reports "not
|
||||
* found" via the -1 sentinel here -- and contains still says yes.) */
|
||||
vector_t vector = vector_container();
|
||||
allocator_t libc_alloc = libc_allocator();
|
||||
container_mptr_t container = setup_container(&vector, &libc_alloc, sizeof(int), 4);
|
||||
MICROTEST(vector_container, clear_resets_count_but_keeps_capacity) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
int a = 1, b = 2;
|
||||
container_append(container, &a);
|
||||
container_append(container, &b); /* [1, 2], no adjacent duplicates */
|
||||
malunal_int32_t a = 1;
|
||||
malunal_int32_t b = 2;
|
||||
vector_container_append(&vector, &a);
|
||||
vector_container_append(&vector, &b);
|
||||
|
||||
int missing = 999;
|
||||
container_exception_t rc = container_contains(container, &missing);
|
||||
CHECK_EQ_INT(rc, CONTAINER_ERROR_FAILURE, "contains should report failure for an absent element");
|
||||
result = vector_container_clear(&vector);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
container_finalize(container);
|
||||
malunal_size_t count = 99;
|
||||
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);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* runner */
|
||||
/* ------------------------------------------------------------------ */
|
||||
MICROTEST(vector_container, set_overwrites_existing_element) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
int main(void) {
|
||||
int passed = 0;
|
||||
malunal_int32_t vals[3] = { 10, 20, 333 };
|
||||
vector_container_append(&vector, &vals[0]);
|
||||
vector_container_append(&vector, &vals[1]);
|
||||
|
||||
printf("running %d tests\n\n", g_test_count);
|
||||
result = vector_container_set(&vector, 0, &vals[2]);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
for (int i = 0; i < g_test_count; i++) {
|
||||
printf("[ RUN ] %s\n", g_tests[i].name);
|
||||
int ok = run_isolated(g_tests[i].fn);
|
||||
printf("[%s] %s\n\n", ok ? " PASS " : " FAIL ", g_tests[i].name);
|
||||
passed += ok;
|
||||
malunal_int32_t out = 0;
|
||||
vector_container_get(&vector, 0, &out);
|
||||
MICROTEST_EXPECT_EQ(out, 333);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
printf("---------------------------------------------\n");
|
||||
printf("%d / %d tests passed\n", passed, g_test_count);
|
||||
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);
|
||||
|
||||
return passed == g_test_count ? 0 : 1;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
MICROTEST(vector_container, insert_at_end_appends_value) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
malunal_int32_t vals[3] = { 10, 20, 30 };
|
||||
vector_container_append(&vector, &vals[0]);
|
||||
vector_container_append(&vector, &vals[1]);
|
||||
|
||||
result = vector_container_insert_at(&vector, 2, &vals[2]);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
malunal_int32_t out = 0;
|
||||
vector_container_get(&vector, 2, &out);
|
||||
MICROTEST_EXPECT_EQ(out, vals[2]);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
MICROTEST(vector_container, insert_at_middle_shifts_later_elements) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
malunal_int32_t vals[3] = { 10, 20, 30 };
|
||||
vector_container_append(&vector, &vals[0]);
|
||||
vector_container_append(&vector, &vals[2]);
|
||||
|
||||
result = vector_container_insert_at(&vector, 1, &vals[1]);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
malunal_int32_t outs[3] = { 0, 0, 0};
|
||||
vector_container_get(&vector, 0, &outs[0]);
|
||||
vector_container_get(&vector, 1, &outs[1]);
|
||||
vector_container_get(&vector, 2, &outs[2]);
|
||||
MICROTEST_EXPECT_EQ(outs[0], vals[0]);
|
||||
MICROTEST_EXPECT_EQ(outs[1], vals[1]);
|
||||
MICROTEST_EXPECT_EQ(outs[2], vals[2]);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
MICROTEST(vector_container, remove_at_last_preserves_earlier_elements) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
malunal_int32_t vals[3] = { 10, 20, 30 };
|
||||
vector_container_append(&vector, &vals[0]);
|
||||
vector_container_append(&vector, &vals[1]);
|
||||
vector_container_append(&vector, &vals[2]);
|
||||
|
||||
result = vector_container_remove_at(&vector, 2);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
malunal_size_t count = 0;
|
||||
vector_container_count(&vector, &count);
|
||||
MICROTEST_EXPECT_EQ(count, 2);
|
||||
|
||||
malunal_int32_t outs[2] = { 0, 0 };
|
||||
vector_container_get(&vector, 0, &outs[0]);
|
||||
vector_container_get(&vector, 1, &outs[1]);
|
||||
MICROTEST_EXPECT_EQ(outs[0], vals[0]);
|
||||
MICROTEST_EXPECT_EQ(outs[1], vals[1]);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
malunal_int32_t vals[3] = { 10, 20, 30 };
|
||||
vector_container_append(&vector, &vals[0]);
|
||||
vector_container_append(&vector, &vals[1]);
|
||||
vector_container_append(&vector, &vals[2]);
|
||||
|
||||
result = vector_container_remove_at(&vector, 0);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
|
||||
malunal_size_t count = 0;
|
||||
vector_container_count(&vector, &count);
|
||||
MICROTEST_EXPECT_EQ(count, 2);
|
||||
|
||||
malunal_int32_t outs[2] = { 0, 0 };
|
||||
vector_container_get(&vector, 0, &outs[0]);
|
||||
vector_container_get(&vector, 1, &outs[1]);
|
||||
MICROTEST_EXPECT_EQ(outs[0], vals[1]);
|
||||
MICROTEST_EXPECT_EQ(outs[1], vals[2]);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
MICROTEST(vector_container, index_of_finds_present_element) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
malunal_int32_t vals[3] = { 50, 60, 70 };
|
||||
vector_container_append(&vector, &vals[0]);
|
||||
vector_container_append(&vector, &vals[1]);
|
||||
vector_container_append(&vector, &vals[2]);
|
||||
|
||||
malunal_size_t index = (malunal_size_t)-1;
|
||||
result = vector_container_index_of(&vector, &vals[1], &index);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(index, 1);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
MICROTEST(vector_container, index_of_reports_missing_element) {
|
||||
error_t result = {};
|
||||
vector_container_t vector = {};
|
||||
vector_container_init(4, 32, libc_allocator(), &vector);
|
||||
|
||||
malunal_int32_t a = 50;
|
||||
vector_container_append(&vector, &a);
|
||||
|
||||
malunal_size_t index = 0;
|
||||
malunal_int32_t needle = 333;
|
||||
result = vector_container_index_of(&vector, &needle, &index);
|
||||
MICROTEST_EXPECT_NULL(result.domain);
|
||||
MICROTEST_EXPECT_EQ(index, (malunal_size_t)-1);
|
||||
|
||||
vector_container_free(&vector);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user