|  | void VectorData::Construct(u32 type_size_, bool trivial_construct, bool trivial_copy_construct, bool trivial_destruct) | 
        
          |  | { | 
        
          |  | // Set default properties with type traits | 
        
          |  | items = nullptr; | 
        
          |  | size = 0; | 
        
          |  | capacity = 0; | 
        
          |  | type_size = type_size_; | 
        
          |  | construct = !trivial_construct; | 
        
          |  | copy_construct = !trivial_copy_construct; | 
        
          |  | destruct = !trivial_destruct; | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::Construct(const VectorData& rhs, CopyConstructPtr copy_constructor) | 
        
          |  | { | 
        
          |  | // Copy properties from source VectorData | 
        
          |  | size = rhs.size; | 
        
          |  | capacity = rhs.capacity; | 
        
          |  | type_size = rhs.type_size; | 
        
          |  | construct = rhs.construct; | 
        
          |  | copy_construct = rhs.copy_construct; | 
        
          |  | destruct = rhs.destruct; | 
        
          |  |  | 
        
          |  | // Allocate capacity | 
        
          |  | items = (u8*)malloc(capacity * type_size); | 
        
          |  |  | 
        
          |  | if (copy_construct) | 
        
          |  | { | 
        
          |  | // Copy construct all items from source VectorData | 
        
          |  | u8* dest_item = items; | 
        
          |  | u8* src_item = rhs.items; | 
        
          |  | for (u32 i = 0; i < size; i++) | 
        
          |  | { | 
        
          |  | copy_constructor(dest_item, src_item); | 
        
          |  | dest_item += type_size; | 
        
          |  | src_item += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | else | 
        
          |  | { | 
        
          |  | // Memory copy is safe otherwise | 
        
          |  | memcpy(items, rhs.items, size * type_size); | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::Construct(u32 size_, u32 type_size_, bool trivial_construct, bool trivial_copy_construct, bool trivial_destruct, ConstructPtr constructor) | 
        
          |  | { | 
        
          |  | size = size_; | 
        
          |  | capacity = size_; | 
        
          |  | type_size = type_size_; | 
        
          |  | construct = !trivial_construct; | 
        
          |  | copy_construct = !trivial_copy_construct; | 
        
          |  | destruct = !trivial_destruct; | 
        
          |  |  | 
        
          |  | // Allocate capacity | 
        
          |  | items = (u8*)malloc(capacity * type_size); | 
        
          |  |  | 
        
          |  | // Call default constructor for all items, only if required | 
        
          |  | if (construct) | 
        
          |  | { | 
        
          |  | u8* item_ptr = items; | 
        
          |  | for (u32 i = 0; i < size; i++) | 
        
          |  | { | 
        
          |  | constructor(item_ptr); | 
        
          |  | item_ptr += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::Destruct(DestructPtr destructor) | 
        
          |  | { | 
        
          |  | if (items != nullptr) | 
        
          |  | { | 
        
          |  | Clear(destructor); | 
        
          |  | free(items); | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::Reserve(u32 new_capacity, CopyConstructPtr copy_constructor, DestructPtr destructor) | 
        
          |  | { | 
        
          |  | // Only reserve larger capacities | 
        
          |  | if (new_capacity > capacity) | 
        
          |  | { | 
        
          |  | // Allocate a new set of data with an aligned size | 
        
          |  | capacity = AlignPow2(new_capacity, 16); | 
        
          |  | u8* new_items = (u8*)malloc(capacity * type_size); | 
        
          |  |  | 
        
          |  | // Need to copy existing items to the new storage area | 
        
          |  | if (items != nullptr) | 
        
          |  | { | 
        
          |  | // Memory walk is far more expensive than an extra outer loop branch so try to do everything in | 
        
          |  | // one pass with trickier code than using the simpler two pass approach. | 
        
          |  |  | 
        
          |  | if (copy_construct) | 
        
          |  | { | 
        
          |  | if (destruct) | 
        
          |  | { | 
        
          |  | // Copy construct the old data to the new location while destructing the old data | 
        
          |  | u8* dest_item = new_items; | 
        
          |  | u8* src_item = items; | 
        
          |  | for (u32 i = 0; i < size; i++) | 
        
          |  | { | 
        
          |  | copy_constructor(dest_item, src_item); | 
        
          |  | destructor(src_item); | 
        
          |  | dest_item += type_size; | 
        
          |  | src_item += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | else | 
        
          |  | { | 
        
          |  | // Destruction not required so just copy construct all items to the new location | 
        
          |  | u8* dest_item = new_items; | 
        
          |  | u8* src_item = items; | 
        
          |  | for (u32 i = 0; i < size; i++) | 
        
          |  | { | 
        
          |  | copy_constructor(dest_item, src_item); | 
        
          |  | dest_item += type_size; | 
        
          |  | src_item += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | else | 
        
          |  | { | 
        
          |  | // No construction required so use a byte copy | 
        
          |  | memcpy(new_items, items, size * type_size); | 
        
          |  |  | 
        
          |  | // Call destructor on old items if required | 
        
          |  | if (destruct) | 
        
          |  | { | 
        
          |  | u8* item_ptr = items; | 
        
          |  | for (u32 i = 0; i < size; i++) | 
        
          |  | { | 
        
          |  | destructor(item_ptr); | 
        
          |  | item_ptr += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Release old item storage | 
        
          |  | free(items); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | items = new_items; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::Resize(u32 new_size, ConstructPtr constructor, CopyConstructPtr copy_constructor, DestructPtr destructor) | 
        
          |  | { | 
        
          |  | // Ensure there's enough allocated memory | 
        
          |  | // TODO: Better growth strategy | 
        
          |  | if (new_size > capacity) | 
        
          |  | Reserve(new_size, copy_constructor, destructor); | 
        
          |  |  | 
        
          |  | // Destruct elements above the new size | 
        
          |  | if (new_size < size && destruct) | 
        
          |  | { | 
        
          |  | u8* item_ptr = items + new_size * type_size; | 
        
          |  | for (u32 i = new_size; i < size; i++) | 
        
          |  | { | 
        
          |  | destructor(item_ptr); | 
        
          |  | item_ptr += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Default construct elements above the old size | 
        
          |  | else if (new_size > size && construct) | 
        
          |  | { | 
        
          |  | u8* item_ptr = items + size * type_size; | 
        
          |  | for (u32 i = size; i < new_size; i++) | 
        
          |  | { | 
        
          |  | constructor(item_ptr); | 
        
          |  | item_ptr += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | size = new_size; | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | // TODO: Remove | 
        
          |  | void VectorData::ClearResize(u32 new_size, ConstructPtr constructor, CopyConstructPtr copy_constructor, DestructPtr destructor) | 
        
          |  | { | 
        
          |  | Clear(destructor); | 
        
          |  | Resize(new_size, constructor, copy_constructor, destructor); | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::Clear(DestructPtr destructor) | 
        
          |  | { | 
        
          |  | // Destruct all items if required | 
        
          |  | if (destruct) | 
        
          |  | { | 
        
          |  | u8* item_ptr = items; | 
        
          |  | for (u32 i = 0; i < size; i++) | 
        
          |  | { | 
        
          |  | destructor(item_ptr); | 
        
          |  | item_ptr += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | size = 0; | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void* VectorData::PushBack(const void* object, CopyConstructPtr copy_constructor, DestructPtr destructor) | 
        
          |  | { | 
        
          |  | // Ensure there's enough space to store the new item | 
        
          |  | Reserve(size + 1, copy_constructor, destructor); | 
        
          |  |  | 
        
          |  | // Copy new item to the end of the vector | 
        
          |  | // TODO: Improve performance with a switch on common integer types sizes? | 
        
          |  | u8* dest_item = items + size * type_size; | 
        
          |  | if (copy_construct) | 
        
          |  | copy_constructor(dest_item, object); | 
        
          |  | else | 
        
          |  | memcpy(dest_item, object, type_size); | 
        
          |  |  | 
        
          |  | size++; | 
        
          |  |  | 
        
          |  | return dest_item; | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::PushBackUnique(const void* object, CopyConstructPtr copy_constructor, DestructPtr destructor, EqualPtr equal) | 
        
          |  | { | 
        
          |  | // Leave, not adding if the item already exists | 
        
          |  | u8* item_ptr = items; | 
        
          |  | for (u32 i = 0; i < size; i++) | 
        
          |  | { | 
        
          |  | if (equal(item_ptr, object)) | 
        
          |  | return; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | PushBack(object, copy_constructor, destructor); | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::PopBack(DestructPtr destructor) | 
        
          |  | { | 
        
          |  | Assert(size > 0); | 
        
          |  |  | 
        
          |  | size--; | 
        
          |  |  | 
        
          |  | if (destruct) | 
        
          |  | destructor(items + size * type_size); | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::RemoveUnstable(u32 index, DestructPtr destructor, CopyPtr copy) | 
        
          |  | { | 
        
          |  | // Copy from the back over the top of the item being removed | 
        
          |  | // Copy function will always contain the optimal compiler-generated copy, | 
        
          |  | // which includes calling the destructor on the dest object | 
        
          |  | u8* last_item = items + (size - 1) * type_size; | 
        
          |  | copy(items + index * type_size, last_item); | 
        
          |  |  | 
        
          |  | // Destruct the copied item | 
        
          |  | if (destruct) | 
        
          |  | destructor(last_item); | 
        
          |  |  | 
        
          |  | size--; | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::Remove(u32 index, DestructPtr destructor, CopyPtr copy) | 
        
          |  | { | 
        
          |  | // Shift all items above the removed item, one down | 
        
          |  | // TODO: memcpy everything for trivial destructor + assignment | 
        
          |  | u8* item_ptr = items + index * type_size; | 
        
          |  | for (u32 i = index; i < size - 1; i++) | 
        
          |  | { | 
        
          |  | copy(item_ptr, item_ptr + type_size); | 
        
          |  | item_ptr += type_size; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Destruct the left over item | 
        
          |  | if (destruct) | 
        
          |  | destructor(item_ptr); | 
        
          |  |  | 
        
          |  | size--; | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::RemoveRange(u32 start, u32 count, DestructPtr destructor, CopyPtr copy) | 
        
          |  | { | 
        
          |  | u32 end = start + count; | 
        
          |  | Assert(end <= size); | 
        
          |  | u32 left_over = size - end; | 
        
          |  |  | 
        
          |  | // Shift all items above the removed items down | 
        
          |  | // TODO: memcpy? Needs trivial assign detection | 
        
          |  | u8* dest_item = items + start * type_size; | 
        
          |  | u8* src_item = dest_item + count * type_size; | 
        
          |  | for (u32 i = 0; i < left_over; i++) | 
        
          |  | { | 
        
          |  | copy(dest_item, src_item); | 
        
          |  | dest_item += type_size; | 
        
          |  | src_item += type_size; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Destruct the items left behind at the top | 
        
          |  | if (destruct) | 
        
          |  | { | 
        
          |  | u8* item_ptr = items + end * type_size; | 
        
          |  | for (u32 i = end; i < size; i++) | 
        
          |  | { | 
        
          |  | destructor(item_ptr); | 
        
          |  | item_ptr += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | size -= count; | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | void VectorData::CopyFrom(const VectorData& src, CopyConstructPtr copy_constructor, DestructPtr destructor) | 
        
          |  | { | 
        
          |  | // Empty and ensure there's enough capacity | 
        
          |  | Clear(destructor); | 
        
          |  | Reserve(src.size, copy_constructor, destructor); | 
        
          |  |  | 
        
          |  | if (copy_construct) | 
        
          |  | { | 
        
          |  | // Copy construct all items over from source | 
        
          |  | u8* dest_item = items; | 
        
          |  | u8* src_item = src.items; | 
        
          |  | for (u32 i = 0; i < src.size; i++) | 
        
          |  | { | 
        
          |  | copy_constructor(dest_item, src_item); | 
        
          |  | dest_item += type_size; | 
        
          |  | src_item += type_size; | 
        
          |  | } | 
        
          |  | } | 
        
          |  | else | 
        
          |  | { | 
        
          |  | // Bit copy instead | 
        
          |  | memcpy(items, src.items, src.size * type_size); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | size = src.size; | 
        
          |  | } |