From 482104191f646563e96ae8e6d586009e96154b4e Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 18 Dec 2023 21:40:24 +0100 Subject: [PATCH] Added more containers from the pandemonium engine. --- sfw/containers/cowdata.h | 394 ++++++++++++++++ sfw/containers/hash_set.h | 506 ++++++++++++++++++++ sfw/containers/list.h | 701 ++++++++++++++++++++++++++++ sfw/containers/local_vector.h | 323 +++++++++++++ sfw/containers/rb_map.h | 679 +++++++++++++++++++++++++++ sfw/containers/rb_set.h | 633 +++++++++++++++++++++++++ sfw/containers/ring_buffer.h | 223 +++++++++ sfw/containers/rvector.h | 287 ++++++++++++ sfw/containers/tight_local_vector.h | 319 +++++++++++++ sfw/containers/vector.h | 439 +++++++---------- sfw/containers/vmap.h | 203 ++++++++ sfw/containers/vset.h | 142 ++++++ 12 files changed, 4585 insertions(+), 264 deletions(-) create mode 100644 sfw/containers/cowdata.h create mode 100644 sfw/containers/hash_set.h create mode 100644 sfw/containers/list.h create mode 100644 sfw/containers/local_vector.h create mode 100644 sfw/containers/rb_map.h create mode 100644 sfw/containers/rb_set.h create mode 100644 sfw/containers/ring_buffer.h create mode 100644 sfw/containers/rvector.h create mode 100644 sfw/containers/tight_local_vector.h create mode 100644 sfw/containers/vmap.h create mode 100644 sfw/containers/vset.h diff --git a/sfw/containers/cowdata.h b/sfw/containers/cowdata.h new file mode 100644 index 0000000..0525d12 --- /dev/null +++ b/sfw/containers/cowdata.h @@ -0,0 +1,394 @@ +#ifndef COWDATA_H_ +#define COWDATA_H_ + +/*************************************************************************/ +/* cowdata.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include + +#include "core/error/error_macros.h" +#include "core/os/memory.h" +#include "core/os/safe_refcount.h" + +template +class Vector; +class String; +class Char16String; +class CharString; +template +class VMap; + +#if !defined(NO_THREADS) +SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t) +#endif + +template +class CowData { + template + friend class Vector; + friend class String; + friend class Char16String; + friend class CharString; + template + friend class VMap; + +private: + mutable T *_ptr; + + // internal helpers + + _FORCE_INLINE_ SafeNumeric *_get_refcount() const { + if (!_ptr) { + return nullptr; + } + + return reinterpret_cast *>(_ptr) - 2; + } + + _FORCE_INLINE_ uint32_t *_get_size() const { + if (!_ptr) { + return nullptr; + } + + return reinterpret_cast(_ptr) - 1; + } + + _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { + //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); + return next_power_of_2(p_elements * sizeof(T)); + } + + _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { +#if defined(_add_overflow) && defined(_mul_overflow) + size_t o; + size_t p; + if (_mul_overflow(p_elements, sizeof(T), &o)) { + *out = 0; + return false; + } + *out = next_power_of_2(o); + if (_add_overflow(o, static_cast(32), &p)) { + return false; //no longer allocated here + } + return true; +#else + // Speed is more important than correctness here, do the operations unchecked + // and hope the best + *out = _get_alloc_size(p_elements); + return true; +#endif + } + + void _unref(void *p_data); + void _ref(const CowData *p_from); + void _ref(const CowData &p_from); + uint32_t _copy_on_write(); + +public: + void operator=(const CowData &p_from) { _ref(p_from); } + + _FORCE_INLINE_ T *ptrw() { + _copy_on_write(); + return _ptr; + } + + _FORCE_INLINE_ const T *ptr() const { + return _ptr; + } + + _FORCE_INLINE_ int size() const { + uint32_t *size = (uint32_t *)_get_size(); + if (size) { + return *size; + } else { + return 0; + } + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _ptr == nullptr; } + + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + _ptr[p_index] = p_elem; + } + + _FORCE_INLINE_ T &get_m(int p_index) { + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + return _ptr[p_index]; + } + + _FORCE_INLINE_ const T &get(int p_index) const { + CRASH_BAD_INDEX(p_index, size()); + + return _ptr[p_index]; + } + + Error resize(int p_size); + + _FORCE_INLINE_ void remove(int p_index) { + ERR_FAIL_INDEX(p_index, size()); + T *p = ptrw(); + int len = size(); + for (int i = p_index; i < len - 1; i++) { + p[i] = p[i + 1]; + }; + + resize(len - 1); + } + + Error insert(int p_pos, const T &p_val) { + ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); + resize(size() + 1); + for (int i = (size() - 1); i > p_pos; i--) { + set(i, get(i - 1)); + } + set(p_pos, p_val); + + return OK; + } + + void fill(const T &p_val) { + int len = size(); + + if (len == 0) { + return; + } + + T *p = ptrw(); + for (int i = 0; i < len; ++i) { + p[i] = p_val; + } + } + + int find(const T &p_val, int p_from = 0) const; + + _FORCE_INLINE_ CowData(); + _FORCE_INLINE_ ~CowData(); + _FORCE_INLINE_ CowData(CowData &p_from) { _ref(p_from); }; +}; + +template +void CowData::_unref(void *p_data) { + if (!p_data) { + return; + } + + SafeNumeric *refc = _get_refcount(); + + if (refc->decrement() > 0) { + return; // still in use + } + // clean up + + if (!HAS_TRIVIAL_DESTRUCTOR(T)) { + uint32_t *count = _get_size(); + T *data = (T *)(count + 1); + + for (uint32_t i = 0; i < *count; ++i) { + // call destructors + data[i].~T(); + } + } + + // free mem + Memory::free_static((uint8_t *)p_data, true); +} + +template +uint32_t CowData::_copy_on_write() { + if (!_ptr) { + return 0; + } + + SafeNumeric *refc = _get_refcount(); + + uint32_t rc = refc->get(); + if (likely(rc > 1)) { + /* in use by more than me */ + uint32_t current_size = *_get_size(); + + uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); + + new (mem_new - 2, sizeof(uint32_t), "") SafeNumeric(1); //refcount + *(mem_new - 1) = current_size; //size + + T *_data = (T *)(mem_new); + + // initialize new elements + if (HAS_TRIVIAL_COPY(T)) { + memcpy(mem_new, _ptr, current_size * sizeof(T)); + + } else { + for (uint32_t i = 0; i < current_size; i++) { + memnew_placement(&_data[i], T(_ptr[i])); + } + } + + _unref(_ptr); + _ptr = _data; + + rc = 1; + } + return rc; +} + +template +Error CowData::resize(int p_size) { + ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); + + int current_size = size(); + + if (p_size == current_size) { + return OK; + } + + if (p_size == 0) { + // wants to clean up + _unref(_ptr); + _ptr = nullptr; + return OK; + } + + // possibly changing size, copy on write + uint32_t rc = _copy_on_write(); + + size_t current_alloc_size = _get_alloc_size(current_size); + size_t alloc_size; + ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); + + if (p_size > current_size) { + if (alloc_size != current_alloc_size) { + if (current_size == 0) { + // alloc from scratch + uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); + ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); + *(ptr - 1) = 0; //size, currently none + new (ptr - 2, sizeof(uint32_t), "") SafeNumeric(1); //refcount + + _ptr = (T *)ptr; + + } else { + uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric(rc); //refcount + + _ptr = (T *)(_ptrnew); + } + } + + // construct the newly created elements + + if (!HAS_TRIVIAL_CONSTRUCTOR(T)) { + for (int i = *_get_size(); i < p_size; i++) { + memnew_placement(&_ptr[i], T); + } + } + + *_get_size() = p_size; + + } else if (p_size < current_size) { + if (!HAS_TRIVIAL_DESTRUCTOR(T)) { + // deinitialize no longer needed elements + for (uint32_t i = p_size; i < *_get_size(); i++) { + T *t = &_ptr[i]; + t->~T(); + } + } + + if (alloc_size != current_alloc_size) { + uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric(rc); //refcount + + _ptr = (T *)(_ptrnew); + } + + *_get_size() = p_size; + } + + return OK; +} + +template +int CowData::find(const T &p_val, int p_from) const { + int ret = -1; + + if (p_from < 0 || size() == 0) { + return ret; + } + + for (int i = p_from; i < size(); i++) { + if (get(i) == p_val) { + ret = i; + break; + } + } + + return ret; +} + +template +void CowData::_ref(const CowData *p_from) { + _ref(*p_from); +} + +template +void CowData::_ref(const CowData &p_from) { + if (_ptr == p_from._ptr) { + return; // self assign, do nothing. + } + + _unref(_ptr); + _ptr = nullptr; + + if (!p_from._ptr) { + return; //nothing to do + } + + if (p_from._get_refcount()->conditional_increment() > 0) { // could reference + _ptr = p_from._ptr; + } +} + +template +CowData::CowData() { + _ptr = nullptr; +} + +template +CowData::~CowData() { + _unref(_ptr); +} + +#endif /* COW_H_ */ diff --git a/sfw/containers/hash_set.h b/sfw/containers/hash_set.h new file mode 100644 index 0000000..197f6e2 --- /dev/null +++ b/sfw/containers/hash_set.h @@ -0,0 +1,506 @@ +#ifndef HASH_SET_H +#define HASH_SET_H + +/*************************************************************************/ +/* hash_set.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/containers/hash_map.h" +#include "core/containers/hashfuncs.h" +#include "core/math/math_funcs.h" +#include "core/os/memory.h" + +/** + * Implementation of Set using a bidi indexed hash map. + * Use RBSet instead of this only if the following conditions are met: + * + * - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime. + * - Iteration order does matter (via operator<) + * + */ + +template > +class HashSet { +public: + static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. + static constexpr float MAX_OCCUPANCY = 0.75; + static constexpr uint32_t EMPTY_HASH = 0; + +private: + TKey *keys = nullptr; + uint32_t *hash_to_key = nullptr; + uint32_t *key_to_hash = nullptr; + uint32_t *hashes = nullptr; + + uint32_t capacity_index = 0; + uint32_t num_elements = 0; + + _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const { + uint32_t hash = Hasher::hash(p_key); + + if (unlikely(hash == EMPTY_HASH)) { + hash = EMPTY_HASH + 1; + } + + return hash; + } + + static _FORCE_INLINE_ uint32_t _get_probe_length(const uint32_t p_pos, const uint32_t p_hash, const uint32_t p_capacity, const uint64_t p_capacity_inv) { + const uint32_t original_pos = fastmod(p_hash, p_capacity_inv, p_capacity); + return fastmod(p_pos - original_pos + p_capacity, p_capacity_inv, p_capacity); + } + + bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { + if (keys == nullptr || num_elements == 0) { + return false; // Failed lookups, no elements + } + + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t hash = _hash(p_key); + uint32_t pos = fastmod(hash, capacity_inv, capacity); + uint32_t distance = 0; + + while (true) { + if (hashes[pos] == EMPTY_HASH) { + return false; + } + + if (distance > _get_probe_length(pos, hashes[pos], capacity, capacity_inv)) { + return false; + } + + if (hashes[pos] == hash && Comparator::compare(keys[hash_to_key[pos]], p_key)) { + r_pos = hash_to_key[pos]; + return true; + } + + pos = fastmod(pos + 1, capacity_inv, capacity); + distance++; + } + } + + uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) { + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t hash = p_hash; + uint32_t index = p_index; + uint32_t distance = 0; + uint32_t pos = fastmod(hash, capacity_inv, capacity); + + while (true) { + if (hashes[pos] == EMPTY_HASH) { + hashes[pos] = hash; + key_to_hash[index] = pos; + hash_to_key[pos] = index; + return pos; + } + + // Not an empty slot, let's check the probing length of the existing one. + uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity, capacity_inv); + if (existing_probe_len < distance) { + key_to_hash[index] = pos; + SWAP(hash, hashes[pos]); + SWAP(index, hash_to_key[pos]); + distance = existing_probe_len; + } + + pos = fastmod(pos + 1, capacity_inv, capacity); + distance++; + } + } + + void _resize_and_rehash(uint32_t p_new_capacity_index) { + // Capacity can't be 0. + capacity_index = MAX((uint32_t)MIN_CAPACITY_INDEX, p_new_capacity_index); + + uint32_t capacity = hash_table_size_primes[capacity_index]; + + uint32_t *old_hashes = hashes; + uint32_t *old_key_to_hash = key_to_hash; + + hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + keys = reinterpret_cast(Memory::realloc_static(keys, sizeof(TKey) * capacity)); + key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + hash_to_key = reinterpret_cast(Memory::realloc_static(hash_to_key, sizeof(uint32_t) * capacity)); + + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = EMPTY_HASH; + } + + for (uint32_t i = 0; i < num_elements; i++) { + uint32_t h = old_hashes[old_key_to_hash[i]]; + _insert_with_hash(h, i); + } + + Memory::free_static(old_hashes); + Memory::free_static(old_key_to_hash); + } + + _FORCE_INLINE_ int32_t _insert(const TKey &p_key) { + uint32_t capacity = hash_table_size_primes[capacity_index]; + if (unlikely(keys == nullptr)) { + // Allocate on demand to save memory. + + hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + keys = reinterpret_cast(Memory::alloc_static(sizeof(TKey) * capacity)); + key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + hash_to_key = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = EMPTY_HASH; + } + } + + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (exists) { + return pos; + } else { + if (num_elements + 1 > MAX_OCCUPANCY * capacity) { + ERR_FAIL_COND_V_MSG(capacity_index + 1 == HASH_TABLE_SIZE_MAX, -1, "Hash table maximum capacity reached, aborting insertion."); + _resize_and_rehash(capacity_index + 1); + } + + uint32_t hash = _hash(p_key); + memnew_placement(&keys[num_elements], TKey(p_key)); + _insert_with_hash(hash, num_elements); + num_elements++; + return num_elements - 1; + } + } + + void _init_from(const HashSet &p_other) { + capacity_index = p_other.capacity_index; + num_elements = p_other.num_elements; + + if (p_other.num_elements == 0) { + return; + } + + uint32_t capacity = hash_table_size_primes[capacity_index]; + + hashes = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + keys = reinterpret_cast(Memory::alloc_static(sizeof(TKey) * capacity)); + key_to_hash = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + hash_to_key = reinterpret_cast(Memory::alloc_static(sizeof(uint32_t) * capacity)); + + for (uint32_t i = 0; i < num_elements; i++) { + memnew_placement(&keys[i], TKey(p_other.keys[i])); + key_to_hash[i] = p_other.key_to_hash[i]; + } + + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = p_other.hashes[i]; + hash_to_key[i] = p_other.hash_to_key[i]; + } + } + +public: + _FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[capacity_index]; } + _FORCE_INLINE_ uint32_t size() const { return num_elements; } + + /* Standard Godot Container API */ + + bool is_empty() const { + return num_elements == 0; + } + + void clear() { + if (keys == nullptr || num_elements == 0) { + return; + } + uint32_t capacity = hash_table_size_primes[capacity_index]; + for (uint32_t i = 0; i < capacity; i++) { + hashes[i] = EMPTY_HASH; + } + for (uint32_t i = 0; i < num_elements; i++) { + keys[i].~TKey(); + } + + num_elements = 0; + } + + _FORCE_INLINE_ bool has(const TKey &p_key) const { + uint32_t _pos = 0; + return _lookup_pos(p_key, _pos); + } + + bool erase(const TKey &p_key) { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + + if (!exists) { + return false; + } + + uint32_t key_pos = pos; + pos = key_to_hash[pos]; //make hash pos + + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t next_pos = fastmod(pos + 1, capacity_inv, capacity); + while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) { + uint32_t kpos = hash_to_key[pos]; + uint32_t kpos_next = hash_to_key[next_pos]; + SWAP(key_to_hash[kpos], key_to_hash[kpos_next]); + SWAP(hashes[next_pos], hashes[pos]); + SWAP(hash_to_key[next_pos], hash_to_key[pos]); + + pos = next_pos; + next_pos = fastmod(pos + 1, capacity_inv, capacity); + } + + hashes[pos] = EMPTY_HASH; + keys[key_pos].~TKey(); + num_elements--; + if (key_pos < num_elements) { + // Not the last key, move the last one here to keep keys lineal + memnew_placement(&keys[key_pos], TKey(keys[num_elements])); + keys[num_elements].~TKey(); + key_to_hash[key_pos] = key_to_hash[num_elements]; + hash_to_key[key_to_hash[num_elements]] = key_pos; + } + + return true; + } + + // Reserves space for a number of elements, useful to avoid many resizes and rehashes. + // If adding a known (possibly large) number of elements at once, must be larger than old capacity. + void reserve(uint32_t p_new_capacity) { + uint32_t new_index = capacity_index; + + while (hash_table_size_primes[new_index] < p_new_capacity) { + ERR_FAIL_COND_MSG(new_index + 1 == (uint32_t)HASH_TABLE_SIZE_MAX, nullptr); + new_index++; + } + + if (new_index == capacity_index) { + return; + } + + if (keys == nullptr) { + capacity_index = new_index; + return; // Unallocated yet. + } + _resize_and_rehash(new_index); + } + + /** Iterator API **/ + + struct Iterator { + _FORCE_INLINE_ const TKey &operator*() const { + return keys[index]; + } + _FORCE_INLINE_ const TKey *operator->() const { + return &keys[index]; + } + _FORCE_INLINE_ Iterator &operator++() { + index++; + if (index >= (int32_t)num_keys) { + index = -1; + keys = nullptr; + num_keys = 0; + } + return *this; + } + _FORCE_INLINE_ Iterator &operator--() { + index--; + if (index < 0) { + index = -1; + keys = nullptr; + num_keys = 0; + } + return *this; + } + + _FORCE_INLINE_ const TKey &key() const { + return keys[index]; + } + _FORCE_INLINE_ const TKey *key_ptr() const { + return &keys[index]; + } + + _FORCE_INLINE_ Iterator &next() { + index++; + if (index >= (int32_t)num_keys) { + index = -1; + keys = nullptr; + num_keys = 0; + } + return *this; + } + _FORCE_INLINE_ Iterator &prev() { + index--; + if (index < 0) { + index = -1; + keys = nullptr; + num_keys = 0; + } + return *this; + } + + _FORCE_INLINE_ bool valid() const { + return keys != nullptr; + } + + _FORCE_INLINE_ bool operator==(const Iterator &b) const { return keys == b.keys && index == b.index; } + _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return keys != b.keys || index != b.index; } + + _FORCE_INLINE_ explicit operator bool() const { + return keys != nullptr; + } + + _FORCE_INLINE_ Iterator(const TKey *p_keys, uint32_t p_num_keys, int32_t p_index = -1) { + keys = p_keys; + num_keys = p_num_keys; + index = p_index; + } + _FORCE_INLINE_ Iterator() {} + _FORCE_INLINE_ Iterator(const Iterator &p_it) { + keys = p_it.keys; + num_keys = p_it.num_keys; + index = p_it.index; + } + _FORCE_INLINE_ void operator=(const Iterator &p_it) { + keys = p_it.keys; + num_keys = p_it.num_keys; + index = p_it.index; + } + + private: + const TKey *keys = nullptr; + uint32_t num_keys = 0; + int32_t index = -1; + }; + + _FORCE_INLINE_ Iterator begin() const { + return num_elements ? Iterator(keys, num_elements, 0) : Iterator(); + } + _FORCE_INLINE_ Iterator end() const { + return Iterator(); + } + _FORCE_INLINE_ Iterator last() const { + if (num_elements == 0) { + return Iterator(); + } + return Iterator(keys, num_elements, num_elements - 1); + } + + _FORCE_INLINE_ Iterator find(const TKey &p_key) const { + uint32_t pos = 0; + bool exists = _lookup_pos(p_key, pos); + if (!exists) { + return end(); + } + return Iterator(keys, num_elements, pos); + } + + _FORCE_INLINE_ void remove(const Iterator &p_iter) { + if (p_iter) { + erase(*p_iter); + } + } + + /* Insert */ + + Iterator insert(const TKey &p_key) { + uint32_t pos = _insert(p_key); + return Iterator(keys, num_elements, pos); + } + + /* Constructors */ + + HashSet(const HashSet &p_other) { + _init_from(p_other); + } + + void operator=(const HashSet &p_other) { + if (this == &p_other) { + return; // Ignore self assignment. + } + + clear(); + + if (keys != nullptr) { + Memory::free_static(keys); + Memory::free_static(key_to_hash); + Memory::free_static(hash_to_key); + Memory::free_static(hashes); + keys = nullptr; + hashes = nullptr; + hash_to_key = nullptr; + key_to_hash = nullptr; + } + + _init_from(p_other); + } + + HashSet(uint32_t p_initial_capacity) { + // Capacity can't be 0. + capacity_index = 0; + reserve(p_initial_capacity); + } + HashSet() { + capacity_index = MIN_CAPACITY_INDEX; + } + + void reset() { + clear(); + + if (keys != nullptr) { + Memory::free_static(keys); + Memory::free_static(key_to_hash); + Memory::free_static(hash_to_key); + Memory::free_static(hashes); + keys = nullptr; + hashes = nullptr; + hash_to_key = nullptr; + key_to_hash = nullptr; + } + capacity_index = MIN_CAPACITY_INDEX; + } + + ~HashSet() { + clear(); + + if (keys != nullptr) { + Memory::free_static(keys); + Memory::free_static(key_to_hash); + Memory::free_static(hash_to_key); + Memory::free_static(hashes); + } + } +}; + +#endif // HASH_SET_H diff --git a/sfw/containers/list.h b/sfw/containers/list.h new file mode 100644 index 0000000..aeb21e3 --- /dev/null +++ b/sfw/containers/list.h @@ -0,0 +1,701 @@ +#ifndef GLOBALS_LIST_H +#define GLOBALS_LIST_H + +/*************************************************************************/ +/* list.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/containers/sort_array.h" +#include "core/error/error_macros.h" +#include "core/os/memory.h" + +/** + * Generic Templatized Linked List Implementation. + * The implementation differs from the STL one because + * a compatible preallocated linked list can be written + * using the same API, or features such as erasing an element + * from the iterator. + */ + +template +class List { + struct _Data; + +public: + class Element { + private: + friend class List; + + T value; + Element *next_ptr; + Element *prev_ptr; + _Data *data; + + public: + /** + * Get NEXT Element iterator, for constant lists. + */ + _FORCE_INLINE_ const Element *next() const { + return next_ptr; + }; + /** + * Get NEXT Element iterator, + */ + _FORCE_INLINE_ Element *next() { + return next_ptr; + }; + + /** + * Get PREV Element iterator, for constant lists. + */ + _FORCE_INLINE_ const Element *prev() const { + return prev_ptr; + }; + /** + * Get PREV Element iterator, + */ + _FORCE_INLINE_ Element *prev() { + return prev_ptr; + }; + + /** + * * operator, for using as *iterator, when iterators are defined on stack. + */ + _FORCE_INLINE_ const T &operator*() const { + return value; + }; + /** + * operator->, for using as iterator->, when iterators are defined on stack, for constant lists. + */ + _FORCE_INLINE_ const T *operator->() const { + return &value; + }; + /** + * * operator, for using as *iterator, when iterators are defined on stack, + */ + _FORCE_INLINE_ T &operator*() { + return value; + }; + /** + * operator->, for using as iterator->, when iterators are defined on stack, for constant lists. + */ + _FORCE_INLINE_ T *operator->() { + return &value; + }; + + /** + * get the value stored in this element. + */ + _FORCE_INLINE_ T &get() { + return value; + }; + /** + * get the value stored in this element, for constant lists + */ + _FORCE_INLINE_ const T &get() const { + return value; + }; + /** + * set the value stored in this element. + */ + _FORCE_INLINE_ void set(const T &p_value) { + value = (T &)p_value; + }; + + void erase() { + data->erase(this); + } + + _FORCE_INLINE_ Element() { + next_ptr = nullptr; + prev_ptr = nullptr; + data = nullptr; + }; + }; + +private: + struct _Data { + Element *first; + Element *last; + int size_cache; + + bool erase(const Element *p_I) { + ERR_FAIL_COND_V(!p_I, false); + ERR_FAIL_COND_V(p_I->data != this, false); + + if (first == p_I) { + first = p_I->next_ptr; + }; + + if (last == p_I) { + last = p_I->prev_ptr; + } + + if (p_I->prev_ptr) { + p_I->prev_ptr->next_ptr = p_I->next_ptr; + } + + if (p_I->next_ptr) { + p_I->next_ptr->prev_ptr = p_I->prev_ptr; + } + + memdelete_allocator(const_cast(p_I)); + size_cache--; + + return true; + } + }; + + _Data *_data; + +public: + /** + * return a const iterator to the beginning of the list. + */ + _FORCE_INLINE_ const Element *front() const { + return _data ? _data->first : nullptr; + }; + + /** + * return an iterator to the beginning of the list. + */ + _FORCE_INLINE_ Element *front() { + return _data ? _data->first : nullptr; + }; + + /** + * return a const iterator to the last member of the list. + */ + _FORCE_INLINE_ const Element *back() const { + return _data ? _data->last : nullptr; + }; + + /** + * return an iterator to the last member of the list. + */ + _FORCE_INLINE_ Element *back() { + return _data ? _data->last : nullptr; + }; + + /** + * store a new element at the end of the list + */ + Element *push_back(const T &value) { + if (!_data) { + _data = memnew_allocator(_Data, A); + _data->first = nullptr; + _data->last = nullptr; + _data->size_cache = 0; + } + + Element *n = memnew_allocator(Element, A); + n->value = (T &)value; + + n->prev_ptr = _data->last; + n->next_ptr = nullptr; + n->data = _data; + + if (_data->last) { + _data->last->next_ptr = n; + } + + _data->last = n; + + if (!_data->first) { + _data->first = n; + } + + _data->size_cache++; + + return n; + }; + + void pop_back() { + if (_data && _data->last) { + erase(_data->last); + } + } + + /** + * store a new element at the beginning of the list + */ + Element *push_front(const T &value) { + if (!_data) { + _data = memnew_allocator(_Data, A); + _data->first = nullptr; + _data->last = nullptr; + _data->size_cache = 0; + } + + Element *n = memnew_allocator(Element, A); + n->value = (T &)value; + n->prev_ptr = nullptr; + n->next_ptr = _data->first; + n->data = _data; + + if (_data->first) { + _data->first->prev_ptr = n; + } + + _data->first = n; + + if (!_data->last) { + _data->last = n; + } + + _data->size_cache++; + + return n; + }; + + void pop_front() { + if (_data && _data->first) { + erase(_data->first); + } + } + + Element *insert_after(Element *p_element, const T &p_value) { + CRASH_COND(p_element && (!_data || p_element->data != _data)); + + if (!p_element) { + return push_back(p_value); + } + + Element *n = memnew_allocator(Element, A); + n->value = (T &)p_value; + n->prev_ptr = p_element; + n->next_ptr = p_element->next_ptr; + n->data = _data; + + if (!p_element->next_ptr) { + _data->last = n; + } else { + p_element->next_ptr->prev_ptr = n; + } + + p_element->next_ptr = n; + + _data->size_cache++; + + return n; + } + + Element *insert_before(Element *p_element, const T &p_value) { + CRASH_COND(p_element && (!_data || p_element->data != _data)); + + if (!p_element) { + return push_back(p_value); + } + + Element *n = memnew_allocator(Element, A); + n->value = (T &)p_value; + n->prev_ptr = p_element->prev_ptr; + n->next_ptr = p_element; + n->data = _data; + + if (!p_element->prev_ptr) { + _data->first = n; + } else { + p_element->prev_ptr->next_ptr = n; + } + + p_element->prev_ptr = n; + + _data->size_cache++; + + return n; + } + + /** + * find an element in the list, + */ + template + Element *find(const T_v &p_val) { + Element *it = front(); + while (it) { + if (it->value == p_val) { + return it; + } + it = it->next(); + }; + + return nullptr; + }; + + /** + * erase an element in the list, by iterator pointing to it. Return true if it was found/erased. + */ + bool erase(const Element *p_I) { + if (_data) { + bool ret = _data->erase(p_I); + + if (_data->size_cache == 0) { + memdelete_allocator<_Data, A>(_data); + _data = nullptr; + } + + return ret; + } + + return false; + }; + + /** + * erase the first element in the list, that contains value + */ + bool erase(const T &value) { + Element *I = find(value); + return erase(I); + }; + + /** + * return whether the list is empty + */ + _FORCE_INLINE_ bool empty() const { + return (!_data || !_data->size_cache); + } + + /** + * clear the list + */ + void clear() { + while (front()) { + erase(front()); + }; + }; + + _FORCE_INLINE_ int size() const { + return _data ? _data->size_cache : 0; + } + + void swap(Element *p_A, Element *p_B) { + ERR_FAIL_COND(!p_A || !p_B); + ERR_FAIL_COND(p_A->data != _data); + ERR_FAIL_COND(p_B->data != _data); + + if (p_A == p_B) { + return; + } + Element *A_prev = p_A->prev_ptr; + Element *A_next = p_A->next_ptr; + Element *B_prev = p_B->prev_ptr; + Element *B_next = p_B->next_ptr; + + if (A_prev) { + A_prev->next_ptr = p_B; + } else { + _data->first = p_B; + } + if (B_prev) { + B_prev->next_ptr = p_A; + } else { + _data->first = p_A; + } + if (A_next) { + A_next->prev_ptr = p_B; + } else { + _data->last = p_B; + } + if (B_next) { + B_next->prev_ptr = p_A; + } else { + _data->last = p_A; + } + p_A->prev_ptr = A_next == p_B ? p_B : B_prev; + p_A->next_ptr = B_next == p_A ? p_B : B_next; + p_B->prev_ptr = B_next == p_A ? p_A : A_prev; + p_B->next_ptr = A_next == p_B ? p_A : A_next; + } + /** + * copy the list + */ + void operator=(const List &p_list) { + clear(); + const Element *it = p_list.front(); + while (it) { + push_back(it->get()); + it = it->next(); + } + } + + T &operator[](int p_index) { + CRASH_BAD_INDEX(p_index, size()); + + Element *I = front(); + int c = 0; + while (I) { + if (c == p_index) { + return I->get(); + } + I = I->next(); + c++; + } + + CRASH_NOW(); // bug!! + } + + const T &operator[](int p_index) const { + CRASH_BAD_INDEX(p_index, size()); + + const Element *I = front(); + int c = 0; + while (I) { + if (c == p_index) { + return I->get(); + } + I = I->next(); + c++; + } + + CRASH_NOW(); // bug!! + } + + void move_to_back(Element *p_I) { + ERR_FAIL_COND(p_I->data != _data); + if (!p_I->next_ptr) { + return; + } + + if (_data->first == p_I) { + _data->first = p_I->next_ptr; + }; + + if (_data->last == p_I) { + _data->last = p_I->prev_ptr; + } + + if (p_I->prev_ptr) { + p_I->prev_ptr->next_ptr = p_I->next_ptr; + } + + p_I->next_ptr->prev_ptr = p_I->prev_ptr; + + _data->last->next_ptr = p_I; + p_I->prev_ptr = _data->last; + p_I->next_ptr = nullptr; + _data->last = p_I; + } + + void invert() { + int s = size() / 2; + Element *F = front(); + Element *B = back(); + for (int i = 0; i < s; i++) { + SWAP(F->value, B->value); + F = F->next(); + B = B->prev(); + } + } + + void move_to_front(Element *p_I) { + ERR_FAIL_COND(p_I->data != _data); + if (!p_I->prev_ptr) { + return; + } + + if (_data->first == p_I) { + _data->first = p_I->next_ptr; + }; + + if (_data->last == p_I) { + _data->last = p_I->prev_ptr; + } + + p_I->prev_ptr->next_ptr = p_I->next_ptr; + + if (p_I->next_ptr) { + p_I->next_ptr->prev_ptr = p_I->prev_ptr; + } + + _data->first->prev_ptr = p_I; + p_I->next_ptr = _data->first; + p_I->prev_ptr = nullptr; + _data->first = p_I; + } + + void move_before(Element *value, Element *where) { + if (value->prev_ptr) { + value->prev_ptr->next_ptr = value->next_ptr; + } else { + _data->first = value->next_ptr; + } + if (value->next_ptr) { + value->next_ptr->prev_ptr = value->prev_ptr; + } else { + _data->last = value->prev_ptr; + } + + value->next_ptr = where; + if (!where) { + value->prev_ptr = _data->last; + _data->last = value; + return; + }; + + value->prev_ptr = where->prev_ptr; + + if (where->prev_ptr) { + where->prev_ptr->next_ptr = value; + } else { + _data->first = value; + }; + + where->prev_ptr = value; + }; + + /** + * simple insertion sort + */ + + void sort() { + sort_custom>(); + } + + template + void sort_custom_inplace() { + if (size() < 2) { + return; + } + + Element *from = front(); + Element *current = from; + Element *to = from; + + while (current) { + Element *next = current->next_ptr; + + if (from != current) { + current->prev_ptr = NULL; + current->next_ptr = from; + + Element *find = from; + C less; + while (find && less(find->value, current->value)) { + current->prev_ptr = find; + current->next_ptr = find->next_ptr; + find = find->next_ptr; + } + + if (current->prev_ptr) { + current->prev_ptr->next_ptr = current; + } else { + from = current; + } + + if (current->next_ptr) { + current->next_ptr->prev_ptr = current; + } else { + to = current; + } + } else { + current->prev_ptr = NULL; + current->next_ptr = NULL; + } + + current = next; + } + _data->first = from; + _data->last = to; + } + + template + struct AuxiliaryComparator { + C compare; + _FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const { + return compare(a->value, b->value); + } + }; + + template + void sort_custom() { + //this version uses auxiliary memory for speed. + //if you don't want to use auxiliary memory, use the in_place version + + int s = size(); + if (s < 2) { + return; + } + + Element **aux_buffer = memnew_arr(Element *, s); + + int idx = 0; + for (Element *E = front(); E; E = E->next_ptr) { + aux_buffer[idx] = E; + idx++; + } + + SortArray> sort; + sort.sort(aux_buffer, s); + + _data->first = aux_buffer[0]; + aux_buffer[0]->prev_ptr = nullptr; + aux_buffer[0]->next_ptr = aux_buffer[1]; + + _data->last = aux_buffer[s - 1]; + aux_buffer[s - 1]->prev_ptr = aux_buffer[s - 2]; + aux_buffer[s - 1]->next_ptr = nullptr; + + for (int i = 1; i < s - 1; i++) { + aux_buffer[i]->prev_ptr = aux_buffer[i - 1]; + aux_buffer[i]->next_ptr = aux_buffer[i + 1]; + } + + memdelete_arr(aux_buffer); + } + + const void *id() const { + return (void *)_data; + } + + /** + * copy constructor for the list + */ + List(const List &p_list) { + _data = nullptr; + const Element *it = p_list.front(); + while (it) { + push_back(it->get()); + it = it->next(); + } + } + + List() { + _data = nullptr; + }; + ~List() { + clear(); + if (_data) { + ERR_FAIL_COND(_data->size_cache); + memdelete_allocator<_Data, A>(_data); + } + }; +}; + +#endif diff --git a/sfw/containers/local_vector.h b/sfw/containers/local_vector.h new file mode 100644 index 0000000..16f891b --- /dev/null +++ b/sfw/containers/local_vector.h @@ -0,0 +1,323 @@ +#ifndef LOCAL_VECTOR_H +#define LOCAL_VECTOR_H + +/*************************************************************************/ +/* local_vector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/containers/pool_vector.h" +#include "core/containers/sort_array.h" +#include "core/containers/vector.h" +#include "core/error/error_macros.h" +#include "core/os/memory.h" + +template +class LocalVector { +protected: + U count = 0; + U capacity = 0; + T *data = nullptr; + +public: + T *ptr() { + return data; + } + + const T *ptr() const { + return data; + } + + _FORCE_INLINE_ void push_back(T p_elem) { + if (unlikely(count == capacity)) { + if (capacity == 0) { + capacity = 1; + } else { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + + if (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { + memnew_placement(&data[count++], T(p_elem)); + } else { + data[count++] = p_elem; + } + } + + void remove(U p_index) { + ERR_FAIL_UNSIGNED_INDEX(p_index, count); + count--; + for (U i = p_index; i < count; i++) { + data[i] = data[i + 1]; + } + if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + data[count].~T(); + } + } + + // Removes the item copying the last value into the position of the one to + // remove. It's generally faster than `remove`. + void remove_unordered(U p_index) { + ERR_FAIL_INDEX(p_index, count); + count--; + if (count > p_index) { + data[p_index] = data[count]; + } + if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + data[count].~T(); + } + } + + _FORCE_INLINE_ bool erase(const T &p_val) { + int64_t idx = find(p_val); + if (idx >= 0) { + remove(idx); + return true; + } + return false; + } + + U erase_multiple_unordered(const T &p_val) { + U from = 0; + U count = 0; + while (true) { + int64_t idx = find(p_val, from); + + if (idx == -1) { + break; + } + remove_unordered(idx); + from = idx; + count++; + } + return count; + } + + void invert() { + for (U i = 0; i < count / 2; i++) { + SWAP(data[i], data[count - i - 1]); + } + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ void reset() { + clear(); + if (data) { + memfree(data); + data = nullptr; + capacity = 0; + } + } + _FORCE_INLINE_ bool empty() const { return count == 0; } + _FORCE_INLINE_ U get_capacity() const { return capacity; } + _FORCE_INLINE_ void reserve(U p_size, bool p_allow_shrink = false) { + p_size = nearest_power_of_2_templated(p_size); + if (!p_allow_shrink ? p_size > capacity : ((p_size >= count) && (p_size != capacity))) { + capacity = p_size; + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + } + + _FORCE_INLINE_ U size() const { return count; } + void resize(U p_size) { + if (p_size < count) { + if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + for (U i = p_size; i < count; i++) { + data[i].~T(); + } + } + count = p_size; + } else if (p_size > count) { + if (unlikely(p_size > capacity)) { + if (capacity == 0) { + capacity = 1; + } + while (capacity < p_size) { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + if (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { + for (U i = count; i < p_size; i++) { + memnew_placement(&data[i], T); + } + } + count = p_size; + } + } + _FORCE_INLINE_ const T &operator[](U p_index) const { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + _FORCE_INLINE_ T &operator[](U p_index) { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + + void fill(T p_val) { + for (U i = 0; i < count; i++) { + data[i] = p_val; + } + } + + void insert(U p_pos, T p_val) { + ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1); + if (p_pos == count) { + push_back(p_val); + } else { + resize(count + 1); + for (U i = count - 1; i > p_pos; i--) { + data[i] = data[i - 1]; + } + data[p_pos] = p_val; + } + } + + int64_t find(const T &p_val, U p_from = 0) const { + for (U i = p_from; i < count; i++) { + if (data[i] == p_val) { + return int64_t(i); + } + } + return -1; + } + + template + void sort_custom() { + U len = count; + if (len == 0) { + return; + } + + SortArray sorter; + sorter.sort(data, len); + } + + void sort() { + sort_custom<_DefaultComparator>(); + } + + void ordered_insert(T p_val) { + U i; + for (i = 0; i < count; i++) { + if (p_val < data[i]) { + break; + } + } + insert(i, p_val); + } + + operator Vector() const { + Vector ret; + ret.resize(size()); + T *w = ret.ptrw(); + memcpy(w, data, sizeof(T) * count); + return ret; + } + + operator PoolVector() const { + PoolVector pl; + if (size()) { + pl.resize(size()); + typename PoolVector::Write w = pl.write(); + T *dest = w.ptr(); + memcpy(dest, data, sizeof(T) * count); + } + return pl; + } + + Vector to_byte_array() const { //useful to pass stuff to gpu or variant + Vector ret; + ret.resize(count * sizeof(T)); + uint8_t *w = ret.ptrw(); + memcpy(w, data, sizeof(T) * count); + return ret; + } + + _FORCE_INLINE_ LocalVector() {} + _FORCE_INLINE_ LocalVector(const LocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + } + LocalVector(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + } + LocalVector(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + } + + inline LocalVector &operator=(const LocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + return *this; + } + inline LocalVector &operator=(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + return *this; + } + inline LocalVector &operator=(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + return *this; + } + + _FORCE_INLINE_ ~LocalVector() { + if (data) { + reset(); + } + } +}; + +// Integer default version +template +class LocalVectori : public LocalVector { +}; + +#endif // LOCAL_VECTOR_H diff --git a/sfw/containers/rb_map.h b/sfw/containers/rb_map.h new file mode 100644 index 0000000..c324d78 --- /dev/null +++ b/sfw/containers/rb_map.h @@ -0,0 +1,679 @@ +#ifndef RB_MAP_H +#define RB_MAP_H + +/*************************************************************************/ +/* rb_map.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/error/error_macros.h" +#include "core/os/memory.h" + +// based on the very nice implementation of rb-trees by: +// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html + +template , class A = DefaultAllocator> +class RBMap { + enum Color { + RED, + BLACK + }; + struct _Data; + +public: + class Element { + private: + friend class RBMap; + int color; + Element *right; + Element *left; + Element *parent; + Element *_next; + Element *_prev; + K _key; + V _value; + //_Data *data; + + public: + const Element *next() const { + return _next; + } + Element *next() { + return _next; + } + const Element *prev() const { + return _prev; + } + Element *prev() { + return _prev; + } + const K &key() const { + return _key; + }; + V &value() { + return _value; + }; + const V &value() const { + return _value; + }; + V &get() { + return _value; + }; + const V &get() const { + return _value; + }; + Element() { + color = RED; + right = nullptr; + left = nullptr; + parent = nullptr; + _next = nullptr; + _prev = nullptr; + }; + }; + +private: + struct _Data { + Element *_root; + Element *_nil; + int size_cache; + + _FORCE_INLINE_ _Data() { +#ifdef GLOBALNIL_DISABLED + _nil = memnew_allocator(Element, A); + _nil->parent = _nil->left = _nil->right = _nil; + _nil->color = BLACK; +#else + _nil = (Element *)&_GlobalNilClass::_nil; +#endif + _root = nullptr; + size_cache = 0; + } + + void _create_root() { + _root = memnew_allocator(Element, A); + _root->parent = _root->left = _root->right = _nil; + _root->color = BLACK; + } + + void _free_root() { + if (_root) { + memdelete_allocator(_root); + _root = nullptr; + } + } + + ~_Data() { + _free_root(); + +#ifdef GLOBALNIL_DISABLED + memdelete_allocator(_nil); +#endif + } + }; + + _Data _data; + + inline void _set_color(Element *p_node, int p_color) { + ERR_FAIL_COND(p_node == _data._nil && p_color == RED); + p_node->color = p_color; + } + + inline void _rotate_left(Element *p_node) { + Element *r = p_node->right; + p_node->right = r->left; + if (r->left != _data._nil) { + r->left->parent = p_node; + } + r->parent = p_node->parent; + if (p_node == p_node->parent->left) { + p_node->parent->left = r; + } else { + p_node->parent->right = r; + } + + r->left = p_node; + p_node->parent = r; + } + + inline void _rotate_right(Element *p_node) { + Element *l = p_node->left; + p_node->left = l->right; + if (l->right != _data._nil) { + l->right->parent = p_node; + } + l->parent = p_node->parent; + if (p_node == p_node->parent->right) { + p_node->parent->right = l; + } else { + p_node->parent->left = l; + } + + l->right = p_node; + p_node->parent = l; + } + + inline Element *_successor(Element *p_node) const { + Element *node = p_node; + + if (node->right != _data._nil) { + node = node->right; + while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */ + node = node->left; + } + return node; + } else { + while (node == node->parent->right) { + node = node->parent; + } + + if (node->parent == _data._root) { + return nullptr; // No successor, as p_node = last node + } + return node->parent; + } + } + + inline Element *_predecessor(Element *p_node) const { + Element *node = p_node; + + if (node->left != _data._nil) { + node = node->left; + while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */ + node = node->right; + } + return node; + } else { + while (node == node->parent->left) { + node = node->parent; + } + + if (node == _data._root) { + return nullptr; // No predecessor, as p_node = first node + } + return node->parent; + } + } + + Element *_find(const K &p_key) const { + Element *node = _data._root->left; + C less; + + while (node != _data._nil) { + if (less(p_key, node->_key)) { + node = node->left; + } else if (less(node->_key, p_key)) { + node = node->right; + } else { + return node; // found + } + } + + return nullptr; + } + + Element *_find_closest(const K &p_key) const { + Element *node = _data._root->left; + Element *prev = nullptr; + C less; + + while (node != _data._nil) { + prev = node; + + if (less(p_key, node->_key)) { + node = node->left; + } else if (less(node->_key, p_key)) { + node = node->right; + } else { + return node; // found + } + } + + if (prev == nullptr) { + return nullptr; // tree empty + } + + if (less(p_key, prev->_key)) { + prev = prev->_prev; + } + + return prev; + } + + void _insert_rb_fix(Element *p_new_node) { + Element *node = p_new_node; + Element *nparent = node->parent; + Element *ngrand_parent; + + while (nparent->color == RED) { + ngrand_parent = nparent->parent; + + if (nparent == ngrand_parent->left) { + if (ngrand_parent->right->color == RED) { + _set_color(nparent, BLACK); + _set_color(ngrand_parent->right, BLACK); + _set_color(ngrand_parent, RED); + node = ngrand_parent; + nparent = node->parent; + } else { + if (node == nparent->right) { + _rotate_left(nparent); + node = nparent; + nparent = node->parent; + } + _set_color(nparent, BLACK); + _set_color(ngrand_parent, RED); + _rotate_right(ngrand_parent); + } + } else { + if (ngrand_parent->left->color == RED) { + _set_color(nparent, BLACK); + _set_color(ngrand_parent->left, BLACK); + _set_color(ngrand_parent, RED); + node = ngrand_parent; + nparent = node->parent; + } else { + if (node == nparent->left) { + _rotate_right(nparent); + node = nparent; + nparent = node->parent; + } + _set_color(nparent, BLACK); + _set_color(ngrand_parent, RED); + _rotate_left(ngrand_parent); + } + } + } + + _set_color(_data._root->left, BLACK); + } + + Element *_insert(const K &p_key, const V &p_value) { + Element *new_parent = _data._root; + Element *node = _data._root->left; + C less; + + while (node != _data._nil) { + new_parent = node; + + if (less(p_key, node->_key)) { + node = node->left; + } else if (less(node->_key, p_key)) { + node = node->right; + } else { + node->_value = p_value; + return node; // Return existing node with new value + } + } + + Element *new_node = memnew_allocator(Element, A); + new_node->parent = new_parent; + new_node->right = _data._nil; + new_node->left = _data._nil; + new_node->_key = p_key; + new_node->_value = p_value; + //new_node->data=_data; + + if (new_parent == _data._root || less(p_key, new_parent->_key)) { + new_parent->left = new_node; + } else { + new_parent->right = new_node; + } + + new_node->_next = _successor(new_node); + new_node->_prev = _predecessor(new_node); + if (new_node->_next) { + new_node->_next->_prev = new_node; + } + if (new_node->_prev) { + new_node->_prev->_next = new_node; + } + + _data.size_cache++; + _insert_rb_fix(new_node); + return new_node; + } + + void _erase_fix_rb(Element *p_node) { + Element *root = _data._root->left; + Element *node = _data._nil; + Element *sibling = p_node; + Element *parent = sibling->parent; + + while (node != root) { // If red node found, will exit at a break + if (sibling->color == RED) { + _set_color(sibling, BLACK); + _set_color(parent, RED); + if (sibling == parent->right) { + sibling = sibling->left; + _rotate_left(parent); + } else { + sibling = sibling->right; + _rotate_right(parent); + } + } + if ((sibling->left->color == BLACK) && (sibling->right->color == BLACK)) { + _set_color(sibling, RED); + if (parent->color == RED) { + _set_color(parent, BLACK); + break; + } else { // loop: haven't found any red nodes yet + node = parent; + parent = node->parent; + sibling = (node == parent->left) ? parent->right : parent->left; + } + } else { + if (sibling == parent->right) { + if (sibling->right->color == BLACK) { + _set_color(sibling->left, BLACK); + _set_color(sibling, RED); + _rotate_right(sibling); + sibling = sibling->parent; + } + _set_color(sibling, parent->color); + _set_color(parent, BLACK); + _set_color(sibling->right, BLACK); + _rotate_left(parent); + break; + } else { + if (sibling->left->color == BLACK) { + _set_color(sibling->right, BLACK); + _set_color(sibling, RED); + _rotate_left(sibling); + sibling = sibling->parent; + } + + _set_color(sibling, parent->color); + _set_color(parent, BLACK); + _set_color(sibling->left, BLACK); + _rotate_right(parent); + break; + } + } + } + + ERR_FAIL_COND(_data._nil->color != BLACK); + } + + void _erase(Element *p_node) { + Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next; + Element *node = (rp->left == _data._nil) ? rp->right : rp->left; + + Element *sibling; + if (rp == rp->parent->left) { + rp->parent->left = node; + sibling = rp->parent->right; + } else { + rp->parent->right = node; + sibling = rp->parent->left; + } + + if (node->color == RED) { + node->parent = rp->parent; + _set_color(node, BLACK); + } else if (rp->color == BLACK && rp->parent != _data._root) { + _erase_fix_rb(sibling); + } + + if (rp != p_node) { + ERR_FAIL_COND(rp == _data._nil); + + rp->left = p_node->left; + rp->right = p_node->right; + rp->parent = p_node->parent; + rp->color = p_node->color; + if (p_node->left != _data._nil) { + p_node->left->parent = rp; + } + if (p_node->right != _data._nil) { + p_node->right->parent = rp; + } + + if (p_node == p_node->parent->left) { + p_node->parent->left = rp; + } else { + p_node->parent->right = rp; + } + } + + if (p_node->_next) { + p_node->_next->_prev = p_node->_prev; + } + if (p_node->_prev) { + p_node->_prev->_next = p_node->_next; + } + + memdelete_allocator(p_node); + _data.size_cache--; + ERR_FAIL_COND(_data._nil->color == RED); + } + + void _calculate_depth(Element *p_element, int &max_d, int d) const { + if (p_element == _data._nil) { + return; + } + + _calculate_depth(p_element->left, max_d, d + 1); + _calculate_depth(p_element->right, max_d, d + 1); + + if (d > max_d) { + max_d = d; + } + } + + void _cleanup_tree(Element *p_element) { + if (p_element == _data._nil) { + return; + } + + _cleanup_tree(p_element->left); + _cleanup_tree(p_element->right); + memdelete_allocator(p_element); + } + + void _copy_from(const RBMap &p_map) { + clear(); + // not the fastest way, but safeset to write. + for (Element *I = p_map.front(); I; I = I->next()) { + insert(I->key(), I->value()); + } + } + +public: + const Element *find(const K &p_key) const { + if (!_data._root) { + return nullptr; + } + + const Element *res = _find(p_key); + return res; + } + + Element *find(const K &p_key) { + if (!_data._root) { + return nullptr; + } + + Element *res = _find(p_key); + return res; + } + + const Element *find_closest(const K &p_key) const { + if (!_data._root) { + return NULL; + } + + const Element *res = _find_closest(p_key); + return res; + } + + Element *find_closest(const K &p_key) { + if (!_data._root) { + return nullptr; + } + + Element *res = _find_closest(p_key); + return res; + } + + bool has(const K &p_key) const { + return find(p_key) != nullptr; + } + + Element *insert(const K &p_key, const V &p_value) { + if (!_data._root) { + _data._create_root(); + } + return _insert(p_key, p_value); + } + + void erase(Element *p_element) { + if (!_data._root || !p_element) { + return; + } + + _erase(p_element); + if (_data.size_cache == 0 && _data._root) { + _data._free_root(); + } + } + + bool erase(const K &p_key) { + if (!_data._root) { + return false; + } + + Element *e = find(p_key); + if (!e) { + return false; + } + + _erase(e); + if (_data.size_cache == 0 && _data._root) { + _data._free_root(); + } + return true; + } + + const V &operator[](const K &p_key) const { + CRASH_COND(!_data._root); + const Element *e = find(p_key); + CRASH_COND(!e); + return e->_value; + } + + V &operator[](const K &p_key) { + if (!_data._root) { + _data._create_root(); + } + + Element *e = find(p_key); + if (!e) { + e = insert(p_key, V()); + } + + return e->_value; + } + + Element *front() const { + if (!_data._root) { + return nullptr; + } + + Element *e = _data._root->left; + if (e == _data._nil) { + return nullptr; + } + + while (e->left != _data._nil) { + e = e->left; + } + + return e; + } + + Element *back() const { + if (!_data._root) { + return nullptr; + } + + Element *e = _data._root->left; + if (e == _data._nil) { + return nullptr; + } + + while (e->right != _data._nil) { + e = e->right; + } + + return e; + } + + inline bool empty() const { return _data.size_cache == 0; } + inline int size() const { return _data.size_cache; } + + int calculate_depth() const { + // used for debug mostly + if (!_data._root) { + return 0; + } + + int max_d = 0; + _calculate_depth(_data._root->left, max_d, 0); + return max_d; + } + + void clear() { + if (!_data._root) { + return; + } + + _cleanup_tree(_data._root->left); + _data._root->left = _data._nil; + _data.size_cache = 0; + _data._free_root(); + } + + void operator=(const RBMap &p_map) { + _copy_from(p_map); + } + + RBMap(const RBMap &p_map) { + _copy_from(p_map); + } + + _FORCE_INLINE_ RBMap() { + } + + ~RBMap() { + clear(); + } +}; + +#endif diff --git a/sfw/containers/rb_set.h b/sfw/containers/rb_set.h new file mode 100644 index 0000000..bc351df --- /dev/null +++ b/sfw/containers/rb_set.h @@ -0,0 +1,633 @@ +#ifndef RB_SET_H +#define RB_SET_H + +/*************************************************************************/ +/* rb_set.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/os/memory.h" +#include "core/typedefs.h" + +// based on the very nice implementation of rb-trees by: +// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html + +template , class A = DefaultAllocator> +class RBSet { + enum Color { + RED, + BLACK + }; + struct _Data; + +public: + class Element { + private: + friend class RBSet; + int color; + Element *right; + Element *left; + Element *parent; + Element *_next; + Element *_prev; + T value; + //_Data *data; + + public: + const Element *next() const { + return _next; + } + Element *next() { + return _next; + } + const Element *prev() const { + return _prev; + } + Element *prev() { + return _prev; + } + const T &get() const { + return value; + }; + Element() { + color = RED; + right = nullptr; + left = nullptr; + parent = nullptr; + _next = nullptr; + _prev = nullptr; + }; + }; + +private: + struct _Data { + Element *_root; + Element *_nil; + int size_cache; + + _FORCE_INLINE_ _Data() { +#ifdef GLOBALNIL_DISABLED + _nil = memnew_allocator(Element, A); + _nil->parent = _nil->left = _nil->right = _nil; + _nil->color = BLACK; +#else + _nil = (Element *)&_GlobalNilClass::_nil; +#endif + _root = nullptr; + size_cache = 0; + } + + void _create_root() { + _root = memnew_allocator(Element, A); + _root->parent = _root->left = _root->right = _nil; + _root->color = BLACK; + } + + void _free_root() { + if (_root) { + memdelete_allocator(_root); + _root = nullptr; + } + } + + ~_Data() { + _free_root(); + +#ifdef GLOBALNIL_DISABLED + memdelete_allocator(_nil); +#endif + } + }; + + _Data _data; + + inline void _set_color(Element *p_node, int p_color) { + ERR_FAIL_COND(p_node == _data._nil && p_color == RED); + p_node->color = p_color; + } + + inline void _rotate_left(Element *p_node) { + Element *r = p_node->right; + p_node->right = r->left; + if (r->left != _data._nil) { + r->left->parent = p_node; + } + r->parent = p_node->parent; + if (p_node == p_node->parent->left) { + p_node->parent->left = r; + } else { + p_node->parent->right = r; + } + + r->left = p_node; + p_node->parent = r; + } + + inline void _rotate_right(Element *p_node) { + Element *l = p_node->left; + p_node->left = l->right; + if (l->right != _data._nil) { + l->right->parent = p_node; + } + l->parent = p_node->parent; + if (p_node == p_node->parent->right) { + p_node->parent->right = l; + } else { + p_node->parent->left = l; + } + + l->right = p_node; + p_node->parent = l; + } + + inline Element *_successor(Element *p_node) const { + Element *node = p_node; + + if (node->right != _data._nil) { + node = node->right; + while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */ + node = node->left; + } + return node; + } else { + while (node == node->parent->right) { + node = node->parent; + } + + if (node->parent == _data._root) { + return nullptr; // No successor, as p_node = last node + } + return node->parent; + } + } + + inline Element *_predecessor(Element *p_node) const { + Element *node = p_node; + + if (node->left != _data._nil) { + node = node->left; + while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */ + node = node->right; + } + return node; + } else { + while (node == node->parent->left) { + node = node->parent; + } + + if (node == _data._root) { + return nullptr; // No predecessor, as p_node = first node. + } + return node->parent; + } + } + + Element *_find(const T &p_value) const { + Element *node = _data._root->left; + C less; + + while (node != _data._nil) { + if (less(p_value, node->value)) { + node = node->left; + } else if (less(node->value, p_value)) { + node = node->right; + } else { + return node; // found + } + } + + return nullptr; + } + + Element *_lower_bound(const T &p_value) const { + Element *node = _data._root->left; + Element *prev = nullptr; + C less; + + while (node != _data._nil) { + prev = node; + + if (less(p_value, node->value)) { + node = node->left; + } else if (less(node->value, p_value)) { + node = node->right; + } else { + return node; // found + } + } + + if (prev == nullptr) { + return nullptr; // tree empty + } + + if (less(prev->value, p_value)) { + prev = prev->_next; + } + + return prev; + } + + void _insert_rb_fix(Element *p_new_node) { + Element *node = p_new_node; + Element *nparent = node->parent; + Element *ngrand_parent; + + while (nparent->color == RED) { + ngrand_parent = nparent->parent; + + if (nparent == ngrand_parent->left) { + if (ngrand_parent->right->color == RED) { + _set_color(nparent, BLACK); + _set_color(ngrand_parent->right, BLACK); + _set_color(ngrand_parent, RED); + node = ngrand_parent; + nparent = node->parent; + } else { + if (node == nparent->right) { + _rotate_left(nparent); + node = nparent; + nparent = node->parent; + } + _set_color(nparent, BLACK); + _set_color(ngrand_parent, RED); + _rotate_right(ngrand_parent); + } + } else { + if (ngrand_parent->left->color == RED) { + _set_color(nparent, BLACK); + _set_color(ngrand_parent->left, BLACK); + _set_color(ngrand_parent, RED); + node = ngrand_parent; + nparent = node->parent; + } else { + if (node == nparent->left) { + _rotate_right(nparent); + node = nparent; + nparent = node->parent; + } + _set_color(nparent, BLACK); + _set_color(ngrand_parent, RED); + _rotate_left(ngrand_parent); + } + } + } + + _set_color(_data._root->left, BLACK); + } + + Element *_insert(const T &p_value) { + Element *new_parent = _data._root; + Element *node = _data._root->left; + C less; + + while (node != _data._nil) { + new_parent = node; + + if (less(p_value, node->value)) { + node = node->left; + } else if (less(node->value, p_value)) { + node = node->right; + } else { + return node; // Return existing node + } + } + + Element *new_node = memnew_allocator(Element, A); + new_node->parent = new_parent; + new_node->right = _data._nil; + new_node->left = _data._nil; + new_node->value = p_value; + //new_node->data=_data; + + if (new_parent == _data._root || less(p_value, new_parent->value)) { + new_parent->left = new_node; + } else { + new_parent->right = new_node; + } + + new_node->_next = _successor(new_node); + new_node->_prev = _predecessor(new_node); + if (new_node->_next) { + new_node->_next->_prev = new_node; + } + if (new_node->_prev) { + new_node->_prev->_next = new_node; + } + + _data.size_cache++; + _insert_rb_fix(new_node); + return new_node; + } + + void _erase_fix_rb(Element *p_node) { + Element *root = _data._root->left; + Element *node = _data._nil; + Element *sibling = p_node; + Element *parent = sibling->parent; + + while (node != root) { // If red node found, will exit at a break + if (sibling->color == RED) { + _set_color(sibling, BLACK); + _set_color(parent, RED); + if (sibling == parent->right) { + sibling = sibling->left; + _rotate_left(parent); + } else { + sibling = sibling->right; + _rotate_right(parent); + } + } + if ((sibling->left->color == BLACK) && (sibling->right->color == BLACK)) { + _set_color(sibling, RED); + if (parent->color == RED) { + _set_color(parent, BLACK); + break; + } else { // loop: haven't found any red nodes yet + node = parent; + parent = node->parent; + sibling = (node == parent->left) ? parent->right : parent->left; + } + } else { + if (sibling == parent->right) { + if (sibling->right->color == BLACK) { + _set_color(sibling->left, BLACK); + _set_color(sibling, RED); + _rotate_right(sibling); + sibling = sibling->parent; + } + _set_color(sibling, parent->color); + _set_color(parent, BLACK); + _set_color(sibling->right, BLACK); + _rotate_left(parent); + break; + } else { + if (sibling->left->color == BLACK) { + _set_color(sibling->right, BLACK); + _set_color(sibling, RED); + _rotate_left(sibling); + sibling = sibling->parent; + } + + _set_color(sibling, parent->color); + _set_color(parent, BLACK); + _set_color(sibling->left, BLACK); + _rotate_right(parent); + break; + } + } + } + + ERR_FAIL_COND(_data._nil->color != BLACK); + } + + void _erase(Element *p_node) { + Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next; + Element *node = (rp->left == _data._nil) ? rp->right : rp->left; + + Element *sibling; + if (rp == rp->parent->left) { + rp->parent->left = node; + sibling = rp->parent->right; + } else { + rp->parent->right = node; + sibling = rp->parent->left; + } + + if (node->color == RED) { + node->parent = rp->parent; + _set_color(node, BLACK); + } else if (rp->color == BLACK && rp->parent != _data._root) { + _erase_fix_rb(sibling); + } + + if (rp != p_node) { + ERR_FAIL_COND(rp == _data._nil); + + rp->left = p_node->left; + rp->right = p_node->right; + rp->parent = p_node->parent; + rp->color = p_node->color; + if (p_node->left != _data._nil) { + p_node->left->parent = rp; + } + if (p_node->right != _data._nil) { + p_node->right->parent = rp; + } + + if (p_node == p_node->parent->left) { + p_node->parent->left = rp; + } else { + p_node->parent->right = rp; + } + } + + if (p_node->_next) { + p_node->_next->_prev = p_node->_prev; + } + if (p_node->_prev) { + p_node->_prev->_next = p_node->_next; + } + + memdelete_allocator(p_node); + _data.size_cache--; + ERR_FAIL_COND(_data._nil->color == RED); + } + + void _calculate_depth(Element *p_element, int &max_d, int d) const { + if (p_element == _data._nil) { + return; + } + + _calculate_depth(p_element->left, max_d, d + 1); + _calculate_depth(p_element->right, max_d, d + 1); + + if (d > max_d) { + max_d = d; + } + } + + void _cleanup_tree(Element *p_element) { + if (p_element == _data._nil) { + return; + } + + _cleanup_tree(p_element->left); + _cleanup_tree(p_element->right); + memdelete_allocator(p_element); + } + + void _copy_from(const RBSet &p_set) { + clear(); + // not the fastest way, but safeset to write. + for (Element *I = p_set.front(); I; I = I->next()) { + insert(I->get()); + } + } + +public: + const Element *find(const T &p_value) const { + if (!_data._root) { + return nullptr; + } + + const Element *res = _find(p_value); + return res; + } + + Element *find(const T &p_value) { + if (!_data._root) { + return nullptr; + } + + Element *res = _find(p_value); + return res; + } + + Element *lower_bound(const T &p_value) const { + if (!_data._root) { + return nullptr; + } + return _lower_bound(p_value); + } + + bool has(const T &p_value) const { + return find(p_value) != nullptr; + } + + Element *insert(const T &p_value) { + if (!_data._root) { + _data._create_root(); + } + return _insert(p_value); + } + + void erase(Element *p_element) { + if (!_data._root || !p_element) { + return; + } + + _erase(p_element); + if (_data.size_cache == 0 && _data._root) { + _data._free_root(); + } + } + + bool erase(const T &p_value) { + if (!_data._root) { + return false; + } + + Element *e = find(p_value); + if (!e) { + return false; + } + + _erase(e); + if (_data.size_cache == 0 && _data._root) { + _data._free_root(); + } + return true; + } + + Element *front() const { + if (!_data._root) { + return nullptr; + } + + Element *e = _data._root->left; + if (e == _data._nil) { + return nullptr; + } + + while (e->left != _data._nil) { + e = e->left; + } + + return e; + } + + Element *back() const { + if (!_data._root) { + return nullptr; + } + + Element *e = _data._root->left; + if (e == _data._nil) { + return nullptr; + } + + while (e->right != _data._nil) { + e = e->right; + } + + return e; + } + + inline bool empty() const { return _data.size_cache == 0; } + inline int size() const { return _data.size_cache; } + + int calculate_depth() const { + // used for debug mostly + if (!_data._root) { + return 0; + } + + int max_d = 0; + _calculate_depth(_data._root->left, max_d, 0); + return max_d; + } + + void clear() { + if (!_data._root) { + return; + } + + _cleanup_tree(_data._root->left); + _data._root->left = _data._nil; + _data.size_cache = 0; + _data._free_root(); + } + + void operator=(const RBSet &p_set) { + _copy_from(p_set); + } + + RBSet(const RBSet &p_set) { + _copy_from(p_set); + } + + _FORCE_INLINE_ RBSet() { + } + + ~RBSet() { + clear(); + } +}; + +#endif diff --git a/sfw/containers/ring_buffer.h b/sfw/containers/ring_buffer.h new file mode 100644 index 0000000..358bad0 --- /dev/null +++ b/sfw/containers/ring_buffer.h @@ -0,0 +1,223 @@ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +/*************************************************************************/ +/* ring_buffer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/containers/vector.h" + +template +class RingBuffer { + Vector data; + int read_pos; + int write_pos; + int size_mask; + + inline int inc(int &p_var, int p_size) const { + int ret = p_var; + p_var += p_size; + p_var = p_var & size_mask; + return ret; + }; + +public: + T read() { + ERR_FAIL_COND_V(space_left() < 1, T()); + return data.ptr()[inc(read_pos, 1)]; + }; + + int read(T *p_buf, int p_size, bool p_advance = true) { + int left = data_left(); + p_size = MIN(left, p_size); + int pos = read_pos; + int to_read = p_size; + int dst = 0; + while (to_read) { + int end = pos + to_read; + end = MIN(end, size()); + int total = end - pos; + const T *read = data.ptr(); + for (int i = 0; i < total; i++) { + p_buf[dst++] = read[pos + i]; + }; + to_read -= total; + pos = 0; + }; + if (p_advance) { + inc(read_pos, p_size); + }; + return p_size; + }; + + int copy(T *p_buf, int p_offset, int p_size) const { + int left = data_left(); + if ((p_offset + p_size) > left) { + p_size -= left - p_offset; + if (p_size <= 0) { + return 0; + } + } + p_size = MIN(left, p_size); + int pos = read_pos; + inc(pos, p_offset); + int to_read = p_size; + int dst = 0; + while (to_read) { + int end = pos + to_read; + end = MIN(end, size()); + int total = end - pos; + for (int i = 0; i < total; i++) { + p_buf[dst++] = data[pos + i]; + }; + to_read -= total; + pos = 0; + }; + return p_size; + }; + + int find(const T &t, int p_offset, int p_max_size) const { + int left = data_left(); + if ((p_offset + p_max_size) > left) { + p_max_size -= left - p_offset; + if (p_max_size <= 0) { + return 0; + } + } + p_max_size = MIN(left, p_max_size); + int pos = read_pos; + inc(pos, p_offset); + int to_read = p_max_size; + while (to_read) { + int end = pos + to_read; + end = MIN(end, size()); + int total = end - pos; + for (int i = 0; i < total; i++) { + if (data[pos + i] == t) { + return i + (p_max_size - to_read); + } + }; + to_read -= total; + pos = 0; + } + return -1; + } + + inline int advance_read(int p_n) { + p_n = MIN(p_n, data_left()); + inc(read_pos, p_n); + return p_n; + }; + + inline int decrease_write(int p_n) { + p_n = MIN(p_n, data_left()); + inc(write_pos, size_mask + 1 - p_n); + return p_n; + } + + Error write(const T &p_v) { + ERR_FAIL_COND_V(space_left() < 1, FAILED); + data.write[inc(write_pos, 1)] = p_v; + return OK; + }; + + int write(const T *p_buf, int p_size) { + int left = space_left(); + p_size = MIN(left, p_size); + + int pos = write_pos; + int to_write = p_size; + int src = 0; + while (to_write) { + int end = pos + to_write; + end = MIN(end, size()); + int total = end - pos; + + for (int i = 0; i < total; i++) { + data.write[pos + i] = p_buf[src++]; + }; + to_write -= total; + pos = 0; + }; + + inc(write_pos, p_size); + return p_size; + }; + + inline int space_left() const { + int left = read_pos - write_pos; + if (left < 0) { + return size() + left - 1; + }; + if (left == 0) { + return size() - 1; + }; + return left - 1; + }; + inline int data_left() const { + return size() - space_left() - 1; + }; + + inline int size() const { + return data.size(); + }; + + inline void clear() { + read_pos = 0; + write_pos = 0; + } + + void resize(int p_power) { + int old_size = size(); + int new_size = 1 << p_power; + int mask = new_size - 1; + data.resize(1 << p_power); + if (old_size < new_size && read_pos > write_pos) { + for (int i = 0; i < write_pos; i++) { + data.write[(old_size + i) & mask] = data[i]; + }; + write_pos = (old_size + write_pos) & mask; + } else { + read_pos = read_pos & mask; + write_pos = write_pos & mask; + }; + + size_mask = mask; + }; + + RingBuffer(int p_power = 0) { + read_pos = 0; + write_pos = 0; + resize(p_power); + }; + ~RingBuffer(){}; +}; + +#endif diff --git a/sfw/containers/rvector.h b/sfw/containers/rvector.h new file mode 100644 index 0000000..5dc9ab0 --- /dev/null +++ b/sfw/containers/rvector.h @@ -0,0 +1,287 @@ +#ifndef VECTOR_H +#define VECTOR_H + +#include "core/error_macros.h" + +template +class Vector { + +public: + void push_back(const T &element); + void pop_back(); + void remove(const int index); + void remove_keep_order(const int index); + void erase(const T &element); + void clear(); + bool empty() const; + T get(const int index); + const T &get(const int index) const; + void set(const int index, const T &value); + void swap(const int index1, const int index2); + + void sort_inc(); + void sort_dec(); + + int size() const; + int capacity() const; + void ensure_capacity(const int capacity); + void resize(const int s); + void append_array(const Vector &other); + int find(const T &val) const; + + T *dataw(); + const T *data() const; + + const T &operator[](const int index) const; + T &operator[](const int index); + + Vector &operator=(const Vector &other); + + Vector(); + Vector(int prealloc); + Vector(int prealloc, int grow_by); + Vector(const Vector &other); + ~Vector(); + +private: + T *_data; + int _actual_size; + int _size; + int _grow_by; +}; + +template +void Vector::push_back(const T &element) { + ensure_capacity(_size + 1); + + _data[_size++] = element; +} + +template +void Vector::pop_back() { + if (_size == 0) { + return; + } + + --_size; +} + +template +void Vector::remove(const int index) { + _data[index] = _data[_size - 1]; + + --_size; +} + +template +void Vector::remove_keep_order(const int index) { + --_size; + + for (int i = index; i < _size; ++i) { + _data[i] = _data[i + 1]; + } +} + +template +void Vector::erase(const T &element) { + int index = find(element); + + if (index != -1) { + remove(index); + } +} + +template +void Vector::clear() { + _size = 0; +} + +template +bool Vector::empty() const { + return _size == 0; +} + +template +T Vector::get(const int index) { + return _data[index]; +} + +template +const T &Vector::get(const int index) const { + return _data[index]; +} + +template +void Vector::set(const int index, const T &value) { + _data[index] = value; +} + +template +void Vector::swap(const int index1, const int index2) { + T e = _data[index1]; + _data[index1] = _data[index2]; + _data[index2] = e; +} + +template +void Vector::sort_inc() { + for (int i = 0; i < _size; ++i) { + for (int j = i + 1; j < _size; ++j) { + if (_data[j] < _data[i]) { + swap(i, j); + } + } + } +} + +template +void Vector::sort_dec() { + for (int i = 0; i < _size; ++i) { + for (int j = i + 1; j < _size; ++j) { + if (_data[j] > _data[i]) { + swap(i, j); + } + } + } +} + +template +int Vector::size() const { + return _size; +} + +template +int Vector::capacity() const { + return _actual_size; +} + +template +void Vector::ensure_capacity(const int capacity) { + if (capacity <= _actual_size) { + return; + } + + int tsize = capacity + _grow_by; + + T *nd = new T[tsize]; + + if (_data) { + for (int i = 0; i < _size; ++i) { + nd[i] = _data[i]; + } + + delete[] _data; + } + + _data = nd; + _actual_size = tsize; +} + +template +void Vector::resize(const int s) { + ensure_capacity(s); + + _size = s; +} + +template +void Vector::append_array(const Vector &other) { + ensure_capacity(_size + other._size); + + for (int i = 0; i < other._size; ++i) { + _data[_size++] = other._data[i]; + } +} + +template +int Vector::find(const T &val) const { + for (int i = 0; i < _size; ++i) { + if (_data[i] == val) { + return i; + } + } + + return -1; +} + +template +T *Vector::dataw() { + return _data; +} + +template +const T *Vector::data() const { + return _data; +} + +template +const T &Vector::operator[](const int index) const { + return _data[index]; +} + +template +T &Vector::operator[](const int index) { + return _data[index]; +} + +template +Vector &Vector::operator=(const Vector &other) { + resize(0); + ensure_capacity(other.size()); + append_array(other); + + return *this; +} + +template +Vector::Vector() { + _data = nullptr; + _actual_size = 0; + _size = 0; + _grow_by = 100; +} + +template +Vector::Vector(int prealloc) { + _data = nullptr; + _actual_size = 0; + _size = 0; + _grow_by = 100; + + ensure_capacity(prealloc); +} + +template +Vector::Vector(int prealloc, int grow_by) { + _data = nullptr; + _actual_size = 0; + _size = 0; + _grow_by = grow_by; + + ensure_capacity(prealloc); +} + +template +Vector::Vector(const Vector &other) { + _actual_size = other._actual_size; + _size = other._size; + _grow_by = other._grow_by; + + if (other._data) { + _data = new T[_actual_size]; + + for (int i = 0; i < _size; ++i) { + _data[i] = other._data[i]; + } + } +} + +template +Vector::~Vector() { + if (_data) { + delete[] _data; + _data = nullptr; + } +} + +#endif \ No newline at end of file diff --git a/sfw/containers/tight_local_vector.h b/sfw/containers/tight_local_vector.h new file mode 100644 index 0000000..13ee006 --- /dev/null +++ b/sfw/containers/tight_local_vector.h @@ -0,0 +1,319 @@ +#ifndef TIGHT_LOCAL_VECTOR_H +#define TIGHT_LOCAL_VECTOR_H + +/*************************************************************************/ +/* tight_local_vector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/containers/pool_vector.h" +#include "core/containers/sort_array.h" +#include "core/containers/vector.h" +#include "core/error/error_macros.h" +#include "core/os/memory.h" + +// It grows strictly as much as needed. (The vanilla LocalVector is what you want in most cases). +template +class TightLocalVector { +private: + U count = 0; + U capacity = 0; + T *data = nullptr; + +public: + T *ptr() { + return data; + } + + const T *ptr() const { + return data; + } + + _FORCE_INLINE_ void push_back(T p_elem) { + if (unlikely(count == capacity)) { + if (capacity == 0) { + capacity = 1; + } else { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + + if constexpr (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { + memnew_placement(&data[count++], T(p_elem)); + } else { + data[count++] = p_elem; + } + } + + void remove(U p_index) { + ERR_FAIL_UNSIGNED_INDEX(p_index, count); + count--; + for (U i = p_index; i < count; i++) { + data[i] = data[i + 1]; + } + if constexpr (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + data[count].~T(); + } + } + + /// Removes the item copying the last value into the position of the one to + /// remove. It's generally faster than `remove`. + void remove_unordered(U p_index) { + ERR_FAIL_INDEX(p_index, count); + count--; + if (count > p_index) { + data[p_index] = data[count]; + } + if constexpr (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + data[count].~T(); + } + } + + void erase(const T &p_val) { + int64_t idx = find(p_val); + if (idx >= 0) { + remove(idx); + } + } + + U erase_multiple_unordered(const T &p_val) { + U from = 0; + U count = 0; + while (true) { + int64_t idx = find(p_val, from); + + if (idx == -1) { + break; + } + remove_unordered(idx); + from = idx; + count++; + } + return count; + } + + void invert() { + for (U i = 0; i < count / 2; i++) { + SWAP(data[i], data[count - i - 1]); + } + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ void reset() { + clear(); + if (data) { + memfree(data); + data = nullptr; + capacity = 0; + } + } + _FORCE_INLINE_ bool empty() const { return count == 0; } + _FORCE_INLINE_ U get_capacity() const { return capacity; } + _FORCE_INLINE_ void reserve(U p_size) { + if (p_size > capacity) { + capacity = p_size; + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + } + + _FORCE_INLINE_ U size() const { return count; } + void resize(U p_size) { + if (p_size < count) { + if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { + for (U i = p_size; i < count; i++) { + data[i].~T(); + } + } + count = p_size; + } else if (p_size > count) { + if (unlikely(p_size > capacity)) { + if (capacity == 0) { + capacity = 1; + } + while (capacity < p_size) { + capacity <<= 1; + } + data = (T *)memrealloc(data, capacity * sizeof(T)); + CRASH_COND_MSG(!data, "Out of memory"); + } + if (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { + for (U i = count; i < p_size; i++) { + memnew_placement(&data[i], T); + } + } + count = p_size; + } + } + _FORCE_INLINE_ const T &operator[](U p_index) const { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + _FORCE_INLINE_ T &operator[](U p_index) { + CRASH_BAD_UNSIGNED_INDEX(p_index, count); + return data[p_index]; + } + + void fill(T p_val) { + for (U i = 0; i < count; i++) { + data[i] = p_val; + } + } + + void insert(U p_pos, T p_val) { + ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1); + if (p_pos == count) { + push_back(p_val); + } else { + resize(count + 1); + for (U i = count - 1; i > p_pos; i--) { + data[i] = data[i - 1]; + } + data[p_pos] = p_val; + } + } + + int64_t find(const T &p_val, U p_from = 0) const { + for (U i = p_from; i < count; i++) { + if (data[i] == p_val) { + return int64_t(i); + } + } + return -1; + } + + template + void sort_custom() { + U len = count; + if (len == 0) { + return; + } + + SortArray sorter; + sorter.sort(data, len); + } + + void sort() { + sort_custom<_DefaultComparator>(); + } + + void ordered_insert(T p_val) { + U i; + for (i = 0; i < count; i++) { + if (p_val < data[i]) { + break; + } + } + insert(i, p_val); + } + + operator Vector() const { + Vector ret; + ret.resize(size()); + T *w = ret.ptrw(); + memcpy(w, data, sizeof(T) * count); + return ret; + } + + operator PoolVector() const { + PoolVector pl; + if (size()) { + pl.resize(size()); + typename PoolVector::Write w = pl.write(); + T *dest = w.ptr(); + memcpy(dest, data, sizeof(T) * count); + } + return pl; + } + + Vector to_byte_array() const { //useful to pass stuff to gpu or variant + Vector ret; + ret.resize(count * sizeof(T)); + uint8_t *w = ret.ptrw(); + memcpy(w, data, sizeof(T) * count); + return ret; + } + + _FORCE_INLINE_ TightLocalVector() {} + _FORCE_INLINE_ TightLocalVector(const TightLocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + } + TightLocalVector(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + } + TightLocalVector(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + } + + inline void operator=(const TightLocalVector &p_from) { + resize(p_from.size()); + for (U i = 0; i < p_from.count; i++) { + data[i] = p_from.data[i]; + } + } + inline void operator=(const Vector &p_from) { + resize(p_from.size()); + for (U i = 0; i < count; i++) { + data[i] = p_from[i]; + } + } + inline TightLocalVector &operator=(const PoolVector &p_from) { + resize(p_from.size()); + typename PoolVector::Read r = p_from.read(); + for (U i = 0; i < count; i++) { + data[i] = r[i]; + } + return *this; + } + + _FORCE_INLINE_ ~TightLocalVector() { + if (data) { + reset(); + } + } +}; + +// Integer default version +template +class TightLocalVectori : public TightLocalVector { +}; + +#endif // TIGHT_LOCAL_VECTOR_H diff --git a/sfw/containers/vector.h b/sfw/containers/vector.h index 5dc9ab0..a05361e 100644 --- a/sfw/containers/vector.h +++ b/sfw/containers/vector.h @@ -1,287 +1,198 @@ #ifndef VECTOR_H #define VECTOR_H -#include "core/error_macros.h" +/*************************************************************************/ +/* vector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/** + * @class Vector + * @author Juan Linietsky + * Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use PoolVector for large arrays. + */ + +#include "core/containers/cowdata.h" +#include "core/containers/sort_array.h" +#include "core/error/error_macros.h" +#include "core/os/memory.h" template -class Vector { - +class VectorWriteProxy { public: - void push_back(const T &element); - void pop_back(); - void remove(const int index); - void remove_keep_order(const int index); - void erase(const T &element); - void clear(); - bool empty() const; - T get(const int index); - const T &get(const int index) const; - void set(const int index, const T &value); - void swap(const int index1, const int index2); + _FORCE_INLINE_ T &operator[](int p_index) { + CRASH_BAD_INDEX(p_index, ((Vector *)(this))->_cowdata.size()); - void sort_inc(); - void sort_dec(); - - int size() const; - int capacity() const; - void ensure_capacity(const int capacity); - void resize(const int s); - void append_array(const Vector &other); - int find(const T &val) const; - - T *dataw(); - const T *data() const; - - const T &operator[](const int index) const; - T &operator[](const int index); - - Vector &operator=(const Vector &other); - - Vector(); - Vector(int prealloc); - Vector(int prealloc, int grow_by); - Vector(const Vector &other); - ~Vector(); - -private: - T *_data; - int _actual_size; - int _size; - int _grow_by; + return ((Vector *)(this))->_cowdata.ptrw()[p_index]; + } }; template -void Vector::push_back(const T &element) { - ensure_capacity(_size + 1); +class Vector { + friend class VectorWriteProxy; - _data[_size++] = element; +public: + VectorWriteProxy write; + +private: + CowData _cowdata; + +public: + bool push_back(T p_elem); + + void remove(int p_index) { _cowdata.remove(p_index); } + _FORCE_INLINE_ bool erase(const T &p_val) { + int idx = find(p_val); + if (idx >= 0) { + remove(idx); + return true; + } + return false; + }; + void invert(); + + _FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } + + _FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const T &get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); } + Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); } + int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); } + _FORCE_INLINE_ void fill(const T &p_val) { _cowdata.fill(p_val); } + + void append_array(Vector p_other); + + template + void sort_custom() { + int len = _cowdata.size(); + if (len == 0) { + return; + } + + T *data = ptrw(); + SortArray sorter; + sorter.sort(data, len); + } + + void sort() { + sort_custom<_DefaultComparator>(); + } + + void ordered_insert(const T &p_val) { + int i; + for (i = 0; i < _cowdata.size(); i++) { + if (p_val < operator[](i)) { + break; + }; + }; + insert(i, p_val); + } + + _FORCE_INLINE_ Vector() {} + _FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); } + inline Vector &operator=(const Vector &p_from) { + _cowdata._ref(p_from._cowdata); + return *this; + } + + Vector to_byte_array() const { + Vector ret; + ret.resize(size() * sizeof(T)); + memcpy(ret.ptrw(), ptr(), sizeof(T) * size()); + return ret; + } + + Vector slice(int p_begin, int p_end = INT32_MAX) const { + Vector result; + + const int s = size(); + + int begin = CLAMP(p_begin, -s, s); + if (begin < 0) { + begin += s; + } + int end = CLAMP(p_end, -s, s); + if (end < 0) { + end += s; + } + + ERR_FAIL_COND_V(begin > end, result); + + int result_size = end - begin; + result.resize(result_size); + + const T *const r = ptr(); + T *const w = result.ptrw(); + for (int i = 0; i < result_size; ++i) { + w[i] = r[begin + i]; + } + + return result; + } + + _FORCE_INLINE_ ~Vector() {} +}; + +template +void Vector::invert() { + for (int i = 0; i < size() / 2; i++) { + T *p = ptrw(); + SWAP(p[i], p[size() - i - 1]); + } } template -void Vector::pop_back() { - if (_size == 0) { +void Vector::append_array(Vector p_other) { + const int ds = p_other.size(); + if (ds == 0) { return; } - - --_size; -} - -template -void Vector::remove(const int index) { - _data[index] = _data[_size - 1]; - - --_size; -} - -template -void Vector::remove_keep_order(const int index) { - --_size; - - for (int i = index; i < _size; ++i) { - _data[i] = _data[i + 1]; + const int bs = size(); + resize(bs + ds); + for (int i = 0; i < ds; ++i) { + ptrw()[bs + i] = p_other[i]; } } template -void Vector::erase(const T &element) { - int index = find(element); +bool Vector::push_back(T p_elem) { + Error err = resize(size() + 1); + ERR_FAIL_COND_V(err, true); + set(size() - 1, p_elem); - if (index != -1) { - remove(index); - } + return false; } -template -void Vector::clear() { - _size = 0; -} - -template -bool Vector::empty() const { - return _size == 0; -} - -template -T Vector::get(const int index) { - return _data[index]; -} - -template -const T &Vector::get(const int index) const { - return _data[index]; -} - -template -void Vector::set(const int index, const T &value) { - _data[index] = value; -} - -template -void Vector::swap(const int index1, const int index2) { - T e = _data[index1]; - _data[index1] = _data[index2]; - _data[index2] = e; -} - -template -void Vector::sort_inc() { - for (int i = 0; i < _size; ++i) { - for (int j = i + 1; j < _size; ++j) { - if (_data[j] < _data[i]) { - swap(i, j); - } - } - } -} - -template -void Vector::sort_dec() { - for (int i = 0; i < _size; ++i) { - for (int j = i + 1; j < _size; ++j) { - if (_data[j] > _data[i]) { - swap(i, j); - } - } - } -} - -template -int Vector::size() const { - return _size; -} - -template -int Vector::capacity() const { - return _actual_size; -} - -template -void Vector::ensure_capacity(const int capacity) { - if (capacity <= _actual_size) { - return; - } - - int tsize = capacity + _grow_by; - - T *nd = new T[tsize]; - - if (_data) { - for (int i = 0; i < _size; ++i) { - nd[i] = _data[i]; - } - - delete[] _data; - } - - _data = nd; - _actual_size = tsize; -} - -template -void Vector::resize(const int s) { - ensure_capacity(s); - - _size = s; -} - -template -void Vector::append_array(const Vector &other) { - ensure_capacity(_size + other._size); - - for (int i = 0; i < other._size; ++i) { - _data[_size++] = other._data[i]; - } -} - -template -int Vector::find(const T &val) const { - for (int i = 0; i < _size; ++i) { - if (_data[i] == val) { - return i; - } - } - - return -1; -} - -template -T *Vector::dataw() { - return _data; -} - -template -const T *Vector::data() const { - return _data; -} - -template -const T &Vector::operator[](const int index) const { - return _data[index]; -} - -template -T &Vector::operator[](const int index) { - return _data[index]; -} - -template -Vector &Vector::operator=(const Vector &other) { - resize(0); - ensure_capacity(other.size()); - append_array(other); - - return *this; -} - -template -Vector::Vector() { - _data = nullptr; - _actual_size = 0; - _size = 0; - _grow_by = 100; -} - -template -Vector::Vector(int prealloc) { - _data = nullptr; - _actual_size = 0; - _size = 0; - _grow_by = 100; - - ensure_capacity(prealloc); -} - -template -Vector::Vector(int prealloc, int grow_by) { - _data = nullptr; - _actual_size = 0; - _size = 0; - _grow_by = grow_by; - - ensure_capacity(prealloc); -} - -template -Vector::Vector(const Vector &other) { - _actual_size = other._actual_size; - _size = other._size; - _grow_by = other._grow_by; - - if (other._data) { - _data = new T[_actual_size]; - - for (int i = 0; i < _size; ++i) { - _data[i] = other._data[i]; - } - } -} - -template -Vector::~Vector() { - if (_data) { - delete[] _data; - _data = nullptr; - } -} - -#endif \ No newline at end of file +#endif diff --git a/sfw/containers/vmap.h b/sfw/containers/vmap.h new file mode 100644 index 0000000..c1351a4 --- /dev/null +++ b/sfw/containers/vmap.h @@ -0,0 +1,203 @@ +#ifndef VMAP_H +#define VMAP_H + +/*************************************************************************/ +/* vmap.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/containers/cowdata.h" +#include "core/typedefs.h" + +template +class VMap { +public: + struct Pair { + T key; + V value; + + _FORCE_INLINE_ Pair() {} + + _FORCE_INLINE_ Pair(const T &p_key, const V &p_value) { + key = p_key; + value = p_value; + } + }; + +private: + CowData _cowdata; + + _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { + r_exact = false; + if (_cowdata.empty()) { + return 0; + } + + int low = 0; + int high = _cowdata.size() - 1; + const Pair *a = _cowdata.ptr(); + int middle = 0; + +#ifdef DEBUG_ENABLED + if (low > high) + ERR_PRINT("low > high, this may be a bug"); +#endif + while (low <= high) { + middle = (low + high) / 2; + + if (p_val < a[middle].key) { + high = middle - 1; //search low end of array + } else if (a[middle].key < p_val) { + low = middle + 1; //search high end of array + } else { + r_exact = true; + return middle; + } + } + + //return the position where this would be inserted + if (a[middle].key < p_val) { + middle++; + } + return middle; + } + + _FORCE_INLINE_ int _find_exact(const T &p_val) const { + if (_cowdata.empty()) { + return -1; + } + + int low = 0; + int high = _cowdata.size() - 1; + int middle; + const Pair *a = _cowdata.ptr(); + + while (low <= high) { + middle = (low + high) / 2; + + if (p_val < a[middle].key) { + high = middle - 1; //search low end of array + } else if (a[middle].key < p_val) { + low = middle + 1; //search high end of array + } else { + return middle; + } + } + + return -1; + } + +public: + int insert(const T &p_key, const V &p_val) { + bool exact; + int pos = _find(p_key, exact); + if (exact) { + _cowdata.get_m(pos).value = p_val; + return pos; + } + _cowdata.insert(pos, Pair(p_key, p_val)); + return pos; + } + + bool has(const T &p_val) const { + return _find_exact(p_val) != -1; + } + + void erase(const T &p_val) { + int pos = _find_exact(p_val); + if (pos < 0) { + return; + } + _cowdata.remove(pos); + } + + int find(const T &p_val) const { + return _find_exact(p_val); + } + + int find_nearest(const T &p_val) const { + if (_cowdata.empty()) { + return -1; + } + bool exact; + return _find(p_val, exact); + } + + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } + + const Pair *get_array() const { + return _cowdata.ptr(); + } + + Pair *get_array() { + return _cowdata.ptrw(); + } + + const V &getv(int p_index) const { + return _cowdata.get(p_index).value; + } + + V &getv(int p_index) { + return _cowdata.get_m(p_index).value; + } + + const T &getk(int p_index) const { + return _cowdata.get(p_index).key; + } + + T &getk(int p_index) { + return _cowdata.get_m(p_index).key; + } + + inline const V &operator[](const T &p_key) const { + int pos = _find_exact(p_key); + + CRASH_COND(pos < 0); + + return _cowdata.get(pos).value; + } + + inline V &operator[](const T &p_key) { + int pos = _find_exact(p_key); + if (pos < 0) { + pos = insert(p_key, V()); + } + + return _cowdata.get_m(pos).value; + } + + _FORCE_INLINE_ VMap(){}; + _FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); } + inline VMap &operator=(const VMap &p_from) { + _cowdata._ref(p_from._cowdata); + return *this; + } +}; +#endif // VMAP_H diff --git a/sfw/containers/vset.h b/sfw/containers/vset.h new file mode 100644 index 0000000..d75013c --- /dev/null +++ b/sfw/containers/vset.h @@ -0,0 +1,142 @@ +#ifndef VSET_H +#define VSET_H + +/*************************************************************************/ +/* vset.h */ +/*************************************************************************/ +/* This file is part of: */ +/* PANDEMONIUM ENGINE */ +/* https://github.com/Relintai/pandemonium_engine */ +/*************************************************************************/ +/* Copyright (c) 2022-present Péter Magyar. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/containers/vector.h" +#include "core/typedefs.h" + +template +class VSet { + Vector _data; + + _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { + r_exact = false; + if (_data.empty()) { + return 0; + } + + int low = 0; + int high = _data.size() - 1; + const T *a = &_data[0]; + int middle = 0; + +#ifdef DEBUG_ENABLED + if (low > high) + ERR_PRINT("low > high, this may be a bug"); +#endif + + while (low <= high) { + middle = (low + high) / 2; + + if (p_val < a[middle]) { + high = middle - 1; //search low end of array + } else if (a[middle] < p_val) { + low = middle + 1; //search high end of array + } else { + r_exact = true; + return middle; + } + } + + //return the position where this would be inserted + if (a[middle] < p_val) { + middle++; + } + return middle; + } + + _FORCE_INLINE_ int _find_exact(const T &p_val) const { + if (_data.empty()) { + return -1; + } + + int low = 0; + int high = _data.size() - 1; + int middle; + const T *a = &_data[0]; + + while (low <= high) { + middle = (low + high) / 2; + + if (p_val < a[middle]) { + high = middle - 1; //search low end of array + } else if (a[middle] < p_val) { + low = middle + 1; //search high end of array + } else { + return middle; + } + } + + return -1; + } + +public: + void insert(const T &p_val) { + bool exact; + int pos = _find(p_val, exact); + if (exact) { + return; + } + _data.insert(pos, p_val); + } + + bool has(const T &p_val) const { + return _find_exact(p_val) != -1; + } + + void erase(const T &p_val) { + int pos = _find_exact(p_val); + if (pos < 0) { + return; + } + _data.remove(pos); + } + + int find(const T &p_val) const { + return _find_exact(p_val); + } + + _FORCE_INLINE_ bool empty() const { return _data.empty(); } + + _FORCE_INLINE_ int size() const { return _data.size(); } + + inline T &operator[](int p_index) { + return _data.write[p_index]; + } + + inline const T &operator[](int p_index) const { + return _data[p_index]; + } +}; + +#endif // VSET_H