feat: 实现Containers、Memory、Threading核心模块及单元测试

- Containers: String, Array, HashMap 容器实现及测试
- Memory: Allocator, LinearAllocator, PoolAllocator, ProxyAllocator, MemoryManager 实现及测试
- Threading: Mutex, SpinLock, ReadWriteLock, Thread, Task, TaskSystem 实现及测试
- 修复Windows平台兼容性: _aligned_malloc, std::hash特化
- 修复构建错误和测试用例问题
This commit is contained in:
2026-03-13 20:37:08 +08:00
parent 508ee0bdc8
commit 34c75e7129
42 changed files with 3370 additions and 1 deletions

View File

@@ -0,0 +1,292 @@
#pragma once
#include <cstddef>
#include <initializer_list>
#include <algorithm>
#include <stdexcept>
#include "../Memory/Allocator.h"
namespace XCEngine {
namespace Containers {
template<typename T>
class Array {
public:
using Iterator = T*;
using ConstIterator = const T*;
Array() = default;
explicit Array(size_t capacity);
Array(size_t count, const T& value);
Array(std::initializer_list<T> init);
~Array();
Array(const Array& other);
Array(Array&& other) noexcept;
Array& operator=(const Array& other);
Array& operator=(Array&& other) noexcept;
T& operator[](size_t index);
const T& operator[](size_t index) const;
T* Data() { return m_data; }
const T* Data() const { return m_data; }
size_t Size() const { return m_size; }
size_t Capacity() const { return m_capacity; }
bool Empty() const { return m_size == 0; }
void Clear();
void Reserve(size_t capacity);
void Resize(size_t newSize);
void Resize(size_t newSize, const T& value);
void PushBack(const T& value);
void PushBack(T&& value);
template<typename... Args>
T& EmplaceBack(Args&&... args);
void PopBack();
T& Front() { return m_data[0]; }
const T& Front() const { return m_data[0]; }
T& Back() { return m_data[m_size - 1]; }
const T& Back() const { return m_data[m_size - 1]; }
Iterator begin() { return m_data; }
Iterator end() { return m_data + m_size; }
ConstIterator begin() const { return m_data; }
ConstIterator end() const { return m_data + m_size; }
void SetAllocator(Memory::IAllocator* allocator) { m_allocator = allocator; }
private:
T* m_data = nullptr;
size_t m_size = 0;
size_t m_capacity = 0;
Memory::IAllocator* m_allocator = nullptr;
void Reallocate(size_t newCapacity);
void DestructRange(T* begin, T* end);
void CopyRange(const T* src, T* dst, size_t count);
};
template<typename T>
Array<T>::Array(size_t capacity) : m_capacity(capacity) {
if (capacity > 0) {
m_data = static_cast<T*>(::operator new(capacity * sizeof(T)));
}
}
template<typename T>
Array<T>::Array(size_t count, const T& value) : m_size(count), m_capacity(count) {
if (count > 0) {
m_data = static_cast<T*>(::operator new(count * sizeof(T)));
for (size_t i = 0; i < count; ++i) {
new (&m_data[i]) T(value);
}
}
}
template<typename T>
Array<T>::Array(std::initializer_list<T> init) : m_size(init.size()), m_capacity(init.size()) {
if (m_size > 0) {
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
size_t i = 0;
for (const auto& item : init) {
new (&m_data[i++]) T(item);
}
}
}
template<typename T>
Array<T>::~Array() {
DestructRange(m_data, m_data + m_size);
::operator delete(m_data);
}
template<typename T>
Array<T>::Array(const Array& other) : m_size(other.m_size), m_capacity(other.m_size) {
if (m_size > 0) {
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
CopyRange(other.m_data, m_data, m_size);
}
}
template<typename T>
Array<T>::Array(Array&& other) noexcept
: m_data(other.m_data), m_size(other.m_size), m_capacity(other.m_capacity), m_allocator(other.m_allocator) {
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
}
template<typename T>
Array<T>& Array<T>::operator=(const Array& other) {
if (this != &other) {
DestructRange(m_data, m_data + m_size);
::operator delete(m_data);
m_size = other.m_size;
m_capacity = other.m_size;
m_allocator = other.m_allocator;
if (m_size > 0) {
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
CopyRange(other.m_data, m_data, m_size);
} else {
m_data = nullptr;
}
}
return *this;
}
template<typename T>
Array<T>& Array<T>::operator=(Array&& other) noexcept {
if (this != &other) {
DestructRange(m_data, m_data + m_size);
::operator delete(m_data);
m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
m_allocator = other.m_allocator;
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
}
return *this;
}
template<typename T>
T& Array<T>::operator[](size_t index) {
return m_data[index];
}
template<typename T>
const T& Array<T>::operator[](size_t index) const {
return m_data[index];
}
template<typename T>
void Array<T>::Clear() {
DestructRange(m_data, m_data + m_size);
m_size = 0;
}
template<typename T>
void Array<T>::Reserve(size_t capacity) {
if (capacity > m_capacity) {
Reallocate(capacity);
}
}
template<typename T>
void Array<T>::Resize(size_t newSize) {
if (newSize > m_capacity) {
Reallocate(newSize);
}
if (newSize > m_size) {
for (size_t i = m_size; i < newSize; ++i) {
new (&m_data[i]) T();
}
} else if (newSize < m_size) {
DestructRange(m_data + newSize, m_data + m_size);
}
m_size = newSize;
}
template<typename T>
void Array<T>::Resize(size_t newSize, const T& value) {
if (newSize > m_capacity) {
Reallocate(newSize);
}
if (newSize > m_size) {
for (size_t i = m_size; i < newSize; ++i) {
new (&m_data[i]) T(value);
}
} else if (newSize < m_size) {
DestructRange(m_data + newSize, m_data + m_size);
}
m_size = newSize;
}
template<typename T>
void Array<T>::PushBack(const T& value) {
if (m_size >= m_capacity) {
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
Reallocate(newCapacity);
}
new (&m_data[m_size]) T(value);
++m_size;
}
template<typename T>
void Array<T>::PushBack(T&& value) {
if (m_size >= m_capacity) {
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
Reallocate(newCapacity);
}
new (&m_data[m_size]) T(std::move(value));
++m_size;
}
template<typename T>
template<typename... Args>
T& Array<T>::EmplaceBack(Args&&... args) {
if (m_size >= m_capacity) {
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
Reallocate(newCapacity);
}
new (&m_data[m_size]) T(std::forward<Args>(args)...);
++m_size;
return m_data[m_size - 1];
}
template<typename T>
void Array<T>::PopBack() {
if (m_size > 0) {
--m_size;
m_data[m_size].~T();
}
}
template<typename T>
void Array<T>::Reallocate(size_t newCapacity) {
T* newData = nullptr;
if (newCapacity > 0) {
newData = static_cast<T*>(::operator new(newCapacity * sizeof(T)));
size_t count = std::min(m_size, newCapacity);
if (count > 0) {
CopyRange(m_data, newData, count);
}
}
DestructRange(m_data, m_data + m_size);
::operator delete(m_data);
m_data = newData;
m_capacity = newCapacity;
}
template<typename T>
void Array<T>::DestructRange(T* begin, T* end) {
for (T* p = begin; p < end; ++p) {
p->~T();
}
}
template<typename T>
void Array<T>::CopyRange(const T* src, T* dst, size_t count) {
for (size_t i = 0; i < count; ++i) {
new (&dst[i]) T(src[i]);
}
}
} // namespace Containers
} // namespace XCEngine

