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:
292
engine/include/XCEngine/Containers/Array.h
Normal file
292
engine/include/XCEngine/Containers/Array.h
Normal 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
|
||||
11
engine/include/XCEngine/Containers/Containers.h
Normal file
11
engine/include/XCEngine/Containers/Containers.h
Normal 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
|
||||
306
engine/include/XCEngine/Containers/HashMap.h
Normal file
306
engine/include/XCEngine/Containers/HashMap.h
Normal 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
|
||||
96
engine/include/XCEngine/Containers/String.h
Normal file
96
engine/include/XCEngine/Containers/String.h
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user