455 lines
10 KiB
C
455 lines
10 KiB
C
#include <memory.h>
|
|
#include "malunal/containers/vector.h"
|
|
|
|
|
|
typedef struct {
|
|
object_t object;
|
|
container_t container;
|
|
|
|
allocator_mptr_t allocator;
|
|
malunal_size_t stride;
|
|
malunal_size_t count;
|
|
malunal_size_t capacity;
|
|
malunal_mptr_t buffer;
|
|
} impl_t;
|
|
|
|
typedef impl_t* impl_mptr_t;
|
|
typedef const impl_t* impl_iptr_t;
|
|
_Static_assert(
|
|
sizeof(vector_container_t) == sizeof(impl_t),
|
|
"Vector container must be the size of its implementation"
|
|
);
|
|
|
|
|
|
static
|
|
error_t
|
|
vector_object_cast_impl(
|
|
malunal_mptr_t object,
|
|
uuid_iptr_t uuid,
|
|
malunal_mptr_t* out
|
|
) {
|
|
if (object == null)
|
|
return (error_t) {
|
|
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
|
.code = CONTAINER_ERROR_NULL_CONTAINER
|
|
};
|
|
|
|
impl_mptr_t self = (impl_mptr_t)object;
|
|
if (uuid_equals(&UUID_OBJECT_T, uuid)) {
|
|
*out = &self->object;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
if (uuid_equals(&UUID_CONTAINER_T, uuid)) {
|
|
*out = &self->container;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
*out = null;
|
|
return (error_t) {
|
|
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
|
.code = CONTAINER_ERROR_FAILURE
|
|
};
|
|
}
|
|
|
|
static
|
|
malunal_uint32_t
|
|
vector_object_retain_impl(
|
|
malunal_mptr_t object
|
|
) {
|
|
// TODO: figure out how to handle this.
|
|
}
|
|
|
|
static
|
|
malunal_uint32_t
|
|
vector_object_release_impl(
|
|
malunal_mptr_t object
|
|
) {
|
|
// TODO: figure out how to handle this.
|
|
}
|
|
|
|
static const
|
|
object_vtable_t vector_object_vtable = {
|
|
.cast = &vector_object_cast_impl,
|
|
.retain = &vector_object_retain_impl,
|
|
.release = &vector_object_release_impl
|
|
};
|
|
|
|
static const
|
|
container_vtable_t vector_container_vtable = {
|
|
.allocator = (container_allocator_pfn_t)&vector_container_allocator,
|
|
.stride = (container_stride_pfn_t)&vector_container_stride,
|
|
.count = (container_count_pfn_t)&vector_container_count,
|
|
.capacity = (container_capacity_pfn_t)&vector_container_capacity,
|
|
.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,
|
|
allocator_mptr_t allocator,
|
|
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;
|
|
error_t result = allocator_acquire(
|
|
allocator,
|
|
stride * capacity,
|
|
&self->buffer
|
|
);
|
|
|
|
if (result.domain != null)
|
|
return result;
|
|
|
|
self->object = (object_t) { &vector_object_vtable };
|
|
self->container = (container_t){ &vector_container_vtable };
|
|
self->allocator = allocator;
|
|
self->stride = stride;
|
|
self->count = 0;
|
|
self->capacity = capacity;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
error_t
|
|
vector_container_free(
|
|
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;
|
|
error_t result = allocator_dispose(
|
|
self->allocator,
|
|
self->buffer,
|
|
self->stride * self->capacity
|
|
);
|
|
|
|
if (result.domain != null)
|
|
return result;
|
|
|
|
self->stride = 0;
|
|
self->count = 0;
|
|
self->capacity = 0;
|
|
self->buffer = null;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
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
|
|
};
|
|
|
|
impl_iptr_t self = (impl_iptr_t)vector;
|
|
*out = self->allocator;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
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
|
|
};
|
|
|
|
impl_iptr_t self = (impl_iptr_t)vector;
|
|
*out = self->stride;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
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 == null)
|
|
return (error_t) {
|
|
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
|
.code = CONTAINER_ERROR_NULL_CONTAINER
|
|
};
|
|
|
|
impl_mptr_t self = (impl_mptr_t)vector;
|
|
error_t result = vector_container_realloc(self);
|
|
if (result.domain != null)
|
|
return result;
|
|
|
|
malunal_uint8_t* buffer = self->buffer;
|
|
buffer += self->stride * self->count;
|
|
memcpy(buffer, element, self->stride);
|
|
self->count++;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
error_t
|
|
vector_container_remove(
|
|
vector_container_mptr_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_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 != (malunal_size_t)-1
|
|
? vector_container_remove_at(vector, index)
|
|
: NO_ERROR;
|
|
}
|
|
|
|
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 element
|
|
) {
|
|
if (vector == null)
|
|
return (error_t) {
|
|
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
|
.code = CONTAINER_ERROR_NULL_CONTAINER
|
|
};
|
|
|
|
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 = self->buffer;
|
|
buffer += self->stride * index;
|
|
memcpy(element, buffer, self->stride);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
error_t
|
|
vector_container_set(
|
|
vector_container_mptr_t vector,
|
|
malunal_size_t index,
|
|
malunal_iptr_t element
|
|
) {
|
|
if (vector == null)
|
|
return (error_t) {
|
|
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
|
.code = CONTAINER_ERROR_NULL_CONTAINER
|
|
};
|
|
|
|
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 = self->buffer;
|
|
buffer += self->stride * index;
|
|
memcpy(buffer, element, self->stride);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
error_t
|
|
vector_container_insert_at(
|
|
vector_container_mptr_t vector,
|
|
malunal_size_t index,
|
|
malunal_iptr_t element
|
|
) {
|
|
if (vector == null)
|
|
return (error_t) {
|
|
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
|
.code = CONTAINER_ERROR_NULL_CONTAINER
|
|
};
|
|
|
|
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
|
|
};
|
|
|
|
error_t result = vector_container_realloc(self);
|
|
if (result.domain != null)
|
|
return result;
|
|
|
|
malunal_uint8_t* orig = self->buffer;
|
|
orig += self->stride * index;
|
|
|
|
malunal_uint8_t* dest = orig + self->stride;
|
|
memmove(dest, orig, self->stride * self->count - index);
|
|
memcpy(orig, element, self->stride);
|
|
self->count++;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
error_t
|
|
vector_container_remove_at(
|
|
vector_container_mptr_t vector,
|
|
malunal_size_t index
|
|
) {
|
|
if (vector == null)
|
|
return (error_t) {
|
|
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
|
.code = CONTAINER_ERROR_NULL_CONTAINER
|
|
};
|
|
|
|
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_uint8_t* dest = self->buffer;
|
|
dest += self->stride * index;
|
|
|
|
malunal_uint8_t* orig = dest + self->stride;
|
|
memmove(dest, orig, self->stride * self->count - index);
|
|
self->count--;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
error_t
|
|
vector_container_index_of(
|
|
vector_container_iptr_t vector,
|
|
malunal_iptr_t element,
|
|
malunal_size_t* outidx
|
|
) {
|
|
if (vector == null)
|
|
return (error_t) {
|
|
.domain = &ERROR_DOMAIN_CONTAINER_T,
|
|
.code = CONTAINER_ERROR_NULL_CONTAINER
|
|
};
|
|
|
|
malunal_size_t index = 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 NO_ERROR;
|
|
}
|
|
|
|
index += 1;
|
|
buffer += self->stride;
|
|
}
|
|
|
|
*outidx = (malunal_size_t)-1;
|
|
return NO_ERROR;
|
|
}
|