View File

@@ -0,0 +1,11 @@
#pragma once
#include "Containers/Array.h"
#include "Containers/String.h"
#include "Containers/HashMap.h"
namespace XCEngine {
namespace Containers {
} // namespace Containers
} // namespace XCEngine

View File

@@ -0,0 +1,306 @@
#pragma once
#include <cstddef>
#include <utility>
#include <functional>
#include "../Memory/Allocator.h"
#include "Array.h"
namespace XCEngine {
namespace Containers {
template<typename Key, typename Value>
class HashMap {
public:
struct Pair {
Key first;
Value second;
};
HashMap() {
m_buckets.Resize(DefaultBucketCount);
}
explicit HashMap(size_t bucketCount, Memory::IAllocator* allocator = nullptr);
~HashMap();
HashMap(const HashMap& other);
HashMap(HashMap&& other) noexcept;
HashMap& operator=(const HashMap& other);
HashMap& operator=(HashMap&& other) noexcept;
Value& operator[](const Key& key);
Value* Find(const Key& key);
const Value* Find(const Key& key) const;
bool Contains(const Key& key) const;
bool Insert(const Key& key, const Value& value);
bool Insert(const Key& key, Value&& value);
bool Insert(Pair&& pair);
bool Erase(const Key& key);
void Clear();
size_t Size() const { return m_size; }
bool Empty() const { return m_size == 0; }
void SetAllocator(Memory::IAllocator* allocator) { m_allocator = allocator; }
private:
struct Bucket {
Array<Pair> pairs;
};
static constexpr size_t DefaultBucketCount = 16;
Array<Bucket> m_buckets;
size_t m_bucketCount = DefaultBucketCount;
size_t m_size = 0;
float m_loadFactor = 0.75f;
Memory::IAllocator* m_allocator = nullptr;
size_t GetBucketIndex(const Key& key) const;
void Resize();
typename Array<Pair>::Iterator FindInBucket(Bucket& bucket, const Key& key);
typename Array<Pair>::ConstIterator FindInBucket(const Bucket& bucket, const Key& key) const;
};
template<typename Key, typename Value>
HashMap<Key, Value>::HashMap(size_t bucketCount, Memory::IAllocator* allocator)
: m_bucketCount(bucketCount), m_allocator(allocator) {
if (m_bucketCount == 0) {
m_bucketCount = 16;
}
m_buckets.Resize(m_bucketCount);
}
template<typename Key, typename Value>
HashMap<Key, Value>::~HashMap() {
Clear();
}
template<typename Key, typename Value>
HashMap<Key, Value>::HashMap(const HashMap& other)
: m_bucketCount(other.m_bucketCount), m_size(other.m_size), m_loadFactor(other.m_loadFactor), m_allocator(other.m_allocator) {
m_buckets.Resize(m_bucketCount);
for (size_t i = 0; i < m_bucketCount; ++i) {
m_buckets[i].pairs = other.m_buckets[i].pairs;
}
}
template<typename Key, typename Value>
HashMap<Key, Value>::HashMap(HashMap&& other) noexcept
: m_buckets(std::move(other.m_buckets)),
m_bucketCount(other.m_bucketCount),
m_size(other.m_size),
m_loadFactor(other.m_loadFactor),
m_allocator(other.m_allocator) {
other.m_buckets.Clear();
other.m_bucketCount = 0;
other.m_size = 0;
}
template<typename Key, typename Value>
HashMap<Key, Value>& HashMap<Key, Value>::operator=(const HashMap& other) {
if (this != &other) {
Clear();
m_bucketCount = other.m_bucketCount;
m_size = other.m_size;
m_loadFactor = other.m_loadFactor;
m_allocator = other.m_allocator;
m_buckets.Resize(m_bucketCount);
for (size_t i = 0; i < m_bucketCount; ++i) {
m_buckets[i].pairs = other.m_buckets[i].pairs;
}
}
return *this;
}
template<typename Key, typename Value>
HashMap<Key, Value>& HashMap<Key, Value>::operator=(HashMap&& other) noexcept {
if (this != &other) {
Clear();
m_buckets = std::move(other.m_buckets);
m_bucketCount = other.m_bucketCount;
m_size = other.m_size;
m_loadFactor = other.m_loadFactor;
m_allocator = other.m_allocator;
other.m_buckets.Clear();
other.m_bucketCount = 0;
other.m_size = 0;
}
return *this;
}
template<typename Key, typename Value>
Value& HashMap<Key, Value>::operator[](const Key& key) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
return it->second;
}
bucket.pairs.EmplaceBack(Pair{key, Value()});
++m_size;
if (static_cast<float>(m_size) / static_cast<float>(m_bucketCount) > m_loadFactor) {
Resize();
}
return bucket.pairs.Back().second;
}
template<typename Key, typename Value>
Value* HashMap<Key, Value>::Find(const Key& key) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
return &it->second;
}
return nullptr;
}
template<typename Key, typename Value>
const Value* HashMap<Key, Value>::Find(const Key& key) const {
size_t bucketIndex = GetBucketIndex(key);
const Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
return &it->second;
}
return nullptr;
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Contains(const Key& key) const {
size_t bucketIndex = GetBucketIndex(key);
const Bucket& bucket = m_buckets[bucketIndex];
return FindInBucket(bucket, key) != bucket.pairs.end();
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Insert(const Key& key, const Value& value) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
it->second = value;
return false;
}
bucket.pairs.EmplaceBack(Pair{key, value});
++m_size;
if (static_cast<float>(m_size) / static_cast<float>(m_bucketCount) > m_loadFactor) {
Resize();
}
return true;
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Insert(const Key& key, Value&& value) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
it->second = std::move(value);
return false;
}
bucket.pairs.EmplaceBack(Pair{key, std::move(value)});
++m_size;
if (static_cast<float>(m_size) / static_cast<float>(m_bucketCount) > m_loadFactor) {
Resize();
}
return true;
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Insert(Pair&& pair) {
return Insert(std::move(pair.first), std::move(pair.second));
}
template<typename Key, typename Value>
bool HashMap<Key, Value>::Erase(const Key& key) {
size_t bucketIndex = GetBucketIndex(key);
Bucket& bucket = m_buckets[bucketIndex];
auto it = FindInBucket(bucket, key);
if (it != bucket.pairs.end()) {
size_t index = it - bucket.pairs.begin();
bucket.pairs[index] = bucket.pairs.Back();
bucket.pairs.PopBack();
--m_size;
return true;
}
return false;
}
template<typename Key, typename Value>
void HashMap<Key, Value>::Clear() {
for (size_t i = 0; i < m_buckets.Size(); ++i) {
m_buckets[i].pairs.Clear();
}
m_size = 0;
}
template<typename Key, typename Value>
size_t HashMap<Key, Value>::GetBucketIndex(const Key& key) const {
if (m_bucketCount == 0) {
return 0;
}
std::hash<Key> hasher;
return hasher(key) % m_bucketCount;
}
template<typename Key, typename Value>
void HashMap<Key, Value>::Resize() {
size_t newBucketCount = m_bucketCount * 2;
Array<Bucket> newBuckets;
newBuckets.Resize(newBucketCount);
for (size_t i = 0; i < m_bucketCount; ++i) {
Bucket& oldBucket = m_buckets[i];
for (size_t j = 0; j < oldBucket.pairs.Size(); ++j) {
Pair& pair = oldBucket.pairs[j];
std::hash<Key> hasher;
size_t newIndex = hasher(pair.first) % newBucketCount;
newBuckets[newIndex].pairs.EmplaceBack(std::move(pair));
}
}
m_buckets = std::move(newBuckets);
m_bucketCount = newBucketCount;
}
template<typename Key, typename Value>
typename Array<typename HashMap<Key, Value>::Pair>::Iterator HashMap<Key, Value>::FindInBucket(Bucket& bucket, const Key& key) {
for (auto it = bucket.pairs.begin(); it != bucket.pairs.end(); ++it) {
if (it->first == key) {
return it;
}
}
return bucket.pairs.end();
}
template<typename Key, typename Value>
typename Array<typename HashMap<Key, Value>::Pair>::ConstIterator HashMap<Key, Value>::FindInBucket(const Bucket& bucket, const Key& key) const {
for (auto it = bucket.pairs.begin(); it != bucket.pairs.end(); ++it) {
if (it->first == key) {
return it;
}
}
return bucket.pairs.end();
}
} // namespace Containers
} // namespace XCEngine

