feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
39
engine/core/templates/a_hash_map.cpp
Normal file
39
engine/core/templates/a_hash_map.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**************************************************************************/
|
||||
/* a_hash_map.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 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 "a_hash_map.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
// Explicit instantiation.
|
||||
template class AHashMap<int, int>;
|
||||
template class AHashMap<String, int>;
|
||||
template class AHashMap<StringName, StringName>;
|
||||
template class AHashMap<StringName, Variant>;
|
||||
template class AHashMap<StringName, int>;
|
||||
740
engine/core/templates/a_hash_map.h
Normal file
740
engine/core/templates/a_hash_map.h
Normal file
|
|
@ -0,0 +1,740 @@
|
|||
/**************************************************************************/
|
||||
/* a_hash_map.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef A_HASH_MAP_H
|
||||
#define A_HASH_MAP_H
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
|
||||
struct HashMapData {
|
||||
union {
|
||||
uint64_t data;
|
||||
struct
|
||||
{
|
||||
uint32_t hash;
|
||||
uint32_t hash_to_key;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(HashMapData) == 8);
|
||||
|
||||
/**
|
||||
* An array-based implementation of a hash map. It is very efficient in terms of performance and
|
||||
* memory usage. Works like a dynamic array, adding elements to the end of the array, and
|
||||
* allows you to access array elements by their index by using `get_by_index` method.
|
||||
* Example:
|
||||
* ```
|
||||
* AHashMap<int, Object *> map;
|
||||
*
|
||||
* int get_object_id_by_number(int p_number) {
|
||||
* int id = map.get_index(p_number);
|
||||
* return id;
|
||||
* }
|
||||
*
|
||||
* Object *get_object_by_id(int p_id) {
|
||||
* map.get_by_index(p_id).value;
|
||||
* }
|
||||
* ```
|
||||
* Still, don`t erase the elements because ID can break.
|
||||
*
|
||||
* When an element erase, its place is taken by the element from the end.
|
||||
*
|
||||
* <-------------
|
||||
* | |
|
||||
* 6 8 X 9 32 -1 5 -10 7 X X X
|
||||
* 6 8 7 9 32 -1 5 -10 X X X X
|
||||
*
|
||||
*
|
||||
* Use RBMap if you need to iterate over sorted elements.
|
||||
*
|
||||
* Use HashMap if:
|
||||
* - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime.
|
||||
* - You need to preserve the insertion order when using erase.
|
||||
*
|
||||
* It is recommended to use `HashMap` if `KeyValue` size is very large.
|
||||
*/
|
||||
template <typename TKey, typename TValue,
|
||||
typename Hasher = HashMapHasherDefault,
|
||||
typename Comparator = HashMapComparatorDefault<TKey>>
|
||||
class AHashMap {
|
||||
public:
|
||||
// Must be a power of two.
|
||||
static constexpr uint32_t INITIAL_CAPACITY = 16;
|
||||
static constexpr uint32_t EMPTY_HASH = 0;
|
||||
static_assert(EMPTY_HASH == 0, "EMPTY_HASH must always be 0 for the memcpy() optimization.");
|
||||
|
||||
private:
|
||||
typedef KeyValue<TKey, TValue> MapKeyValue;
|
||||
MapKeyValue *elements = nullptr;
|
||||
HashMapData *map_data = nullptr;
|
||||
|
||||
// Due to optimization, this is `capacity - 1`. Use + 1 to get normal capacity.
|
||||
uint32_t capacity = 0;
|
||||
uint32_t num_elements = 0;
|
||||
|
||||
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_resize_count(uint32_t p_capacity) {
|
||||
return p_capacity ^ (p_capacity + 1) >> 2; // = get_capacity() * 0.75 - 1; Works only if p_capacity = 2^n - 1.
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_local_capacity) {
|
||||
const uint32_t original_pos = p_hash & p_local_capacity;
|
||||
return (p_pos - original_pos + p_local_capacity + 1) & p_local_capacity;
|
||||
}
|
||||
|
||||
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos) const {
|
||||
if (unlikely(elements == nullptr)) {
|
||||
return false; // Failed lookups, no elements.
|
||||
}
|
||||
return _lookup_pos_with_hash(p_key, r_pos, r_hash_pos, _hash(p_key));
|
||||
}
|
||||
|
||||
bool _lookup_pos_with_hash(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos, uint32_t p_hash) const {
|
||||
if (unlikely(elements == nullptr)) {
|
||||
return false; // Failed lookups, no elements.
|
||||
}
|
||||
|
||||
uint32_t pos = p_hash & capacity;
|
||||
HashMapData data = map_data[pos];
|
||||
if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) {
|
||||
r_pos = data.hash_to_key;
|
||||
r_hash_pos = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data.data == EMPTY_HASH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A collision occurred.
|
||||
pos = (pos + 1) & capacity;
|
||||
uint32_t distance = 1;
|
||||
while (true) {
|
||||
data = map_data[pos];
|
||||
if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) {
|
||||
r_pos = data.hash_to_key;
|
||||
r_hash_pos = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data.data == EMPTY_HASH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distance > _get_probe_length(pos, data.hash, capacity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = (pos + 1) & capacity;
|
||||
distance++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) {
|
||||
uint32_t pos = p_hash & capacity;
|
||||
|
||||
if (map_data[pos].data == EMPTY_HASH) {
|
||||
uint64_t data = ((uint64_t)p_index << 32) | p_hash;
|
||||
map_data[pos].data = data;
|
||||
return pos;
|
||||
}
|
||||
|
||||
uint32_t distance = 1;
|
||||
pos = (pos + 1) & capacity;
|
||||
HashMapData c_data;
|
||||
c_data.hash = p_hash;
|
||||
c_data.hash_to_key = p_index;
|
||||
|
||||
while (true) {
|
||||
if (map_data[pos].data == EMPTY_HASH) {
|
||||
#ifdef DEV_ENABLED
|
||||
if (unlikely(distance > 12)) {
|
||||
WARN_PRINT("Excessive collision count (" +
|
||||
itos(distance) + "), is the right hash function being used?");
|
||||
}
|
||||
#endif
|
||||
map_data[pos] = c_data;
|
||||
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, map_data[pos].hash, capacity);
|
||||
if (existing_probe_len < distance) {
|
||||
SWAP(c_data, map_data[pos]);
|
||||
distance = existing_probe_len;
|
||||
}
|
||||
|
||||
pos = (pos + 1) & capacity;
|
||||
distance++;
|
||||
}
|
||||
}
|
||||
|
||||
void _resize_and_rehash(uint32_t p_new_capacity) {
|
||||
uint32_t real_old_capacity = capacity + 1;
|
||||
// Capacity can't be 0 and must be 2^n - 1.
|
||||
capacity = MAX(4u, p_new_capacity);
|
||||
uint32_t real_capacity = next_power_of_2(capacity);
|
||||
capacity = real_capacity - 1;
|
||||
|
||||
HashMapData *old_map_data = map_data;
|
||||
|
||||
map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
|
||||
elements = reinterpret_cast<MapKeyValue *>(Memory::realloc_static(elements, sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
|
||||
|
||||
memset(map_data, EMPTY_HASH, real_capacity * sizeof(HashMapData));
|
||||
|
||||
if (num_elements != 0) {
|
||||
for (uint32_t i = 0; i < real_old_capacity; i++) {
|
||||
HashMapData data = old_map_data[i];
|
||||
if (data.data != EMPTY_HASH) {
|
||||
_insert_with_hash(data.hash, data.hash_to_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Memory::free_static(old_map_data);
|
||||
}
|
||||
|
||||
int32_t _insert_element(const TKey &p_key, const TValue &p_value, uint32_t p_hash) {
|
||||
if (unlikely(elements == nullptr)) {
|
||||
// Allocate on demand to save memory.
|
||||
|
||||
uint32_t real_capacity = capacity + 1;
|
||||
map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
|
||||
elements = reinterpret_cast<MapKeyValue *>(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
|
||||
|
||||
memset(map_data, EMPTY_HASH, real_capacity * sizeof(HashMapData));
|
||||
}
|
||||
|
||||
if (unlikely(num_elements > _get_resize_count(capacity))) {
|
||||
_resize_and_rehash(capacity * 2);
|
||||
}
|
||||
|
||||
memnew_placement(&elements[num_elements], MapKeyValue(p_key, p_value));
|
||||
|
||||
_insert_with_hash(p_hash, num_elements);
|
||||
num_elements++;
|
||||
return num_elements - 1;
|
||||
}
|
||||
|
||||
void _init_from(const AHashMap &p_other) {
|
||||
capacity = p_other.capacity;
|
||||
uint32_t real_capacity = capacity + 1;
|
||||
num_elements = p_other.num_elements;
|
||||
|
||||
if (p_other.num_elements == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
|
||||
elements = reinterpret_cast<MapKeyValue *>(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
|
||||
|
||||
if constexpr (std::is_trivially_copyable_v<TKey> && std::is_trivially_copyable_v<TValue>) {
|
||||
void *destination = elements;
|
||||
const void *source = p_other.elements;
|
||||
memcpy(destination, source, sizeof(MapKeyValue) * num_elements);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < num_elements; i++) {
|
||||
memnew_placement(&elements[i], MapKeyValue(p_other.elements[i]));
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(map_data, p_other.map_data, sizeof(HashMapData) * real_capacity);
|
||||
}
|
||||
|
||||
public:
|
||||
/* Standard Godot Container API */
|
||||
|
||||
_FORCE_INLINE_ uint32_t get_capacity() const { return capacity + 1; }
|
||||
_FORCE_INLINE_ uint32_t size() const { return num_elements; }
|
||||
|
||||
_FORCE_INLINE_ bool is_empty() const {
|
||||
return num_elements == 0;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (elements == nullptr || num_elements == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(map_data, EMPTY_HASH, (capacity + 1) * sizeof(HashMapData));
|
||||
if constexpr (!(std::is_trivially_destructible_v<TKey> && std::is_trivially_destructible_v<TValue>)) {
|
||||
for (uint32_t i = 0; i < num_elements; i++) {
|
||||
elements[i].key.~TKey();
|
||||
elements[i].value.~TValue();
|
||||
}
|
||||
}
|
||||
|
||||
num_elements = 0;
|
||||
}
|
||||
|
||||
TValue &get(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t hash_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, hash_pos);
|
||||
CRASH_COND_MSG(!exists, "AHashMap key not found.");
|
||||
return elements[pos].value;
|
||||
}
|
||||
|
||||
const TValue &get(const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
uint32_t hash_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, hash_pos);
|
||||
CRASH_COND_MSG(!exists, "AHashMap key not found.");
|
||||
return elements[pos].value;
|
||||
}
|
||||
|
||||
const TValue *getptr(const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
uint32_t hash_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, hash_pos);
|
||||
|
||||
if (exists) {
|
||||
return &elements[pos].value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TValue *getptr(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t hash_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, hash_pos);
|
||||
|
||||
if (exists) {
|
||||
return &elements[pos].value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool has(const TKey &p_key) const {
|
||||
uint32_t _pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
return _lookup_pos(p_key, _pos, h_pos);
|
||||
}
|
||||
|
||||
bool erase(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t element_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, element_pos, pos);
|
||||
|
||||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t next_pos = (pos + 1) & capacity;
|
||||
while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) {
|
||||
SWAP(map_data[next_pos], map_data[pos]);
|
||||
|
||||
pos = next_pos;
|
||||
next_pos = (next_pos + 1) & capacity;
|
||||
}
|
||||
|
||||
map_data[pos].data = EMPTY_HASH;
|
||||
elements[element_pos].key.~TKey();
|
||||
elements[element_pos].value.~TValue();
|
||||
num_elements--;
|
||||
|
||||
if (element_pos < num_elements) {
|
||||
void *destination = &elements[element_pos];
|
||||
const void *source = &elements[num_elements];
|
||||
memcpy(destination, source, sizeof(MapKeyValue));
|
||||
uint32_t h_pos = 0;
|
||||
_lookup_pos(elements[num_elements].key, pos, h_pos);
|
||||
map_data[h_pos].hash_to_key = element_pos;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration.
|
||||
// p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key.
|
||||
bool replace_key(const TKey &p_old_key, const TKey &p_new_key) {
|
||||
if (p_old_key == p_new_key) {
|
||||
return true;
|
||||
}
|
||||
uint32_t pos = 0;
|
||||
uint32_t element_pos = 0;
|
||||
ERR_FAIL_COND_V(_lookup_pos(p_new_key, element_pos, pos), false);
|
||||
ERR_FAIL_COND_V(!_lookup_pos(p_old_key, element_pos, pos), false);
|
||||
MapKeyValue &element = elements[element_pos];
|
||||
const_cast<TKey &>(element.key) = p_new_key;
|
||||
|
||||
uint32_t next_pos = (pos + 1) & capacity;
|
||||
while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) {
|
||||
SWAP(map_data[next_pos], map_data[pos]);
|
||||
|
||||
pos = next_pos;
|
||||
next_pos = (next_pos + 1) & capacity;
|
||||
}
|
||||
|
||||
map_data[pos].data = EMPTY_HASH;
|
||||
|
||||
uint32_t hash = _hash(p_new_key);
|
||||
_insert_with_hash(hash, element_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) {
|
||||
ERR_FAIL_COND_MSG(p_new_capacity < get_capacity(), "It is impossible to reserve less capacity than is currently available.");
|
||||
if (elements == nullptr) {
|
||||
capacity = MAX(4u, p_new_capacity);
|
||||
capacity = next_power_of_2(capacity) - 1;
|
||||
return; // Unallocated yet.
|
||||
}
|
||||
_resize_and_rehash(p_new_capacity);
|
||||
}
|
||||
|
||||
/** Iterator API **/
|
||||
|
||||
struct ConstIterator {
|
||||
_FORCE_INLINE_ const MapKeyValue &operator*() const {
|
||||
return *pair;
|
||||
}
|
||||
_FORCE_INLINE_ const MapKeyValue *operator->() const {
|
||||
return pair;
|
||||
}
|
||||
_FORCE_INLINE_ ConstIterator &operator++() {
|
||||
pair++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ConstIterator &operator--() {
|
||||
pair--;
|
||||
if (pair < begin) {
|
||||
pair = end;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return pair == b.pair; }
|
||||
_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return pair != b.pair; }
|
||||
|
||||
_FORCE_INLINE_ explicit operator bool() const {
|
||||
return pair != end;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ConstIterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) {
|
||||
pair = p_key;
|
||||
begin = p_begin;
|
||||
end = p_end;
|
||||
}
|
||||
_FORCE_INLINE_ ConstIterator() {}
|
||||
_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) {
|
||||
pair = p_it.pair;
|
||||
begin = p_it.begin;
|
||||
end = p_it.end;
|
||||
}
|
||||
_FORCE_INLINE_ void operator=(const ConstIterator &p_it) {
|
||||
pair = p_it.pair;
|
||||
begin = p_it.begin;
|
||||
end = p_it.end;
|
||||
}
|
||||
|
||||
private:
|
||||
MapKeyValue *pair = nullptr;
|
||||
MapKeyValue *begin = nullptr;
|
||||
MapKeyValue *end = nullptr;
|
||||
};
|
||||
|
||||
struct Iterator {
|
||||
_FORCE_INLINE_ MapKeyValue &operator*() const {
|
||||
return *pair;
|
||||
}
|
||||
_FORCE_INLINE_ MapKeyValue *operator->() const {
|
||||
return pair;
|
||||
}
|
||||
_FORCE_INLINE_ Iterator &operator++() {
|
||||
pair++;
|
||||
return *this;
|
||||
}
|
||||
_FORCE_INLINE_ Iterator &operator--() {
|
||||
pair--;
|
||||
if (pair < begin) {
|
||||
pair = end;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return pair == b.pair; }
|
||||
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return pair != b.pair; }
|
||||
|
||||
_FORCE_INLINE_ explicit operator bool() const {
|
||||
return pair != end;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Iterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) {
|
||||
pair = p_key;
|
||||
begin = p_begin;
|
||||
end = p_end;
|
||||
}
|
||||
_FORCE_INLINE_ Iterator() {}
|
||||
_FORCE_INLINE_ Iterator(const Iterator &p_it) {
|
||||
pair = p_it.pair;
|
||||
begin = p_it.begin;
|
||||
end = p_it.end;
|
||||
}
|
||||
_FORCE_INLINE_ void operator=(const Iterator &p_it) {
|
||||
pair = p_it.pair;
|
||||
begin = p_it.begin;
|
||||
end = p_it.end;
|
||||
}
|
||||
|
||||
operator ConstIterator() const {
|
||||
return ConstIterator(pair, begin, end);
|
||||
}
|
||||
|
||||
private:
|
||||
MapKeyValue *pair = nullptr;
|
||||
MapKeyValue *begin = nullptr;
|
||||
MapKeyValue *end = nullptr;
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ Iterator begin() {
|
||||
return Iterator(elements, elements, elements + num_elements);
|
||||
}
|
||||
_FORCE_INLINE_ Iterator end() {
|
||||
return Iterator(elements + num_elements, elements, elements + num_elements);
|
||||
}
|
||||
_FORCE_INLINE_ Iterator last() {
|
||||
if (unlikely(num_elements == 0)) {
|
||||
return Iterator(nullptr, nullptr, nullptr);
|
||||
}
|
||||
return Iterator(elements + num_elements - 1, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
Iterator find(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, h_pos);
|
||||
if (!exists) {
|
||||
return end();
|
||||
}
|
||||
return Iterator(elements + pos, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
void remove(const Iterator &p_iter) {
|
||||
if (p_iter) {
|
||||
erase(p_iter->key);
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ConstIterator begin() const {
|
||||
return ConstIterator(elements, elements, elements + num_elements);
|
||||
}
|
||||
_FORCE_INLINE_ ConstIterator end() const {
|
||||
return ConstIterator(elements + num_elements, elements, elements + num_elements);
|
||||
}
|
||||
_FORCE_INLINE_ ConstIterator last() const {
|
||||
if (unlikely(num_elements == 0)) {
|
||||
return ConstIterator(nullptr, nullptr, nullptr);
|
||||
}
|
||||
return ConstIterator(elements + num_elements - 1, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
ConstIterator find(const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, h_pos);
|
||||
if (!exists) {
|
||||
return end();
|
||||
}
|
||||
return ConstIterator(elements + pos, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
/* Indexing */
|
||||
|
||||
const TValue &operator[](const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, h_pos);
|
||||
CRASH_COND(!exists);
|
||||
return elements[pos].value;
|
||||
}
|
||||
|
||||
TValue &operator[](const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
uint32_t hash = _hash(p_key);
|
||||
bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash);
|
||||
|
||||
if (exists) {
|
||||
return elements[pos].value;
|
||||
} else {
|
||||
pos = _insert_element(p_key, TValue(), hash);
|
||||
return elements[pos].value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert */
|
||||
|
||||
Iterator insert(const TKey &p_key, const TValue &p_value) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
uint32_t hash = _hash(p_key);
|
||||
bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash);
|
||||
|
||||
if (!exists) {
|
||||
pos = _insert_element(p_key, p_value, hash);
|
||||
} else {
|
||||
elements[pos].value = p_value;
|
||||
}
|
||||
return Iterator(elements + pos, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
// Inserts an element without checking if it already exists.
|
||||
Iterator insert_new(const TKey &p_key, const TValue &p_value) {
|
||||
DEV_ASSERT(!has(p_key));
|
||||
uint32_t hash = _hash(p_key);
|
||||
uint32_t pos = _insert_element(p_key, p_value, hash);
|
||||
return Iterator(elements + pos, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
/* Array methods. */
|
||||
|
||||
// Unsafe. Changing keys and going outside the bounds of an array can lead to undefined behavior.
|
||||
KeyValue<TKey, TValue> *get_elements_ptr() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
// Returns the element index. If not found, returns -1.
|
||||
int get_index(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, h_pos);
|
||||
if (!exists) {
|
||||
return -1;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
KeyValue<TKey, TValue> &get_by_index(uint32_t p_index) {
|
||||
CRASH_BAD_UNSIGNED_INDEX(p_index, num_elements);
|
||||
return elements[p_index];
|
||||
}
|
||||
|
||||
bool erase_by_index(uint32_t p_index) {
|
||||
if (p_index >= size()) {
|
||||
return false;
|
||||
}
|
||||
return erase(elements[p_index].key);
|
||||
}
|
||||
|
||||
/* Constructors */
|
||||
|
||||
AHashMap(const AHashMap &p_other) {
|
||||
_init_from(p_other);
|
||||
}
|
||||
|
||||
AHashMap(const HashMap<TKey, TValue> &p_other) {
|
||||
reserve(p_other.size());
|
||||
for (const KeyValue<TKey, TValue> &E : p_other) {
|
||||
uint32_t hash = _hash(E.key);
|
||||
_insert_element(E.key, E.value, hash);
|
||||
}
|
||||
}
|
||||
|
||||
void operator=(const AHashMap &p_other) {
|
||||
if (this == &p_other) {
|
||||
return; // Ignore self assignment.
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
_init_from(p_other);
|
||||
}
|
||||
|
||||
void operator=(const HashMap<TKey, TValue> &p_other) {
|
||||
reset();
|
||||
if (p_other.size() > get_capacity()) {
|
||||
reserve(p_other.size());
|
||||
}
|
||||
for (const KeyValue<TKey, TValue> &E : p_other) {
|
||||
uint32_t hash = _hash(E.key);
|
||||
_insert_element(E.key, E.value, hash);
|
||||
}
|
||||
}
|
||||
|
||||
AHashMap(uint32_t p_initial_capacity) {
|
||||
// Capacity can't be 0 and must be 2^n - 1.
|
||||
capacity = MAX(4u, p_initial_capacity);
|
||||
capacity = next_power_of_2(capacity) - 1;
|
||||
}
|
||||
AHashMap() :
|
||||
capacity(INITIAL_CAPACITY - 1) {
|
||||
}
|
||||
|
||||
AHashMap(std::initializer_list<KeyValue<TKey, TValue>> p_init) {
|
||||
reserve(p_init.size());
|
||||
for (const KeyValue<TKey, TValue> &E : p_init) {
|
||||
insert(E.key, E.value);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (elements != nullptr) {
|
||||
if constexpr (!(std::is_trivially_destructible_v<TKey> && std::is_trivially_destructible_v<TValue>)) {
|
||||
for (uint32_t i = 0; i < num_elements; i++) {
|
||||
elements[i].key.~TKey();
|
||||
elements[i].value.~TValue();
|
||||
}
|
||||
}
|
||||
Memory::free_static(elements);
|
||||
Memory::free_static(map_data);
|
||||
elements = nullptr;
|
||||
}
|
||||
capacity = INITIAL_CAPACITY - 1;
|
||||
num_elements = 0;
|
||||
}
|
||||
|
||||
~AHashMap() {
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
extern template class AHashMap<int, int>;
|
||||
extern template class AHashMap<String, int>;
|
||||
extern template class AHashMap<StringName, StringName>;
|
||||
extern template class AHashMap<StringName, Variant>;
|
||||
extern template class AHashMap<StringName, int>;
|
||||
|
||||
#endif // A_HASH_MAP_H
|
||||
|
|
@ -30,17 +30,6 @@
|
|||
|
||||
#include "command_queue_mt.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
void CommandQueueMT::lock() {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void CommandQueueMT::unlock() {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
CommandQueueMT::CommandQueueMT() {
|
||||
command_mem.reserve(DEFAULT_COMMAND_MEM_SIZE_KB * 1024);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,295 +33,74 @@
|
|||
|
||||
#include "core/object/worker_thread_pool.h"
|
||||
#include "core/os/condition_variable.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/simple_type.h"
|
||||
#include "core/templates/tuple.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#define COMMA(N) _COMMA_##N
|
||||
#define _COMMA_0
|
||||
#define _COMMA_1 ,
|
||||
#define _COMMA_2 ,
|
||||
#define _COMMA_3 ,
|
||||
#define _COMMA_4 ,
|
||||
#define _COMMA_5 ,
|
||||
#define _COMMA_6 ,
|
||||
#define _COMMA_7 ,
|
||||
#define _COMMA_8 ,
|
||||
#define _COMMA_9 ,
|
||||
#define _COMMA_10 ,
|
||||
#define _COMMA_11 ,
|
||||
#define _COMMA_12 ,
|
||||
#define _COMMA_13 ,
|
||||
#define _COMMA_14 ,
|
||||
#define _COMMA_15 ,
|
||||
|
||||
// 1-based comma separated list of ITEMs
|
||||
#define COMMA_SEP_LIST(ITEM, LENGTH) _COMMA_SEP_LIST_##LENGTH(ITEM)
|
||||
#define _COMMA_SEP_LIST_15(ITEM) \
|
||||
_COMMA_SEP_LIST_14(ITEM) \
|
||||
, ITEM(15)
|
||||
#define _COMMA_SEP_LIST_14(ITEM) \
|
||||
_COMMA_SEP_LIST_13(ITEM) \
|
||||
, ITEM(14)
|
||||
#define _COMMA_SEP_LIST_13(ITEM) \
|
||||
_COMMA_SEP_LIST_12(ITEM) \
|
||||
, ITEM(13)
|
||||
#define _COMMA_SEP_LIST_12(ITEM) \
|
||||
_COMMA_SEP_LIST_11(ITEM) \
|
||||
, ITEM(12)
|
||||
#define _COMMA_SEP_LIST_11(ITEM) \
|
||||
_COMMA_SEP_LIST_10(ITEM) \
|
||||
, ITEM(11)
|
||||
#define _COMMA_SEP_LIST_10(ITEM) \
|
||||
_COMMA_SEP_LIST_9(ITEM) \
|
||||
, ITEM(10)
|
||||
#define _COMMA_SEP_LIST_9(ITEM) \
|
||||
_COMMA_SEP_LIST_8(ITEM) \
|
||||
, ITEM(9)
|
||||
#define _COMMA_SEP_LIST_8(ITEM) \
|
||||
_COMMA_SEP_LIST_7(ITEM) \
|
||||
, ITEM(8)
|
||||
#define _COMMA_SEP_LIST_7(ITEM) \
|
||||
_COMMA_SEP_LIST_6(ITEM) \
|
||||
, ITEM(7)
|
||||
#define _COMMA_SEP_LIST_6(ITEM) \
|
||||
_COMMA_SEP_LIST_5(ITEM) \
|
||||
, ITEM(6)
|
||||
#define _COMMA_SEP_LIST_5(ITEM) \
|
||||
_COMMA_SEP_LIST_4(ITEM) \
|
||||
, ITEM(5)
|
||||
#define _COMMA_SEP_LIST_4(ITEM) \
|
||||
_COMMA_SEP_LIST_3(ITEM) \
|
||||
, ITEM(4)
|
||||
#define _COMMA_SEP_LIST_3(ITEM) \
|
||||
_COMMA_SEP_LIST_2(ITEM) \
|
||||
, ITEM(3)
|
||||
#define _COMMA_SEP_LIST_2(ITEM) \
|
||||
_COMMA_SEP_LIST_1(ITEM) \
|
||||
, ITEM(2)
|
||||
#define _COMMA_SEP_LIST_1(ITEM) \
|
||||
_COMMA_SEP_LIST_0(ITEM) \
|
||||
ITEM(1)
|
||||
#define _COMMA_SEP_LIST_0(ITEM)
|
||||
|
||||
// 1-based semicolon separated list of ITEMs
|
||||
#define SEMIC_SEP_LIST(ITEM, LENGTH) _SEMIC_SEP_LIST_##LENGTH(ITEM)
|
||||
#define _SEMIC_SEP_LIST_15(ITEM) \
|
||||
_SEMIC_SEP_LIST_14(ITEM); \
|
||||
ITEM(15)
|
||||
#define _SEMIC_SEP_LIST_14(ITEM) \
|
||||
_SEMIC_SEP_LIST_13(ITEM); \
|
||||
ITEM(14)
|
||||
#define _SEMIC_SEP_LIST_13(ITEM) \
|
||||
_SEMIC_SEP_LIST_12(ITEM); \
|
||||
ITEM(13)
|
||||
#define _SEMIC_SEP_LIST_12(ITEM) \
|
||||
_SEMIC_SEP_LIST_11(ITEM); \
|
||||
ITEM(12)
|
||||
#define _SEMIC_SEP_LIST_11(ITEM) \
|
||||
_SEMIC_SEP_LIST_10(ITEM); \
|
||||
ITEM(11)
|
||||
#define _SEMIC_SEP_LIST_10(ITEM) \
|
||||
_SEMIC_SEP_LIST_9(ITEM); \
|
||||
ITEM(10)
|
||||
#define _SEMIC_SEP_LIST_9(ITEM) \
|
||||
_SEMIC_SEP_LIST_8(ITEM); \
|
||||
ITEM(9)
|
||||
#define _SEMIC_SEP_LIST_8(ITEM) \
|
||||
_SEMIC_SEP_LIST_7(ITEM); \
|
||||
ITEM(8)
|
||||
#define _SEMIC_SEP_LIST_7(ITEM) \
|
||||
_SEMIC_SEP_LIST_6(ITEM); \
|
||||
ITEM(7)
|
||||
#define _SEMIC_SEP_LIST_6(ITEM) \
|
||||
_SEMIC_SEP_LIST_5(ITEM); \
|
||||
ITEM(6)
|
||||
#define _SEMIC_SEP_LIST_5(ITEM) \
|
||||
_SEMIC_SEP_LIST_4(ITEM); \
|
||||
ITEM(5)
|
||||
#define _SEMIC_SEP_LIST_4(ITEM) \
|
||||
_SEMIC_SEP_LIST_3(ITEM); \
|
||||
ITEM(4)
|
||||
#define _SEMIC_SEP_LIST_3(ITEM) \
|
||||
_SEMIC_SEP_LIST_2(ITEM); \
|
||||
ITEM(3)
|
||||
#define _SEMIC_SEP_LIST_2(ITEM) \
|
||||
_SEMIC_SEP_LIST_1(ITEM); \
|
||||
ITEM(2)
|
||||
#define _SEMIC_SEP_LIST_1(ITEM) \
|
||||
_SEMIC_SEP_LIST_0(ITEM) \
|
||||
ITEM(1)
|
||||
#define _SEMIC_SEP_LIST_0(ITEM)
|
||||
|
||||
// 1-based space separated list of ITEMs
|
||||
#define SPACE_SEP_LIST(ITEM, LENGTH) _SPACE_SEP_LIST_##LENGTH(ITEM)
|
||||
#define _SPACE_SEP_LIST_15(ITEM) \
|
||||
_SPACE_SEP_LIST_14(ITEM) \
|
||||
ITEM(15)
|
||||
#define _SPACE_SEP_LIST_14(ITEM) \
|
||||
_SPACE_SEP_LIST_13(ITEM) \
|
||||
ITEM(14)
|
||||
#define _SPACE_SEP_LIST_13(ITEM) \
|
||||
_SPACE_SEP_LIST_12(ITEM) \
|
||||
ITEM(13)
|
||||
#define _SPACE_SEP_LIST_12(ITEM) \
|
||||
_SPACE_SEP_LIST_11(ITEM) \
|
||||
ITEM(12)
|
||||
#define _SPACE_SEP_LIST_11(ITEM) \
|
||||
_SPACE_SEP_LIST_10(ITEM) \
|
||||
ITEM(11)
|
||||
#define _SPACE_SEP_LIST_10(ITEM) \
|
||||
_SPACE_SEP_LIST_9(ITEM) \
|
||||
ITEM(10)
|
||||
#define _SPACE_SEP_LIST_9(ITEM) \
|
||||
_SPACE_SEP_LIST_8(ITEM) \
|
||||
ITEM(9)
|
||||
#define _SPACE_SEP_LIST_8(ITEM) \
|
||||
_SPACE_SEP_LIST_7(ITEM) \
|
||||
ITEM(8)
|
||||
#define _SPACE_SEP_LIST_7(ITEM) \
|
||||
_SPACE_SEP_LIST_6(ITEM) \
|
||||
ITEM(7)
|
||||
#define _SPACE_SEP_LIST_6(ITEM) \
|
||||
_SPACE_SEP_LIST_5(ITEM) \
|
||||
ITEM(6)
|
||||
#define _SPACE_SEP_LIST_5(ITEM) \
|
||||
_SPACE_SEP_LIST_4(ITEM) \
|
||||
ITEM(5)
|
||||
#define _SPACE_SEP_LIST_4(ITEM) \
|
||||
_SPACE_SEP_LIST_3(ITEM) \
|
||||
ITEM(4)
|
||||
#define _SPACE_SEP_LIST_3(ITEM) \
|
||||
_SPACE_SEP_LIST_2(ITEM) \
|
||||
ITEM(3)
|
||||
#define _SPACE_SEP_LIST_2(ITEM) \
|
||||
_SPACE_SEP_LIST_1(ITEM) \
|
||||
ITEM(2)
|
||||
#define _SPACE_SEP_LIST_1(ITEM) \
|
||||
_SPACE_SEP_LIST_0(ITEM) \
|
||||
ITEM(1)
|
||||
#define _SPACE_SEP_LIST_0(ITEM)
|
||||
|
||||
#define ARG(N) p##N
|
||||
#define PARAM(N) P##N p##N
|
||||
#define TYPE_PARAM(N) typename P##N
|
||||
#define PARAM_DECL(N) GetSimpleTypeT<P##N> p##N
|
||||
|
||||
#define DECL_CMD(N) \
|
||||
template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
|
||||
struct Command##N : public CommandBase { \
|
||||
T *instance; \
|
||||
M method; \
|
||||
SEMIC_SEP_LIST(PARAM_DECL, N); \
|
||||
virtual void call() override { \
|
||||
(instance->*method)(COMMA_SEP_LIST(ARG, N)); \
|
||||
} \
|
||||
};
|
||||
|
||||
#define DECL_CMD_RET(N) \
|
||||
template <typename T, typename M, COMMA_SEP_LIST(TYPE_PARAM, N) COMMA(N) typename R> \
|
||||
struct CommandRet##N : public SyncCommand { \
|
||||
R *ret; \
|
||||
T *instance; \
|
||||
M method; \
|
||||
SEMIC_SEP_LIST(PARAM_DECL, N); \
|
||||
virtual void call() override { \
|
||||
*ret = (instance->*method)(COMMA_SEP_LIST(ARG, N)); \
|
||||
} \
|
||||
};
|
||||
|
||||
#define DECL_CMD_SYNC(N) \
|
||||
template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
|
||||
struct CommandSync##N : public SyncCommand { \
|
||||
T *instance; \
|
||||
M method; \
|
||||
SEMIC_SEP_LIST(PARAM_DECL, N); \
|
||||
virtual void call() override { \
|
||||
(instance->*method)(COMMA_SEP_LIST(ARG, N)); \
|
||||
} \
|
||||
};
|
||||
|
||||
#define TYPE_ARG(N) P##N
|
||||
#define CMD_TYPE(N) Command##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)>
|
||||
#define CMD_ASSIGN_PARAM(N) cmd->p##N = p##N
|
||||
|
||||
#define DECL_PUSH(N) \
|
||||
template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
|
||||
void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
|
||||
MutexLock mlock(mutex); \
|
||||
CMD_TYPE(N) *cmd = allocate<CMD_TYPE(N)>(); \
|
||||
cmd->instance = p_instance; \
|
||||
cmd->method = p_method; \
|
||||
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
|
||||
if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
|
||||
WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CMD_RET_TYPE(N) CommandRet##N<T, M, COMMA_SEP_LIST(TYPE_ARG, N) COMMA(N) R>
|
||||
|
||||
#define DECL_PUSH_AND_RET(N) \
|
||||
template <typename T, typename M, COMMA_SEP_LIST(TYPE_PARAM, N) COMMA(N) typename R> \
|
||||
void push_and_ret(T *p_instance, M p_method, COMMA_SEP_LIST(PARAM, N) COMMA(N) R *r_ret) { \
|
||||
MutexLock mlock(mutex); \
|
||||
CMD_RET_TYPE(N) *cmd = allocate<CMD_RET_TYPE(N)>(); \
|
||||
cmd->instance = p_instance; \
|
||||
cmd->method = p_method; \
|
||||
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
|
||||
cmd->ret = r_ret; \
|
||||
if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
|
||||
WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
|
||||
} \
|
||||
sync_tail++; \
|
||||
_wait_for_sync(mlock); \
|
||||
}
|
||||
|
||||
#define CMD_SYNC_TYPE(N) CommandSync##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)>
|
||||
|
||||
#define DECL_PUSH_AND_SYNC(N) \
|
||||
template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
|
||||
void push_and_sync(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
|
||||
MutexLock mlock(mutex); \
|
||||
CMD_SYNC_TYPE(N) *cmd = allocate<CMD_SYNC_TYPE(N)>(); \
|
||||
cmd->instance = p_instance; \
|
||||
cmd->method = p_method; \
|
||||
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
|
||||
if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
|
||||
WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
|
||||
} \
|
||||
sync_tail++; \
|
||||
_wait_for_sync(mlock); \
|
||||
}
|
||||
|
||||
#define MAX_CMD_PARAMS 15
|
||||
|
||||
class CommandQueueMT {
|
||||
struct CommandBase {
|
||||
bool sync = false;
|
||||
virtual void call() = 0;
|
||||
virtual ~CommandBase() = default;
|
||||
|
||||
CommandBase(bool p_sync) :
|
||||
sync(p_sync) {}
|
||||
};
|
||||
|
||||
struct SyncCommand : public CommandBase {
|
||||
virtual void call() override {}
|
||||
SyncCommand() {
|
||||
sync = true;
|
||||
template <typename T, typename M, bool NeedsSync, typename... Args>
|
||||
struct Command : public CommandBase {
|
||||
T *instance;
|
||||
M method;
|
||||
Tuple<GetSimpleTypeT<Args>...> args;
|
||||
|
||||
template <typename... FwdArgs>
|
||||
_FORCE_INLINE_ Command(T *p_instance, M p_method, FwdArgs &&...p_args) :
|
||||
CommandBase(NeedsSync), instance(p_instance), method(p_method), args(std::forward<FwdArgs>(p_args)...) {}
|
||||
|
||||
void call() {
|
||||
call_impl(BuildIndexSequence<sizeof...(Args)>{});
|
||||
}
|
||||
|
||||
private:
|
||||
template <size_t... I>
|
||||
_FORCE_INLINE_ void call_impl(IndexSequence<I...>) {
|
||||
// Move out of the Tuple, this will be destroyed as soon as the call is complete.
|
||||
(instance->*method)(std::move(get<I>())...);
|
||||
}
|
||||
|
||||
// This method exists so we can call it in the parameter pack expansion in call_impl.
|
||||
template <size_t I>
|
||||
_FORCE_INLINE_ auto &get() { return ::tuple_get<I>(args); }
|
||||
};
|
||||
|
||||
DECL_CMD(0)
|
||||
SPACE_SEP_LIST(DECL_CMD, 15)
|
||||
// Separate class from Command so we can save the space of the ret pointer for commands that don't return.
|
||||
template <typename T, typename M, typename R, typename... Args>
|
||||
struct CommandRet : public CommandBase {
|
||||
T *instance;
|
||||
M method;
|
||||
R *ret;
|
||||
Tuple<GetSimpleTypeT<Args>...> args;
|
||||
|
||||
// Commands that return.
|
||||
DECL_CMD_RET(0)
|
||||
SPACE_SEP_LIST(DECL_CMD_RET, 15)
|
||||
_FORCE_INLINE_ CommandRet(T *p_instance, M p_method, R *p_ret, GetSimpleTypeT<Args>... p_args) :
|
||||
CommandBase(true), instance(p_instance), method(p_method), ret(p_ret), args{ p_args... } {}
|
||||
|
||||
/* commands that don't return but sync */
|
||||
DECL_CMD_SYNC(0)
|
||||
SPACE_SEP_LIST(DECL_CMD_SYNC, 15)
|
||||
void call() override {
|
||||
*ret = call_impl(BuildIndexSequence<sizeof...(Args)>{});
|
||||
}
|
||||
|
||||
private:
|
||||
template <size_t... I>
|
||||
_FORCE_INLINE_ R call_impl(IndexSequence<I...>) {
|
||||
// Move out of the Tuple, this will be destroyed as soon as the call is complete.
|
||||
return (instance->*method)(std::move(get<I>())...);
|
||||
}
|
||||
|
||||
// This method exists so we can call it in the parameter pack expansion in call_impl.
|
||||
template <size_t I>
|
||||
_FORCE_INLINE_ auto &get() { return ::tuple_get<I>(args); }
|
||||
};
|
||||
|
||||
/***** BASE *******/
|
||||
|
||||
|
|
@ -336,15 +115,32 @@ class CommandQueueMT {
|
|||
WorkerThreadPool::TaskID pump_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||
uint64_t flush_read_ptr = 0;
|
||||
|
||||
template <typename T>
|
||||
T *allocate() {
|
||||
template <typename T, typename... Args>
|
||||
_FORCE_INLINE_ void create_command(Args &&...p_args) {
|
||||
// alloc size is size+T+safeguard
|
||||
uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1));
|
||||
constexpr uint64_t alloc_size = ((sizeof(T) + 8U - 1U) & ~(8U - 1U));
|
||||
static_assert(alloc_size < UINT32_MAX, "Type too large to fit in the command queue.");
|
||||
|
||||
uint64_t size = command_mem.size();
|
||||
command_mem.resize(size + alloc_size + 8);
|
||||
command_mem.resize(size + alloc_size + sizeof(uint64_t));
|
||||
*(uint64_t *)&command_mem[size] = alloc_size;
|
||||
T *cmd = memnew_placement(&command_mem[size + 8], T);
|
||||
return cmd;
|
||||
void *cmd = &command_mem[size + sizeof(uint64_t)];
|
||||
new (cmd) T(std::forward<Args>(p_args)...);
|
||||
}
|
||||
|
||||
template <typename T, bool NeedsSync, typename... Args>
|
||||
_FORCE_INLINE_ void _push_internal(Args &&...args) {
|
||||
MutexLock mlock(mutex);
|
||||
create_command<T>(std::forward<Args>(args)...);
|
||||
|
||||
if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) {
|
||||
WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id);
|
||||
}
|
||||
|
||||
if constexpr (NeedsSync) {
|
||||
sync_tail++;
|
||||
_wait_for_sync(mlock);
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void _prevent_sync_wraparound() {
|
||||
|
|
@ -362,23 +158,24 @@ class CommandQueueMT {
|
|||
return;
|
||||
}
|
||||
|
||||
lock();
|
||||
MutexLock lock(mutex);
|
||||
|
||||
uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(&mutex);
|
||||
while (flush_read_ptr < command_mem.size()) {
|
||||
uint64_t size = *(uint64_t *)&command_mem[flush_read_ptr];
|
||||
flush_read_ptr += 8;
|
||||
CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
|
||||
uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(lock);
|
||||
cmd->call();
|
||||
WorkerThreadPool::thread_exit_unlock_allowance_zone(allowance_id);
|
||||
|
||||
// Handle potential realloc due to the command and unlock allowance.
|
||||
cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
|
||||
|
||||
if (unlikely(cmd->sync)) {
|
||||
sync_head++;
|
||||
unlock(); // Give an opportunity to awaiters right away.
|
||||
lock.~MutexLock(); // Give an opportunity to awaiters right away.
|
||||
sync_cond_var.notify_all();
|
||||
lock();
|
||||
new (&lock) MutexLock(mutex);
|
||||
// Handle potential realloc happened during unlock.
|
||||
cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
|
||||
}
|
||||
|
|
@ -387,14 +184,11 @@ class CommandQueueMT {
|
|||
|
||||
flush_read_ptr += size;
|
||||
}
|
||||
WorkerThreadPool::thread_exit_unlock_allowance_zone(allowance_id);
|
||||
|
||||
command_mem.clear();
|
||||
flush_read_ptr = 0;
|
||||
|
||||
_prevent_sync_wraparound();
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void _wait_for_sync(MutexLock<BinaryMutex> &p_lock) {
|
||||
|
|
@ -410,20 +204,26 @@ class CommandQueueMT {
|
|||
void _no_op() {}
|
||||
|
||||
public:
|
||||
void lock();
|
||||
void unlock();
|
||||
template <typename T, typename M, typename... Args>
|
||||
void push(T *p_instance, M p_method, Args &&...p_args) {
|
||||
// Standard command, no sync.
|
||||
using CommandType = Command<T, M, false, Args...>;
|
||||
_push_internal<CommandType, false>(p_instance, p_method, std::forward<Args>(p_args)...);
|
||||
}
|
||||
|
||||
/* NORMAL PUSH COMMANDS */
|
||||
DECL_PUSH(0)
|
||||
SPACE_SEP_LIST(DECL_PUSH, 15)
|
||||
template <typename T, typename M, typename... Args>
|
||||
void push_and_sync(T *p_instance, M p_method, Args... p_args) {
|
||||
// Standard command, sync.
|
||||
using CommandType = Command<T, M, true, Args...>;
|
||||
_push_internal<CommandType, true>(p_instance, p_method, std::forward<Args>(p_args)...);
|
||||
}
|
||||
|
||||
/* PUSH AND RET COMMANDS */
|
||||
DECL_PUSH_AND_RET(0)
|
||||
SPACE_SEP_LIST(DECL_PUSH_AND_RET, 15)
|
||||
|
||||
/* PUSH AND RET SYNC COMMANDS*/
|
||||
DECL_PUSH_AND_SYNC(0)
|
||||
SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 15)
|
||||
template <typename T, typename M, typename R, typename... Args>
|
||||
void push_and_ret(T *p_instance, M p_method, R *r_ret, Args... p_args) {
|
||||
// Command with return value, sync.
|
||||
using CommandType = CommandRet<T, M, R, Args...>;
|
||||
_push_internal<CommandType, true>(p_instance, p_method, r_ret, std::forward<Args>(p_args)...);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void flush_if_pending() {
|
||||
if (unlikely(command_mem.size() > 0)) {
|
||||
|
|
@ -446,29 +246,12 @@ public:
|
|||
}
|
||||
|
||||
void set_pump_task_id(WorkerThreadPool::TaskID p_task_id) {
|
||||
lock();
|
||||
MutexLock lock(mutex);
|
||||
pump_task_id = p_task_id;
|
||||
unlock();
|
||||
}
|
||||
|
||||
CommandQueueMT();
|
||||
~CommandQueueMT();
|
||||
};
|
||||
|
||||
#undef ARG
|
||||
#undef PARAM
|
||||
#undef TYPE_PARAM
|
||||
#undef PARAM_DECL
|
||||
#undef DECL_CMD
|
||||
#undef DECL_CMD_RET
|
||||
#undef DECL_CMD_SYNC
|
||||
#undef TYPE_ARG
|
||||
#undef CMD_TYPE
|
||||
#undef CMD_ASSIGN_PARAM
|
||||
#undef DECL_PUSH
|
||||
#undef CMD_RET_TYPE
|
||||
#undef DECL_PUSH_AND_RET
|
||||
#undef CMD_SYNC_TYPE
|
||||
#undef DECL_CMD_SYNC
|
||||
|
||||
#endif // COMMAND_QUEUE_MT_H
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@
|
|||
#include "core/templates/safe_refcount.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
template <typename T>
|
||||
class Vector;
|
||||
|
|
@ -160,13 +162,25 @@ private:
|
|||
return *out;
|
||||
}
|
||||
|
||||
void _unref(void *p_data);
|
||||
// Decrements the reference count. Deallocates the backing buffer if needed.
|
||||
// After this function, _ptr is guaranteed to be NULL.
|
||||
void _unref();
|
||||
void _ref(const CowData *p_from);
|
||||
void _ref(const CowData &p_from);
|
||||
USize _copy_on_write();
|
||||
Error _realloc(Size p_alloc_size);
|
||||
|
||||
public:
|
||||
void operator=(const CowData<T> &p_from) { _ref(p_from); }
|
||||
void operator=(CowData<T> &&p_from) {
|
||||
if (_ptr == p_from._ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_unref();
|
||||
_ptr = p_from._ptr;
|
||||
p_from._ptr = nullptr;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ T *ptrw() {
|
||||
_copy_on_write();
|
||||
|
|
@ -215,19 +229,22 @@ public:
|
|||
T *p = ptrw();
|
||||
Size len = size();
|
||||
for (Size i = p_index; i < len - 1; i++) {
|
||||
p[i] = p[i + 1];
|
||||
p[i] = std::move(p[i + 1]);
|
||||
}
|
||||
|
||||
resize(len - 1);
|
||||
}
|
||||
|
||||
Error insert(Size p_pos, const T &p_val) {
|
||||
ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER);
|
||||
resize(size() + 1);
|
||||
for (Size i = (size() - 1); i > p_pos; i--) {
|
||||
set(i, get(i - 1));
|
||||
Size new_size = size() + 1;
|
||||
ERR_FAIL_INDEX_V(p_pos, new_size, ERR_INVALID_PARAMETER);
|
||||
Error err = resize(new_size);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
T *p = ptrw();
|
||||
for (Size i = new_size - 1; i > p_pos; i--) {
|
||||
p[i] = std::move(p[i - 1]);
|
||||
}
|
||||
set(p_pos, p_val);
|
||||
p[p_pos] = p_val;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
|
@ -237,35 +254,47 @@ public:
|
|||
Size count(const T &p_val) const;
|
||||
|
||||
_FORCE_INLINE_ CowData() {}
|
||||
_FORCE_INLINE_ ~CowData();
|
||||
_FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); };
|
||||
_FORCE_INLINE_ ~CowData() { _unref(); }
|
||||
_FORCE_INLINE_ CowData(std::initializer_list<T> p_init);
|
||||
_FORCE_INLINE_ CowData(const CowData<T> &p_from) { _ref(p_from); }
|
||||
_FORCE_INLINE_ CowData(CowData<T> &&p_from) {
|
||||
_ptr = p_from._ptr;
|
||||
p_from._ptr = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void CowData<T>::_unref(void *p_data) {
|
||||
if (!p_data) {
|
||||
void CowData<T>::_unref() {
|
||||
if (!_ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
SafeNumeric<USize> *refc = _get_refcount();
|
||||
|
||||
if (refc->decrement() > 0) {
|
||||
return; // still in use
|
||||
// Data is still in use elsewhere.
|
||||
_ptr = nullptr;
|
||||
return;
|
||||
}
|
||||
// clean up
|
||||
// Clean up.
|
||||
// First, invalidate our own reference.
|
||||
// NOTE: It is required to do so immediately because it must not be observable outside of this
|
||||
// function after refcount has already been reduced to 0.
|
||||
// WARNING: It must be done before calling the destructors, because one of them may otherwise
|
||||
// observe it through a reference to us. In this case, it may try to access the buffer,
|
||||
// which is illegal after some of the elements in it have already been destructed, and
|
||||
// may lead to a segmentation fault.
|
||||
USize current_size = *_get_size();
|
||||
T *prev_ptr = _ptr;
|
||||
_ptr = nullptr;
|
||||
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
USize *count = _get_size();
|
||||
T *data = (T *)(count + 1);
|
||||
|
||||
for (USize i = 0; i < *count; ++i) {
|
||||
// call destructors
|
||||
data[i].~T();
|
||||
for (USize i = 0; i < current_size; ++i) {
|
||||
prev_ptr[i].~T();
|
||||
}
|
||||
}
|
||||
|
||||
// free mem
|
||||
Memory::free_static(((uint8_t *)p_data) - DATA_OFFSET, false);
|
||||
Memory::free_static((uint8_t *)prev_ptr - DATA_OFFSET, false);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -300,7 +329,7 @@ typename CowData<T>::USize CowData<T>::_copy_on_write() {
|
|||
}
|
||||
}
|
||||
|
||||
_unref(_ptr);
|
||||
_unref();
|
||||
_ptr = _data_ptr;
|
||||
|
||||
rc = 1;
|
||||
|
|
@ -320,14 +349,13 @@ Error CowData<T>::resize(Size p_size) {
|
|||
}
|
||||
|
||||
if (p_size == 0) {
|
||||
// wants to clean up
|
||||
_unref(_ptr);
|
||||
_ptr = nullptr;
|
||||
// Wants to clean up.
|
||||
_unref(); // Resets _ptr to nullptr.
|
||||
return OK;
|
||||
}
|
||||
|
||||
// possibly changing size, copy on write
|
||||
USize rc = _copy_on_write();
|
||||
_copy_on_write();
|
||||
|
||||
USize current_alloc_size = _get_alloc_size(current_size);
|
||||
USize alloc_size;
|
||||
|
|
@ -350,15 +378,10 @@ Error CowData<T>::resize(Size p_size) {
|
|||
_ptr = _data_ptr;
|
||||
|
||||
} else {
|
||||
uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false);
|
||||
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
|
||||
|
||||
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
|
||||
T *_data_ptr = _get_data_ptr(mem_new);
|
||||
|
||||
new (_refc_ptr) SafeNumeric<USize>(rc); //refcount
|
||||
|
||||
_ptr = _data_ptr;
|
||||
const Error error = _realloc(alloc_size);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -384,15 +407,10 @@ Error CowData<T>::resize(Size p_size) {
|
|||
}
|
||||
|
||||
if (alloc_size != current_alloc_size) {
|
||||
uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false);
|
||||
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
|
||||
|
||||
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
|
||||
T *_data_ptr = _get_data_ptr(mem_new);
|
||||
|
||||
new (_refc_ptr) SafeNumeric<USize>(rc); //refcount
|
||||
|
||||
_ptr = _data_ptr;
|
||||
const Error error = _realloc(alloc_size);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
*_get_size() = p_size;
|
||||
|
|
@ -401,6 +419,21 @@ Error CowData<T>::resize(Size p_size) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Error CowData<T>::_realloc(Size p_alloc_size) {
|
||||
uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, p_alloc_size + DATA_OFFSET, false);
|
||||
ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
|
||||
|
||||
SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
|
||||
T *_data_ptr = _get_data_ptr(mem_new);
|
||||
|
||||
// If we realloc, we're guaranteed to be the only reference.
|
||||
new (_refc_ptr) SafeNumeric<USize>(1);
|
||||
_ptr = _data_ptr;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename CowData<T>::Size CowData<T>::find(const T &p_val, Size p_from) const {
|
||||
Size ret = -1;
|
||||
|
|
@ -460,8 +493,7 @@ void CowData<T>::_ref(const CowData &p_from) {
|
|||
return; // self assign, do nothing.
|
||||
}
|
||||
|
||||
_unref(_ptr);
|
||||
_ptr = nullptr;
|
||||
_unref(); // Resets _ptr to nullptr.
|
||||
|
||||
if (!p_from._ptr) {
|
||||
return; //nothing to do
|
||||
|
|
@ -473,8 +505,16 @@ void CowData<T>::_ref(const CowData &p_from) {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
CowData<T>::~CowData() {
|
||||
_unref(_ptr);
|
||||
CowData<T>::CowData(std::initializer_list<T> p_init) {
|
||||
Error err = resize(p_init.size());
|
||||
if (err != OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
Size i = 0;
|
||||
for (const T &element : p_init) {
|
||||
set(i++, element);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
|
|
|
|||
43
engine/core/templates/hash_map.cpp
Normal file
43
engine/core/templates/hash_map.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/**************************************************************************/
|
||||
/* hash_map.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 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 "hash_map.h"
|
||||
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
bool _hashmap_variant_less_than(const Variant &p_left, const Variant &p_right) {
|
||||
bool valid = false;
|
||||
Variant res;
|
||||
Variant::evaluate(Variant::OP_LESS, p_left, p_right, res, valid);
|
||||
if (!valid) {
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
@ -31,12 +31,12 @@
|
|||
#ifndef HASH_MAP_H
|
||||
#define HASH_MAP_H
|
||||
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/templates/hashfuncs.h"
|
||||
#include "core/templates/paged_allocator.h"
|
||||
#include "core/templates/pair.h"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
/**
|
||||
* A HashMap implementation that uses open addressing with Robin Hood hashing.
|
||||
* Robin Hood hashing swaps out entries that have a smaller probing distance
|
||||
|
|
@ -61,6 +61,8 @@ struct HashMapElement {
|
|||
data(p_key, p_value) {}
|
||||
};
|
||||
|
||||
bool _hashmap_variant_less_than(const Variant &p_left, const Variant &p_right);
|
||||
|
||||
template <typename TKey, typename TValue,
|
||||
typename Hasher = HashMapHasherDefault,
|
||||
typename Comparator = HashMapComparatorDefault<TKey>,
|
||||
|
|
@ -271,6 +273,47 @@ public:
|
|||
num_elements = 0;
|
||||
}
|
||||
|
||||
void sort() {
|
||||
if (elements == nullptr || num_elements < 2) {
|
||||
return; // An empty or single element HashMap is already sorted.
|
||||
}
|
||||
// Use insertion sort because we want this operation to be fast for the
|
||||
// common case where the input is already sorted or nearly sorted.
|
||||
HashMapElement<TKey, TValue> *inserting = head_element->next;
|
||||
while (inserting != nullptr) {
|
||||
HashMapElement<TKey, TValue> *after = nullptr;
|
||||
for (HashMapElement<TKey, TValue> *current = inserting->prev; current != nullptr; current = current->prev) {
|
||||
if (_hashmap_variant_less_than(inserting->data.key, current->data.key)) {
|
||||
after = current;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
HashMapElement<TKey, TValue> *next = inserting->next;
|
||||
if (after != nullptr) {
|
||||
// Modify the elements around `inserting` to remove it from its current position.
|
||||
inserting->prev->next = next;
|
||||
if (next == nullptr) {
|
||||
tail_element = inserting->prev;
|
||||
} else {
|
||||
next->prev = inserting->prev;
|
||||
}
|
||||
// Modify `before` and `after` to insert `inserting` between them.
|
||||
HashMapElement<TKey, TValue> *before = after->prev;
|
||||
if (before == nullptr) {
|
||||
head_element = inserting;
|
||||
} else {
|
||||
before->next = inserting;
|
||||
}
|
||||
after->prev = inserting;
|
||||
// Point `inserting` to its new surroundings.
|
||||
inserting->prev = before;
|
||||
inserting->next = after;
|
||||
}
|
||||
inserting = next;
|
||||
}
|
||||
}
|
||||
|
||||
TValue &get(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos);
|
||||
|
|
@ -597,6 +640,13 @@ public:
|
|||
capacity_index = MIN_CAPACITY_INDEX;
|
||||
}
|
||||
|
||||
HashMap(std::initializer_list<KeyValue<TKey, TValue>> p_init) {
|
||||
reserve(p_init.size());
|
||||
for (const KeyValue<TKey, TValue> &E : p_init) {
|
||||
insert(E.key, E.value);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t debug_get_hash(uint32_t p_index) {
|
||||
if (num_elements == 0) {
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -445,6 +445,13 @@ public:
|
|||
capacity_index = MIN_CAPACITY_INDEX;
|
||||
}
|
||||
|
||||
HashSet(std::initializer_list<TKey> p_init) {
|
||||
reserve(p_init.size());
|
||||
for (const TKey &E : p_init) {
|
||||
insert(E);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,17 @@
|
|||
#define HASHFUNCS_H
|
||||
|
||||
#include "core/math/aabb.h"
|
||||
#include "core/math/basis.h"
|
||||
#include "core/math/color.h"
|
||||
#include "core/math/math_defs.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/math/plane.h"
|
||||
#include "core/math/projection.h"
|
||||
#include "core/math/quaternion.h"
|
||||
#include "core/math/rect2.h"
|
||||
#include "core/math/rect2i.h"
|
||||
#include "core/math/transform_2d.h"
|
||||
#include "core/math/transform_3d.h"
|
||||
#include "core/math/vector2.h"
|
||||
#include "core/math/vector2i.h"
|
||||
#include "core/math/vector3.h"
|
||||
|
|
@ -103,6 +110,16 @@ static _FORCE_INLINE_ uint32_t hash_one_uint64(const uint64_t p_int) {
|
|||
return uint32_t(v);
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint64_t hash64_murmur3_64(uint64_t key, uint64_t seed) {
|
||||
key ^= seed;
|
||||
key ^= key >> 33;
|
||||
key *= 0xff51afd7ed558ccd;
|
||||
key ^= key >> 33;
|
||||
key *= 0xc4ceb9fe1a85ec53;
|
||||
key ^= key >> 33;
|
||||
return key;
|
||||
}
|
||||
|
||||
#define HASH_MURMUR3_SEED 0x7F07C65
|
||||
// Murmurhash3 32-bit version.
|
||||
// All MurmurHash versions are public domain software, and the author disclaims all copyright to their code.
|
||||
|
|
@ -306,9 +323,9 @@ struct HashMapHasherDefault {
|
|||
|
||||
static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(p_wchar); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(p_uchar); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(uint32_t(p_wchar)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(uint32_t(p_uchar)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(uint32_t(p_uchar)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.get_data()); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
|
||||
|
|
@ -316,31 +333,31 @@ struct HashMapHasherDefault {
|
|||
static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(uint64_t(p_int)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_murmur3_one_float(p_float); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_murmur3_one_double(p_double); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return hash_fmix32(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return hash_fmix32(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return hash_fmix32(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return hash_fmix32(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return hash_fmix32(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return hash_fmix32(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return hash_fmix32(uint32_t(p_int)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return hash_fmix32(uint32_t(p_int)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return hash_fmix32(uint32_t(p_int)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return hash_fmix32(uint32_t(p_int)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return hash_fmix32(uint32_t(p_int)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) {
|
||||
uint32_t h = hash_murmur3_one_32(p_vec.x);
|
||||
h = hash_murmur3_one_32(p_vec.y, h);
|
||||
uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
|
||||
h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
|
||||
return hash_fmix32(h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) {
|
||||
uint32_t h = hash_murmur3_one_32(p_vec.x);
|
||||
h = hash_murmur3_one_32(p_vec.y, h);
|
||||
h = hash_murmur3_one_32(p_vec.z, h);
|
||||
uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
|
||||
h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
|
||||
h = hash_murmur3_one_32(uint32_t(p_vec.z), h);
|
||||
return hash_fmix32(h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector4i &p_vec) {
|
||||
uint32_t h = hash_murmur3_one_32(p_vec.x);
|
||||
h = hash_murmur3_one_32(p_vec.y, h);
|
||||
h = hash_murmur3_one_32(p_vec.z, h);
|
||||
h = hash_murmur3_one_32(p_vec.w, h);
|
||||
uint32_t h = hash_murmur3_one_32(uint32_t(p_vec.x));
|
||||
h = hash_murmur3_one_32(uint32_t(p_vec.y), h);
|
||||
h = hash_murmur3_one_32(uint32_t(p_vec.z), h);
|
||||
h = hash_murmur3_one_32(uint32_t(p_vec.w), h);
|
||||
return hash_fmix32(h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
|
||||
|
|
@ -362,10 +379,10 @@ struct HashMapHasherDefault {
|
|||
return hash_fmix32(h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
|
||||
uint32_t h = hash_murmur3_one_32(p_rect.position.x);
|
||||
h = hash_murmur3_one_32(p_rect.position.y, h);
|
||||
h = hash_murmur3_one_32(p_rect.size.x, h);
|
||||
h = hash_murmur3_one_32(p_rect.size.y, h);
|
||||
uint32_t h = hash_murmur3_one_32(uint32_t(p_rect.position.x));
|
||||
h = hash_murmur3_one_32(uint32_t(p_rect.position.y), h);
|
||||
h = hash_murmur3_one_32(uint32_t(p_rect.size.x), h);
|
||||
h = hash_murmur3_one_32(uint32_t(p_rect.size.y), h);
|
||||
return hash_fmix32(h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) {
|
||||
|
|
@ -386,6 +403,13 @@ struct HashMapHasherDefault {
|
|||
}
|
||||
};
|
||||
|
||||
struct HashHasher {
|
||||
static _FORCE_INLINE_ uint32_t hash(const int32_t hash) { return hash; }
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint32_t hash) { return hash; }
|
||||
static _FORCE_INLINE_ uint64_t hash(const int64_t hash) { return hash; }
|
||||
static _FORCE_INLINE_ uint64_t hash(const uint64_t hash) { return hash; }
|
||||
};
|
||||
|
||||
// TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed
|
||||
template <typename T>
|
||||
struct HashableHasher {
|
||||
|
|
@ -413,6 +437,13 @@ struct HashMapComparatorDefault<double> {
|
|||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Color> {
|
||||
static bool compare(const Color &p_lhs, const Color &p_rhs) {
|
||||
return ((p_lhs.r == p_rhs.r) || (Math::is_nan(p_lhs.r) && Math::is_nan(p_rhs.r))) && ((p_lhs.g == p_rhs.g) || (Math::is_nan(p_lhs.g) && Math::is_nan(p_rhs.g))) && ((p_lhs.b == p_rhs.b) || (Math::is_nan(p_lhs.b) && Math::is_nan(p_rhs.b))) && ((p_lhs.a == p_rhs.a) || (Math::is_nan(p_lhs.a) && Math::is_nan(p_rhs.a)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Vector2> {
|
||||
static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) {
|
||||
|
|
@ -427,6 +458,87 @@ struct HashMapComparatorDefault<Vector3> {
|
|||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Vector4> {
|
||||
static bool compare(const Vector4 &p_lhs, const Vector4 &p_rhs) {
|
||||
return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Rect2> {
|
||||
static bool compare(const Rect2 &p_lhs, const Rect2 &p_rhs) {
|
||||
return HashMapComparatorDefault<Vector2>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector2>().compare(p_lhs.size, p_rhs.size);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<AABB> {
|
||||
static bool compare(const AABB &p_lhs, const AABB &p_rhs) {
|
||||
return HashMapComparatorDefault<Vector3>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector3>().compare(p_lhs.size, p_rhs.size);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Plane> {
|
||||
static bool compare(const Plane &p_lhs, const Plane &p_rhs) {
|
||||
return HashMapComparatorDefault<Vector3>().compare(p_lhs.normal, p_rhs.normal) && ((p_lhs.d == p_rhs.d) || (Math::is_nan(p_lhs.d) && Math::is_nan(p_rhs.d)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Transform2D> {
|
||||
static bool compare(const Transform2D &p_lhs, const Transform2D &p_rhs) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (!HashMapComparatorDefault<Vector2>().compare(p_lhs.columns[i], p_rhs.columns[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Basis> {
|
||||
static bool compare(const Basis &p_lhs, const Basis &p_rhs) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (!HashMapComparatorDefault<Vector3>().compare(p_lhs.rows[i], p_rhs.rows[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Transform3D> {
|
||||
static bool compare(const Transform3D &p_lhs, const Transform3D &p_rhs) {
|
||||
return HashMapComparatorDefault<Basis>().compare(p_lhs.basis, p_rhs.basis) && HashMapComparatorDefault<Vector3>().compare(p_lhs.origin, p_rhs.origin);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Projection> {
|
||||
static bool compare(const Projection &p_lhs, const Projection &p_rhs) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!HashMapComparatorDefault<Vector4>().compare(p_lhs.columns[i], p_rhs.columns[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<Quaternion> {
|
||||
static bool compare(const Quaternion &p_lhs, const Quaternion &p_rhs) {
|
||||
return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w)));
|
||||
}
|
||||
};
|
||||
|
||||
constexpr uint32_t HASH_TABLE_SIZE_MAX = 29;
|
||||
|
||||
inline constexpr uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
#include "core/os/memory.h"
|
||||
#include "core/templates/sort_array.h"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
/**
|
||||
* Generic Templatized Linked List Implementation.
|
||||
* The implementation differs from the STL one because
|
||||
|
|
@ -224,7 +226,7 @@ private:
|
|||
Element *last = nullptr;
|
||||
int size_cache = 0;
|
||||
|
||||
bool erase(const Element *p_I) {
|
||||
bool erase(Element *p_I) {
|
||||
ERR_FAIL_NULL_V(p_I, false);
|
||||
ERR_FAIL_COND_V(p_I->data != this, false);
|
||||
|
||||
|
|
@ -244,7 +246,7 @@ private:
|
|||
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
|
||||
}
|
||||
|
||||
memdelete_allocator<Element, A>(const_cast<Element *>(p_I));
|
||||
memdelete_allocator<Element, A>(p_I);
|
||||
size_cache--;
|
||||
|
||||
return true;
|
||||
|
|
@ -430,7 +432,7 @@ public:
|
|||
/**
|
||||
* erase an element in the list, by iterator pointing to it. Return true if it was found/erased.
|
||||
*/
|
||||
bool erase(const Element *p_I) {
|
||||
bool erase(Element *p_I) {
|
||||
if (_data && p_I) {
|
||||
bool ret = _data->erase(p_I);
|
||||
|
||||
|
|
@ -522,6 +524,15 @@ public:
|
|||
it = it->next();
|
||||
}
|
||||
}
|
||||
void operator=(List &&p_list) {
|
||||
if (unlikely(this == &p_list)) {
|
||||
return;
|
||||
}
|
||||
|
||||
clear();
|
||||
_data = p_list._data;
|
||||
p_list._data = nullptr;
|
||||
}
|
||||
|
||||
// Random access to elements, use with care,
|
||||
// do not use for iteration.
|
||||
|
|
@ -760,9 +771,19 @@ public:
|
|||
it = it->next();
|
||||
}
|
||||
}
|
||||
List(List &&p_list) {
|
||||
_data = p_list._data;
|
||||
p_list._data = nullptr;
|
||||
}
|
||||
|
||||
List() {}
|
||||
|
||||
List(std::initializer_list<T> p_init) {
|
||||
for (const T &E : p_init) {
|
||||
push_back(E);
|
||||
}
|
||||
}
|
||||
|
||||
~List() {
|
||||
clear();
|
||||
if (_data) {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ public:
|
|||
return data;
|
||||
}
|
||||
|
||||
// Must take a copy instead of a reference (see GH-31736).
|
||||
_FORCE_INLINE_ void push_back(T p_elem) {
|
||||
if (unlikely(count == capacity)) {
|
||||
capacity = tight ? (capacity + 1) : MAX((U)1, capacity << 1);
|
||||
|
|
@ -67,7 +68,7 @@ public:
|
|||
if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
|
||||
memnew_placement(&data[count++], T(p_elem));
|
||||
} else {
|
||||
data[count++] = p_elem;
|
||||
data[count++] = std::move(p_elem);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ public:
|
|||
ERR_FAIL_UNSIGNED_INDEX(p_index, count);
|
||||
count--;
|
||||
for (U i = p_index; i < count; i++) {
|
||||
data[i] = data[i + 1];
|
||||
data[i] = std::move(data[i + 1]);
|
||||
}
|
||||
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
|
||||
data[count].~T();
|
||||
|
|
@ -88,7 +89,7 @@ public:
|
|||
ERR_FAIL_INDEX(p_index, count);
|
||||
count--;
|
||||
if (count > p_index) {
|
||||
data[p_index] = data[count];
|
||||
data[p_index] = std::move(data[count]);
|
||||
}
|
||||
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
|
||||
data[count].~T();
|
||||
|
|
@ -245,13 +246,13 @@ public:
|
|||
void insert(U p_pos, T p_val) {
|
||||
ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1);
|
||||
if (p_pos == count) {
|
||||
push_back(p_val);
|
||||
push_back(std::move(p_val));
|
||||
} else {
|
||||
resize(count + 1);
|
||||
for (U i = count - 1; i > p_pos; i--) {
|
||||
data[i] = data[i - 1];
|
||||
data[i] = std::move(data[i - 1]);
|
||||
}
|
||||
data[p_pos] = p_val;
|
||||
data[p_pos] = std::move(p_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -295,9 +296,17 @@ public:
|
|||
|
||||
operator Vector<T>() const {
|
||||
Vector<T> ret;
|
||||
ret.resize(size());
|
||||
ret.resize(count);
|
||||
T *w = ret.ptrw();
|
||||
memcpy(w, data, sizeof(T) * count);
|
||||
if (w) {
|
||||
if constexpr (std::is_trivially_copyable_v<T>) {
|
||||
memcpy(w, data, sizeof(T) * count);
|
||||
} else {
|
||||
for (U i = 0; i < count; i++) {
|
||||
w[i] = data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +314,9 @@ public:
|
|||
Vector<uint8_t> ret;
|
||||
ret.resize(count * sizeof(T));
|
||||
uint8_t *w = ret.ptrw();
|
||||
memcpy(w, data, sizeof(T) * count);
|
||||
if (w) {
|
||||
memcpy(w, data, sizeof(T) * count);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -322,6 +333,16 @@ public:
|
|||
data[i] = p_from.data[i];
|
||||
}
|
||||
}
|
||||
_FORCE_INLINE_ LocalVector(LocalVector &&p_from) {
|
||||
data = p_from.data;
|
||||
count = p_from.count;
|
||||
capacity = p_from.capacity;
|
||||
|
||||
p_from.data = nullptr;
|
||||
p_from.count = 0;
|
||||
p_from.capacity = 0;
|
||||
}
|
||||
|
||||
inline void operator=(const LocalVector &p_from) {
|
||||
resize(p_from.size());
|
||||
for (U i = 0; i < p_from.count; i++) {
|
||||
|
|
@ -334,6 +355,26 @@ public:
|
|||
data[i] = p_from[i];
|
||||
}
|
||||
}
|
||||
inline void operator=(LocalVector &&p_from) {
|
||||
if (unlikely(this == &p_from)) {
|
||||
return;
|
||||
}
|
||||
reset();
|
||||
|
||||
data = p_from.data;
|
||||
count = p_from.count;
|
||||
capacity = p_from.capacity;
|
||||
|
||||
p_from.data = nullptr;
|
||||
p_from.count = 0;
|
||||
p_from.capacity = 0;
|
||||
}
|
||||
inline void operator=(Vector<T> &&p_from) {
|
||||
resize(p_from.size());
|
||||
for (U i = 0; i < count; i++) {
|
||||
data[i] = std::move(p_from[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ~LocalVector() {
|
||||
if (data) {
|
||||
|
|
|
|||
|
|
@ -35,9 +35,21 @@
|
|||
#include "hash_map.h"
|
||||
#include "list.h"
|
||||
|
||||
template <typename TKey, typename TData, typename Hasher = HashMapHasherDefault, typename Comparator = HashMapComparatorDefault<TKey>>
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#define ADDRESS_DIAGNOSTIC_WARNING_DISABLE \
|
||||
_Pragma("GCC diagnostic push"); \
|
||||
_Pragma("GCC diagnostic ignored \"-Waddress\"");
|
||||
|
||||
#define ADDRESS_DIAGNOSTIC_POP \
|
||||
_Pragma("GCC diagnostic pop");
|
||||
#else
|
||||
#define ADDRESS_DIAGNOSTIC_WARNING_DISABLE
|
||||
#define ADDRESS_DIAGNOSTIC_POP
|
||||
#endif
|
||||
|
||||
template <typename TKey, typename TData, typename Hasher = HashMapHasherDefault, typename Comparator = HashMapComparatorDefault<TKey>, void (*BeforeEvict)(TKey &, TData &) = nullptr>
|
||||
class LRUCache {
|
||||
private:
|
||||
public:
|
||||
struct Pair {
|
||||
TKey key;
|
||||
TData data;
|
||||
|
|
@ -51,16 +63,22 @@ private:
|
|||
|
||||
typedef typename List<Pair>::Element *Element;
|
||||
|
||||
private:
|
||||
List<Pair> _list;
|
||||
HashMap<TKey, Element, Hasher, Comparator> _map;
|
||||
size_t capacity;
|
||||
|
||||
public:
|
||||
const TData *insert(const TKey &p_key, const TData &p_value) {
|
||||
const Pair *insert(const TKey &p_key, const TData &p_value) {
|
||||
Element *e = _map.getptr(p_key);
|
||||
Element n = _list.push_front(Pair(p_key, p_value));
|
||||
|
||||
if (e) {
|
||||
ADDRESS_DIAGNOSTIC_WARNING_DISABLE;
|
||||
if constexpr (BeforeEvict != nullptr) {
|
||||
BeforeEvict((*e)->get().key, (*e)->get().data);
|
||||
}
|
||||
ADDRESS_DIAGNOSTIC_POP;
|
||||
_list.erase(*e);
|
||||
_map.erase(p_key);
|
||||
}
|
||||
|
|
@ -68,11 +86,16 @@ public:
|
|||
|
||||
while (_map.size() > capacity) {
|
||||
Element d = _list.back();
|
||||
ADDRESS_DIAGNOSTIC_WARNING_DISABLE
|
||||
if constexpr (BeforeEvict != nullptr) {
|
||||
BeforeEvict(d->get().key, d->get().data);
|
||||
}
|
||||
ADDRESS_DIAGNOSTIC_POP
|
||||
_map.erase(d->get().key);
|
||||
_list.pop_back();
|
||||
}
|
||||
|
||||
return &n->get().data;
|
||||
return &n->get();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
|
|
@ -84,12 +107,23 @@ public:
|
|||
return _map.getptr(p_key);
|
||||
}
|
||||
|
||||
bool erase(const TKey &p_key) {
|
||||
Element *e = _map.getptr(p_key);
|
||||
if (!e) {
|
||||
return false;
|
||||
}
|
||||
_list.move_to_front(*e);
|
||||
_map.erase(p_key);
|
||||
_list.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
const TData &get(const TKey &p_key) {
|
||||
Element *e = _map.getptr(p_key);
|
||||
CRASH_COND(!e);
|
||||
_list.move_to_front(*e);
|
||||
return (*e)->get().data;
|
||||
};
|
||||
}
|
||||
|
||||
const TData *getptr(const TKey &p_key) {
|
||||
Element *e = _map.getptr(p_key);
|
||||
|
|
@ -109,6 +143,11 @@ public:
|
|||
capacity = p_capacity;
|
||||
while (_map.size() > capacity) {
|
||||
Element d = _list.back();
|
||||
ADDRESS_DIAGNOSTIC_WARNING_DISABLE;
|
||||
if constexpr (BeforeEvict != nullptr) {
|
||||
BeforeEvict(d->get().key, d->get().data);
|
||||
}
|
||||
ADDRESS_DIAGNOSTIC_POP;
|
||||
_map.erase(d->get().key);
|
||||
_list.pop_back();
|
||||
}
|
||||
|
|
@ -124,4 +163,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
#undef ADDRESS_DIAGNOSTIC_WARNING_DISABLE
|
||||
#undef ADDRESS_DIAGNOSTIC_POP
|
||||
|
||||
#endif // LRU_H
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "core/math/math_funcs.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/templates/hashfuncs.h"
|
||||
#include "core/templates/pair.h"
|
||||
|
||||
/**
|
||||
* A HashMap implementation that uses open addressing with Robin Hood hashing.
|
||||
|
|
@ -353,6 +354,13 @@ public:
|
|||
return it;
|
||||
}
|
||||
|
||||
OAHashMap(std::initializer_list<KeyValue<TKey, TValue>> p_init) {
|
||||
reserve(p_init.size());
|
||||
for (const KeyValue<TKey, TValue> &E : p_init) {
|
||||
set(E.key, E.value);
|
||||
}
|
||||
}
|
||||
|
||||
OAHashMap(const OAHashMap &p_other) {
|
||||
(*this) = p_other;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class PagedAllocator {
|
|||
public:
|
||||
template <typename... Args>
|
||||
T *alloc(Args &&...p_args) {
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.lock();
|
||||
}
|
||||
if (unlikely(allocs_available == 0)) {
|
||||
|
|
@ -76,7 +76,7 @@ public:
|
|||
|
||||
allocs_available--;
|
||||
T *alloc = available_pool[allocs_available >> page_shift][allocs_available & page_mask];
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
memnew_placement(alloc, T(p_args...));
|
||||
|
|
@ -84,13 +84,13 @@ public:
|
|||
}
|
||||
|
||||
void free(T *p_mem) {
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.lock();
|
||||
}
|
||||
p_mem->~T();
|
||||
available_pool[allocs_available >> page_shift][allocs_available & page_mask] = p_mem;
|
||||
allocs_available++;
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
|
@ -120,28 +120,28 @@ private:
|
|||
|
||||
public:
|
||||
void reset(bool p_allow_unfreed = false) {
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.lock();
|
||||
}
|
||||
_reset(p_allow_unfreed);
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_configured() const {
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.lock();
|
||||
}
|
||||
bool result = page_size > 0;
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void configure(uint32_t p_page_size) {
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.lock();
|
||||
}
|
||||
ERR_FAIL_COND(page_pool != nullptr); // Safety check.
|
||||
|
|
@ -149,7 +149,7 @@ public:
|
|||
page_size = nearest_power_of_2_templated(p_page_size);
|
||||
page_mask = page_size - 1;
|
||||
page_shift = get_shift_from_power_of_2(page_size);
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +161,7 @@ public:
|
|||
}
|
||||
|
||||
~PagedAllocator() {
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.lock();
|
||||
}
|
||||
bool leaked = allocs_available < pages_allocated * page_size;
|
||||
|
|
@ -172,7 +172,7 @@ public:
|
|||
} else {
|
||||
_reset(false);
|
||||
}
|
||||
if (thread_safe) {
|
||||
if constexpr (thread_safe) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,70 +31,134 @@
|
|||
#ifndef PASS_FUNC_H
|
||||
#define PASS_FUNC_H
|
||||
|
||||
#define PASS0R(m_r, m_name) \
|
||||
m_r m_name() { return PASSBASE->m_name(); }
|
||||
#define PASS0RC(m_r, m_name) \
|
||||
m_r m_name() const { return PASSBASE->m_name(); }
|
||||
#define PASS1R(m_r, m_name, m_type1) \
|
||||
m_r m_name(m_type1 arg1) { return PASSBASE->m_name(arg1); }
|
||||
#define PASS1RC(m_r, m_name, m_type1) \
|
||||
m_r m_name(m_type1 arg1) const { return PASSBASE->m_name(arg1); }
|
||||
#define PASS0R(m_r, m_name) \
|
||||
m_r m_name() { \
|
||||
return PASSBASE->m_name(); \
|
||||
}
|
||||
#define PASS0RC(m_r, m_name) \
|
||||
m_r m_name() const { \
|
||||
return PASSBASE->m_name(); \
|
||||
}
|
||||
#define PASS1R(m_r, m_name, m_type1) \
|
||||
m_r m_name(m_type1 arg1) { \
|
||||
return PASSBASE->m_name(arg1); \
|
||||
}
|
||||
#define PASS1RC(m_r, m_name, m_type1) \
|
||||
m_r m_name(m_type1 arg1) const { \
|
||||
return PASSBASE->m_name(arg1); \
|
||||
}
|
||||
#define PASS2R(m_r, m_name, m_type1, m_type2) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2) { return PASSBASE->m_name(arg1, arg2); }
|
||||
#define PASS2RC(m_r, m_name, m_type1, m_type2) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2) const { return PASSBASE->m_name(arg1, arg2); }
|
||||
#define PASS3R(m_r, m_name, m_type1, m_type2, m_type3) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) { return PASSBASE->m_name(arg1, arg2, arg3); }
|
||||
#define PASS3RC(m_r, m_name, m_type1, m_type2, m_type3) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) const { return PASSBASE->m_name(arg1, arg2, arg3); }
|
||||
#define PASS4R(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) { return PASSBASE->m_name(arg1, arg2, arg3, arg4); }
|
||||
#define PASS4RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) const { return PASSBASE->m_name(arg1, arg2, arg3, arg4); }
|
||||
#define PASS5R(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) { return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); }
|
||||
#define PASS5RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) const { return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); }
|
||||
#define PASS6R(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) { return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); }
|
||||
#define PASS6RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) const { return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); }
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2) { \
|
||||
return PASSBASE->m_name(arg1, arg2); \
|
||||
}
|
||||
#define PASS2RC(m_r, m_name, m_type1, m_type2) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2) const { \
|
||||
return PASSBASE->m_name(arg1, arg2); \
|
||||
}
|
||||
#define PASS3R(m_r, m_name, m_type1, m_type2, m_type3) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) { \
|
||||
return PASSBASE->m_name(arg1, arg2, arg3); \
|
||||
}
|
||||
#define PASS3RC(m_r, m_name, m_type1, m_type2, m_type3) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) const { \
|
||||
return PASSBASE->m_name(arg1, arg2, arg3); \
|
||||
}
|
||||
#define PASS4R(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) { \
|
||||
return PASSBASE->m_name(arg1, arg2, arg3, arg4); \
|
||||
}
|
||||
#define PASS4RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) const { \
|
||||
return PASSBASE->m_name(arg1, arg2, arg3, arg4); \
|
||||
}
|
||||
#define PASS5R(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) { \
|
||||
return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); \
|
||||
}
|
||||
#define PASS5RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) const { \
|
||||
return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); \
|
||||
}
|
||||
#define PASS6R(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) { \
|
||||
return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); \
|
||||
}
|
||||
#define PASS6RC(m_r, m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \
|
||||
m_r m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) const { \
|
||||
return PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); \
|
||||
}
|
||||
|
||||
#define PASS0(m_name) \
|
||||
void m_name() { PASSBASE->m_name(); }
|
||||
#define PASS1(m_name, m_type1) \
|
||||
void m_name(m_type1 arg1) { PASSBASE->m_name(arg1); }
|
||||
#define PASS1C(m_name, m_type1) \
|
||||
void m_name(m_type1 arg1) const { PASSBASE->m_name(arg1); }
|
||||
#define PASS2(m_name, m_type1, m_type2) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2) { PASSBASE->m_name(arg1, arg2); }
|
||||
#define PASS2C(m_name, m_type1, m_type2) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2) const { PASSBASE->m_name(arg1, arg2); }
|
||||
#define PASS3(m_name, m_type1, m_type2, m_type3) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) { PASSBASE->m_name(arg1, arg2, arg3); }
|
||||
#define PASS4(m_name, m_type1, m_type2, m_type3, m_type4) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) { PASSBASE->m_name(arg1, arg2, arg3, arg4); }
|
||||
#define PASS5(m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); }
|
||||
#define PASS6(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); }
|
||||
#define PASS7(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7); }
|
||||
#define PASS8(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); }
|
||||
#define PASS9(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); }
|
||||
#define PASS10(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); }
|
||||
#define PASS11(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); }
|
||||
#define PASS12(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); }
|
||||
#define PASS13(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); }
|
||||
#define PASS14(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13, m_type14) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13, m_type14 arg14) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); }
|
||||
#define PASS15(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13, m_type14, m_type15) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13, m_type14 arg14, m_type15 arg15) { PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); }
|
||||
#define PASS0(m_name) \
|
||||
void m_name() { \
|
||||
PASSBASE->m_name(); \
|
||||
}
|
||||
#define PASS1(m_name, m_type1) \
|
||||
void m_name(m_type1 arg1) { \
|
||||
PASSBASE->m_name(arg1); \
|
||||
}
|
||||
#define PASS1C(m_name, m_type1) \
|
||||
void m_name(m_type1 arg1) const { \
|
||||
PASSBASE->m_name(arg1); \
|
||||
}
|
||||
#define PASS2(m_name, m_type1, m_type2) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2) { \
|
||||
PASSBASE->m_name(arg1, arg2); \
|
||||
}
|
||||
#define PASS2C(m_name, m_type1, m_type2) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2) const { \
|
||||
PASSBASE->m_name(arg1, arg2); \
|
||||
}
|
||||
#define PASS3(m_name, m_type1, m_type2, m_type3) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3); \
|
||||
}
|
||||
#define PASS4(m_name, m_type1, m_type2, m_type3, m_type4) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4); \
|
||||
}
|
||||
#define PASS5(m_name, m_type1, m_type2, m_type3, m_type4, m_type5) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5); \
|
||||
}
|
||||
#define PASS6(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6); \
|
||||
}
|
||||
#define PASS7(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7); \
|
||||
}
|
||||
#define PASS8(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); \
|
||||
}
|
||||
#define PASS9(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \
|
||||
}
|
||||
#define PASS10(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); \
|
||||
}
|
||||
#define PASS11(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); \
|
||||
}
|
||||
#define PASS12(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); \
|
||||
}
|
||||
#define PASS13(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); \
|
||||
}
|
||||
#define PASS14(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13, m_type14) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13, m_type14 arg14) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); \
|
||||
}
|
||||
#define PASS15(m_name, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6, m_type7, m_type8, m_type9, m_type10, m_type11, m_type12, m_type13, m_type14, m_type15) \
|
||||
void m_name(m_type1 arg1, m_type2 arg2, m_type3 arg3, m_type4 arg4, m_type5 arg5, m_type6 arg6, m_type7 arg7, m_type8 arg8, m_type9 arg9, m_type10 arg10, m_type11 arg11, m_type12 arg12, m_type13 arg13, m_type14 arg14, m_type15 arg15) { \
|
||||
PASSBASE->m_name(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); \
|
||||
}
|
||||
|
||||
#endif // PASS_FUNC_H
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
#include "core/os/memory.h"
|
||||
#include "core/templates/pair.h"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
// based on the very nice implementation of rb-trees by:
|
||||
// https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
|
||||
|
||||
|
|
@ -763,6 +765,12 @@ public:
|
|||
_copy_from(p_map);
|
||||
}
|
||||
|
||||
RBMap(std::initializer_list<KeyValue<K, V>> p_init) {
|
||||
for (const KeyValue<K, V> &E : p_init) {
|
||||
insert(E.key, E.value);
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ RBMap() {}
|
||||
|
||||
~RBMap() {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
#include "core/os/memory.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
// based on the very nice implementation of rb-trees by:
|
||||
// https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
|
||||
|
||||
|
|
@ -76,7 +78,7 @@ public:
|
|||
}
|
||||
const T &get() const {
|
||||
return value;
|
||||
};
|
||||
}
|
||||
Element() {}
|
||||
};
|
||||
|
||||
|
|
@ -701,6 +703,12 @@ public:
|
|||
_copy_from(p_set);
|
||||
}
|
||||
|
||||
RBSet(std::initializer_list<T> p_init) {
|
||||
for (const T &E : p_init) {
|
||||
insert(E);
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ RBSet() {}
|
||||
|
||||
~RBSet() {
|
||||
|
|
|
|||
|
|
@ -32,17 +32,49 @@
|
|||
#define RID_OWNER_H
|
||||
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/spin_lock.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/templates/oa_hash_map.h"
|
||||
#include "core/templates/rid.h"
|
||||
#include "core/templates/safe_refcount.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <typeinfo>
|
||||
|
||||
#ifdef SANITIZERS_ENABLED
|
||||
#ifdef __has_feature
|
||||
#if __has_feature(thread_sanitizer)
|
||||
#define TSAN_ENABLED
|
||||
#endif
|
||||
#elif defined(__SANITIZE_THREAD__)
|
||||
#define TSAN_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef TSAN_ENABLED
|
||||
#include <sanitizer/tsan_interface.h>
|
||||
#endif
|
||||
|
||||
// The following macros would need to be implemented somehow
|
||||
// for purely weakly ordered architectures. There's a test case
|
||||
// ("[RID_Owner] Thread safety") with potential to catch issues
|
||||
// on such architectures if these primitives fail to be implemented.
|
||||
// For now, they will be just markers about needs that may arise.
|
||||
#define WEAK_MEMORY_ORDER 0
|
||||
#if WEAK_MEMORY_ORDER
|
||||
// Ideally, we'd have implementations that collaborate with the
|
||||
// sync mechanism used (e.g., the mutex) so instead of some full
|
||||
// memory barriers being issued, some acquire-release on the
|
||||
// primitive itself. However, these implementations will at least
|
||||
// provide correctness.
|
||||
#define SYNC_ACQUIRE std::atomic_thread_fence(std::memory_order_acquire);
|
||||
#define SYNC_RELEASE std::atomic_thread_fence(std::memory_order_release);
|
||||
#else
|
||||
// Compiler barriers are enough in this case.
|
||||
#define SYNC_ACQUIRE std::atomic_signal_fence(std::memory_order_acquire);
|
||||
#define SYNC_RELEASE std::atomic_signal_fence(std::memory_order_release);
|
||||
#endif
|
||||
|
||||
class RID_AllocBase {
|
||||
static SafeNumeric<uint64_t> base_id;
|
||||
|
||||
|
|
@ -69,46 +101,63 @@ public:
|
|||
|
||||
template <typename T, bool THREAD_SAFE = false>
|
||||
class RID_Alloc : public RID_AllocBase {
|
||||
T **chunks = nullptr;
|
||||
struct Chunk {
|
||||
T data;
|
||||
uint32_t validator;
|
||||
};
|
||||
Chunk **chunks = nullptr;
|
||||
uint32_t **free_list_chunks = nullptr;
|
||||
uint32_t **validator_chunks = nullptr;
|
||||
|
||||
uint32_t elements_in_chunk;
|
||||
uint32_t max_alloc = 0;
|
||||
uint32_t alloc_count = 0;
|
||||
uint32_t chunk_limit = 0;
|
||||
|
||||
const char *description = nullptr;
|
||||
|
||||
mutable SpinLock spin_lock;
|
||||
mutable Mutex mutex;
|
||||
|
||||
_FORCE_INLINE_ RID _allocate_rid() {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.lock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
if (alloc_count == max_alloc) {
|
||||
//allocate a new chunk
|
||||
uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk);
|
||||
if (THREAD_SAFE && chunk_count == chunk_limit) {
|
||||
mutex.unlock();
|
||||
if (description != nullptr) {
|
||||
ERR_FAIL_V_MSG(RID(), vformat("Element limit for RID of type '%s' reached.", String(description)));
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(RID(), "Element limit reached.");
|
||||
}
|
||||
}
|
||||
|
||||
//grow chunks
|
||||
chunks = (T **)memrealloc(chunks, sizeof(T *) * (chunk_count + 1));
|
||||
chunks[chunk_count] = (T *)memalloc(sizeof(T) * elements_in_chunk); //but don't initialize
|
||||
|
||||
//grow validators
|
||||
validator_chunks = (uint32_t **)memrealloc(validator_chunks, sizeof(uint32_t *) * (chunk_count + 1));
|
||||
validator_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
|
||||
if constexpr (!THREAD_SAFE) {
|
||||
chunks = (Chunk **)memrealloc(chunks, sizeof(Chunk *) * (chunk_count + 1));
|
||||
}
|
||||
chunks[chunk_count] = (Chunk *)memalloc(sizeof(Chunk) * elements_in_chunk); //but don't initialize
|
||||
//grow free lists
|
||||
free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
|
||||
if constexpr (!THREAD_SAFE) {
|
||||
free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
|
||||
}
|
||||
free_list_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
|
||||
|
||||
//initialize
|
||||
for (uint32_t i = 0; i < elements_in_chunk; i++) {
|
||||
// Don't initialize chunk.
|
||||
validator_chunks[chunk_count][i] = 0xFFFFFFFF;
|
||||
chunks[chunk_count][i].validator = 0xFFFFFFFF;
|
||||
free_list_chunks[chunk_count][i] = alloc_count + i;
|
||||
}
|
||||
|
||||
max_alloc += elements_in_chunk;
|
||||
if constexpr (THREAD_SAFE) {
|
||||
// Store atomically to avoid data race with the load in get_or_null().
|
||||
((std::atomic<uint32_t> *)&max_alloc)->store(max_alloc + elements_in_chunk, std::memory_order_relaxed);
|
||||
} else {
|
||||
max_alloc += elements_in_chunk;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t free_index = free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk];
|
||||
|
|
@ -122,14 +171,13 @@ class RID_Alloc : public RID_AllocBase {
|
|||
id <<= 32;
|
||||
id |= free_index;
|
||||
|
||||
validator_chunks[free_chunk][free_element] = validator;
|
||||
|
||||
validator_chunks[free_chunk][free_element] |= 0x80000000; //mark uninitialized bit
|
||||
chunks[free_chunk][free_element].validator = validator;
|
||||
chunks[free_chunk][free_element].validator |= 0x80000000; //mark uninitialized bit
|
||||
|
||||
alloc_count++;
|
||||
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
return _make_from_id(id);
|
||||
|
|
@ -156,16 +204,20 @@ public:
|
|||
if (p_rid == RID()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.lock();
|
||||
|
||||
if constexpr (THREAD_SAFE) {
|
||||
SYNC_ACQUIRE;
|
||||
}
|
||||
|
||||
uint64_t id = p_rid.get_id();
|
||||
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
|
||||
if (unlikely(idx >= max_alloc)) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
uint32_t ma;
|
||||
if constexpr (THREAD_SAFE) { // Read atomically to avoid data race with the store in _allocate_rid().
|
||||
ma = ((std::atomic<uint32_t> *)&max_alloc)->load(std::memory_order_relaxed);
|
||||
} else {
|
||||
ma = max_alloc;
|
||||
}
|
||||
if (unlikely(idx >= ma)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -174,62 +226,101 @@ public:
|
|||
|
||||
uint32_t validator = uint32_t(id >> 32);
|
||||
|
||||
if constexpr (THREAD_SAFE) {
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_acquire(&chunks[idx_chunk]); // We know not a race in practice.
|
||||
__tsan_acquire(&chunks[idx_chunk][idx_element]); // We know not a race in practice.
|
||||
#endif
|
||||
}
|
||||
|
||||
Chunk &c = chunks[idx_chunk][idx_element];
|
||||
|
||||
if constexpr (THREAD_SAFE) {
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_release(&chunks[idx_chunk]);
|
||||
__tsan_release(&chunks[idx_chunk][idx_element]);
|
||||
__tsan_acquire(&c.validator); // We know not a race in practice.
|
||||
#endif
|
||||
}
|
||||
|
||||
if (unlikely(p_initialize)) {
|
||||
if (unlikely(!(validator_chunks[idx_chunk][idx_element] & 0x80000000))) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
if (unlikely(!(c.validator & 0x80000000))) {
|
||||
ERR_FAIL_V_MSG(nullptr, "Initializing already initialized RID");
|
||||
}
|
||||
|
||||
if (unlikely((validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) != validator)) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
if (unlikely((c.validator & 0x7FFFFFFF) != validator)) {
|
||||
ERR_FAIL_V_MSG(nullptr, "Attempting to initialize the wrong RID");
|
||||
}
|
||||
|
||||
validator_chunks[idx_chunk][idx_element] &= 0x7FFFFFFF; //initialized
|
||||
c.validator &= 0x7FFFFFFF; //initialized
|
||||
|
||||
} else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
}
|
||||
if ((validator_chunks[idx_chunk][idx_element] & 0x80000000) && validator_chunks[idx_chunk][idx_element] != 0xFFFFFFFF) {
|
||||
} else if (unlikely(c.validator != validator)) {
|
||||
if ((c.validator & 0x80000000) && c.validator != 0xFFFFFFFF) {
|
||||
ERR_FAIL_V_MSG(nullptr, "Attempting to use an uninitialized RID");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
T *ptr = &chunks[idx_chunk][idx_element];
|
||||
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_release(&c.validator);
|
||||
#endif
|
||||
}
|
||||
|
||||
T *ptr = &c.data;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
void initialize_rid(RID p_rid) {
|
||||
T *mem = get_or_null(p_rid, true);
|
||||
ERR_FAIL_NULL(mem);
|
||||
|
||||
if constexpr (THREAD_SAFE) {
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_acquire(mem); // We know not a race in practice.
|
||||
#endif
|
||||
}
|
||||
|
||||
memnew_placement(mem, T);
|
||||
|
||||
if constexpr (THREAD_SAFE) {
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_release(mem);
|
||||
#endif
|
||||
SYNC_RELEASE;
|
||||
}
|
||||
}
|
||||
|
||||
void initialize_rid(RID p_rid, const T &p_value) {
|
||||
T *mem = get_or_null(p_rid, true);
|
||||
ERR_FAIL_NULL(mem);
|
||||
|
||||
if constexpr (THREAD_SAFE) {
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_acquire(mem); // We know not a race in practice.
|
||||
#endif
|
||||
}
|
||||
|
||||
memnew_placement(mem, T(p_value));
|
||||
|
||||
if constexpr (THREAD_SAFE) {
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_release(mem);
|
||||
#endif
|
||||
SYNC_RELEASE;
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool owns(const RID &p_rid) const {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.lock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
uint64_t id = p_rid.get_id();
|
||||
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
|
||||
if (unlikely(idx >= max_alloc)) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -239,25 +330,25 @@ public:
|
|||
|
||||
uint32_t validator = uint32_t(id >> 32);
|
||||
|
||||
bool owned = (validator != 0x7FFFFFFF) && (validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) == validator;
|
||||
bool owned = (validator != 0x7FFFFFFF) && (chunks[idx_chunk][idx_element].validator & 0x7FFFFFFF) == validator;
|
||||
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
return owned;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void free(const RID &p_rid) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.lock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
uint64_t id = p_rid.get_id();
|
||||
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
|
||||
if (unlikely(idx >= max_alloc)) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
|
@ -266,26 +357,26 @@ public:
|
|||
uint32_t idx_element = idx % elements_in_chunk;
|
||||
|
||||
uint32_t validator = uint32_t(id >> 32);
|
||||
if (unlikely(validator_chunks[idx_chunk][idx_element] & 0x80000000)) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
if (unlikely(chunks[idx_chunk][idx_element].validator & 0x80000000)) {
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID.");
|
||||
} else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID");
|
||||
} else if (unlikely(chunks[idx_chunk][idx_element].validator != validator)) {
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
chunks[idx_chunk][idx_element].~T();
|
||||
validator_chunks[idx_chunk][idx_element] = 0xFFFFFFFF; // go invalid
|
||||
chunks[idx_chunk][idx_element].data.~T();
|
||||
chunks[idx_chunk][idx_element].validator = 0xFFFFFFFF; // go invalid
|
||||
|
||||
alloc_count--;
|
||||
free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx;
|
||||
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -293,58 +384,69 @@ public:
|
|||
return alloc_count;
|
||||
}
|
||||
void get_owned_list(List<RID> *p_owned) const {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.lock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.lock();
|
||||
}
|
||||
for (size_t i = 0; i < max_alloc; i++) {
|
||||
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
|
||||
uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
|
||||
if (validator != 0xFFFFFFFF) {
|
||||
p_owned->push_back(_make_from_id((validator << 32) | i));
|
||||
}
|
||||
}
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
//used for fast iteration in the elements or RIDs
|
||||
void fill_owned_buffer(RID *p_rid_buffer) const {
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.lock();
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.lock();
|
||||
}
|
||||
uint32_t idx = 0;
|
||||
for (size_t i = 0; i < max_alloc; i++) {
|
||||
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
|
||||
uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
|
||||
if (validator != 0xFFFFFFFF) {
|
||||
p_rid_buffer[idx] = _make_from_id((validator << 32) | i);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
if (THREAD_SAFE) {
|
||||
spin_lock.unlock();
|
||||
|
||||
if constexpr (THREAD_SAFE) {
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void set_description(const char *p_descrption) {
|
||||
description = p_descrption;
|
||||
void set_description(const char *p_description) {
|
||||
description = p_description;
|
||||
}
|
||||
|
||||
RID_Alloc(uint32_t p_target_chunk_byte_size = 65536) {
|
||||
RID_Alloc(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) {
|
||||
elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T));
|
||||
if constexpr (THREAD_SAFE) {
|
||||
chunk_limit = (p_maximum_number_of_elements / elements_in_chunk) + 1;
|
||||
chunks = (Chunk **)memalloc(sizeof(Chunk *) * chunk_limit);
|
||||
free_list_chunks = (uint32_t **)memalloc(sizeof(uint32_t *) * chunk_limit);
|
||||
SYNC_RELEASE;
|
||||
}
|
||||
}
|
||||
|
||||
~RID_Alloc() {
|
||||
if constexpr (THREAD_SAFE) {
|
||||
SYNC_ACQUIRE;
|
||||
}
|
||||
|
||||
if (alloc_count) {
|
||||
print_error(vformat("ERROR: %d RID allocations of type '%s' were leaked at exit.",
|
||||
alloc_count, description ? description : typeid(T).name()));
|
||||
|
||||
for (size_t i = 0; i < max_alloc; i++) {
|
||||
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
|
||||
uint32_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
|
||||
if (validator & 0x80000000) {
|
||||
continue; //uninitialized
|
||||
}
|
||||
if (validator != 0xFFFFFFFF) {
|
||||
chunks[i / elements_in_chunk][i % elements_in_chunk].~T();
|
||||
chunks[i / elements_in_chunk][i % elements_in_chunk].data.~T();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -352,14 +454,12 @@ public:
|
|||
uint32_t chunk_count = max_alloc / elements_in_chunk;
|
||||
for (uint32_t i = 0; i < chunk_count; i++) {
|
||||
memfree(chunks[i]);
|
||||
memfree(validator_chunks[i]);
|
||||
memfree(free_list_chunks[i]);
|
||||
}
|
||||
|
||||
if (chunks) {
|
||||
memfree(chunks);
|
||||
memfree(free_list_chunks);
|
||||
memfree(validator_chunks);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -415,12 +515,12 @@ public:
|
|||
alloc.fill_owned_buffer(p_rid_buffer);
|
||||
}
|
||||
|
||||
void set_description(const char *p_descrption) {
|
||||
alloc.set_description(p_descrption);
|
||||
void set_description(const char *p_description) {
|
||||
alloc.set_description(p_description);
|
||||
}
|
||||
|
||||
RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536) :
|
||||
alloc(p_target_chunk_byte_size) {}
|
||||
RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) :
|
||||
alloc(p_target_chunk_byte_size, p_maximum_number_of_elements) {}
|
||||
};
|
||||
|
||||
template <typename T, bool THREAD_SAFE = false>
|
||||
|
|
@ -470,11 +570,11 @@ public:
|
|||
alloc.fill_owned_buffer(p_rid_buffer);
|
||||
}
|
||||
|
||||
void set_description(const char *p_descrption) {
|
||||
alloc.set_description(p_descrption);
|
||||
void set_description(const char *p_description) {
|
||||
alloc.set_description(p_description);
|
||||
}
|
||||
RID_Owner(uint32_t p_target_chunk_byte_size = 65536) :
|
||||
alloc(p_target_chunk_byte_size) {}
|
||||
RID_Owner(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) :
|
||||
alloc(p_target_chunk_byte_size, p_maximum_number_of_elements) {}
|
||||
};
|
||||
|
||||
#endif // RID_OWNER_H
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
// Design goals for these classes:
|
||||
|
|
@ -226,6 +227,13 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ SafeList() {}
|
||||
_FORCE_INLINE_ SafeList(std::initializer_list<T> p_init) {
|
||||
for (const T &E : p_init) {
|
||||
insert(E);
|
||||
}
|
||||
}
|
||||
|
||||
~SafeList() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!maybe_cleanup()) {
|
||||
|
|
|
|||
|
|
@ -174,14 +174,14 @@ public:
|
|||
|
||||
while (true) {
|
||||
while (compare(p_array[p_first], p_pivot)) {
|
||||
if (Validate) {
|
||||
if constexpr (Validate) {
|
||||
ERR_BAD_COMPARE(p_first == unmodified_last - 1);
|
||||
}
|
||||
p_first++;
|
||||
}
|
||||
p_last--;
|
||||
while (compare(p_pivot, p_array[p_last])) {
|
||||
if (Validate) {
|
||||
if constexpr (Validate) {
|
||||
ERR_BAD_COMPARE(p_last == unmodified_first);
|
||||
}
|
||||
p_last--;
|
||||
|
|
@ -251,7 +251,7 @@ public:
|
|||
inline void unguarded_linear_insert(int64_t p_last, T p_value, T *p_array) const {
|
||||
int64_t next = p_last - 1;
|
||||
while (compare(p_value, p_array[next])) {
|
||||
if (Validate) {
|
||||
if constexpr (Validate) {
|
||||
ERR_BAD_COMPARE(next == 0);
|
||||
}
|
||||
p_array[p_last] = p_array[next];
|
||||
|
|
|
|||
121
engine/core/templates/tuple.h
Normal file
121
engine/core/templates/tuple.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/**************************************************************************/
|
||||
/* tuple.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TUPLE_H
|
||||
#define TUPLE_H
|
||||
|
||||
// Simple recursive Tuple type that has no runtime overhead.
|
||||
//
|
||||
// The compile-time recursion works as follows:
|
||||
// Assume the following: Tuple<int, float> my_tuple(42, 3.14f);
|
||||
// This expands to a class hierarchy that inherits from the previous step.
|
||||
// So in this case this leads to:
|
||||
// - struct Tuple<int> : Tuple<float> <--- This contains the int value.
|
||||
// - struct Tuple<float> <--- This contains the float value.
|
||||
// where each of the classes has a single field of the type for that step in the
|
||||
// recursion. So: float value; int value; etc.
|
||||
//
|
||||
// This works by splitting up the parameter pack for each step in the recursion minus the first.
|
||||
// so the the first step creates the "T value" from the first template parameter.
|
||||
// any further template arguments end up in "Rest", which we then use to instantiate a new
|
||||
// tuple, but now minus the first argument. To write this all out:
|
||||
//
|
||||
// Tuple<int, float>
|
||||
// step 1: Tuple T = int, Rest = float. Results in a Tuple<int> : Tuple<float>
|
||||
// step 2: Tuple T = float, no Rest. Results in a Tuple<float>
|
||||
//
|
||||
// tuple_get<I> works through a similar recursion, using the inheritance chain to walk to the right node.
|
||||
// In order to tuple_get<1>(my_tuple), from the example tuple above:
|
||||
//
|
||||
// 1. We want tuple_get<1> to return the float, which is one level "up" from Tuple<int> : Tuple<float>,
|
||||
// (the real type of the Tuple "root").
|
||||
// 2. Since index 1 > 0, it casts the tuple to its parent type (Tuple<float>). This works because
|
||||
// we cast to Tuple<Rest...> which in this case is just float.
|
||||
// 3. Now we're looking for index 0 in Tuple<float>, which directly returns its value field. Note
|
||||
// how get<0> is a template specialization.
|
||||
//
|
||||
// At compile time, this gets fully resolved. The compiler sees get<1>(my_tuple) and:
|
||||
// 1. Creates TupleGet<1, Tuple<int, float>>::tuple_get which contains the cast to Tuple<float>.
|
||||
// 2. Creates TupleGet<0, Tuple<float>>::tuple_get which directly returns the value.
|
||||
// 3. The compiler will then simply optimize all of this nonsense away and return the float directly.
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
template <typename... Types>
|
||||
struct Tuple;
|
||||
|
||||
template <>
|
||||
struct Tuple<> {};
|
||||
|
||||
template <typename T, typename... Rest>
|
||||
struct Tuple<T, Rest...> : Tuple<Rest...> {
|
||||
T value;
|
||||
|
||||
Tuple() = default;
|
||||
|
||||
template <typename F, typename... R>
|
||||
_FORCE_INLINE_ Tuple(F &&f, R &&...rest) :
|
||||
Tuple<Rest...>(std::forward<R>(rest)...),
|
||||
value(std::forward<F>(f)) {}
|
||||
};
|
||||
|
||||
template <size_t I, typename Tuple>
|
||||
struct TupleGet;
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
struct TupleGet<0, Tuple<First, Rest...>> {
|
||||
_FORCE_INLINE_ static First &tuple_get(Tuple<First, Rest...> &t) {
|
||||
return t.value;
|
||||
}
|
||||
};
|
||||
|
||||
// Rationale for using auto here is that the alternative is writing a
|
||||
// helper struct to create an otherwise useless type. we would have to write
|
||||
// a second recursive template chain like: TupleGetType<I, Tuple<First, Rest...>>::type
|
||||
// just to recover the type in the most baroque way possible.
|
||||
|
||||
template <size_t I, typename First, typename... Rest>
|
||||
struct TupleGet<I, Tuple<First, Rest...>> {
|
||||
_FORCE_INLINE_ static auto &tuple_get(Tuple<First, Rest...> &t) {
|
||||
return TupleGet<I - 1, Tuple<Rest...>>::tuple_get(static_cast<Tuple<Rest...> &>(t));
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t I, typename... Types>
|
||||
_FORCE_INLINE_ auto &tuple_get(Tuple<Types...> &t) {
|
||||
return TupleGet<I, Tuple<Types...>>::tuple_get(t);
|
||||
}
|
||||
|
||||
template <size_t I, typename... Types>
|
||||
_FORCE_INLINE_ const auto &tuple_get(const Tuple<Types...> &t) {
|
||||
return TupleGet<I, Tuple<Types...>>::tuple_get(t);
|
||||
}
|
||||
|
||||
#endif // TUPLE_H
|
||||
|
|
@ -33,7 +33,10 @@
|
|||
|
||||
/**
|
||||
* @class Vector
|
||||
* Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use Vector for large arrays.
|
||||
* Vector container. Simple copy-on-write container.
|
||||
*
|
||||
* LocalVector is an alternative available for internal use when COW is not
|
||||
* required.
|
||||
*/
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
|
|
@ -44,6 +47,7 @@
|
|||
|
||||
#include <climits>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
template <typename T>
|
||||
class VectorWriteProxy {
|
||||
|
|
@ -67,6 +71,7 @@ private:
|
|||
CowData<T> _cowdata;
|
||||
|
||||
public:
|
||||
// Must take a copy instead of a reference (see GH-31736).
|
||||
bool push_back(T p_elem);
|
||||
_FORCE_INLINE_ bool append(const T &p_elem) { return push_back(p_elem); } //alias
|
||||
void fill(T p_elem);
|
||||
|
|
@ -95,12 +100,14 @@ public:
|
|||
Error resize(Size p_size) { return _cowdata.resize(p_size); }
|
||||
Error resize_zeroed(Size p_size) { return _cowdata.template resize<true>(p_size); }
|
||||
_FORCE_INLINE_ const T &operator[](Size p_index) const { return _cowdata.get(p_index); }
|
||||
// Must take a copy instead of a reference (see GH-31736).
|
||||
Error insert(Size p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
|
||||
Size find(const T &p_val, Size p_from = 0) const { return _cowdata.find(p_val, p_from); }
|
||||
Size rfind(const T &p_val, Size p_from = -1) const { return _cowdata.rfind(p_val, p_from); }
|
||||
Size count(const T &p_val) const { return _cowdata.count(p_val); }
|
||||
|
||||
void append_array(const Vector<T> &p_other);
|
||||
// Must take a copy instead of a reference (see GH-31736).
|
||||
void append_array(Vector<T> p_other);
|
||||
|
||||
_FORCE_INLINE_ bool has(const T &p_val) const { return find(p_val) != -1; }
|
||||
|
||||
|
|
@ -144,17 +151,19 @@ public:
|
|||
insert(i, p_val);
|
||||
}
|
||||
|
||||
inline void operator=(const Vector &p_from) {
|
||||
_cowdata._ref(p_from._cowdata);
|
||||
}
|
||||
void operator=(const Vector &p_from) { _cowdata._ref(p_from._cowdata); }
|
||||
void operator=(Vector &&p_from) { _cowdata = std::move(p_from._cowdata); }
|
||||
|
||||
Vector<uint8_t> to_byte_array() const {
|
||||
Vector<uint8_t> ret;
|
||||
if (is_empty()) {
|
||||
return ret;
|
||||
}
|
||||
ret.resize(size() * sizeof(T));
|
||||
memcpy(ret.ptrw(), ptr(), sizeof(T) * size());
|
||||
size_t alloc_size = size() * sizeof(T);
|
||||
ret.resize(alloc_size);
|
||||
if (alloc_size) {
|
||||
memcpy(ret.ptrw(), ptr(), alloc_size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -277,16 +286,11 @@ public:
|
|||
}
|
||||
|
||||
_FORCE_INLINE_ Vector() {}
|
||||
_FORCE_INLINE_ Vector(std::initializer_list<T> p_init) {
|
||||
Error err = _cowdata.resize(p_init.size());
|
||||
ERR_FAIL_COND(err);
|
||||
|
||||
Size i = 0;
|
||||
for (const T &element : p_init) {
|
||||
_cowdata.set(i++, element);
|
||||
}
|
||||
}
|
||||
_FORCE_INLINE_ Vector(std::initializer_list<T> p_init) :
|
||||
_cowdata(p_init) {}
|
||||
_FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); }
|
||||
_FORCE_INLINE_ Vector(Vector &&p_from) :
|
||||
_cowdata(std::move(p_from._cowdata)) {}
|
||||
|
||||
_FORCE_INLINE_ ~Vector() {}
|
||||
};
|
||||
|
|
@ -300,7 +304,7 @@ void Vector<T>::reverse() {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void Vector<T>::append_array(const Vector<T> &p_other) {
|
||||
void Vector<T>::append_array(Vector<T> p_other) {
|
||||
const Size ds = p_other.size();
|
||||
if (ds == 0) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -194,6 +194,8 @@ public:
|
|||
}
|
||||
|
||||
_FORCE_INLINE_ VMap() {}
|
||||
_FORCE_INLINE_ VMap(std::initializer_list<T> p_init) :
|
||||
_cowdata(p_init) {}
|
||||
_FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); }
|
||||
|
||||
inline void operator=(const VMap &p_from) {
|
||||
|
|
|
|||
|
|
@ -137,6 +137,10 @@ public:
|
|||
inline const T &operator[](int p_index) const {
|
||||
return _data[p_index];
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ VSet() {}
|
||||
_FORCE_INLINE_ VSet(std::initializer_list<T> p_init) :
|
||||
_data(p_init) {}
|
||||
};
|
||||
|
||||
#endif // VSET_H
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue