#ifndef C4_DUMP_HPP_ #define C4_DUMP_HPP_ #include namespace c4 { C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast") //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /** type of the function to dump characters */ using DumperPfn = void (*)(csubstr buf); //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- template inline size_t dump(substr buf, Arg const& a) { size_t sz = to_chars(buf, a); // need to serialize to the buffer if(C4_LIKELY(sz <= buf.len)) dumpfn(buf.first(sz)); return sz; } template inline size_t dump(DumperFn &&dumpfn, substr buf, Arg const& a) { size_t sz = to_chars(buf, a); // need to serialize to the buffer if(C4_LIKELY(sz <= buf.len)) dumpfn(buf.first(sz)); return sz; } template inline size_t dump(substr buf, csubstr a) { if(buf.len) dumpfn(a); // dump directly, no need to serialize to the buffer return 0; // no space was used in the buffer } template inline size_t dump(DumperFn &&dumpfn, substr buf, csubstr a) { if(buf.len) dumpfn(a); // dump directly, no need to serialize to the buffer return 0; // no space was used in the buffer } template inline size_t dump(substr buf, const char (&a)[N]) { if(buf.len) dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer return 0; // no space was used in the buffer } template inline size_t dump(DumperFn &&dumpfn, substr buf, const char (&a)[N]) { if(buf.len) dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer return 0; // no space was used in the buffer } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /** */ struct DumpResults { enum : size_t { noarg = (size_t)-1 }; size_t bufsize = 0; size_t lastok = noarg; bool success_until(size_t expected) const { return lastok == noarg ? false : lastok >= expected; } bool write_arg(size_t arg) const { return lastok == noarg || arg > lastok; } size_t argfail() const { return lastok + 1; } }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// @cond dev // terminates the variadic recursion template size_t cat_dump(DumperFn &&, substr) { return 0; } // terminates the variadic recursion template size_t cat_dump(substr) { return 0; } /// @endcond /** take the function pointer as a function argument */ template size_t cat_dump(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { size_t size_for_a = dump(dumpfn, buf, a); if(C4_UNLIKELY(size_for_a > buf.len)) buf = buf.first(0); // ensure no more calls size_t size_for_more = cat_dump(dumpfn, buf, more...); return size_for_more > size_for_a ? size_for_more : size_for_a; } /** take the function pointer as a template argument */ template size_t cat_dump(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { size_t size_for_a = dump(buf, a); if(C4_LIKELY(size_for_a > buf.len)) buf = buf.first(0); // ensure no more calls size_t size_for_more = cat_dump(buf, more...); return size_for_more > size_for_a ? size_for_more : size_for_a; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// @cond dev namespace detail { // terminates the variadic recursion template DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a) { if(C4_LIKELY(results.write_arg(currarg))) { size_t sz = dump(buf, a); // yield to the specialized function if(currarg == results.lastok + 1 && sz <= buf.len) results.lastok = currarg; results.bufsize = sz > results.bufsize ? sz : results.bufsize; } return results; } // terminates the variadic recursion template DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a) { if(C4_LIKELY(results.write_arg(currarg))) { size_t sz = dump(dumpfn, buf, a); // yield to the specialized function if(currarg == results.lastok + 1 && sz <= buf.len) results.lastok = currarg; results.bufsize = sz > results.bufsize ? sz : results.bufsize; } return results; } template DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { results = detail::cat_dump_resume(currarg, results, buf, a); return detail::cat_dump_resume(currarg + 1u, results, buf, more...); } template DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { results = detail::cat_dump_resume(currarg, dumpfn, results, buf, a); return detail::cat_dump_resume(currarg + 1u, dumpfn, results, buf, more...); } } // namespace detail /// @endcond template C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { if(results.bufsize > buf.len) return results; return detail::cat_dump_resume(0u, results, buf, a, more...); } template C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { if(results.bufsize > buf.len) return results; return detail::cat_dump_resume(0u, dumpfn, results, buf, a, more...); } template C4_ALWAYS_INLINE DumpResults cat_dump_resume(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { return detail::cat_dump_resume(0u, DumpResults{}, buf, a, more...); } template C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { return detail::cat_dump_resume(0u, dumpfn, DumpResults{}, buf, a, more...); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// @cond dev // terminate the recursion template size_t catsep_dump(DumperFn &&, substr, Sep const& C4_RESTRICT) { return 0; } // terminate the recursion template size_t catsep_dump(substr, Sep const& C4_RESTRICT) { return 0; } /// @endcond /** take the function pointer as a function argument */ template size_t catsep_dump(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { size_t sz = dump(dumpfn, buf, a); if(C4_UNLIKELY(sz > buf.len)) buf = buf.first(0); // ensure no more calls if C4_IF_CONSTEXPR (sizeof...(more) > 0) { size_t szsep = dump(dumpfn, buf, sep); if(C4_UNLIKELY(szsep > buf.len)) buf = buf.first(0); // ensure no more calls sz = sz > szsep ? sz : szsep; } size_t size_for_more = catsep_dump(dumpfn, buf, sep, more...); return size_for_more > sz ? size_for_more : sz; } /** take the function pointer as a template argument */ template size_t catsep_dump(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { size_t sz = dump(buf, a); if(C4_UNLIKELY(sz > buf.len)) buf = buf.first(0); // ensure no more calls if C4_IF_CONSTEXPR (sizeof...(more) > 0) { size_t szsep = dump(buf, sep); if(C4_UNLIKELY(szsep > buf.len)) buf = buf.first(0); // ensure no more calls sz = sz > szsep ? sz : szsep; } size_t size_for_more = catsep_dump(buf, sep, more...); return size_for_more > sz ? size_for_more : sz; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// @cond dev namespace detail { template void catsep_dump_resume_(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a) { if(C4_LIKELY(results->write_arg(currarg))) { size_t sz = dump(*buf, a); results->bufsize = sz > results->bufsize ? sz : results->bufsize; if(C4_LIKELY(sz <= buf->len)) results->lastok = currarg; else buf->len = 0; } } template void catsep_dump_resume_(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a) { if(C4_LIKELY(results->write_arg(currarg))) { size_t sz = dump(dumpfn, *buf, a); results->bufsize = sz > results->bufsize ? sz : results->bufsize; if(C4_LIKELY(sz <= buf->len)) results->lastok = currarg; else buf->len = 0; } } template C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a) { detail::catsep_dump_resume_(currarg, results, buf, a); } template C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a) { detail::catsep_dump_resume_(currarg, dumpfn, results, buf, a); } template C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { detail::catsep_dump_resume_(currarg , results, buf, a); detail::catsep_dump_resume_(currarg + 1u, results, buf, sep); detail::catsep_dump_resume (currarg + 2u, results, buf, sep, more...); } template C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { detail::catsep_dump_resume_(currarg , dumpfn, results, buf, a); detail::catsep_dump_resume_(currarg + 1u, dumpfn, results, buf, sep); detail::catsep_dump_resume (currarg + 2u, dumpfn, results, buf, sep, more...); } } // namespace detail /// @endcond template C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more) { detail::catsep_dump_resume(0u, &results, &buf, sep, more...); return results; } template C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more) { detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...); return results; } template C4_ALWAYS_INLINE DumpResults catsep_dump_resume(substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more) { DumpResults results; detail::catsep_dump_resume(0u, &results, &buf, sep, more...); return results; } template C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more) { DumpResults results; detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...); return results; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /** take the function pointer as a function argument */ template C4_ALWAYS_INLINE size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt) { // we can dump without using buf // but we'll only dump if the buffer is ok if(C4_LIKELY(buf.len > 0 && fmt.len)) dumpfn(fmt); return 0u; } /** take the function pointer as a function argument */ template C4_ALWAYS_INLINE size_t format_dump(substr buf, csubstr fmt) { // we can dump without using buf // but we'll only dump if the buffer is ok if(C4_LIKELY(buf.len > 0 && fmt.len > 0)) dumpfn(fmt); return 0u; } /** take the function pointer as a function argument */ template size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { // we can dump without using buf // but we'll only dump if the buffer is ok size_t pos = fmt.find("{}"); // @todo use _find_fmt() if(C4_UNLIKELY(pos == csubstr::npos)) { if(C4_LIKELY(buf.len > 0 && fmt.len > 0)) dumpfn(fmt); return 0u; } if(C4_LIKELY(buf.len > 0 && pos > 0)) dumpfn(fmt.first(pos)); // we can dump without using buf fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again pos = dump(dumpfn, buf, a); if(C4_UNLIKELY(pos > buf.len)) buf.len = 0; // ensure no more calls to dump size_t size_for_more = format_dump(dumpfn, buf, fmt, more...); return size_for_more > pos ? size_for_more : pos; } /** take the function pointer as a template argument */ template size_t format_dump(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { // we can dump without using buf // but we'll only dump if the buffer is ok size_t pos = fmt.find("{}"); // @todo use _find_fmt() if(C4_UNLIKELY(pos == csubstr::npos)) { if(C4_LIKELY(buf.len > 0 && fmt.len > 0)) dumpfn(fmt); return 0u; } if(C4_LIKELY(buf.len > 0 && pos > 0)) dumpfn(fmt.first(pos)); // we can dump without using buf fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again pos = dump(buf, a); if(C4_UNLIKELY(pos > buf.len)) buf.len = 0; // ensure no more calls to dump size_t size_for_more = format_dump(buf, fmt, more...); return size_for_more > pos ? size_for_more : pos; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// @cond dev namespace detail { template DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt) { // we can dump without using buf // but we'll only dump if the buffer is ok if(C4_LIKELY(buf.len > 0)) { dumpfn(fmt); results.lastok = currarg; } return results; } template DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt) { // we can dump without using buf // but we'll only dump if the buffer is ok if(C4_LIKELY(buf.len > 0)) { dumpfn(fmt); results.lastok = currarg; } return results; } template DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { // we need to process the format even if we're not // going to print the first arguments because we're resuming size_t pos = fmt.find("{}"); // @todo use _find_fmt() // we can dump without using buf // but we'll only dump if the buffer is ok if(C4_LIKELY(results.write_arg(currarg))) { if(C4_UNLIKELY(pos == csubstr::npos)) { if(C4_LIKELY(buf.len > 0)) { results.lastok = currarg; dumpfn(fmt); } return results; } if(C4_LIKELY(buf.len > 0)) { results.lastok = currarg; dumpfn(fmt.first(pos)); } } fmt = fmt.sub(pos + 2); if(C4_LIKELY(results.write_arg(currarg + 1))) { pos = dump(buf, a); results.bufsize = pos > results.bufsize ? pos : results.bufsize; if(C4_LIKELY(pos <= buf.len)) results.lastok = currarg + 1; else buf.len = 0; } return detail::format_dump_resume(currarg + 2u, results, buf, fmt, more...); } /// @endcond template DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) { // we need to process the format even if we're not // going to print the first arguments because we're resuming size_t pos = fmt.find("{}"); // @todo use _find_fmt() // we can dump without using buf // but we'll only dump if the buffer is ok if(C4_LIKELY(results.write_arg(currarg))) { if(C4_UNLIKELY(pos == csubstr::npos)) { if(C4_LIKELY(buf.len > 0)) { results.lastok = currarg; dumpfn(fmt); } return results; } if(C4_LIKELY(buf.len > 0)) { results.lastok = currarg; dumpfn(fmt.first(pos)); } } fmt = fmt.sub(pos + 2); if(C4_LIKELY(results.write_arg(currarg + 1))) { pos = dump(dumpfn, buf, a); results.bufsize = pos > results.bufsize ? pos : results.bufsize; if(C4_LIKELY(pos <= buf.len)) results.lastok = currarg + 1; else buf.len = 0; } return detail::format_dump_resume(currarg + 2u, dumpfn, results, buf, fmt, more...); } } // namespace detail template C4_ALWAYS_INLINE DumpResults format_dump_resume(DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more) { return detail::format_dump_resume(0u, results, buf, fmt, more...); } template C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more) { return detail::format_dump_resume(0u, dumpfn, results, buf, fmt, more...); } template C4_ALWAYS_INLINE DumpResults format_dump_resume(substr buf, csubstr fmt, Args const& C4_RESTRICT ...more) { return detail::format_dump_resume(0u, DumpResults{}, buf, fmt, more...); } template C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more) { return detail::format_dump_resume(0u, dumpfn, DumpResults{}, buf, fmt, more...); } C4_SUPPRESS_WARNING_GCC_CLANG_POP } // namespace c4 #endif /* C4_DUMP_HPP_ */