View File

@@ -0,0 +1,96 @@
#pragma once
#include <cstring>
#include <algorithm>
#include <initializer_list>
namespace XCEngine {
namespace Containers {
class String {
public:
using SizeType = size_t;
static constexpr SizeType npos = static_cast<SizeType>(-1);
String();
String(const char* str);
String(const char* str, SizeType len);
String(const String& other);
String(String&& other) noexcept;
~String();
String& operator=(const String& other);
String& operator=(String&& other) noexcept;
String& operator=(const char* str);
String& operator+=(const String& other);
String& operator+=(const char* str);
String& operator+=(char c);
String Substring(SizeType pos, SizeType len = npos) const;
String Trim() const;
String ToLower() const;
String ToUpper() const;
SizeType Find(const char* str, SizeType pos = 0) const;
bool StartsWith(const String& prefix) const;
bool StartsWith(const char* prefix) const;
bool EndsWith(const String& suffix) const;
bool EndsWith(const char* suffix) const;
const char* CStr() const { return m_data; }
SizeType Length() const { return m_length; }
SizeType Capacity() const { return m_capacity; }
bool Empty() const { return m_length == 0; }
char& operator[](SizeType index) { return m_data[index]; }
const char& operator[](SizeType index) const { return m_data[index]; }
void Clear();
void Reserve(SizeType capacity);
void Resize(SizeType newSize);
void Resize(SizeType newSize, char fillChar);
private:
char* m_data = nullptr;
SizeType m_length = 0;
SizeType m_capacity = 0;
void Allocate(SizeType capacity);
void Deallocate();
void CopyFrom(const char* str, SizeType len);
void MoveFrom(String&& other) noexcept;
};
inline String operator+(const String& lhs, const String& rhs) {
String result(lhs);
result += rhs;
return result;
}
inline bool operator==(const String& lhs, const String& rhs) {
return lhs.Length() == rhs.Length() &&
std::strncmp(lhs.CStr(), rhs.CStr(), lhs.Length()) == 0;
}
inline bool operator!=(const String& lhs, const String& rhs) {
return !(lhs == rhs);
}
} // namespace Containers
} // namespace XCEngine
namespace std {
template<>
struct hash<XCEngine::Containers::String> {
size_t operator()(const XCEngine::Containers::String& str) const noexcept {
size_t hash = 5381;
const char* s = str.CStr();
while (*s) {
hash = ((hash << 5) + hash) + *s++;
}
return hash;
}
};
}