diff --git a/include/pocketpy/common/chunkedvector.h b/include/pocketpy/common/chunkedvector.h new file mode 100644 index 00000000..f82f54db --- /dev/null +++ b/include/pocketpy/common/chunkedvector.h @@ -0,0 +1,31 @@ +#pragma once + +#include "pocketpy/export.h" +#include "pocketpy/common/vector.h" +typedef struct c11_chunkedvector_chunk { + int length; + int capacity; + void* data; +} c11_chunkedvector_chunk; + +typedef struct c11_chunkedvector { + c11_vector /*T=c11_chunkedvector_chunk*/ chunks; + int length; + int capacity; + int elem_size; + int initial_chunks; +} c11_chunkedvector; + +// chunks[0]: size=1, total_capacity=1 +// chunks[1]: size=2, total_capacity=3 +// chunks[2]: size=4, total_capacity=7 +// chunks[3]: size=8, total_capacity=15 +// chunks[4]: size=16, total_capacity=31 +// chunks[5]: size=32, total_capacity=63 +// ... +// chunks[n]: size=2^n, total_capacity=2^(n+1)-1 + +void c11_chunkedvector__ctor(c11_chunkedvector* self, int elem_size, int initial_chunks); +void c11_chunkedvector__dtor(c11_chunkedvector* self); +void* c11_chunkedvector__emplace(c11_chunkedvector* self); +void* c11_chunkedvector__at(c11_chunkedvector* self, int index); \ No newline at end of file diff --git a/src/common/chunkedvector.c b/src/common/chunkedvector.c new file mode 100644 index 00000000..e6eedc0e --- /dev/null +++ b/src/common/chunkedvector.c @@ -0,0 +1,86 @@ +#include "pocketpy/common/chunkedvector.h" + +#include +#include +#include "pocketpy/common/utils.h" +#include "pocketpy/config.h" + +#if defined(_MSC_VER) +#include +#endif + +inline static int c11_bit_length(unsigned long x) { +#if(defined(__clang__) || defined(__GNUC__)) + return x == 0 ? 0 : (int)sizeof(unsigned long) * 8 - __builtin_clzl(x); +#elif defined(_MSC_VER) + static_assert(sizeof(unsigned long) <= 4, "unsigned long is greater than 4 bytes"); + unsigned long msb; + if(_BitScanReverse(&msb, x)) { return (int)msb + 1; } + return 0; +#else + const int BIT_LENGTH_TABLE[32] = {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; + int msb = 0; + while(x >= 32) { + msb += 6; + x >>= 6; + } + msb += BIT_LENGTH_TABLE[x]; + return msb; +#endif +} + +void c11_chunkedvector__ctor(c11_chunkedvector* self, int elem_size, int initial_chunks) { + if(initial_chunks < 5) initial_chunks = 5; + c11_vector__ctor(&self->chunks, sizeof(c11_chunkedvector_chunk)); + self->length = 0; + self->capacity = (1U << (unsigned int)initial_chunks) - 1U; + self->elem_size = elem_size; + self->initial_chunks = initial_chunks; + void* chunks_data = PK_MALLOC(elem_size * ((1U << (unsigned int)initial_chunks) - 1)); + for(int i = 0; i < initial_chunks; i++) { + // TODO: optimize? + c11_chunkedvector_chunk chunk = {.length = 0, + .capacity = 1U << i, + .data = (char*)chunks_data + elem_size * ((1U << i) - 1U)}; + c11_vector__push(c11_chunkedvector_chunk, &self->chunks, chunk); + } +} + +void c11_chunkedvector__dtor(c11_chunkedvector* self) { + for(int index = self->initial_chunks; index < self->chunks.length; index++) { + c11_chunkedvector_chunk* chunk = c11__at(c11_chunkedvector_chunk, &self->chunks, index); + PK_FREE(chunk->data); + } + c11_chunkedvector_chunk* initial_chunk = c11__at(c11_chunkedvector_chunk, &self->chunks, 0); + PK_FREE(initial_chunk->data); + c11_vector__dtor(&self->chunks); +} + +void* c11_chunkedvector__emplace(c11_chunkedvector* self) { + if(self->length == self->capacity) { + #ifndef NDEBUG + c11_chunkedvector_chunk last_chunk = c11_vector__back(c11_chunkedvector_chunk, &self->chunks); + assert(last_chunk.capacity == last_chunk.length); + #endif + c11_chunkedvector_chunk chunk = { + .length = 0, + .capacity = 1U << (unsigned int)self->chunks.length, + .data = PK_MALLOC(self->elem_size * ((1U << (unsigned int)self->chunks.length)))}; + self->capacity += chunk.capacity; + c11_vector__push(c11_chunkedvector_chunk, &self->chunks, chunk); + } + int last_chunk_index = c11_bit_length(self->length + 1) - 1; + c11_chunkedvector_chunk* last_chunk = + c11__at(c11_chunkedvector_chunk, &self->chunks, last_chunk_index); + void* p = (char*)last_chunk->data + self->elem_size * last_chunk->length; + last_chunk->length++; + self->length++; + return p; +} + +void* c11_chunkedvector__at(c11_chunkedvector* self, int index) { + int chunk_index = c11_bit_length(index + 1) - 1; + c11_chunkedvector_chunk* chunk = c11__at(c11_chunkedvector_chunk, &self->chunks, chunk_index); + return (char*)chunk->data + (index + 1 - (1U << (unsigned int)chunk_index)) * self->elem_size; +}