Compare commits
6 Commits
54d9cd327a
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 680acdb332 | |||
| 3d507ddef7 | |||
| d35140d278 | |||
| 933a36d992 | |||
| d3659f11f7 | |||
| d207774868 |
@@ -68,12 +68,12 @@ public:
|
|||||||
BoxedObject(const BoxedObject&) = delete;
|
BoxedObject(const BoxedObject&) = delete;
|
||||||
BoxedObject(BoxedObject&&) = delete;
|
BoxedObject(BoxedObject&&) = delete;
|
||||||
|
|
||||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
|
||||||
constexpr ~BoxedObject() noexcept
|
constexpr ~BoxedObject() noexcept
|
||||||
{
|
{
|
||||||
|
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||||
MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroyed prior to destructor!");
|
MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroyed prior to destructor!");
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
BoxedObject& operator=(const BoxedObject&) = delete;
|
BoxedObject& operator=(const BoxedObject&) = delete;
|
||||||
BoxedObject& operator=(BoxedObject&&) = delete;
|
BoxedObject& operator=(BoxedObject&&) = delete;
|
||||||
|
|||||||
@@ -7,56 +7,317 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <limits>
|
#include "../internal/common.hpp"
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include "../debug/assert.hpp"
|
#include "../debug/assert.hpp"
|
||||||
|
#include "../util/align.hpp"
|
||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
template<typename TObject, std::size_t OBJECTS_PER_PAGE = 1024>
|
namespace impl
|
||||||
class ObjectPool
|
|
||||||
{
|
{
|
||||||
private:
|
struct ObjectPoolGap
|
||||||
struct GapInfo
|
{
|
||||||
{
|
std::uint16_t nextGapOffset;
|
||||||
std::uint32_t objectCount;
|
std::uint16_t remainingObjects;
|
||||||
std::uint32_t nextGap;
|
|
||||||
};
|
|
||||||
static constexpr std::size_t ALIGN = std::max(alignof(TObject), alignof(GapInfo));
|
|
||||||
|
|
||||||
struct alignas(ALIGN) ObjectData
|
ObjectPoolGap* nextGap() noexcept {
|
||||||
{
|
return nextGapOffset == 0 ? nullptr : reinterpret_cast<ObjectPoolGap*>(reinterpret_cast<std::byte*>(this) + nextGapOffset);
|
||||||
std::array<std::byte, sizeof(TObject)> bytes;
|
}
|
||||||
};
|
|
||||||
struct Page
|
|
||||||
{
|
|
||||||
std::uint32_t freeOffset = 0;
|
|
||||||
std::array<ObjectData, OBJECTS_PER_PAGE> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Page>> pages;
|
|
||||||
public:
|
|
||||||
[[nodiscard]] TObject* allocate(std::size_t count = 1) noexcept;
|
|
||||||
void free(TObject* object, std::size_t count = 1) noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TObject, std::size_t OBJECTS_PER_PAGE>
|
template<typename TObject, std::size_t OBJECTS_PER_PAGE>
|
||||||
TObject* ObjectPool<TObject, OBJECTS_PER_PAGE>::allocate(std::size_t count) noexcept
|
struct ObjectPoolPage
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(count <= OBJECTS_PER_PAGE, "Cannot allocate more than OBJECTS_PER_PAGE elements at once!");
|
ObjectPoolGap* firstGap;
|
||||||
// first try to find a free spot in the existing pages
|
ObjectPoolPage* nextPage = nullptr;
|
||||||
|
std::array<std::byte, OBJECTS_PER_PAGE * MIJIN_STRIDEOF(TObject)> data;
|
||||||
|
|
||||||
for (std::unique_ptr<Page>& page : pages)
|
ObjectPoolPage() noexcept
|
||||||
{
|
{
|
||||||
std::uint32_t offset = page->freeOffset;
|
firstGap = reinterpret_cast<ObjectPoolGap*>(data.data());
|
||||||
while (offset < OBJECTS_PER_PAGE)
|
firstGap->nextGapOffset = 0;
|
||||||
{
|
firstGap->remainingObjects = OBJECTS_PER_PAGE;
|
||||||
GapInfo& gapInfo = *std::bit_cast<GapInfo*>(&page->data[offset]);
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TObject, std::size_t OBJECTS_PER_PAGE>
|
||||||
|
class ObjectPoolIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = TObject;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
private:
|
||||||
|
using gap_t = impl::ObjectPoolGap;
|
||||||
|
using page_t = impl::ObjectPoolPage<TObject, OBJECTS_PER_PAGE>;
|
||||||
|
|
||||||
|
page_t* currentPage_ = nullptr;
|
||||||
|
gap_t* currentGap_ = nullptr;
|
||||||
|
std::uint16_t currentObject_ = 0;
|
||||||
|
|
||||||
|
explicit ObjectPoolIterator(page_t& firstPage) noexcept
|
||||||
|
{
|
||||||
|
seekNextOnPage(firstPage);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
ObjectPoolIterator() noexcept = default;
|
||||||
|
ObjectPoolIterator(const ObjectPoolIterator&) noexcept = default;
|
||||||
|
|
||||||
|
ObjectPoolIterator& operator=(const ObjectPoolIterator&) noexcept = default;
|
||||||
|
|
||||||
|
auto operator<=>(const ObjectPoolIterator&) const noexcept = default;
|
||||||
|
|
||||||
|
TObject& operator*() const noexcept { return getObject(); }
|
||||||
|
TObject* operator->() const noexcept { return &getObject(); }
|
||||||
|
|
||||||
|
ObjectPoolIterator& operator++() noexcept
|
||||||
|
{
|
||||||
|
seekNext();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectPoolIterator operator++(int) const noexcept
|
||||||
|
{
|
||||||
|
ObjectPoolIterator copy(*this);
|
||||||
|
++(*this);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
TObject& getObject() const noexcept
|
||||||
|
{
|
||||||
|
MIJIN_ASSERT(currentGap_ != nullptr, "Attempting to dereference an invalid iterator.");
|
||||||
|
return *(reinterpret_cast<TObject*>(currentGap_) + currentGap_->remainingObjects + currentObject_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool seekNextOnPage(page_t& firstPage) noexcept
|
||||||
|
{
|
||||||
|
for (page_t* page = &firstPage; page != nullptr; page = page->nextPage)
|
||||||
|
{
|
||||||
|
if (page->firstGap != nullptr && seekNextInGap(*page->firstGap))
|
||||||
|
{
|
||||||
|
currentPage_ = page;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool seekNextInGap(gap_t& firstGap) noexcept
|
||||||
|
{
|
||||||
|
for (gap_t* gap = &firstGap; gap != nullptr; gap = gap->nextGap())
|
||||||
|
{
|
||||||
|
if (gap->remainingObjects < OBJECTS_PER_PAGE)
|
||||||
|
{
|
||||||
|
currentGap_ = gap;
|
||||||
|
currentObject_ = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seekNext() noexcept
|
||||||
|
{
|
||||||
|
if (++currentObject_ < OBJECTS_PER_PAGE - currentGap_->remainingObjects)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentGap_->nextGapOffset != 0 && seekNextInGap(*currentGap_->nextGap())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentPage_->nextPage != nullptr && seekNextOnPage(*currentPage_->nextPage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentPage_ = nullptr;
|
||||||
|
currentGap_ = nullptr;
|
||||||
|
currentObject_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TObject2, std::size_t OBJECTS_PER_PAGE2, template<typename> typename TAllocator>
|
||||||
|
friend class ObjectPool;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TObject, std::size_t OBJECTS_PER_PAGE = 1024, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
||||||
|
class ObjectPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator = ObjectPoolIterator<TObject, OBJECTS_PER_PAGE>;
|
||||||
|
using const_iterator = iterator;
|
||||||
|
private:
|
||||||
|
using gap_t = impl::ObjectPoolGap;
|
||||||
|
using page_t = impl::ObjectPoolPage<TObject, OBJECTS_PER_PAGE>;
|
||||||
|
|
||||||
|
static_assert(sizeof(gap_t) <= sizeof(TObject));
|
||||||
|
|
||||||
|
[[no_unique_address]] TAllocator<page_t> allocator_;
|
||||||
|
page_t* firstPage = nullptr;
|
||||||
|
public:
|
||||||
|
explicit ObjectPool(TAllocator<void> allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator<page_t>, TAllocator<void>&&>))
|
||||||
|
: allocator_(std::move(allocator))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
ObjectPool(const ObjectPool&) = delete;
|
||||||
|
ObjectPool(ObjectPool&&) = default;
|
||||||
|
|
||||||
|
ObjectPool& operator=(const ObjectPool&) = delete;
|
||||||
|
ObjectPool& operator=(ObjectPool&&) = default;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
TObject* allocate(std::size_t count = 1) noexcept
|
||||||
|
{
|
||||||
|
MIJIN_ASSERT(count <= OBJECTS_PER_PAGE, "Cannot allocate that many objects at once.");
|
||||||
|
|
||||||
|
// first try to find a free spot in the existing pages
|
||||||
|
for (page_t* page = firstPage; page != nullptr; page = page->nextPage)
|
||||||
|
{
|
||||||
|
if (TObject* result = allocateFromPage(*page, count); result != nullptr) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing found in the existing pages, allocate a new one and return memory from there
|
||||||
|
page_t* newPage = ::new (allocator_.allocate(1)) page_t;
|
||||||
|
|
||||||
|
if (firstPage == nullptr)
|
||||||
|
{
|
||||||
|
firstPage = newPage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
page_t* lastPage = firstPage;
|
||||||
|
for (; lastPage->nextPage != nullptr; lastPage = lastPage->nextPage);
|
||||||
|
lastPage->nextPage = newPage;
|
||||||
|
}
|
||||||
|
return allocateFromPage(*newPage, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(TObject* object, std::size_t count = 1) noexcept
|
||||||
|
{
|
||||||
|
std::byte* const objectPtr = reinterpret_cast<std::byte*>(object); // for easier comparison
|
||||||
|
for (page_t* page = firstPage; page != nullptr; page = page->nextPage)
|
||||||
|
{
|
||||||
|
// first find the page it's in
|
||||||
|
std::byte* pageStart = page->data.data();
|
||||||
|
std::byte* pageEnd = pageStart + (OBJECTS_PER_PAGE * sizeof(TObject));
|
||||||
|
|
||||||
|
if (objectPtr >= pageStart && objectPtr <= pageEnd)
|
||||||
|
{
|
||||||
|
// then the corresponding gap
|
||||||
|
if (page->firstGap == nullptr)
|
||||||
|
{
|
||||||
|
// everything is used, create a new gap
|
||||||
|
gap_t* newGap = reinterpret_cast<gap_t*>(objectPtr);
|
||||||
|
newGap->remainingObjects = count;
|
||||||
|
newGap->nextGapOffset = 0;
|
||||||
|
page->firstGap = newGap;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (gap_t* gap = page->firstGap; gap != nullptr; gap = gap->nextGap())
|
||||||
|
{
|
||||||
|
std::byte* gapStart = reinterpret_cast<std::byte*>(gap);
|
||||||
|
std::byte* gapEnd = gap->nextGapOffset == 0 ? pageEnd : gapStart + gap->nextGapOffset;
|
||||||
|
if (objectPtr > gapStart && objectPtr < gapEnd)
|
||||||
|
{
|
||||||
|
if (objectPtr + (count * sizeof(TObject)) == gapEnd)
|
||||||
|
{
|
||||||
|
// deallocating right from the end -> just increase the gap remainingObjects
|
||||||
|
gap->remainingObjects += count;
|
||||||
|
mergeGaps(gap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// deallocating from the middle -> create a new gap
|
||||||
|
gap_t* newGap = reinterpret_cast<gap_t*>(objectPtr);
|
||||||
|
newGap->remainingObjects = count;
|
||||||
|
newGap->nextGapOffset = gap->nextGapOffset == 0 ? 0 : (gapEnd - reinterpret_cast<std::byte*>(newGap));
|
||||||
|
gap->nextGapOffset -= objectPtr - gapStart;
|
||||||
|
mergeGaps(newGap);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
template<typename... TArgs>
|
||||||
|
[[nodiscard]]
|
||||||
|
TObject* create(TArgs&&... args) noexcept
|
||||||
|
{
|
||||||
|
TObject* result = allocate();
|
||||||
|
if (result == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return ::new (result) TObject(std::forward<TArgs>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
TObject* createMultiple(std::size_t count = 1) noexcept
|
||||||
|
{
|
||||||
|
TObject* result = allocate(count);
|
||||||
|
if (result == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return ::new (result) TObject[count];
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy(TObject* ptr, std::size_t count = 1) noexcept
|
||||||
|
{
|
||||||
|
for (std::size_t idx = 0; idx < count; ++idx) {
|
||||||
|
ptr[idx].~TObject();
|
||||||
|
}
|
||||||
|
deallocate(ptr, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
iterator begin() const noexcept { return firstPage != nullptr ? iterator(*firstPage) : iterator(); }
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
iterator end() const noexcept { return {}; }
|
||||||
|
private:
|
||||||
|
TObject* allocateFromPage(page_t& page, std::size_t count)
|
||||||
|
{
|
||||||
|
gap_t* previousGap = nullptr;
|
||||||
|
for (gap_t* gap = page.firstGap; gap != nullptr; previousGap = gap, gap = gap->nextGap())
|
||||||
|
{
|
||||||
|
if (gap->remainingObjects == count)
|
||||||
|
{
|
||||||
|
// exactly the correct size
|
||||||
|
if (previousGap != nullptr) {
|
||||||
|
previousGap->nextGapOffset += gap->nextGapOffset;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
page.firstGap = gap->nextGap();
|
||||||
|
}
|
||||||
|
return reinterpret_cast<TObject*>(gap);
|
||||||
|
}
|
||||||
|
else if (gap->remainingObjects > count)
|
||||||
|
{
|
||||||
|
// still enough space
|
||||||
|
TObject* memory = reinterpret_cast<TObject*>(gap) + gap->remainingObjects - count;
|
||||||
|
gap->remainingObjects -= count;
|
||||||
|
return reinterpret_cast<TObject*>(memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mergeGaps(gap_t* start) noexcept
|
||||||
|
{
|
||||||
|
while (start->nextGapOffset != 0 && start->remainingObjects == start->nextGapOffset * sizeof(TObject))
|
||||||
|
{
|
||||||
|
gap_t& next = *start->nextGap();
|
||||||
|
start->remainingObjects += start->nextGap()->remainingObjects;
|
||||||
|
if (next.nextGapOffset == 0) {
|
||||||
|
start->nextGapOffset = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
start->nextGapOffset += next.nextGapOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace mijin
|
} // namespace mijin
|
||||||
|
|
||||||
#endif // !defined(MIJIN_MEMORY_OBJECT_POOL_HPP_INCLUDED)
|
#endif // !defined(MIJIN_MEMORY_OBJECT_POOL_HPP_INCLUDED)
|
||||||
|
|||||||
122
source/mijin/util/formattable.hpp
Normal file
122
source/mijin/util/formattable.hpp
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED
|
||||||
|
#define MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include "../container/boxed_object.hpp"
|
||||||
|
|
||||||
|
namespace mijin
|
||||||
|
{
|
||||||
|
|
||||||
|
//
|
||||||
|
// public concepts
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept parse_result_type = requires
|
||||||
|
{
|
||||||
|
typename T::parse_result_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename TParseContext>
|
||||||
|
concept parseable_by_type = parse_result_type<T> &&
|
||||||
|
requires(const T& object, TParseContext& parseContext)
|
||||||
|
{
|
||||||
|
{ T::parseFormat(parseContext) } -> std::convertible_to<std::pair<typename TParseContext::iterator, typename T::parse_result_t>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename TFmtContext>
|
||||||
|
concept simple_formattable_to_type = requires(const T& object, TFmtContext& formatContext)
|
||||||
|
{
|
||||||
|
{ object.format(formatContext) } -> std::convertible_to<typename TFmtContext::iterator>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename TFmtContext, typename TParseContext>
|
||||||
|
concept complex_formattable_to_type = parseable_by_type<T, TParseContext> &&
|
||||||
|
requires(const T& object, TParseContext& parseContext, TFmtContext& formatContext, T::parse_result_t& parseResult)
|
||||||
|
{
|
||||||
|
{ object.format(formatContext, parseResult) } -> std::convertible_to<typename TFmtContext::iterator>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename TFmtContext, typename TParseContext>
|
||||||
|
concept formattable_to_type = simple_formattable_to_type<T, TFmtContext> || complex_formattable_to_type<T, TFmtContext, TParseContext>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept cformattable_type = formattable_to_type<T, std::format_context, std::format_parse_context>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept wformattable_type = formattable_to_type<T, std::wformat_context, std::wformat_parse_context>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept formattable_type = cformattable_type<T> && wformattable_type<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept any_formattable_type = cformattable_type<T> || wformattable_type<T>;
|
||||||
|
|
||||||
|
//
|
||||||
|
// internal types
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
struct ParseResult
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template<parse_result_type T>
|
||||||
|
struct ParseResult<T>
|
||||||
|
{
|
||||||
|
BoxedObject<typename T::parse_result_t> value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} // namespace mijin
|
||||||
|
|
||||||
|
template<mijin::any_formattable_type T>
|
||||||
|
struct std::formatter<T>
|
||||||
|
{
|
||||||
|
[[no_unique_address]] [[maybe_unused]] mijin::impl::ParseResult<T> parseResult;
|
||||||
|
|
||||||
|
template<typename TContext>
|
||||||
|
constexpr TContext::iterator parse(TContext& ctx)
|
||||||
|
{
|
||||||
|
if constexpr (mijin::parse_result_type<T>)
|
||||||
|
{
|
||||||
|
static_assert(mijin::parseable_by_type<T, TContext>, "Type does not support parsing by this context.");
|
||||||
|
auto [it, result] = T::parseFormat(ctx);
|
||||||
|
parseResult.value.construct(std::move(result));
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto it = ctx.begin();
|
||||||
|
auto end = ctx.end();
|
||||||
|
|
||||||
|
if (it != end && *it != '}')
|
||||||
|
{
|
||||||
|
throw std::format_error("invalid format");
|
||||||
|
}
|
||||||
|
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TContext>
|
||||||
|
TContext::iterator format(const T& object, TContext& ctx) const
|
||||||
|
{
|
||||||
|
if constexpr (mijin::parse_result_type<T>)
|
||||||
|
{
|
||||||
|
auto it = object.format(ctx, std::move(*parseResult.value));
|
||||||
|
parseResult.destroy();
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static_assert(mijin::simple_formattable_to_type<T, TContext>, "Type does not support formatting to this context.");
|
||||||
|
return object.format(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED)
|
#if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED)
|
||||||
#define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1
|
#define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ namespace mijin
|
|||||||
// internal variables
|
// internal variables
|
||||||
//
|
//
|
||||||
|
|
||||||
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||||
std::mutex gDlErrorMutex; // dlerror may not be thread-safe
|
std::mutex gDlErrorMutex; // dlerror may not be thread-safe
|
||||||
|
|
||||||
const std::uint64_t gCPUClockResolution = []()
|
const std::uint64_t gCPUClockResolution = []()
|
||||||
@@ -52,8 +52,16 @@ const std::uint64_t gCPUClockResolution = []()
|
|||||||
MIJIN_ASSERT(time.tv_sec == 0, "What kind of cpu clock is this?");
|
MIJIN_ASSERT(time.tv_sec == 0, "What kind of cpu clock is this?");
|
||||||
return static_cast<std::uint64_t>(time.tv_nsec);
|
return static_cast<std::uint64_t>(time.tv_nsec);
|
||||||
}();
|
}();
|
||||||
};
|
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
|
||||||
#endif // MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
const std::uint64_t gCPUTickFrequency = []()
|
||||||
|
{
|
||||||
|
LARGE_INTEGER ticks;
|
||||||
|
[[maybe_unused]] const BOOL result = QueryPerformanceFrequency(&ticks);
|
||||||
|
MIJIN_ASSERT(result, "Error getting cpu frequency.");
|
||||||
|
return static_cast<std::uint64_t>(ticks.QuadPart);
|
||||||
|
}();
|
||||||
|
#endif // MIJIN_TARGET_OS == MIJIN_OS_LINUX || MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// internal functions
|
// internal functions
|
||||||
@@ -196,8 +204,10 @@ std::uint64_t getCPUTicks() MIJIN_NOEXCEPT
|
|||||||
MIJIN_ASSERT(result == 0, "Error getting cpu time.");
|
MIJIN_ASSERT(result == 0, "Error getting cpu time.");
|
||||||
return (static_cast<std::uint64_t>(time.tv_sec) * 1e9 + time.tv_nsec) / gCPUClockResolution;
|
return (static_cast<std::uint64_t>(time.tv_sec) * 1e9 + time.tv_nsec) / gCPUClockResolution;
|
||||||
#else
|
#else
|
||||||
MIJIN_ERROR("TODO");
|
LARGE_INTEGER ticks;
|
||||||
return 0;
|
[[maybe_unused]] const BOOL result = QueryPerformanceCounter(&ticks);
|
||||||
|
MIJIN_ASSERT(result, "Error getting cpu time.");
|
||||||
|
return static_cast<std::uint64_t>(ticks.QuadPart);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +216,7 @@ std::uint64_t getCPUTicksPerSecond() MIJIN_NOEXCEPT
|
|||||||
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||||
return 1e9 / gCPUClockResolution;
|
return 1e9 / gCPUClockResolution;
|
||||||
#else
|
#else
|
||||||
MIJIN_ERROR("TODO");
|
return gCPUTickFrequency;
|
||||||
return 0;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} // namespace mijin
|
} // namespace mijin
|
||||||
|
|||||||
Reference in New Issue
Block a user