Added FixedArrayOutputIterator and use it for logging without heap allocations.

This commit is contained in:
Patrick 2025-07-12 16:57:20 +02:00
parent 1208f1f59f
commit f7daa58b38
2 changed files with 82 additions and 9 deletions

View File

@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "../internal/common.hpp" #include "../internal/common.hpp"
#include "../util/annot.hpp" #include "../util/annot.hpp"
#include "../util/iterators.hpp"
namespace mijin namespace mijin
{ {
@ -126,11 +127,8 @@ public:
{} {}
BaseLogger(const BaseLogger&) = default; BaseLogger(const BaseLogger&) = default;
BaseLogger(BaseLogger&&) = default; BaseLogger(BaseLogger&&) = default;
BaseLogger& operator=(const BaseLogger&) = default; BaseLogger& operator=(const BaseLogger&) = default;
BaseLogger& operator=(BaseLogger&&) = default; BaseLogger& operator=(BaseLogger&&) = default;
void addSink(sink_t& sink) void addSink(sink_t& sink)
@ -158,12 +156,28 @@ public:
template<typename... TArgs> template<typename... TArgs>
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, 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 std::basic_format_string<char_t, std::type_identity_t<TArgs>...> fmt, TArgs&& ... args) const MIJIN_NOEXCEPT
MIJIN_NOEXCEPT_IF(noexcept(std::declval<allocator_t>().allocate(1)))
{ {
string_t buffer(allocator_t(mSinks.get_allocator())); // TODO: make the logger use a traits struct to make this adjustable
std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(args)...); static constexpr std::size_t BUFFER_SIZE = 256;
log(level, channel, std::move(sourceLocation), buffer.c_str()); 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);
} }
}; };

View File

@ -13,6 +13,7 @@
#include <variant> #include <variant>
#include "../container/optional.hpp" #include "../container/optional.hpp"
#include "../internal/common.hpp" #include "../internal/common.hpp"
#include "../util/annot.hpp"
namespace mijin namespace mijin
{ {
@ -855,6 +856,64 @@ TAs collect(TIterable&& iterable)
return TAs(iterable.begin(), iterable.end()); 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 namespace pipe
{ {
template<typename T> template<typename T>
@ -973,7 +1032,7 @@ auto operator|(TIterable&& iterable, Xth<idx>)
{ {
return map(std::forward<TIterable>(iterable), [](auto&& element) { return std::get<idx>(element); } ); return map(std::forward<TIterable>(iterable), [](auto&& element) { return std::get<idx>(element); } );
} }
} } // namespace pipe
} // namespace mijin } // namespace mijin
#endif // MIJIN_UTIL_ITERATORS_HPP_INCLUDED #endif // MIJIN_UTIL_ITERATORS_HPP_INCLUDED