mirror of
https://github.com/zeldaret/botw
synced 2026-08-17 21:13:01 -04:00
Switch to subrepos
git subrepo clone https://github.com/open-ead/sead lib/sead subrepo: subdir: "lib/sead" merged: "1b66e825d" upstream: origin: "https://github.com/open-ead/sead" branch: "master" commit: "1b66e825d" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone (merge) https://github.com/open-ead/nnheaders lib/NintendoSDK subrepo: subdir: "lib/NintendoSDK" merged: "9ee21399f" upstream: origin: "https://github.com/open-ead/nnheaders" branch: "master" commit: "9ee21399f" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone https://github.com/open-ead/agl lib/agl subrepo: subdir: "lib/agl" merged: "7c063271b" upstream: origin: "https://github.com/open-ead/agl" branch: "master" commit: "7c063271b" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone https://github.com/open-ead/EventFlow lib/EventFlow subrepo: subdir: "lib/EventFlow" merged: "c35d21b34" upstream: origin: "https://github.com/open-ead/EventFlow" branch: "master" commit: "c35d21b34" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596"
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <ore/Types.h>
|
||||
|
||||
namespace ore {
|
||||
|
||||
class Allocator {
|
||||
public:
|
||||
Allocator() = default;
|
||||
virtual ~Allocator() = default;
|
||||
|
||||
void* New(size_t size, size_t alignment = alignof(std::max_align_t)) {
|
||||
return AllocImpl(size, alignment);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* New(size_t alignment = alignof(std::max_align_t)) {
|
||||
auto* buffer = AllocImpl(sizeof(T), alignment);
|
||||
if (buffer)
|
||||
return new (buffer) T;
|
||||
return static_cast<T*>(buffer);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Delete(T* ptr) {
|
||||
std::destroy_at(ptr);
|
||||
Free(ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DeleteAndNull(T*& ptr) {
|
||||
std::destroy_at(ptr);
|
||||
Free(ptr);
|
||||
ptr = nullptr;
|
||||
}
|
||||
|
||||
void Free(void* ptr) { FreeImpl(ptr); }
|
||||
|
||||
virtual void* AllocImpl(size_t size, size_t alignment) = 0;
|
||||
virtual void FreeImpl(void* ptr) = 0;
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,287 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <ore/Allocator.h>
|
||||
#include <ore/Buffer.h>
|
||||
#include <ore/IterRange.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ore {
|
||||
|
||||
// This is like a std::span, not a fixed-size array like std::array.
|
||||
// Elements will NOT be automatically freed.
|
||||
template <typename T>
|
||||
class Array {
|
||||
public:
|
||||
Array() = default;
|
||||
Array(T* data, int size) : m_data(data), m_size(size) {}
|
||||
|
||||
T* data() const { return m_data; }
|
||||
int size() const { return m_size; }
|
||||
|
||||
auto begin() const { return data(); }
|
||||
auto end() const { return data() + size(); }
|
||||
|
||||
T& operator[](int idx) { return m_data[idx]; }
|
||||
const T& operator[](int idx) const { return m_data[idx]; }
|
||||
|
||||
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]; }
|
||||
|
||||
void SetBuffer(void* new_buffer, int num) {
|
||||
DestructElements();
|
||||
m_data = static_cast<T*>(new_buffer);
|
||||
m_size = num;
|
||||
}
|
||||
|
||||
void SetBuffer(int num, Allocator* allocator) {
|
||||
auto* new_buffer = allocator->AllocImpl(num * int(sizeof(T)), alignof(std::max_align_t));
|
||||
SetBuffer(new_buffer, num);
|
||||
}
|
||||
|
||||
void ConstructElements(int num, Allocator* allocator) {
|
||||
SetBuffer(num, allocator);
|
||||
DefaultConstructElements();
|
||||
}
|
||||
|
||||
void ConstructElements(void* new_buffer, int num) {
|
||||
SetBuffer(new_buffer, num);
|
||||
DefaultConstructElements();
|
||||
}
|
||||
|
||||
void ConstructElements(Buffer buffer) {
|
||||
DestructElements();
|
||||
m_data = reinterpret_cast<T*>(buffer.data);
|
||||
m_size = buffer.size / int(sizeof(T));
|
||||
DefaultConstructElements();
|
||||
}
|
||||
|
||||
void DestructElements() { std::destroy(begin(), end()); }
|
||||
|
||||
void ClearWithoutFreeing() {
|
||||
DestructElements();
|
||||
m_data = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void Clear(Allocator* allocator) {
|
||||
if (!m_data)
|
||||
return;
|
||||
auto* data = m_data;
|
||||
ClearWithoutFreeing();
|
||||
allocator->Free(data);
|
||||
}
|
||||
|
||||
void DefaultConstructElements() {
|
||||
for (auto it = begin(), e = end(); it != e;)
|
||||
new (it++) T;
|
||||
}
|
||||
|
||||
void UninitializedDefaultConstructElements() {
|
||||
std::uninitialized_default_construct(begin(), end());
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_data{};
|
||||
int m_size{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class SelfDestructingArray : public Array<T> {
|
||||
public:
|
||||
~SelfDestructingArray() { this->DestructElements(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ArrayListBase {
|
||||
public:
|
||||
ArrayListBase() : m_data(), m_size(), m_capacity() {}
|
||||
ArrayListBase(T* data, int capacity) {
|
||||
m_size = 0;
|
||||
m_data = data;
|
||||
m_capacity = capacity;
|
||||
}
|
||||
~ArrayListBase() { clear(); }
|
||||
|
||||
ArrayListBase(const ArrayListBase&) = delete;
|
||||
auto operator=(const ArrayListBase&) = delete;
|
||||
|
||||
T* begin() { return m_data; }
|
||||
const T* begin() const { return m_data; }
|
||||
|
||||
T* end() { return m_data + m_size; }
|
||||
const T* end() const { return m_data + m_size; }
|
||||
|
||||
T* data() { return m_data; }
|
||||
const T* data() const { return m_data; }
|
||||
|
||||
int size() const { return m_size; }
|
||||
int capacity() const { return m_capacity; }
|
||||
|
||||
T& operator[](int idx) { return m_data[idx]; }
|
||||
const T& operator[](int idx) const { return m_data[idx]; }
|
||||
|
||||
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]; }
|
||||
|
||||
template <typename... Args>
|
||||
T& emplace_back(Args&&... args) {
|
||||
auto* item = new (&m_data[m_size++]) T(std::forward<Args>(args)...);
|
||||
return *item;
|
||||
}
|
||||
|
||||
void push_back(const T& item) { new (&m_data[m_size++]) T(item); }
|
||||
|
||||
void pop_back() {
|
||||
std::destroy_at(&back());
|
||||
--m_size;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::destroy(begin(), end());
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
T* m_data;
|
||||
int m_size;
|
||||
int m_capacity;
|
||||
};
|
||||
|
||||
template <typename T, int N>
|
||||
class FixedArrayList : public ArrayListBase<T> {
|
||||
public:
|
||||
FixedArrayList() : ArrayListBase<T>(reinterpret_cast<T*>(m_storage), N) {}
|
||||
|
||||
private:
|
||||
std::aligned_storage_t<sizeof(T), alignof(T)> m_storage[N];
|
||||
};
|
||||
|
||||
// This is like a std::vector.
|
||||
template <typename T>
|
||||
class DynArrayList : public ArrayListBase<T> {
|
||||
public:
|
||||
DynArrayList() = default;
|
||||
explicit DynArrayList(Allocator* allocator) : m_allocator(allocator) {}
|
||||
|
||||
~DynArrayList() {
|
||||
clear();
|
||||
m_allocator = nullptr;
|
||||
this->m_size = 0;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
clear();
|
||||
m_allocator = nullptr;
|
||||
}
|
||||
|
||||
DynArrayList(const DynArrayList&) = delete;
|
||||
auto operator=(const DynArrayList&) = delete;
|
||||
|
||||
void Init(Allocator* allocator, int initial_capacity = 1) {
|
||||
clear();
|
||||
m_allocator = allocator;
|
||||
Reallocate(initial_capacity);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
T& emplace_back(Args&&... args) {
|
||||
GrowIfNeeded();
|
||||
return ArrayListBase<T>::emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void push_back(const T& item) {
|
||||
GrowIfNeeded();
|
||||
return ArrayListBase<T>::push_back(item);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::destroy(this->begin(), this->end());
|
||||
auto* data = this->m_data;
|
||||
this->m_data = nullptr;
|
||||
this->m_size = 0;
|
||||
this->m_capacity = 0;
|
||||
Free(data);
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
void OverwriteWith(InputIterator src_begin, InputIterator src_end) {
|
||||
const int src_size = std::distance(src_begin, src_end);
|
||||
if (src_size > this->m_capacity) {
|
||||
this->m_size = 0;
|
||||
Reallocate(2 * src_size);
|
||||
}
|
||||
this->m_size = src_size;
|
||||
std::uninitialized_copy(src_begin, src_end, this->begin());
|
||||
}
|
||||
|
||||
/// Quadratic complexity; only use this for small copies.
|
||||
template <typename Range>
|
||||
void DeduplicateCopy(const Range& range) {
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
|
||||
auto value = *it;
|
||||
if (std::find_if(range.begin(), it, [&](const auto& v) { return value == v; }) == it)
|
||||
this->emplace_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resize the array so that it contains `new_size` elements.
|
||||
///
|
||||
/// - If the new size is greater than the current size, new elements are added and
|
||||
/// default initialized. Iterators may be invalidated.
|
||||
/// - If the new size is less than the current size, excess elements are destroyed.
|
||||
///
|
||||
/// @param new_size The new size of the array.
|
||||
void Resize(int new_size) {
|
||||
if (this->m_capacity < new_size)
|
||||
Reallocate(new_size);
|
||||
|
||||
if (this->m_size < new_size) {
|
||||
std::uninitialized_default_construct(this->m_data + this->m_size,
|
||||
this->m_data + new_size);
|
||||
} else {
|
||||
std::destroy(this->m_data + new_size, this->m_data + this->m_size);
|
||||
}
|
||||
|
||||
this->m_size = new_size;
|
||||
}
|
||||
|
||||
private:
|
||||
void GrowIfNeeded() {
|
||||
if (this->m_size < this->m_capacity)
|
||||
return;
|
||||
Reallocate(2 * this->m_size + 2);
|
||||
}
|
||||
|
||||
void Reallocate(int new_capacity) {
|
||||
const int num_bytes = sizeof(T) * new_capacity;
|
||||
auto* new_buffer =
|
||||
static_cast<T*>(m_allocator->AllocImpl(num_bytes, alignof(std::max_align_t)));
|
||||
auto* old_buffer = this->m_data;
|
||||
auto* capacity = &this->m_capacity;
|
||||
UninitializedCopyTo(new_buffer);
|
||||
this->m_data = new_buffer;
|
||||
*capacity = new_capacity;
|
||||
Free(old_buffer);
|
||||
}
|
||||
|
||||
void UninitializedCopyTo(T* destination) const {
|
||||
std::uninitialized_copy(this->begin(), this->end(), destination);
|
||||
}
|
||||
|
||||
void Free(void* ptr) {
|
||||
if (ptr)
|
||||
m_allocator->Free(ptr);
|
||||
}
|
||||
|
||||
Allocator* m_allocator{};
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include <ore/StringView.h>
|
||||
#include <ore/Types.h>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace ore {
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignUpToPowerOf2(T val, int base) {
|
||||
return val + base - 1 & static_cast<unsigned int>(-base);
|
||||
}
|
||||
|
||||
struct RelocationTable;
|
||||
|
||||
struct BinaryBlockHeader {
|
||||
BinaryBlockHeader* FindNextBlock(int type);
|
||||
const BinaryBlockHeader* FindNextBlock(int type) const;
|
||||
BinaryBlockHeader* GetNextBlock();
|
||||
const BinaryBlockHeader* GetNextBlock() const;
|
||||
void SetNextBlock(BinaryBlockHeader* block);
|
||||
|
||||
u32 magic;
|
||||
int next_block_offset;
|
||||
};
|
||||
|
||||
struct BinaryFileHeader {
|
||||
bool IsValid(s64 magic_, int ver_major_, int ver_minor_, int ver_patch_, int ver_sub_) const;
|
||||
bool IsSignatureValid(s64 magic_) const;
|
||||
bool IsVersionValid(int major, int minor, int patch, int sub) const;
|
||||
bool IsEndianReverse() const;
|
||||
bool IsEndianValid() const;
|
||||
|
||||
bool IsAlignmentValid() const;
|
||||
int GetAlignment() const;
|
||||
void SetAlignment(int alignment_);
|
||||
|
||||
bool IsRelocated() const;
|
||||
void SetRelocated(bool relocated);
|
||||
|
||||
void SetByteOrderMark();
|
||||
|
||||
int GetFileSize() const;
|
||||
void SetFileSize(int size);
|
||||
|
||||
StringView GetFileName() const;
|
||||
void SetFileName(const StringView& name);
|
||||
|
||||
RelocationTable* GetRelocationTable();
|
||||
void SetRelocationTable(RelocationTable* table);
|
||||
|
||||
BinaryBlockHeader* GetFirstBlock();
|
||||
const BinaryBlockHeader* GetFirstBlock() const;
|
||||
void SetFirstBlock(BinaryBlockHeader* block);
|
||||
|
||||
BinaryBlockHeader* FindFirstBlock(int type);
|
||||
const BinaryBlockHeader* FindFirstBlock(int type) const;
|
||||
|
||||
u64 magic;
|
||||
u8 ver_major;
|
||||
u8 ver_minor;
|
||||
u8 ver_patch;
|
||||
u8 ver_sub;
|
||||
s16 bom;
|
||||
u8 alignment;
|
||||
u8 _f;
|
||||
int file_name_offset;
|
||||
u16 relocation_flags;
|
||||
u16 first_block_offset;
|
||||
int relocation_table_offset;
|
||||
int file_size;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BinTString {
|
||||
// Make it impossible to accidentally construct a (partial, broken) copy.
|
||||
BinTString(const BinTString&) = delete;
|
||||
auto operator=(const BinTString&) = delete;
|
||||
|
||||
T* data() { return chars; }
|
||||
const T* data() const { return chars; }
|
||||
|
||||
T& operator[](size_t idx) { return data()[idx]; }
|
||||
const T& operator[](size_t idx) const { return data()[idx]; }
|
||||
|
||||
auto begin() { return data(); }
|
||||
auto begin() const { return data(); }
|
||||
|
||||
auto end() { return data() + length; }
|
||||
auto end() const { return data() + length; }
|
||||
|
||||
bool empty() const { return length == 0; }
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator TStringView<T>() const { return {data(), length}; }
|
||||
|
||||
BinTString* NextString() { return const_cast<BinTString*>(std::as_const(*this).NextString()); }
|
||||
|
||||
const BinTString* NextString() const {
|
||||
// XXX: this shouldn't have to be a separate case.
|
||||
if constexpr (std::is_same_v<T, wchar_t>) {
|
||||
const auto offset = ((2 + (4 * (length + 1) - 1)) & -4) + 2;
|
||||
return reinterpret_cast<const BinTString*>(reinterpret_cast<const char*>(this) +
|
||||
offset);
|
||||
|
||||
} else {
|
||||
// + 1 for the null terminator
|
||||
const auto offset = offsetof(BinTString, chars) + sizeof(T) * (length + 1);
|
||||
return reinterpret_cast<const BinTString*>(
|
||||
reinterpret_cast<const char*>(this) +
|
||||
AlignUpToPowerOf2(offset, alignof(BinTString)));
|
||||
}
|
||||
}
|
||||
|
||||
u16 length;
|
||||
T chars[1];
|
||||
};
|
||||
|
||||
using BinString = BinTString<char>;
|
||||
using BinWString = BinTString<wchar_t>;
|
||||
|
||||
template <typename T>
|
||||
struct BinTPtr {
|
||||
void Clear() { offset_or_ptr = 0; }
|
||||
void Set(T* ptr) { offset_or_ptr = reinterpret_cast<u64>(ptr); }
|
||||
|
||||
// Only use this after relocation.
|
||||
T* Get() { return reinterpret_cast<T*>(offset_or_ptr); }
|
||||
const T* Get() const { return reinterpret_cast<const T*>(offset_or_ptr); }
|
||||
|
||||
void SetOffset(void* base, void* ptr) {
|
||||
offset_or_ptr = static_cast<int>(ptr ? uintptr_t(ptr) - uintptr_t(base) : 0);
|
||||
}
|
||||
|
||||
u64 GetOffset() const { return offset_or_ptr; }
|
||||
|
||||
T* ToPtr(void* base) const {
|
||||
const auto offset = static_cast<int>(offset_or_ptr);
|
||||
if (offset == 0)
|
||||
return nullptr;
|
||||
return reinterpret_cast<T*>(reinterpret_cast<char*>(base) + offset);
|
||||
}
|
||||
|
||||
void Relocate(void* base) { Set(ToPtr(base)); }
|
||||
void Unrelocate(void* base) { SetOffset(base, Get()); }
|
||||
|
||||
u64 offset_or_ptr;
|
||||
};
|
||||
|
||||
static_assert(sizeof(u64) >= sizeof(void*));
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include <ore/Allocator.h>
|
||||
#include <ore/Types.h>
|
||||
|
||||
namespace ore {
|
||||
|
||||
constexpr int PopCount(u32 x) {
|
||||
x = x - ((x >> 1) & 0x55555555);
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
x = (x + (x >> 4)) & 0x0F0F0F0F;
|
||||
x += (x >> 8);
|
||||
x += (x >> 16);
|
||||
return int(x & 0x3f);
|
||||
}
|
||||
|
||||
constexpr int PopCount(u64 x) {
|
||||
x = x - ((x >> 1) & 0x5555555555555555);
|
||||
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333);
|
||||
x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F;
|
||||
x += (x >> 8);
|
||||
x += (x >> 16);
|
||||
x += (x >> 32);
|
||||
return int(x & 0x7f);
|
||||
}
|
||||
|
||||
constexpr int CountTrailingZeros(u32 x) {
|
||||
return PopCount((x & -x) - 1);
|
||||
}
|
||||
|
||||
constexpr int CountTrailingZeros(u64 x) {
|
||||
return PopCount((x & -x) - 1);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
constexpr T AlignUpToPowerOf2(T val, int base) {
|
||||
return val + base - 1 & static_cast<unsigned int>(-base);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
class BitArray {
|
||||
public:
|
||||
using Word = size_t;
|
||||
static constexpr int NumBitsPerWord = sizeof(Word) * 8;
|
||||
static constexpr int ClearMask = ~(NumBitsPerWord - 1);
|
||||
static constexpr int ShiftAmount = CountTrailingZeros(u32(NumBitsPerWord));
|
||||
|
||||
class TestIter {
|
||||
public:
|
||||
TestIter(const Word* start, const Word* end);
|
||||
TestIter& operator++();
|
||||
|
||||
int operator*() const { return m_bit; }
|
||||
bool operator==(const TestIter& other) const { return m_bit == other.m_bit; }
|
||||
bool operator!=(const TestIter& other) const { return !operator==(other); }
|
||||
|
||||
private:
|
||||
void SetInvalid() {
|
||||
m_bit = -1;
|
||||
m_current_word = nullptr;
|
||||
m_last_word = nullptr;
|
||||
m_next = 0;
|
||||
}
|
||||
|
||||
int m_bit;
|
||||
const Word* m_current_word;
|
||||
const Word* m_last_word;
|
||||
Word m_next;
|
||||
};
|
||||
|
||||
/// Same as TestIter but clears bits after iterating over them.
|
||||
class TestClearIter {
|
||||
public:
|
||||
TestClearIter(Word* start, Word* end);
|
||||
TestClearIter& operator++();
|
||||
int operator*() const { return m_bit; }
|
||||
bool operator==(const TestClearIter& other) const { return m_bit == other.m_bit; }
|
||||
bool operator!=(const TestClearIter& other) const { return !operator==(other); }
|
||||
|
||||
private:
|
||||
void SetInvalid() {
|
||||
m_bit = -1;
|
||||
m_current_word = nullptr;
|
||||
m_last_word = nullptr;
|
||||
m_next = 0;
|
||||
}
|
||||
|
||||
int m_bit;
|
||||
Word* m_current_word;
|
||||
Word* m_last_word;
|
||||
Word m_next;
|
||||
};
|
||||
|
||||
constexpr BitArray() = default;
|
||||
constexpr BitArray(void* buffer, int num_bits) { SetData(buffer, num_bits); }
|
||||
constexpr BitArray(ore::Allocator* allocator, int num_bits) {
|
||||
AllocateBuffer(allocator, num_bits);
|
||||
}
|
||||
|
||||
void SetData(void* buffer, int num_bits) {
|
||||
m_words = reinterpret_cast<Word*>(buffer);
|
||||
m_num_bits = num_bits;
|
||||
}
|
||||
|
||||
void AllocateBuffer(ore::Allocator* allocator, int num_bits) {
|
||||
SetData(allocator->New(GetRequiredBufferSize(num_bits)), num_bits);
|
||||
SetAllOff();
|
||||
}
|
||||
|
||||
void FreeBufferIfNeeded(ore::Allocator* allocator) {
|
||||
if (m_words)
|
||||
allocator->Delete(m_words);
|
||||
}
|
||||
|
||||
void FreeBuffer(ore::Allocator* allocator) { allocator->Delete(m_words); }
|
||||
|
||||
bool Test(int bit) const {
|
||||
return (GetWord(bit) & (Word(1) << (Word(bit) % NumBitsPerWord))) != 0;
|
||||
}
|
||||
void Set(int bit) { GetWord(bit) |= Word(1) << (Word(bit) % NumBitsPerWord); }
|
||||
void Clear(int bit) { GetWord(bit) &= ~(Word(1) << (Word(bit) % NumBitsPerWord)); }
|
||||
|
||||
void SetAllOn();
|
||||
void SetAllOff();
|
||||
TestIter BeginTest() const;
|
||||
TestIter EndTest() const;
|
||||
TestClearIter BeginTestClear();
|
||||
TestClearIter EndTestClear();
|
||||
|
||||
static int GetRequiredBufferSize(int num_bits) {
|
||||
return sizeof(Word) * (detail::AlignUpToPowerOf2(num_bits, NumBitsPerWord) >> ShiftAmount);
|
||||
}
|
||||
|
||||
private:
|
||||
Word& GetWord(int bit) const { return m_words[bit >> ShiftAmount]; }
|
||||
int GetNumWords() const { return int((m_num_bits + NumBitsPerWord - 1) >> ShiftAmount); }
|
||||
|
||||
void Fill(int num, Word value) {
|
||||
auto* it = m_words;
|
||||
for (int i = num - 1; i >= 0; --i)
|
||||
*it++ = value;
|
||||
}
|
||||
|
||||
Word* m_words{};
|
||||
int m_num_bits{};
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <ore/Allocator.h>
|
||||
#include <ore/Types.h>
|
||||
|
||||
namespace ore {
|
||||
|
||||
struct Buffer {
|
||||
template <typename T>
|
||||
void Allocate(Allocator* allocator, int num) {
|
||||
size = sizeof(T) * num;
|
||||
data = static_cast<char*>(allocator->New(size));
|
||||
}
|
||||
|
||||
void Free(Allocator* allocator) {
|
||||
allocator->Free(data);
|
||||
data = nullptr;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
char* data;
|
||||
int size;
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <ore/IterRange.h>
|
||||
#include <ore/StringView.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ore {
|
||||
|
||||
namespace detail::EnumUtil {
|
||||
|
||||
int FindIndex(int value, const IterRange<const int*>& values);
|
||||
void Parse(const IterRange<StringView*>& out, StringView definition);
|
||||
|
||||
constexpr int CountValues(const char* text_all, size_t text_all_len) {
|
||||
int count = 1;
|
||||
for (size_t i = 0; i < text_all_len; ++i) {
|
||||
if (text_all[i] == ',')
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace detail::EnumUtil
|
||||
|
||||
template <class T>
|
||||
struct Enum {
|
||||
public:
|
||||
Enum() { T::Init(); }
|
||||
|
||||
static Enum<T>& Info() { return s_Info; }
|
||||
|
||||
StringView name{};
|
||||
IterRange<StringView*> members{};
|
||||
|
||||
private:
|
||||
static inline Enum<T> s_Info{};
|
||||
};
|
||||
|
||||
#define ORE_ENUM(NAME, ...) \
|
||||
class NAME { \
|
||||
public: \
|
||||
enum Type { __VA_ARGS__ }; \
|
||||
\
|
||||
static void Init() { \
|
||||
static ore::StringView names[cCount]; \
|
||||
ore::detail::EnumUtil::Parse(ore::IterRange<ore::StringView*>(names), cTextAll); \
|
||||
ore::Enum<NAME>::Info().name = #NAME; \
|
||||
ore::Enum<NAME>::Info().members = ore::IterRange<ore::StringView*>(names); \
|
||||
} \
|
||||
\
|
||||
static constexpr int Size() { return cCount; } \
|
||||
static constexpr Type Invalid() { return Type(Size()); } \
|
||||
\
|
||||
private: \
|
||||
static constexpr const char* cTextAll = #__VA_ARGS__; \
|
||||
static constexpr size_t cTextAllLen = sizeof(#__VA_ARGS__); \
|
||||
static constexpr int cCount = ore::detail::EnumUtil::CountValues(cTextAll, cTextAllLen); \
|
||||
};
|
||||
|
||||
// FIXME
|
||||
template <class T>
|
||||
class ValuedEnum {
|
||||
public:
|
||||
ValuedEnum() { T::Init(); }
|
||||
|
||||
static Enum<T>& Info() { return s_Info; }
|
||||
|
||||
StringView name{};
|
||||
IterRange<StringView*> members{};
|
||||
|
||||
private:
|
||||
static inline Enum<T> s_Info{};
|
||||
};
|
||||
|
||||
#define ORE_VALUED_ENUM(NAME, ...) \
|
||||
class NAME { \
|
||||
public: \
|
||||
enum Type { __VA_ARGS__ }; \
|
||||
\
|
||||
static void Init() { \
|
||||
static ore::StringView names[cCount]; \
|
||||
ore::detail::EnumUtil::Parse(ore::IterRange<ore::StringView*>(names), cTextAll); \
|
||||
ore::ValuedEnum<NAME>::Info().name = #NAME; \
|
||||
ore::ValuedEnum<NAME>::Info().members = ore::IterRange<ore::StringView*>(names); \
|
||||
} \
|
||||
\
|
||||
static constexpr int Size() { return cCount; } \
|
||||
static constexpr Type Invalid() { return Type(Size()); } \
|
||||
\
|
||||
private: \
|
||||
static constexpr const char* cTextAll = #__VA_ARGS__; \
|
||||
static constexpr size_t cTextAllLen = sizeof(#__VA_ARGS__); \
|
||||
static constexpr int cCount = ore::detail::EnumUtil::CountValues(cTextAll, cTextAllLen); \
|
||||
};
|
||||
|
||||
/// For storing an enum with a particular storage size when specifying the underlying type of the
|
||||
/// enum is not an option.
|
||||
template <typename Enum, typename Storage>
|
||||
struct SizedEnum {
|
||||
static_assert(std::is_enum<Enum>());
|
||||
static_assert(!std::is_enum<Storage>());
|
||||
|
||||
constexpr SizedEnum() = default;
|
||||
constexpr SizedEnum(Enum value) { *this = value; }
|
||||
constexpr operator Enum() const { return static_cast<Enum>(mValue); }
|
||||
constexpr SizedEnum& operator=(Enum value) {
|
||||
mValue = static_cast<Storage>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Storage mValue;
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace ore {
|
||||
|
||||
class IntrusiveListNode {
|
||||
public:
|
||||
constexpr explicit IntrusiveListNode() { m_prev = m_next = this; }
|
||||
|
||||
IntrusiveListNode(const IntrusiveListNode&) = delete;
|
||||
auto operator=(const IntrusiveListNode&) = delete;
|
||||
|
||||
IntrusiveListNode(IntrusiveListNode&& other) noexcept { *this = std::move(other); }
|
||||
IntrusiveListNode& operator=(IntrusiveListNode&& other) noexcept {
|
||||
auto* prev = other.m_prev;
|
||||
other.m_prev = this;
|
||||
prev->m_next = this;
|
||||
m_prev = prev;
|
||||
m_next = &other;
|
||||
other.Erase();
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntrusiveListNode* Prev() const { return m_prev; }
|
||||
IntrusiveListNode* Next() const { return m_next; }
|
||||
bool IsLinked() const { return Prev() || Next(); }
|
||||
|
||||
void Erase() {
|
||||
auto* next = m_next;
|
||||
auto* next_prev = next->m_prev;
|
||||
m_prev->m_next = next;
|
||||
next->m_prev = m_prev;
|
||||
// This is a circular list.
|
||||
next_prev->m_next = this;
|
||||
m_prev = next_prev;
|
||||
}
|
||||
|
||||
void InsertFront(IntrusiveListNode* node) {
|
||||
auto* prev = node->m_prev;
|
||||
node->m_prev = m_prev;
|
||||
prev->m_next = this;
|
||||
m_prev->m_next = node;
|
||||
m_prev = prev;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
friend class IntrusiveList;
|
||||
|
||||
IntrusiveListNode* m_prev{};
|
||||
IntrusiveListNode* m_next{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IntrusiveList {
|
||||
public:
|
||||
void SetOffset(int offset) { m_offset = offset; }
|
||||
|
||||
bool Empty() const { return m_node.m_next == &m_node; }
|
||||
T* Front() { return NodeToItemWithNullCheck(m_node.m_next); }
|
||||
T* Back() { return NodeToItemWithNullCheck(m_node.m_prev); }
|
||||
const T* Front() const { return NodeToItemWithNullCheck(m_node.m_next); }
|
||||
const T* Back() const { return NodeToItemWithNullCheck(m_node.m_prev); }
|
||||
|
||||
void Erase(T* item) { ItemToNode(item)->Erase(); }
|
||||
|
||||
void InsertFront(T* item) { m_node.InsertFront(ItemToNode(item)); }
|
||||
|
||||
private:
|
||||
IntrusiveListNode* ItemToNode(T* item) const {
|
||||
return reinterpret_cast<IntrusiveListNode*>(reinterpret_cast<char*>(item) + m_offset);
|
||||
}
|
||||
|
||||
const IntrusiveListNode* ItemToNode(const T* item) const {
|
||||
return reinterpret_cast<const IntrusiveListNode*>(reinterpret_cast<const char*>(item) +
|
||||
m_offset);
|
||||
}
|
||||
|
||||
T* NodeToItem(IntrusiveListNode* node) const {
|
||||
return reinterpret_cast<T*>(reinterpret_cast<char*>(node) - m_offset);
|
||||
}
|
||||
|
||||
const T* NodeToItem(const IntrusiveListNode* node) const {
|
||||
return reinterpret_cast<const T*>(reinterpret_cast<const char*>(node) - m_offset);
|
||||
}
|
||||
|
||||
T* NodeToItemWithNullCheck(IntrusiveListNode* node) const {
|
||||
return node == &m_node ? nullptr : NodeToItem(node);
|
||||
}
|
||||
|
||||
const T* NodeToItemWithNullCheck(const IntrusiveListNode* node) const {
|
||||
return node == &m_node ? nullptr : NodeToItem(node);
|
||||
}
|
||||
|
||||
IntrusiveListNode m_node;
|
||||
int m_offset = -1;
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace ore {
|
||||
|
||||
template <typename T>
|
||||
class IterRange {
|
||||
public:
|
||||
constexpr IterRange() = default;
|
||||
constexpr IterRange(const T& begin_, const T& end_) : m_begin(begin_), m_end(end_) {}
|
||||
template <typename Other>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr IterRange(Other& x) : IterRange(std::begin(x), std::end(x)) {}
|
||||
|
||||
constexpr IterRange(const T& begin, int size) : m_begin(begin), m_end(begin + size) {}
|
||||
|
||||
const auto& begin() const { return m_begin; }
|
||||
const auto& end() const { return m_end; }
|
||||
|
||||
int size() const { return end() - begin(); }
|
||||
|
||||
private:
|
||||
T m_begin{};
|
||||
T m_end{};
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <ore/Types.h>
|
||||
|
||||
namespace ore {
|
||||
|
||||
struct RelocationTable {
|
||||
struct Section {
|
||||
struct Entry {
|
||||
/// Offset to pointers to relocate
|
||||
int pointers_offset;
|
||||
/// Bit field that determines which pointers need to be relocated
|
||||
/// (next to 32 contiguous pointers starting from the listed offset)
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
void SetPtr(void* ptr_);
|
||||
void* GetPtr() const;
|
||||
void* GetPtrInFile(void* base) const;
|
||||
void* GetBasePtr(void* base) const;
|
||||
u32 GetSize() const;
|
||||
|
||||
u64 ptr;
|
||||
int offset;
|
||||
int size;
|
||||
int first_entry_idx;
|
||||
int num_entries;
|
||||
};
|
||||
|
||||
u32 magic;
|
||||
int table_start_offset;
|
||||
int num_sections;
|
||||
Section sections[1];
|
||||
|
||||
Section* GetSections() { return sections; }
|
||||
const Section* GetSections() const { return sections; }
|
||||
|
||||
Section::Entry* GetEntries() {
|
||||
return reinterpret_cast<Section::Entry*>(GetSections() + num_sections);
|
||||
}
|
||||
const Section::Entry* GetEntries() const {
|
||||
return reinterpret_cast<const Section::Entry*>(GetSections() + num_sections);
|
||||
}
|
||||
|
||||
void Relocate();
|
||||
void Unrelocate();
|
||||
static int CalcSize(int num_sections, int num_entries);
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <ore/BinaryFile.h>
|
||||
#include <ore/StringView.h>
|
||||
#include <ore/Types.h>
|
||||
|
||||
namespace ore {
|
||||
|
||||
struct ResEndian;
|
||||
|
||||
struct ResDicEntry {
|
||||
StringView GetKey() const { return *name.Get(); }
|
||||
|
||||
// Bits 3-7: index of the byte that should be checked
|
||||
// Bits 0-2: index of the bit in that byte
|
||||
int compact_bit_idx;
|
||||
u16 next_indices[2];
|
||||
BinTPtr<BinString> name;
|
||||
};
|
||||
|
||||
struct ResDic {
|
||||
static int FindRefBit(const StringView& str1, const StringView& str2);
|
||||
|
||||
const ResDicEntry* FindEntry(const StringView& key) const {
|
||||
auto* prev = &entries[0];
|
||||
auto* entry = &entries[prev->next_indices[0]];
|
||||
while (prev->compact_bit_idx < entry->compact_bit_idx) {
|
||||
const int bit_idx = entry->compact_bit_idx;
|
||||
long bit = 0;
|
||||
if (u32(key.length()) > u32(bit_idx >> 3))
|
||||
bit = ((key[key.length() + -((bit_idx >> 3) + 1)] >> (bit_idx & 7))) & 1;
|
||||
|
||||
prev = entry;
|
||||
entry = &entries[prev->next_indices[bit]];
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// Returns the index for the specified key or -1 if it cannot be found.
|
||||
int FindIndex(const StringView& key) const {
|
||||
const auto* entry = FindEntry(key);
|
||||
const auto entry_name = entry->GetKey();
|
||||
bool ok = [&] { return StringView(key.data(), key.length()) == entry_name; }();
|
||||
if (!ok)
|
||||
return -1;
|
||||
return static_cast<int>(entry - &GetEntries()[1]);
|
||||
}
|
||||
|
||||
/// Entry 0 is the root entry.
|
||||
ResDicEntry* GetEntries() { return entries; }
|
||||
/// Entry 0 is the root entry.
|
||||
const ResDicEntry* GetEntries() const { return entries; }
|
||||
|
||||
u32 magic;
|
||||
int num_entries;
|
||||
ResDicEntry entries[1];
|
||||
// Followed by ResDicEntry[num_entries].
|
||||
};
|
||||
|
||||
void SwapEndian(ResEndian* endian, ResDic* dic);
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <ore/Types.h>
|
||||
|
||||
namespace ore {
|
||||
|
||||
[[nodiscard]] inline u8 SwapEndian(u8 x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u16 SwapEndian(u16 x) {
|
||||
#ifdef _MSC_VER
|
||||
return _byteswap_ushort(x);
|
||||
#else
|
||||
return __builtin_bswap16(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 SwapEndian(u32 x) {
|
||||
#ifdef _MSC_VER
|
||||
return _byteswap_ulong(x);
|
||||
#else
|
||||
return __builtin_bswap32(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u64 SwapEndian(u64 x) {
|
||||
#ifdef _MSC_VER
|
||||
return _byteswap_uint64(x);
|
||||
#else
|
||||
return __builtin_bswap64(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] inline s8 SwapEndian(s8 x) {
|
||||
return SwapEndian(u8(x));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline s16 SwapEndian(s16 x) {
|
||||
return SwapEndian(u16(x));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline s32 SwapEndian(s32 x) {
|
||||
return SwapEndian(u32(x));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline s64 SwapEndian(s64 x) {
|
||||
return SwapEndian(u64(x));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline f32 SwapEndian(f32 x) {
|
||||
static_assert(sizeof(u32) == sizeof(f32));
|
||||
u32 i;
|
||||
std::memcpy(&i, &x, sizeof(i));
|
||||
i = SwapEndian(i);
|
||||
std::memcpy(&x, &i, sizeof(i));
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void SwapEndian(T* value) {
|
||||
*value = SwapEndian(*value);
|
||||
}
|
||||
|
||||
struct ResEndian {
|
||||
char* base;
|
||||
bool is_serializing;
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <ore/BinaryFile.h>
|
||||
#include <ore/EnumUtil.h>
|
||||
#include <ore/ResDic.h>
|
||||
#include <ore/StringView.h>
|
||||
#include <ore/Types.h>
|
||||
|
||||
namespace ore {
|
||||
|
||||
struct ResDic;
|
||||
struct ResEndian;
|
||||
|
||||
struct ResMetaData {
|
||||
struct ActorIdentifier {
|
||||
BinTPtr<BinString> name;
|
||||
BinTPtr<BinString> sub_name;
|
||||
};
|
||||
|
||||
union Value {
|
||||
BinTPtr<ResMetaData> container;
|
||||
// Also used for booleans. Anything that is != 0 is treated as true.
|
||||
int i;
|
||||
float f;
|
||||
BinTPtr<BinString> str;
|
||||
BinTPtr<BinWString> wstr;
|
||||
ActorIdentifier actor;
|
||||
};
|
||||
|
||||
ORE_ENUM(DataType, kArgument, kContainer, kInt, kBool, kFloat, kString, kWString, kIntArray, kBoolArray, kFloatArray, kStringArray, kWStringArray, kActorIdentifier)
|
||||
|
||||
/// @warning Only usable if type == kContainer.
|
||||
const ResMetaData* Get(const StringView& key, DataType::Type expected_type) const {
|
||||
const int idx = dictionary.Get()->FindIndex(key);
|
||||
if (idx == -1)
|
||||
return nullptr;
|
||||
|
||||
const auto* meta = (&value.container + idx)->Get();
|
||||
if (meta->type != expected_type)
|
||||
return nullptr;
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
SizedEnum<DataType::Type, u8> type;
|
||||
u16 num_items;
|
||||
BinTPtr<ResDic> dictionary;
|
||||
Value value;
|
||||
};
|
||||
|
||||
// XXX: is this unused?
|
||||
struct ResUserData {
|
||||
ORE_ENUM(DataType, kInt, kFloat, kString, kWString, kStream)
|
||||
};
|
||||
|
||||
void SwapEndian(ResEndian* endian, ResMetaData* res);
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <ore/BinaryFile.h>
|
||||
#include <ore/Types.h>
|
||||
|
||||
namespace ore {
|
||||
|
||||
struct StringPool : BinaryBlockHeader {
|
||||
int GetLength() const;
|
||||
void SetLength(int len);
|
||||
|
||||
BinString* GetFirstString() { return dummy_string.NextString(); }
|
||||
|
||||
u32 reserved_8;
|
||||
u32 reserved_c;
|
||||
int length;
|
||||
BinString dummy_string;
|
||||
};
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <ore/Types.h>
|
||||
#include <string>
|
||||
|
||||
namespace ore {
|
||||
|
||||
template <typename T>
|
||||
constexpr size_t StringLength(const T* str) {
|
||||
if (str == nullptr || str[0] == 0)
|
||||
return 0;
|
||||
|
||||
size_t len = 0;
|
||||
while (*str++ != 0)
|
||||
++len;
|
||||
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
__builtin_assume(len <= 0xffffffff);
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class TStringView {
|
||||
public:
|
||||
// Annoyingly enough, this cannot be defaulted (otherwise Clang will not dynamically
|
||||
// initialize static StringView variables).
|
||||
TStringView() {}
|
||||
|
||||
constexpr TStringView(const T* data, size_t len) : m_data(data), m_len(len) {}
|
||||
|
||||
/// @param data A null-terminated string. Must not be nullptr.
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
TStringView(const T* data) : m_data(data), m_len(StringLength(data)) {}
|
||||
|
||||
constexpr const T* data() const { return m_data; }
|
||||
constexpr int size() const { return m_len; }
|
||||
constexpr int length() const { return m_len; }
|
||||
constexpr bool empty() const { return size() == 0; }
|
||||
|
||||
constexpr auto begin() const { return m_data; }
|
||||
constexpr auto cbegin() const { return m_data; }
|
||||
constexpr auto end() const { return m_data + m_len; }
|
||||
constexpr auto cend() const { return m_data + m_len; }
|
||||
|
||||
const T& operator[](size_t idx) const { return m_data[idx]; }
|
||||
|
||||
static int Compare(TStringView lhs, TStringView rhs) {
|
||||
const T* s1 = lhs.data();
|
||||
const T* s2 = rhs.data();
|
||||
int len = std::min(lhs.size(), rhs.size());
|
||||
if (len < 1)
|
||||
return lhs.size() - rhs.size();
|
||||
while (len-- > 0) {
|
||||
if (*s1 == 0 || *s1 != *s2)
|
||||
return *s1 - *s2;
|
||||
++s1, ++s2;
|
||||
}
|
||||
return lhs.size() - rhs.size();
|
||||
}
|
||||
|
||||
int Compare(TStringView rhs) const { return Compare(*this, rhs); }
|
||||
|
||||
friend bool operator==(TStringView lhs, TStringView rhs) {
|
||||
return lhs.size() == rhs.size() && Compare(lhs, rhs) == 0;
|
||||
}
|
||||
|
||||
friend bool operator!=(TStringView lhs, TStringView rhs) { return !operator==(lhs, rhs); }
|
||||
|
||||
private:
|
||||
const T* m_data{};
|
||||
u32 m_len{};
|
||||
};
|
||||
|
||||
using StringView = TStringView<char>;
|
||||
using WStringView = TStringView<wchar_t>;
|
||||
|
||||
} // namespace ore
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
using s8 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
using char16 = char16_t;
|
||||
using size_t = std::size_t;
|
||||
Reference in New Issue
Block a user