Compare commits
2 Commits
dev
...
cae7fecb51
| Author | SHA1 | Date | |
|---|---|---|---|
| cae7fecb51 | |||
| 13f37a81b6 |
@@ -68,12 +68,12 @@ public:
|
||||
BoxedObject(const BoxedObject&) = delete;
|
||||
BoxedObject(BoxedObject&&) = delete;
|
||||
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
constexpr ~BoxedObject() noexcept
|
||||
{
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroyed prior to destructor!");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
BoxedObject& operator=(const BoxedObject&) = delete;
|
||||
BoxedObject& operator=(BoxedObject&&) = delete;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#pragma comment(lib, "kernel32")
|
||||
extern "C" __declspec(dllimport) void __stdcall DebugBreak();
|
||||
#define MIJIN_TRAP() DebugBreak()
|
||||
#define MIJIN_FUNC() __FUNCSIG__
|
||||
#else // _WIN32
|
||||
#include <unistd.h>
|
||||
#define MIJIN_TRAP() \
|
||||
@@ -33,6 +34,7 @@ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
|
||||
: "rcx", "r11", "memory" \
|
||||
); \
|
||||
}
|
||||
#define MIJIN_FUNC() "" // TODO: __PRETTY_FUNCTION__ is not working for some reason -.-
|
||||
#endif // !_WIN32
|
||||
|
||||
namespace mijin
|
||||
|
||||
@@ -113,42 +113,6 @@ StreamError Stream::writeBinaryString(std::string_view str)
|
||||
return writeSpan(str.begin(), str.end());
|
||||
}
|
||||
|
||||
StreamError Stream::readZString(std::string& outString)
|
||||
{
|
||||
char chr = '\0';
|
||||
std::string result;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isAtEnd())
|
||||
{
|
||||
return StreamError::IO_ERROR;
|
||||
}
|
||||
|
||||
if (StreamError error = read(chr); error != StreamError::SUCCESS)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
if (chr == '\0')
|
||||
{
|
||||
outString = std::move(result);
|
||||
return StreamError::SUCCESS;
|
||||
}
|
||||
result.push_back(chr);
|
||||
}
|
||||
}
|
||||
|
||||
StreamError Stream::writeZString(std::string_view str)
|
||||
{
|
||||
static const char ZERO = '\0';
|
||||
|
||||
if (StreamError error = writeRaw(str.data(), str.size() * sizeof(char)); error != StreamError::SUCCESS)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
return write(ZERO);
|
||||
}
|
||||
|
||||
mijin::Task<StreamError> Stream::c_readBinaryString(std::string& outString)
|
||||
{
|
||||
std::uint32_t length; // NOLINT(cppcoreguidelines-init-variables)
|
||||
|
||||
@@ -221,7 +221,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
StreamError write(const T& value)
|
||||
StreamError write(const T& value) requires(std::is_trivial_v<T>)
|
||||
{
|
||||
return writeRaw(&value, sizeof(T));
|
||||
}
|
||||
@@ -261,9 +261,6 @@ public:
|
||||
StreamError readBinaryString(std::string& outString);
|
||||
StreamError writeBinaryString(std::string_view str);
|
||||
|
||||
StreamError readZString(std::string& outString);
|
||||
StreamError writeZString(std::string_view str);
|
||||
|
||||
mijin::Task<StreamError> c_readBinaryString(std::string& outString);
|
||||
mijin::Task<StreamError> c_writeBinaryString(std::string_view str);
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_LOGGING_FILTERS_HPP_INCLUDED)
|
||||
#define MIJIN_LOGGING_FILTERS_HPP_INCLUDED 1
|
||||
|
||||
#include "./logger.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
||||
class BaseLevelFilter : public BaseLogFilter<TChar>
|
||||
{
|
||||
public:
|
||||
using base_t = BaseLogFilter<TChar>;
|
||||
using typename base_t::char_t;
|
||||
using typename base_t::message_t;
|
||||
private:
|
||||
int mMinLevel = 0;
|
||||
int mMaxLevel = 0;
|
||||
public:
|
||||
explicit BaseLevelFilter(int minLevel, int maxLevel = std::numeric_limits<int>::max()) MIJIN_NOEXCEPT
|
||||
: mMinLevel(minLevel), mMaxLevel(maxLevel) {}
|
||||
explicit BaseLevelFilter(const BaseLogLevel<char_t>& minLevel, const BaseLogLevel<char_t>& maxLevel = {nullptr, std::numeric_limits<int>::max()}) MIJIN_NOEXCEPT
|
||||
: mMinLevel(minLevel.value), mMaxLevel(maxLevel.value) {}
|
||||
|
||||
[[nodiscard]]
|
||||
int getMinLevel() const MIJIN_NOEXCEPT { return mMinLevel; }
|
||||
|
||||
[[nodiscard]]
|
||||
int getMaxLevel() const MIJIN_NOEXCEPT { return mMaxLevel; }
|
||||
|
||||
void setMinLevel(int level) MIJIN_NOEXCEPT { mMinLevel = level; }
|
||||
|
||||
void setMinLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMinLevel = level.value; }
|
||||
|
||||
void setMaxLevel(int level) MIJIN_NOEXCEPT { mMaxLevel = level; }
|
||||
|
||||
void setMaxLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMaxLevel = level.value; }
|
||||
|
||||
bool shouldShow(const message_t& message) MIJIN_NOEXCEPT override
|
||||
{
|
||||
return message.level->value >= mMinLevel && message.level->value <= mMaxLevel;
|
||||
}
|
||||
};
|
||||
|
||||
MIJIN_DEFINE_CHAR_VERSIONS(LevelFilter)
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
|
||||
#endif // !defined(MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED)
|
||||
@@ -6,13 +6,11 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <mutex>
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../internal/common.hpp"
|
||||
#include "../util/annot.hpp"
|
||||
#include "../util/iterators.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
@@ -106,20 +104,6 @@ public:
|
||||
|
||||
MIJIN_DEFINE_CHAR_VERSIONS(LogSink)
|
||||
|
||||
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
||||
class BaseLogFilter
|
||||
{
|
||||
public:
|
||||
using char_t = TChar;
|
||||
using message_t = BaseLogMessage<char_t>;
|
||||
|
||||
virtual ~BaseLogFilter() noexcept = default;
|
||||
|
||||
virtual bool shouldShow(const message_t& message) MIJIN_NOEXCEPT = 0;
|
||||
};
|
||||
|
||||
MIJIN_DEFINE_CHAR_VERSIONS(LogFilter)
|
||||
|
||||
#define LOGGER_COMMON_ARGS(chr_type) template<typename T> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR
|
||||
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>, LOGGER_COMMON_ARGS(TChar)>
|
||||
class BaseLogger
|
||||
@@ -130,50 +114,35 @@ public:
|
||||
using allocator_t = TAllocator<char_t>;
|
||||
|
||||
using sink_t = BaseLogSink<char_t>;
|
||||
using filter_t = BaseLogFilter<char_t>;
|
||||
using level_t = BaseLogLevel<char_t>;
|
||||
using channel_t = BaseLogChannel<char_t>;
|
||||
using message_t = BaseLogMessage<char_t>;
|
||||
using string_t = std::basic_string<char_t, traits_t, allocator_t>;
|
||||
private:
|
||||
struct SinkEntry
|
||||
{
|
||||
sink_t* sink;
|
||||
filter_t* filter;
|
||||
};
|
||||
std::vector<SinkEntry, TAllocator<SinkEntry>> mSinks;
|
||||
mutable std::mutex mMutex;
|
||||
std::vector<sink_t*, TAllocator<sink_t*>> mSinks;
|
||||
public:
|
||||
explicit BaseLogger(TAllocator<void> allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator<SinkEntry>, TAllocator<void>&&>))
|
||||
: mSinks(TAllocator<SinkEntry>(std::move(allocator)))
|
||||
explicit BaseLogger(TAllocator<sink_t*> allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<sink_t*>>)
|
||||
: mSinks(std::move(allocator))
|
||||
{}
|
||||
|
||||
BaseLogger(const BaseLogger&) = default;
|
||||
|
||||
BaseLogger(BaseLogger&&) = default;
|
||||
|
||||
BaseLogger& operator=(const BaseLogger&) = default;
|
||||
|
||||
BaseLogger& operator=(BaseLogger&&) = default;
|
||||
|
||||
void addSink(sink_t& sink)
|
||||
{
|
||||
std::unique_lock _(mMutex);
|
||||
mSinks.push_back({&sink, nullptr});
|
||||
}
|
||||
|
||||
void addSink(sink_t& sink, filter_t& filter)
|
||||
{
|
||||
std::unique_lock _(mMutex);
|
||||
mSinks.push_back({&sink, &filter});
|
||||
mSinks.push_back(&sink);
|
||||
}
|
||||
|
||||
void postMessage(const message_t& message) const MIJIN_NOEXCEPT
|
||||
{
|
||||
std::unique_lock _(mMutex);
|
||||
for (const SinkEntry& entry : mSinks)
|
||||
for (sink_t* sink: mSinks)
|
||||
{
|
||||
if (entry.filter != nullptr && !entry.filter->shouldShow(message)) {
|
||||
continue;
|
||||
}
|
||||
entry.sink->handleMessage(message);
|
||||
sink->handleMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,28 +158,12 @@ public:
|
||||
|
||||
template<typename... TArgs>
|
||||
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation,
|
||||
std::basic_format_string<char_t, std::type_identity_t<TArgs>...> fmt, TArgs&& ... args) const MIJIN_NOEXCEPT
|
||||
std::basic_format_string<char_t, std::type_identity_t<TArgs>...> fmt, TArgs&& ... args) const
|
||||
MIJIN_NOEXCEPT_IF(noexcept(std::declval<allocator_t>().allocate(1)))
|
||||
{
|
||||
// TODO: make the logger use a traits struct to make this adjustable
|
||||
static constexpr std::size_t BUFFER_SIZE = 256;
|
||||
std::array<char_t, BUFFER_SIZE> buffer;
|
||||
|
||||
// first try to write into a buffer on the stack
|
||||
FixedArrayOutputIterator itAfter = std::format_to(FixedArrayOutputIterator(buffer), fmt, std::forward<TArgs>(args)...);
|
||||
*itAfter = '\0';
|
||||
++itAfter;
|
||||
if (!itAfter.didOverflow())
|
||||
{
|
||||
log(level, channel, std::move(sourceLocation), buffer.data());
|
||||
return;
|
||||
}
|
||||
|
||||
// if that didn't work, allocate more space
|
||||
const std::size_t newBufferSize = itAfter.getCounter();
|
||||
char_t* newBuffer = static_cast<char_t*>(alloca(newBufferSize * sizeof(char_t)));
|
||||
const std::format_to_n_result result = std::format_to_n(newBuffer, newBufferSize - 1, fmt, std::forward<TArgs>(args)...);
|
||||
*result.out = '\0';
|
||||
log(level, channel, std::move(sourceLocation), newBuffer);
|
||||
string_t buffer(allocator_t(mSinks.get_allocator()));
|
||||
std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(args)...);
|
||||
log(level, channel, std::move(sourceLocation), buffer.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED)
|
||||
#define MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED 1
|
||||
|
||||
#include "./formatting.hpp"
|
||||
#include "../io/stream.hpp"
|
||||
#include "../util/traits.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
|
||||
requires(allocator_type<TAllocator<TChar>>)
|
||||
class BaseStreamSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>
|
||||
{
|
||||
public:
|
||||
using base_t = BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>;
|
||||
using typename base_t::char_t;
|
||||
using typename base_t::allocator_t;
|
||||
using typename base_t::formatter_ptr_t;
|
||||
using typename base_t::message_t;
|
||||
using stream_ptr_t = DynamicPointer<Stream>;
|
||||
private:
|
||||
stream_ptr_t mStream;
|
||||
int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING;
|
||||
public:
|
||||
explicit BaseStreamSink(not_null_t<formatter_ptr_t> formatter, allocator_t allocator = {})
|
||||
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
|
||||
: base_t(std::move(formatter), std::move(allocator)) {}
|
||||
explicit BaseStreamSink(not_null_t<stream_ptr_t> stream, not_null_t<formatter_ptr_t> formatter, allocator_t allocator = {})
|
||||
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
|
||||
: base_t(std::move(formatter), std::move(allocator)), mStream(std::move(stream)) {}
|
||||
|
||||
void setStream(not_null_t<stream_ptr_t> stream) {
|
||||
mStream = std::move(stream).release();
|
||||
}
|
||||
|
||||
void handleMessageFormatted(const message_t& /* message */, const char_t* formatted) MIJIN_NOEXCEPT override
|
||||
{
|
||||
if (!mStream) {
|
||||
return;
|
||||
}
|
||||
(void) mStream->writeSpan(std::basic_string_view(formatted));
|
||||
(void) mStream->write('\n');
|
||||
mStream->flush();
|
||||
}
|
||||
};
|
||||
|
||||
#define SINK_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator, TDeleter
|
||||
|
||||
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(StreamSink, MIJIN_FORMATTING_SINK_COMMON_ARGS, SINK_SET_ARGS)
|
||||
|
||||
#undef SINK_SET_ARGS
|
||||
} // namespace mijin
|
||||
|
||||
|
||||
#endif // !defined(MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED)
|
||||
@@ -17,7 +17,7 @@ namespace mijin
|
||||
{
|
||||
MIJIN_DEFINE_FLAG(Owning);
|
||||
|
||||
template<typename T, deleter_type<T> TDeleter = AllocatorDeleter<MIJIN_DEFAULT_ALLOCATOR<T>>>
|
||||
template<typename T, deleter_type<T> TDeleter = std::default_delete<T>>
|
||||
class DynamicPointer
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -7,317 +7,56 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "../internal/common.hpp"
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "../debug/assert.hpp"
|
||||
#include "../util/align.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
struct ObjectPoolGap
|
||||
{
|
||||
std::uint16_t nextGapOffset;
|
||||
std::uint16_t remainingObjects;
|
||||
|
||||
ObjectPoolGap* nextGap() noexcept {
|
||||
return nextGapOffset == 0 ? nullptr : reinterpret_cast<ObjectPoolGap*>(reinterpret_cast<std::byte*>(this) + nextGapOffset);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TObject, std::size_t OBJECTS_PER_PAGE>
|
||||
struct ObjectPoolPage
|
||||
{
|
||||
ObjectPoolGap* firstGap;
|
||||
ObjectPoolPage* nextPage = nullptr;
|
||||
std::array<std::byte, OBJECTS_PER_PAGE * MIJIN_STRIDEOF(TObject)> data;
|
||||
|
||||
ObjectPoolPage() noexcept
|
||||
{
|
||||
firstGap = reinterpret_cast<ObjectPoolGap*>(data.data());
|
||||
firstGap->nextGapOffset = 0;
|
||||
firstGap->remainingObjects = OBJECTS_PER_PAGE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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>
|
||||
template<typename TObject, std::size_t OBJECTS_PER_PAGE = 1024>
|
||||
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>;
|
||||
struct GapInfo
|
||||
{
|
||||
std::uint32_t objectCount;
|
||||
std::uint32_t nextGap;
|
||||
};
|
||||
static constexpr std::size_t ALIGN = std::max(alignof(TObject), alignof(GapInfo));
|
||||
|
||||
static_assert(sizeof(gap_t) <= sizeof(TObject));
|
||||
struct alignas(ALIGN) ObjectData
|
||||
{
|
||||
std::array<std::byte, sizeof(TObject)> bytes;
|
||||
};
|
||||
struct Page
|
||||
{
|
||||
std::uint32_t freeOffset = 0;
|
||||
std::array<ObjectData, OBJECTS_PER_PAGE> data;
|
||||
};
|
||||
|
||||
[[no_unique_address]] TAllocator<page_t> allocator_;
|
||||
page_t* firstPage = nullptr;
|
||||
std::vector<std::unique_ptr<Page>> pages;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
[[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>
|
||||
TObject* ObjectPool<TObject, OBJECTS_PER_PAGE>::allocate(std::size_t count) noexcept
|
||||
{
|
||||
MIJIN_ASSERT(count <= OBJECTS_PER_PAGE, "Cannot allocate more than OBJECTS_PER_PAGE elements at once!");
|
||||
// first try to find a free spot in the existing pages
|
||||
|
||||
for (std::unique_ptr<Page>& page : pages)
|
||||
{
|
||||
std::uint32_t offset = page->freeOffset;
|
||||
while (offset < OBJECTS_PER_PAGE)
|
||||
{
|
||||
GapInfo& gapInfo = *std::bit_cast<GapInfo*>(&page->data[offset]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_MEMORY_OBJECT_POOL_HPP_INCLUDED)
|
||||
|
||||
@@ -35,10 +35,6 @@ concept nullable_type = !std::is_same_v<T, std::nullptr_t> && requires(T t) { t
|
||||
template<nullable_type T>
|
||||
class NotNullable
|
||||
{
|
||||
public:
|
||||
using wrapped_t = T;
|
||||
using element_type = std::remove_reference_t<decltype(*std::declval<const wrapped_t>())>;
|
||||
using pointer = element_type*;
|
||||
private:
|
||||
T base_;
|
||||
public:
|
||||
@@ -59,12 +55,6 @@ public:
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
template<typename TOther> requires(std::is_base_of_v<typename NotNullable<TOther>::element_type, element_type> && std::is_constructible_v<T, pointer>)
|
||||
explicit constexpr NotNullable(const NotNullable<TOther>& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, pointer>))
|
||||
: base_(static_cast<pointer>(&*other))
|
||||
{
|
||||
MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
|
||||
}
|
||||
template<typename TOther> requires(std::is_constructible_v<T, TOther&&>)
|
||||
constexpr NotNullable(NotNullable<TOther>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TOther&&>))
|
||||
: base_(std::exchange(other.base_, nullptr))
|
||||
@@ -170,9 +160,6 @@ public:
|
||||
[[nodiscard]]
|
||||
constexpr const T& get() const MIJIN_NOEXCEPT { return base_; }
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T release() && MIJIN_NOEXCEPT { return std::exchange(base_, nullptr); }
|
||||
|
||||
template<nullable_type TOther>
|
||||
friend class NotNullable;
|
||||
};
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
|
||||
#include "../debug/assert.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
|
||||
@@ -4,18 +4,8 @@
|
||||
#if !defined(MIJIN_UTIL_COMMON_MACROS_HPP_INCLUDED)
|
||||
#define MIJIN_UTIL_COMMON_MACROS_HPP_INCLUDED 1
|
||||
|
||||
#include "../detect.hpp"
|
||||
|
||||
#define MIJIN_CONCAT_DETAIL(a, b) a ## b
|
||||
#define MIJIN_CONCAT(a, b) MIJIN_CONCAT_DETAIL(a, b)
|
||||
#define MIJIN_CONCAT3(a, b, c) MIJIN_CONCAT(a, MIJIN_CONCAT(b, c))
|
||||
|
||||
#if MIJIN_COMPILER == MIJIN_COMPILER_GCC || MIJIN_COMPILER == MIJIN_COMPILER_CLANG
|
||||
#define MIJIN_FUNCNAME() __PRETTY_FUNCTION__
|
||||
#elif MIJIN_COMPILER == MIJIN_COMPILER_MSVC
|
||||
#define MIJIN_FUNCNAME() __FUNCSIG__
|
||||
#else
|
||||
#define MIJIN_FUNCNAME() __func__
|
||||
#endif
|
||||
|
||||
#endif // defined(MIJIN_UTIL_COMMON_MACROS_HPP_INCLUDED)
|
||||
@@ -1,122 +0,0 @@
|
||||
|
||||
#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,7 +3,6 @@
|
||||
#if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED)
|
||||
#define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
@@ -14,7 +13,6 @@
|
||||
#include <variant>
|
||||
#include "../container/optional.hpp"
|
||||
#include "../internal/common.hpp"
|
||||
#include "../util/annot.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
@@ -857,64 +855,6 @@ TAs collect(TIterable&& iterable)
|
||||
return TAs(iterable.begin(), iterable.end());
|
||||
}
|
||||
|
||||
template<typename TEle, std::size_t numEles>
|
||||
class FixedArrayOutputIterator
|
||||
{
|
||||
public:
|
||||
using array_t = std::array<TEle, numEles>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = TEle;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
static constexpr std::size_t NUM_ELES = numEles;
|
||||
private:
|
||||
not_null_t<array_t*> array_;
|
||||
std::size_t counter_ = 0;
|
||||
public:
|
||||
constexpr explicit FixedArrayOutputIterator(array_t& array) MIJIN_NOEXCEPT : array_(&array) {}
|
||||
constexpr explicit FixedArrayOutputIterator(array_t& array, array_t::iterator pos) MIJIN_NOEXCEPT
|
||||
: array_(&array), counter_(static_cast<std::size_t>(pos - array.begin())) {}
|
||||
constexpr FixedArrayOutputIterator(const FixedArrayOutputIterator&) noexcept = default;
|
||||
|
||||
constexpr FixedArrayOutputIterator& operator=(const FixedArrayOutputIterator&) noexcept = default;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t getCounter() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return counter_;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool didOverflow() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return counter_ >= NUM_ELES;
|
||||
}
|
||||
|
||||
constexpr reference operator*() const MIJIN_NOEXCEPT
|
||||
{
|
||||
static TEle dummy_; // returned when we're at the end of the array
|
||||
if (counter_ < NUM_ELES) {
|
||||
return array_->at(counter_);
|
||||
}
|
||||
return dummy_;
|
||||
}
|
||||
|
||||
constexpr FixedArrayOutputIterator& operator++() MIJIN_NOEXCEPT
|
||||
{
|
||||
++counter_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr FixedArrayOutputIterator operator++(int) const MIJIN_NOEXCEPT
|
||||
{
|
||||
FixedArrayOutputIterator copy(*this);
|
||||
++copy;
|
||||
return copy;
|
||||
}
|
||||
};
|
||||
static_assert(std::output_iterator<FixedArrayOutputIterator<int, 1>, int>);
|
||||
|
||||
namespace pipe
|
||||
{
|
||||
template<typename T>
|
||||
@@ -1033,7 +973,7 @@ auto operator|(TIterable&& iterable, Xth<idx>)
|
||||
{
|
||||
return map(std::forward<TIterable>(iterable), [](auto&& element) { return std::get<idx>(element); } );
|
||||
}
|
||||
} // namespace pipe
|
||||
}
|
||||
} // namespace mijin
|
||||
|
||||
#endif // MIJIN_UTIL_ITERATORS_HPP_INCLUDED
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <mutex>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
|
||||
#include <array>
|
||||
#include <malloc.h>
|
||||
@@ -39,29 +38,12 @@ namespace mijin
|
||||
// internal variables
|
||||
//
|
||||
|
||||
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||
namespace
|
||||
{
|
||||
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||
std::mutex gDlErrorMutex; // dlerror may not be thread-safe
|
||||
|
||||
const std::uint64_t gCPUClockResolution = []()
|
||||
{
|
||||
struct timespec time;
|
||||
[[maybe_unused]] const int result = clock_getres(CLOCK_PROCESS_CPUTIME_ID, &time);
|
||||
MIJIN_ASSERT(result == 0, "Error getting cputime resolution.");
|
||||
MIJIN_ASSERT(time.tv_sec == 0, "What kind of cpu clock is this?");
|
||||
return static_cast<std::uint64_t>(time.tv_nsec);
|
||||
}();
|
||||
#elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
|
||||
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
|
||||
}
|
||||
#endif // MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||
|
||||
//
|
||||
// internal functions
|
||||
@@ -196,27 +178,4 @@ void alignedFree(void* ptr)
|
||||
#endif
|
||||
}
|
||||
|
||||
std::uint64_t getCPUTicks() MIJIN_NOEXCEPT
|
||||
{
|
||||
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||
struct timespec time;
|
||||
[[maybe_unused]] const int result = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time);
|
||||
MIJIN_ASSERT(result == 0, "Error getting cpu time.");
|
||||
return (static_cast<std::uint64_t>(time.tv_sec) * 1e9 + time.tv_nsec) / gCPUClockResolution;
|
||||
#else
|
||||
LARGE_INTEGER ticks;
|
||||
[[maybe_unused]] const BOOL result = QueryPerformanceCounter(&ticks);
|
||||
MIJIN_ASSERT(result, "Error getting cpu time.");
|
||||
return static_cast<std::uint64_t>(ticks.QuadPart);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::uint64_t getCPUTicksPerSecond() MIJIN_NOEXCEPT
|
||||
{
|
||||
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
|
||||
return 1e9 / gCPUClockResolution;
|
||||
#else
|
||||
return gCPUTickFrequency;
|
||||
#endif
|
||||
}
|
||||
} // namespace mijin
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#define MIJIN_UTIL_OS_HPP_INCLUDED 1
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -86,9 +85,6 @@ void setCurrentThreadName(const char* threadName) MIJIN_NOEXCEPT;
|
||||
[[nodiscard]] void* alignedRealloc(void* ptr, std::size_t alignment, std::size_t size) MIJIN_NOEXCEPT;
|
||||
void alignedFree(void* ptr);
|
||||
|
||||
[[nodiscard]] std::uint64_t getCPUTicks() MIJIN_NOEXCEPT;
|
||||
[[nodiscard]] std::uint64_t getCPUTicksPerSecond() MIJIN_NOEXCEPT;
|
||||
|
||||
SharedLibrary::~SharedLibrary() MIJIN_NOEXCEPT
|
||||
{
|
||||
close();
|
||||
|
||||
@@ -33,16 +33,6 @@ namespace mijin
|
||||
// public constants
|
||||
//
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename TChar>
|
||||
static constexpr std::array DEFAULT_TRIM_CHARS_DATA = {TChar(' '), TChar('\t'), TChar('\r'), TChar('\n')};
|
||||
}
|
||||
|
||||
template<typename TChar>
|
||||
static const std::basic_string_view<TChar, std::char_traits<TChar>> DEFAULT_TRIM_CHARS
|
||||
= {detail::DEFAULT_TRIM_CHARS_DATA<TChar>.begin(), detail::DEFAULT_TRIM_CHARS_DATA<TChar>.end()};
|
||||
|
||||
//
|
||||
// public traits
|
||||
//
|
||||
@@ -108,361 +98,6 @@ struct SplitOptions
|
||||
bool ignoreEmpty = true;
|
||||
};
|
||||
|
||||
struct [[nodiscard]] ConvertCharTypeResult
|
||||
{
|
||||
unsigned numRead = 0;
|
||||
unsigned numWritten = 0;
|
||||
|
||||
constexpr operator bool() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return numRead != 0 || numWritten != 0;
|
||||
}
|
||||
constexpr bool operator !() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return !static_cast<bool>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
struct SplitViewOptions
|
||||
{
|
||||
bool ignoreEmpty = true;
|
||||
bool trim = false;
|
||||
};
|
||||
|
||||
template<typename TChar, TChar splitAt, SplitViewOptions options = SplitViewOptions(), typename TCharTraits = std::char_traits<TChar>>
|
||||
struct SplitStringTraitsCT
|
||||
{
|
||||
using char_t = TChar;
|
||||
using string_view_t = std::basic_string_view<TChar, TCharTraits>;
|
||||
|
||||
static constexpr char_t getSplitAt() MIJIN_NOEXCEPT { return splitAt; }
|
||||
static constexpr bool getIgnoreEmpty() MIJIN_NOEXCEPT { return options.ignoreEmpty; }
|
||||
static constexpr bool getTrim() MIJIN_NOEXCEPT { return options.trim; }
|
||||
static constexpr auto getTrimChars() MIJIN_NOEXCEPT { return DEFAULT_TRIM_CHARS<char_t>; }
|
||||
};
|
||||
|
||||
template<typename TChar, typename TCharTraits = std::char_traits<TChar>>
|
||||
struct SplitStringTraitsRT
|
||||
{
|
||||
using char_t = TChar;
|
||||
using string_view_t = std::basic_string_view<TChar, TCharTraits>;
|
||||
|
||||
char_t splitAt;
|
||||
bool ignoreEmpty;
|
||||
string_view_t trimChars = {};
|
||||
|
||||
constexpr char_t getSplitAt() const MIJIN_NOEXCEPT { return splitAt; }
|
||||
constexpr bool getIgnoreEmpty() const MIJIN_NOEXCEPT { return ignoreEmpty; }
|
||||
constexpr bool getTrim() const MIJIN_NOEXCEPT { return !trimChars.empty(); }
|
||||
constexpr string_view_t getTrimChars() const MIJIN_NOEXCEPT { return trimChars; }
|
||||
};
|
||||
|
||||
template<typename T, typename TChar>
|
||||
concept SplitStringTraitsType = std::is_copy_constructible_v<T> && requires(const T& object)
|
||||
{
|
||||
typename T::char_t;
|
||||
typename T::string_view_t;
|
||||
{ object.getSplitAt() } -> std::convertible_to<TChar>;
|
||||
{ object.getIgnoreEmpty() } -> std::convertible_to<bool>;
|
||||
{ object.getTrim() } -> std::convertible_to<bool>;
|
||||
{ object.getTrimChars() } -> std::convertible_to<typename T::string_view_t>;
|
||||
};
|
||||
static_assert(SplitStringTraitsType<SplitStringTraitsCT<char, ' '>, char>);
|
||||
static_assert(SplitStringTraitsType<SplitStringTraitsRT<char>, char>);
|
||||
|
||||
template<typename TChar, typename TLine, SplitViewOptions options = SplitViewOptions(), typename TCharTraits = std::char_traits<TChar>>
|
||||
struct SplitLineTraitsCT : SplitStringTraitsCT<TChar, '\n', options, TCharTraits>
|
||||
{
|
||||
using base_t = SplitStringTraitsCT<TChar, '\n', options, TCharTraits>;
|
||||
using char_t = TChar;
|
||||
using line_t = TLine;
|
||||
|
||||
line_t line = 1;
|
||||
|
||||
using base_t::getSplitAt;
|
||||
using base_t::getIgnoreEmpty;
|
||||
using base_t::getTrim;
|
||||
using base_t::getTrimChars;
|
||||
constexpr void onNext() MIJIN_NOEXCEPT {
|
||||
++line;
|
||||
}
|
||||
constexpr line_t getLine() const MIJIN_NOEXCEPT { return line; }
|
||||
};
|
||||
|
||||
template<typename TChar, typename TLine, typename TCharTraits = std::char_traits<TChar>>
|
||||
struct SplitLineTraitsRT
|
||||
{
|
||||
using char_t = TChar;
|
||||
using line_t = TLine;
|
||||
using string_view_t = std::basic_string_view<TChar, TCharTraits>;
|
||||
|
||||
line_t line = 1;
|
||||
bool ignoreEmpty;
|
||||
string_view_t trimChars = {};
|
||||
|
||||
constexpr char_t getSplitAt() const MIJIN_NOEXCEPT { return '\n'; }
|
||||
constexpr bool getIgnoreEmpty() const MIJIN_NOEXCEPT { return ignoreEmpty; }
|
||||
constexpr bool getTrim() const MIJIN_NOEXCEPT { return !trimChars.empty(); }
|
||||
constexpr string_view_t getTrimChars() const MIJIN_NOEXCEPT { return trimChars; }
|
||||
constexpr line_t getLine() const MIJIN_NOEXCEPT { return line; }
|
||||
constexpr void onNext() MIJIN_NOEXCEPT {
|
||||
++line;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename TChar, typename TLine>
|
||||
concept SplitLineTraitsType = SplitStringTraitsType<T, TChar> && requires (const T& object)
|
||||
{
|
||||
{ object.getLine() } -> std::convertible_to<TLine>;
|
||||
};
|
||||
static_assert(SplitLineTraitsType<SplitLineTraitsCT<char, unsigned>, char, unsigned>);
|
||||
static_assert(SplitLineTraitsType<SplitLineTraitsRT<char, unsigned>, char, unsigned>);
|
||||
|
||||
template<typename TString, typename TChars>
|
||||
[[nodiscard]]
|
||||
auto trim(TString&& string, TChars&& chars);
|
||||
|
||||
template<typename TChar, SplitStringTraitsType<TChar> TTraits>
|
||||
class SplitStringIterator
|
||||
{
|
||||
public:
|
||||
using char_t = TChar;
|
||||
using traits_t = TTraits;
|
||||
using string_view_t = traits_t::string_view_t;
|
||||
using base_t = string_view_t::iterator;
|
||||
using value_type = string_view_t;
|
||||
private:
|
||||
[[no_unique_address]] traits_t traits_;
|
||||
|
||||
string_view_t full_;
|
||||
string_view_t::iterator pos_;
|
||||
string_view_t::iterator next_;
|
||||
|
||||
public:
|
||||
constexpr SplitStringIterator(string_view_t full, base_t pos, traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
|
||||
: full_(full), pos_(pos), traits_(std::move(traits))
|
||||
{
|
||||
findNext();
|
||||
}
|
||||
constexpr explicit SplitStringIterator(traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
|
||||
: traits_(std::move(traits)) {}
|
||||
constexpr SplitStringIterator(const SplitStringIterator&) noexcept(std::is_nothrow_copy_constructible_v<traits_t>) = default;
|
||||
constexpr SplitStringIterator(SplitStringIterator&&) noexcept(std::is_nothrow_move_constructible_v<traits_t>) = default;
|
||||
constexpr SplitStringIterator& operator=(const SplitStringIterator&) noexcept(std::is_nothrow_copy_assignable_v<traits_t>) = default;
|
||||
constexpr SplitStringIterator& operator=(SplitStringIterator&&) noexcept(std::is_nothrow_move_assignable_v<traits_t>) = default;
|
||||
|
||||
constexpr bool operator==(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ == other.pos_; }
|
||||
constexpr bool operator!=(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ != other.pos_; }
|
||||
constexpr bool operator<(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ < other.pos_; }
|
||||
constexpr bool operator<=(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ <= other.pos_; }
|
||||
constexpr bool operator>(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ > other.pos_; }
|
||||
constexpr bool operator>=(const SplitStringIterator& other) const MIJIN_NOEXCEPT { MIJIN_ASSERT(full_ == other.full_, "Comparing unrelated iterators."); return pos_ >= other.pos_; }
|
||||
|
||||
[[nodiscard]]
|
||||
traits_t& getTraits() MIJIN_NOEXCEPT
|
||||
{
|
||||
return traits_;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const traits_t& getTraits() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return traits_;
|
||||
}
|
||||
|
||||
constexpr value_type operator*() const MIJIN_NOEXCEPT
|
||||
{
|
||||
MIJIN_ASSERT(pos_ != full_.end(), "Dereferencing an invalid iterator.");
|
||||
string_view_t result{pos_, next_};
|
||||
if (traits_.getTrim())
|
||||
{
|
||||
result = trim(result, traits_.getTrimChars());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr SplitStringIterator& operator++() MIJIN_NOEXCEPT
|
||||
{
|
||||
MIJIN_ASSERT(pos_ != full_.end(), "Iterating past end.");
|
||||
if (next_ == full_.end()) {
|
||||
pos_ = full_.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
pos_ = std::next(next_);
|
||||
findNext();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr SplitStringIterator operator++(int) const MIJIN_NOEXCEPT
|
||||
{
|
||||
SplitStringIterator copy(*this);
|
||||
++copy;
|
||||
return copy;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// SplitStringIterator& operator--() MIJIN_NOEXCEPT
|
||||
// {
|
||||
// MIJIN_ASSERT(pos_ != full_.begin(), "Iterating past begin.");
|
||||
// next_ = std::prev(pos_);
|
||||
// pos_ = std::find(std::reverse_iterator(next_), std::reverse_iterator(full_.begin()), separator).base();
|
||||
// }
|
||||
private:
|
||||
constexpr void findNext()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if constexpr (requires{{ traits_.onNext() };}) {
|
||||
traits_.onNext();
|
||||
}
|
||||
next_ = std::find(pos_, full_.end(), traits_.getSplitAt());
|
||||
|
||||
if (!traits_.getIgnoreEmpty() || pos_ == full_.end()) {
|
||||
break;
|
||||
}
|
||||
if (traits_.getTrim())
|
||||
{
|
||||
const string_view_t trimChars = traits_.getTrimChars();
|
||||
typename string_view_t::iterator trimmedPos = std::find_if(pos_, next_, [&](char_t chr)
|
||||
{
|
||||
return !trimChars.contains(chr);
|
||||
});
|
||||
if (trimmedPos == next_)
|
||||
{
|
||||
pos_ = next_; // skip this part
|
||||
}
|
||||
}
|
||||
|
||||
if (pos_ != next_) {
|
||||
break;
|
||||
}
|
||||
pos_ = std::next(pos_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TChar, SplitStringTraitsType<TChar> TTraits>
|
||||
class SplitStringRange
|
||||
{
|
||||
public:
|
||||
using char_t = TChar;
|
||||
using traits_t = TTraits;
|
||||
using string_view_t = traits_t::string_view_t;
|
||||
using iterator = SplitStringIterator<char_t, traits_t>;
|
||||
private:
|
||||
[[no_unique_address]] traits_t traits_;
|
||||
string_view_t stringView_;
|
||||
public:
|
||||
constexpr explicit SplitStringRange(string_view_t stringView, traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
|
||||
: stringView_(stringView), traits_(std::move(traits)) {}
|
||||
constexpr SplitStringRange(const SplitStringRange&) noexcept(std::is_nothrow_copy_constructible_v<traits_t>) = default;
|
||||
constexpr SplitStringRange(SplitStringRange&&) noexcept(std::is_nothrow_move_constructible_v<traits_t>) = default;
|
||||
constexpr SplitStringRange& operator=(const SplitStringRange&) noexcept(std::is_nothrow_copy_assignable_v<traits_t>) = default;
|
||||
constexpr SplitStringRange& operator=(SplitStringRange&&) noexcept(std::is_nothrow_move_assignable_v<traits_t>) = default;
|
||||
constexpr auto operator<=>(const SplitStringRange&) const noexcept = default;
|
||||
|
||||
constexpr iterator begin() const MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<traits_t>)
|
||||
{
|
||||
return iterator(stringView_, stringView_.begin(), traits_);
|
||||
}
|
||||
|
||||
constexpr iterator end() const MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<traits_t>)
|
||||
{
|
||||
return iterator(stringView_, stringView_.end(), traits_);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TChar, typename TLine = unsigned, SplitLineTraitsType<TChar, TLine> TTraits = SplitLineTraitsCT<TChar, TLine>>
|
||||
class LineIterator
|
||||
{
|
||||
public:
|
||||
using char_t = TChar;
|
||||
using line_t = TLine;
|
||||
using traits_t = TTraits;
|
||||
using base_t = SplitStringIterator<TChar, traits_t>;
|
||||
using string_view_t = base_t::string_view_t;
|
||||
using value_type = std::pair<string_view_t, line_t>;
|
||||
private:
|
||||
base_t base_ = {};
|
||||
public:
|
||||
constexpr LineIterator(string_view_t full, string_view_t::iterator pos, traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
|
||||
: base_(full, pos, std::move(traits)) {}
|
||||
constexpr explicit LineIterator(traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
|
||||
: base_(std::move(traits)) {}
|
||||
LineIterator(const LineIterator&) noexcept = default;
|
||||
LineIterator(LineIterator&&) noexcept = default;
|
||||
|
||||
LineIterator& operator=(const LineIterator&) noexcept = default;
|
||||
LineIterator& operator=(LineIterator&&) noexcept = default;
|
||||
|
||||
bool operator==(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ == other.base_; }
|
||||
bool operator!=(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ != other.base_; }
|
||||
bool operator<(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ < other.base_; }
|
||||
bool operator>(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ > other.base_; }
|
||||
bool operator<=(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ <= other.base_; }
|
||||
bool operator>=(const LineIterator& other) const MIJIN_NOEXCEPT { return base_ >= other.base_; }
|
||||
|
||||
constexpr value_type operator*() const MIJIN_NOEXCEPT
|
||||
{
|
||||
string_view_t stringView = *base_;
|
||||
if (!base_.getTraits().getTrim())
|
||||
{
|
||||
// always split \r, even if not trimming other whitespace
|
||||
if (stringView.ends_with('\r')) {
|
||||
stringView = stringView.substr(0, stringView.size() - 1);
|
||||
}
|
||||
}
|
||||
return {stringView, base_.getTraits().line};
|
||||
}
|
||||
|
||||
constexpr LineIterator& operator++() MIJIN_NOEXCEPT
|
||||
{
|
||||
++base_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr LineIterator operator++(int) const MIJIN_NOEXCEPT
|
||||
{
|
||||
SplitStringIterator copy(*this);
|
||||
++copy;
|
||||
return copy;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TChar, typename TLine = unsigned, SplitLineTraitsType<TChar, TLine> TTraits = SplitLineTraitsCT<TChar, TLine>>
|
||||
class LineRange
|
||||
{
|
||||
public:
|
||||
using char_t = TChar;
|
||||
using line_t = TLine;
|
||||
using traits_t = TTraits;
|
||||
using iterator = LineIterator<char_t, line_t, traits_t>;
|
||||
using string_view_t = iterator::string_view_t;
|
||||
private:
|
||||
[[no_unique_address]] traits_t traits_;
|
||||
string_view_t stringView_;
|
||||
public:
|
||||
constexpr explicit LineRange(string_view_t stringView, traits_t traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<traits_t>)
|
||||
: traits_(std::move(traits)), stringView_(stringView) {}
|
||||
constexpr LineRange(const LineRange&) noexcept(std::is_nothrow_copy_constructible_v<traits_t>) = default;
|
||||
constexpr LineRange(LineRange&&) noexcept(std::is_nothrow_move_constructible_v<traits_t>) = default;
|
||||
constexpr LineRange& operator=(const LineRange&) noexcept(std::is_nothrow_copy_assignable_v<traits_t>) = default;
|
||||
constexpr LineRange& operator=(LineRange&&) noexcept(std::is_nothrow_move_assignable_v<traits_t>) = default;
|
||||
constexpr auto operator<=>(const LineRange&) const noexcept = default;
|
||||
|
||||
constexpr iterator begin() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return iterator(stringView_, stringView_.begin(), traits_);
|
||||
}
|
||||
|
||||
constexpr iterator end() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return iterator(stringView_, stringView_.end(), traits_);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
@@ -638,6 +273,13 @@ std::basic_string_view<TChar, TTraits> trimImpl(std::basic_string_view<TChar, TT
|
||||
{
|
||||
return trimPrefixImpl(trimSuffixImpl(stringView, charsToTrim), charsToTrim);
|
||||
}
|
||||
|
||||
template<typename TChar>
|
||||
static const std::array DEFAULT_TRIM_CHARS_DATA = {TChar(' '), TChar('\t'), TChar('\r'), TChar('\n')};
|
||||
|
||||
template<typename TChar>
|
||||
static const std::basic_string_view<TChar, std::char_traits<TChar>> DEFAULT_TRIM_CHARS
|
||||
= {DEFAULT_TRIM_CHARS_DATA<TChar>.begin(), DEFAULT_TRIM_CHARS_DATA<TChar>.end()};
|
||||
}
|
||||
|
||||
template<typename TLeft, typename TRight>
|
||||
@@ -654,61 +296,6 @@ template<std::size_t count, typename TLeft, typename TRight>
|
||||
std::basic_string_view(std::forward<TRight>(separator)), options, outNumResults);
|
||||
}
|
||||
|
||||
template<typename TTraits, typename TChar> requires (SplitStringTraitsType<TTraits, TChar>)
|
||||
[[nodiscard]] SplitStringRange<TChar, TTraits> splitView(typename TTraits::string_view_t stringView, TTraits traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TTraits>)
|
||||
{
|
||||
return SplitStringRange<TChar, TTraits>(stringView, std::move(traits));
|
||||
}
|
||||
|
||||
template<auto splitAt, SplitViewOptions options = SplitViewOptions(), typename TStringView, typename TChar = decltype(splitAt), typename TCharTraits = std::char_traits<TChar>>
|
||||
[[nodiscard]] SplitStringRange<TChar, SplitStringTraitsCT<TChar, splitAt, options, TCharTraits>> splitView(TStringView&& stringView) MIJIN_NOEXCEPT
|
||||
{
|
||||
return splitView<SplitStringTraitsCT<TChar, splitAt, options, TCharTraits>>(std::basic_string_view<TChar, TCharTraits>(std::forward<TStringView>(stringView)));
|
||||
}
|
||||
|
||||
template<typename TStringView, typename TChar, typename TCharTraits = std::char_traits<TChar>, typename TTrimChars = std::basic_string_view<TChar, TCharTraits>>
|
||||
[[nodiscard]] auto splitView(TStringView&& stringView, TChar splitAt, bool ignoreEmpty = true, TTrimChars trimChars = {}) MIJIN_NOEXCEPT
|
||||
{
|
||||
return splitView(std::basic_string_view<TChar, TCharTraits>(std::forward<TStringView>(stringView)), SplitStringTraitsRT<TChar, TCharTraits>{
|
||||
.splitAt = splitAt,
|
||||
.ignoreEmpty = ignoreEmpty,
|
||||
.trimChars = std::basic_string_view<TChar, TCharTraits>(std::forward<TTrimChars>(trimChars))
|
||||
});
|
||||
}
|
||||
|
||||
template<typename TTraits, typename TChar, typename TLine = unsigned> requires(SplitLineTraitsType<TTraits, TChar, TLine>)
|
||||
[[nodiscard]] LineRange<TChar, TLine, TTraits> splitLines(typename TTraits::string_view_t stringView, TTraits traits = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TTraits>)
|
||||
{
|
||||
return LineRange<TChar, TLine, TTraits>(stringView, std::move(traits));
|
||||
}
|
||||
|
||||
template<typename TLine = unsigned, SplitViewOptions options = SplitViewOptions{.ignoreEmpty=false},
|
||||
typename TParam,
|
||||
typename TStringView = decltype(std::basic_string_view(std::declval<TParam&&>())),
|
||||
typename TChar = typename TStringView::value_type,
|
||||
typename TCharTraits = typename TStringView::traits_type,
|
||||
typename TTraits = SplitLineTraitsCT<TChar, TLine, options, TCharTraits>>
|
||||
[[nodiscard]]
|
||||
auto splitLines(TParam&& stringView) MIJIN_NOEXCEPT -> LineRange<TChar, TLine, TTraits>
|
||||
{
|
||||
return LineRange<TChar, TLine, TTraits>(std::basic_string_view(std::forward<TParam>(stringView)));
|
||||
}
|
||||
|
||||
template<typename TParam, typename TLine = unsigned,
|
||||
typename TStringView = decltype(std::basic_string_view(std::declval<TParam&&>())),
|
||||
typename TChar = typename TStringView::value_type,
|
||||
typename TCharTraits = typename TStringView::traits_type,
|
||||
typename TTraits = SplitLineTraitsRT<TChar, TLine, TCharTraits>,
|
||||
typename TTrimChars = TStringView>
|
||||
[[nodiscard]]
|
||||
auto splitLines(TParam&& stringView, bool ignoreEmpty, TTrimChars&& trimChars = {}) MIJIN_NOEXCEPT -> LineRange<TChar, TLine, TTraits>
|
||||
{
|
||||
return LineRange<TChar, TLine, TTraits>(std::basic_string_view(std::forward<TParam>(stringView)), TTraits{
|
||||
.ignoreEmpty = ignoreEmpty,
|
||||
.trimChars = std::basic_string_view<TChar, TCharTraits>(std::forward<TTrimChars>(trimChars))
|
||||
});
|
||||
}
|
||||
|
||||
template<typename TString, typename TChars>
|
||||
[[nodiscard]]
|
||||
auto trimPrefix(TString&& string, TChars&& chars)
|
||||
@@ -720,7 +307,7 @@ template<typename TString>
|
||||
[[nodiscard]]
|
||||
auto trimPrefix(TString&& string)
|
||||
{
|
||||
return trimPrefix(string, DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
return trimPrefix(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
}
|
||||
|
||||
template<typename TString, typename TChars>
|
||||
@@ -734,7 +321,7 @@ template<typename TString>
|
||||
[[nodiscard]]
|
||||
auto trimSuffix(TString&& string)
|
||||
{
|
||||
return trimSuffix(string, DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
return trimSuffix(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
}
|
||||
|
||||
template<typename TString, typename TChars>
|
||||
@@ -748,7 +335,7 @@ template<typename TString>
|
||||
[[nodiscard]]
|
||||
auto trim(TString&& string)
|
||||
{
|
||||
return trim(string, DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
return trim(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||
}
|
||||
|
||||
template<typename TLeft, typename TRight>
|
||||
@@ -900,6 +487,21 @@ auto operator|(TIterable&& iterable, const Join& joiner)
|
||||
}
|
||||
} // namespace pipe
|
||||
|
||||
struct [[nodiscard]] ConvertCharTypeResult
|
||||
{
|
||||
unsigned numRead = 0;
|
||||
unsigned numWritten = 0;
|
||||
|
||||
constexpr operator bool() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return numRead != 0 || numWritten != 0;
|
||||
}
|
||||
constexpr bool operator !() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return !static_cast<bool>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TFrom, typename TTo>
|
||||
ConvertCharTypeResult convertCharType(const TFrom* chrFrom, std::size_t numFrom, TTo* outTo, std::size_t numTo, std::mbstate_t& mbstate) MIJIN_NOEXCEPT
|
||||
{
|
||||
|
||||
@@ -253,42 +253,6 @@ struct optional_base<T, false>
|
||||
template<typename T, bool enable>
|
||||
using optional_base_t = optional_base<T, enable>::type;
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template<typename TFunc>
|
||||
struct function_traits_base {};
|
||||
|
||||
template<typename TResult, typename... TParams>
|
||||
struct function_traits_base<TResult (*)(TParams...)>
|
||||
{
|
||||
using result_t = TResult;
|
||||
using params_t = std::tuple<TParams...>;
|
||||
};
|
||||
|
||||
template<typename TResult, typename TType, typename... TParams>
|
||||
struct function_traits_base<TResult (TType::*)(TParams...)>
|
||||
{
|
||||
using result_t = TResult;
|
||||
using params_t = std::tuple<TType*, TParams...>;
|
||||
};
|
||||
|
||||
template<typename TResult, typename TType, typename... TParams>
|
||||
struct function_traits_base<TResult (TType::*)(TParams...) const>
|
||||
{
|
||||
using result_t = TResult;
|
||||
using params_t = std::tuple<const TType*, TParams...>;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename TFunc>
|
||||
struct function_traits : impl::function_traits_base<TFunc>
|
||||
{
|
||||
static constexpr std::size_t NUM_PARAMS = std::tuple_size_v<typename impl::function_traits_base<TFunc>::params_t>;
|
||||
|
||||
template<std::size_t pos>
|
||||
using param_t = std::tuple_element_t<pos, typename impl::function_traits_base<TFunc>::params_t>;
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#if !defined(MIJIN_UTIL_VARIANT_HPP_INCLUDED)
|
||||
#define MIJIN_UTIL_VARIANT_HPP_INCLUDED 1
|
||||
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include "./traits.hpp"
|
||||
|
||||
@@ -24,21 +23,10 @@ inline constexpr bool variant_contains_v<TSearch, std::variant<TVariantTypes...>
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
template<typename... TCallable>
|
||||
struct Visitor : TCallable ...
|
||||
{
|
||||
using TCallable::operator()...;
|
||||
};
|
||||
|
||||
template<typename TRet>
|
||||
struct CastTo
|
||||
{
|
||||
template<typename T>
|
||||
TRet operator()(T&& arg) const
|
||||
{
|
||||
return static_cast<TRet>(std::forward<T>(arg));
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif // !defined(MIJIN_UTIL_VARIANT_HPP_INCLUDED)
|
||||
|
||||
@@ -26,7 +26,3 @@
|
||||
#if defined(DEBUG)
|
||||
#undef DEBUG
|
||||
#endif
|
||||
|
||||
#if defined(VOID)
|
||||
#undef VOID
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user