19#ifndef MTCORE_WRITER_HPP
20#define MTCORE_WRITER_HPP
41 template<WriterImpl Impl>
50 template<WriterImpl Impl>
167 if (r.value() != s.size()) {
168 return error(ErrType::OUT_OF_ROOM);
195 for (
size_t i = 0; i < n; ++i) {
214 for (
size_t i = 0; i < n; ++i) {
250 struct SliceWriterImpl {
251 static_assert(!std::is_const_v<T>,
"Cannot write to const pointer!");
253 size_t curWriteIndex = 0;
260 for (; i < bytes.size() && curWriteIndex < out.
size(); ++i, ++curWriteIndex) {
261 out[curWriteIndex] = bytes[i];
266 [[nodiscard]]
size_t bytes_written()
const {
return curWriteIndex; }
268 [[nodiscard]]
Slice<T> written()
const {
return out.
sub(0, bytes_written()); }
273 if constexpr (std::is_same_v<T, char>) {
274 memset(out.
head, 0, out.
len *
sizeof(
char));
281 struct VoidWriterImpl {
284 Result<size_t, ErrType> write(Slice<std::add_const_t<T>> elems) {
return success(elems.size()); }
287 template<WriterImpl WI>
288 struct EnsureWriteable : std::true_type {};
289 static_assert(EnsureWriteable<VoidWriterImpl<char>>::value);
292 struct OstreamWriterImpl {
297 Result<size_t, OstreamWriteErr> write(Slice<std::add_const_t<T>> elems) {
298 for (
size_t i = 0; i < elems.size(); ++i) {
305 template<typename InType, WriterImpl WI, typename Err = typename io::Writer<WI>::ErrType,
306 typename FuncType = std::function<Result<size_t, Err>(io::Writer<WI> &, Slice<std::add_const_t<InType>>)>>
307 struct WriteTransformer {
308 static constexpr bool IsWriteThrough =
true;
309 using WriteElem = InType;
311 io::Writer<WI> &underlying;
319 void deinit(Allocator &allocator)
320 requires(AllocatorDeinit<WI>)
322 underlying.deinit(allocator);
330 requires(DefaultDeinit<WI>)
338 [[nodiscard]]
size_t bytes_written() const
339 requires(WriterImplBytes<WI>)
341 return underlying.bytes_written();
347 [[nodiscard]] Slice<typename WI::WriteElem> written() const
348 requires(WriterImplWritten<WI>)
350 return underlying.written();
356 [[nodiscard]]
size_t reset() const
357 requires(WriterImplResettable<WI>)
359 return underlying.reset();
366 Result<size_t, ErrType> write(Slice<std::add_const_t<InType>> elems) {
return mapper(underlying, elems); }
382 template<typename T, WriterImpl WI, typename Err = typename io::Writer<WI>::ErrType,
383 typename FuncType = std::function<Result<size_t, Err>(io::Writer<WI> &, Slice<std::add_const_t<T>>)>>
419 template<Formattable T>
443 template<WriterImpl WI,
typename Arg>
487 template<
WriterImpl WI,
typename A,
typename... Args>
489 const A &curArg,
const Args &...args);
491 template<WriterImpl WI>
528 template<WriterImpl WI,
typename... Args>
530 const Args &...args) {
531 return impl::print(0, writer, fmt, args...);
536 enum class EscapeType { CHAR, FORMAT };
540 Slice<const char> escaped;
543 inline Optional<NextEscape> next_escape(Slice<const char> fmt) {
544 static constexpr auto printStartChars =
"{";
545 static constexpr auto printStart =
slice_from(printStartChars);
546 static constexpr auto printEndChars =
"}";
547 static constexpr auto printEnd =
slice_from(printEndChars);
548 static constexpr auto esc =
'\\';
550 for (
size_t i = 0; i < fmt.size(); ++i) {
552 ensure(i + 1 < fmt.size(),
"INVALID FORMAT STRING! EXPECTED CHARACTER AFTER '\\'");
553 return NextEscape{NextEscape::EscapeType::CHAR, i, i + 1, fmt.sub(i + 1, 1)};
557 ensure(endOpt.has_value(),
"INVALID FORMAT STRING! UNTERMINATED FORMATTING SPECIFIER! EXPECTED '}'");
558 auto escaped = fmt.sub(i + printStart.size(), endOpt.value());
559 auto end = endOpt.value() + i + printStart.size();
560 return NextEscape{NextEscape::EscapeType::FORMAT, i, end, escaped};
566 template<WriterImpl WI>
567 Result<size_t, typename Writer<WI>::ErrType> print(
size_t written, Writer<WI> &writer, Slice<const char> fmt) {
572 using WriteAllRes = Result<size_t, typename Writer<WI>::ErrType>;
573 for (
size_t i = 0; !fmt.empty() && i < 1000; ++i) {
574 auto s = next_escape(fmt);
575 if (!s.has_value()) {
576 written += fmt.size();
581 NextEscape
ne = s.value();
582 auto prefix = fmt.sub(0,
ne.startIndex);
583 if (!prefix.empty()) {
584 WriteAllRes pRes =
writer.write_all(prefix);
585 if (pRes.is_error()) {
588 written += prefix.size();
591 ensure(
ne.type == NextEscape::EscapeType::CHAR,
"Missing argument for format specifier");
592 WriteAllRes escRes =
writer.write_all(
ne.escaped);
593 if (escRes.is_error()) {
594 return escRes.error();
596 written +=
ne.escaped.size();
597 fmt = fmt.sub(
ne.endIndex + 1);
602 template<WriterImpl WI,
typename A,
typename... Args>
603 Result<size_t, typename Writer<WI>::ErrType> print(
size_t written, Writer<WI> &writer, Slice<const char> fmt,
604 const A &curArg,
const Args &...args) {
605 using PrintRes = Result<size_t, typename Writer<WI>::ErrType>;
606 using WriteAllRes = Result<size_t, typename Writer<WI>::ErrType>;
607 for (
size_t i = 0; !fmt.empty() && i < 1000; ++i) {
608 auto s = next_escape(fmt);
609 ensure(s.has_value(),
"Missing format specifier for argument");
611 NextEscape
ne = s.value();
612 auto prefix = fmt.sub(0,
ne.startIndex);
613 if (!prefix.empty()) {
614 WriteAllRes pRes =
writer.write_all(prefix);
615 if (pRes.is_error()) {
618 written += prefix.size();
621 if (
ne.type == NextEscape::EscapeType::CHAR) {
622 WriteAllRes escRes =
writer.write_all(
ne.escaped);
623 if (escRes.is_error()) {
624 return escRes.error();
626 written +=
ne.escaped.size();
627 fmt = fmt.sub(
ne.endIndex + 1);
630 PrintRes fRes =
format(writer, FormatOptions{
ne.escaped}, curArg);
631 if (fRes.is_error()) {
634 written += fRes.value();
635 return print(written, writer, fmt.sub(
ne.endIndex + 1), args...);
638 unreachable(
"Missing Format Specifier for Argument");
Represents a type that has a deinit which takes an allocator (usually does memory cleanup)
Represents a type that has a default (no allocator) deinit method.
Represents an implementation of a writer that we can get how many bytes were written.
Represents an implementation of a writer that we can reset.
Represents an implementation of a writer that we can get the output.
Represents an implementation of a writer (basically checks if the type can be wrapped with Writer<>)
bool starts_with(const Slice< T > &needle, const Slice< T > &haystack)
Checks whether a slice (the haystack) starts with elements in another slice in the same order (the ne...
constexpr auto nullopt
Placeholder value for an empty Optional.
mtcore::Optional< size_t > first_index_not_proceeded_by(const std::remove_const_t< T > &prefix, const std::remove_const_t< T > &needle, const Slice< T > &haystack)
Gets the first index that a needle appears in the haystack, or nullopt if the needle does not appear ...
constexpr Slice< const char32_t > slice_from(char32_t *cstr)
Creates a slice from a utf32 string in the form of a c string.
io::Writer< csv::impl::Writer< WI > > writer(io::Writer< WI > &underlying, Options opts={})
Creates a CSV writer which will encode the data before writing it out.
SliceWriteError
Errors when writing to a slice.
OstreamWriteErr
Errors when writing to a slice.
VoidWriteError
Errors when writing to a slice.
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
#define unreachable(...)
Marks code as unreachable.
Success< void > success()
Creates a successful void Result object.
Error< Underlying > error(Underlying err)
Creates an error.
constexpr bool is_flushable
Boolean check for if something has the trait Flushable.
io::Writer< impl::WriteTransformer< T, WI, Err, FuncType > > write_transformer(io::Writer< WI > &writer, FuncType mapper)
A specialized writer that will transform input data before passing it through to another writer Usefu...
Result< size_t, typename Writer< WI >::ErrType > format(Writer< WI > &writer, const FormatOptions &opts, Arg arg)
Generic format function which takes a bunch of arguments (of the same type) and formatting options an...
io::Writer< impl::VoidWriterImpl< T > > void_writer()
Creates a writer which discards what's written (think of it as writer to /dev/null) Useful for doing ...
auto ostream_writer(std::ostream &os)
Creates a writer which passes its output to an ostream Useful for compatibility with the C++ standard...
io::Writer< impl::SliceWriterImpl< T > > slice_writer(Slice< T > out)
Creates a writer to write to a Slice.
constexpr bool is_write_through
Checks if a writer type is a write-through writer.
auto ne(const L &left, const R &right)
Inequality comparison (used by macros)
Represents a memory allocator Exact behavior depends on the underlying VTable used Should use the a_*...
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
constexpr Slice sub(size_t start) const noexcept
Gets a sub Slice from start.
constexpr size_t size() const noexcept
Gets the size of a Slice.
A writer that writes data to some sort of stream or buffer Note: the data elements written should be ...
size_t bytes_written() const
Gets the number of bytes written.
Result< size_t, ErrType > write(const std::remove_const_t< WriteElem > &one)
Will write a single element If there is no room, will return the error OUT_OF_ROOM.
Result< size_t, ErrType > write_n_times(const std::remove_const_t< WriteElem > &one, size_t n)
Will write a single element N times If there is no room, will return the error OUT_OF_ROOM Note: May ...
typename Impl::WriteElem WriteElem
size_t bytes_written() const
Result< size_t, ErrType > write(const Slice< std::add_const_t< WriteElem > > &s)
Will write as much of a Slice as possible.
Result< void, ErrType > write_n_times(const Slice< std::remove_const_t< WriteElem > > &s, size_t n)
Will write all the elements in a Slice N times If there is no room, will return the error OUT_OF_ROOM...
void deinit(Allocator &allocator)
Cleans up any memory stuff Only usable if the underlying writer has a deinit that takes an allocator.
typename Impl::ErrType ErrType
void deinit()
Cleans up writer Only usable if the underlying writer has a deinit that takes an allocator.
Result< size_t, ErrType > write_n_times(const Slice< std::add_const_t< WriteElem > > &s, size_t n)
Will write all the elements in a Slice N times If there is no room, will return the error OUT_OF_ROOM...
Result< void, ErrType > flush()
Flushes a writer (i.e.
Result< size_t, ErrType > write_all(const Slice< std::add_const_t< WriteElem > > &s)
Will write all the elements in a Slice If there is no room, will return the error OUT_OF_ROOM Note: M...
Result< size_t, ErrType > write(const Slice< std::remove_const_t< WriteElem > > &s)
Will write as much of a Slice as possible.
Result< size_t, ErrType > write_all(const Slice< std::remove_const_t< WriteElem > > &s)
Will write all the elements in a Slice If there is no room, will return the error OUT_OF_ROOM Note: M...