duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

dump.hpp (20996B)


      1 #ifndef C4_DUMP_HPP_
      2 #define C4_DUMP_HPP_
      3 
      4 #include <c4/substr.hpp>
      5 
      6 namespace c4 {
      7 
      8 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
      9 
     10 
     11 //-----------------------------------------------------------------------------
     12 //-----------------------------------------------------------------------------
     13 //-----------------------------------------------------------------------------
     14 /** type of the function to dump characters */
     15 using DumperPfn = void (*)(csubstr buf);
     16 
     17 
     18 //-----------------------------------------------------------------------------
     19 //-----------------------------------------------------------------------------
     20 //-----------------------------------------------------------------------------
     21 
     22 template<DumperPfn dumpfn, class Arg>
     23 inline size_t dump(substr buf, Arg const& a)
     24 {
     25     size_t sz = to_chars(buf, a); // need to serialize to the buffer
     26     if(C4_LIKELY(sz <= buf.len))
     27         dumpfn(buf.first(sz));
     28     return sz;
     29 }
     30 
     31 template<class DumperFn, class Arg>
     32 inline size_t dump(DumperFn &&dumpfn, substr buf, Arg const& a)
     33 {
     34     size_t sz = to_chars(buf, a); // need to serialize to the buffer
     35     if(C4_LIKELY(sz <= buf.len))
     36         dumpfn(buf.first(sz));
     37     return sz;
     38 }
     39 
     40 template<DumperPfn dumpfn>
     41 inline size_t dump(substr buf, csubstr a)
     42 {
     43     if(buf.len)
     44         dumpfn(a); // dump directly, no need to serialize to the buffer
     45     return 0; // no space was used in the buffer
     46 }
     47 
     48 template<class DumperFn>
     49 inline size_t dump(DumperFn &&dumpfn, substr buf, csubstr a)
     50 {
     51     if(buf.len)
     52         dumpfn(a); // dump directly, no need to serialize to the buffer
     53     return 0; // no space was used in the buffer
     54 }
     55 
     56 template<DumperPfn dumpfn, size_t N>
     57 inline size_t dump(substr buf, const char (&a)[N])
     58 {
     59     if(buf.len)
     60         dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer
     61     return 0; // no space was used in the buffer
     62 }
     63 
     64 template<class DumperFn, size_t N>
     65 inline size_t dump(DumperFn &&dumpfn, substr buf, const char (&a)[N])
     66 {
     67     if(buf.len)
     68         dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer
     69     return 0; // no space was used in the buffer
     70 }
     71 
     72 
     73 //-----------------------------------------------------------------------------
     74 //-----------------------------------------------------------------------------
     75 //-----------------------------------------------------------------------------
     76 
     77 /** */
     78 struct DumpResults
     79 {
     80     enum : size_t { noarg = (size_t)-1 };
     81     size_t bufsize = 0;
     82     size_t lastok = noarg;
     83     bool success_until(size_t expected) const { return lastok == noarg ? false : lastok >= expected; }
     84     bool write_arg(size_t arg) const { return lastok == noarg || arg > lastok; }
     85     size_t argfail() const { return lastok + 1; }
     86 };
     87 
     88 
     89 //-----------------------------------------------------------------------------
     90 //-----------------------------------------------------------------------------
     91 //-----------------------------------------------------------------------------
     92 
     93 /// @cond dev
     94 // terminates the variadic recursion
     95 template<class DumperFn>
     96 size_t cat_dump(DumperFn &&, substr)
     97 {
     98     return 0;
     99 }
    100 
    101 // terminates the variadic recursion
    102 template<DumperPfn dumpfn>
    103 size_t cat_dump(substr)
    104 {
    105     return 0;
    106 }
    107 /// @endcond
    108 
    109 /** take the function pointer as a function argument */
    110 template<class DumperFn, class Arg, class... Args>
    111 size_t cat_dump(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    112 {
    113     size_t size_for_a = dump(dumpfn, buf, a);
    114     if(C4_UNLIKELY(size_for_a > buf.len))
    115         buf = buf.first(0); // ensure no more calls
    116     size_t size_for_more = cat_dump(dumpfn, buf, more...);
    117     return size_for_more > size_for_a ? size_for_more : size_for_a;
    118 }
    119 
    120 /** take the function pointer as a template argument */
    121 template<DumperPfn dumpfn,class Arg, class... Args>
    122 size_t cat_dump(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    123 {
    124     size_t size_for_a = dump<dumpfn>(buf, a);
    125     if(C4_LIKELY(size_for_a > buf.len))
    126         buf = buf.first(0); // ensure no more calls
    127     size_t size_for_more = cat_dump<dumpfn>(buf, more...);
    128     return size_for_more > size_for_a ? size_for_more : size_for_a;
    129 }
    130 
    131 
    132 //-----------------------------------------------------------------------------
    133 //-----------------------------------------------------------------------------
    134 //-----------------------------------------------------------------------------
    135 
    136 /// @cond dev
    137 namespace detail {
    138 
    139 // terminates the variadic recursion
    140 template<DumperPfn dumpfn, class Arg>
    141 DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a)
    142 {
    143     if(C4_LIKELY(results.write_arg(currarg)))
    144     {
    145         size_t sz = dump<dumpfn>(buf, a);  // yield to the specialized function
    146         if(currarg == results.lastok + 1 && sz <= buf.len)
    147             results.lastok = currarg;
    148         results.bufsize = sz > results.bufsize ? sz : results.bufsize;
    149     }
    150     return results;
    151 }
    152 
    153 // terminates the variadic recursion
    154 template<class DumperFn, class Arg>
    155 DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a)
    156 {
    157     if(C4_LIKELY(results.write_arg(currarg)))
    158     {
    159         size_t sz = dump(dumpfn, buf, a);  // yield to the specialized function
    160         if(currarg == results.lastok + 1 && sz <= buf.len)
    161             results.lastok = currarg;
    162         results.bufsize = sz > results.bufsize ? sz : results.bufsize;
    163     }
    164     return results;
    165 }
    166 
    167 template<DumperPfn dumpfn, class Arg, class... Args>
    168 DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    169 {
    170     results = detail::cat_dump_resume<dumpfn>(currarg, results, buf, a);
    171     return detail::cat_dump_resume<dumpfn>(currarg + 1u, results, buf, more...);
    172 }
    173 
    174 template<class DumperFn, class Arg, class... Args>
    175 DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    176 {
    177     results = detail::cat_dump_resume(currarg, dumpfn, results, buf, a);
    178     return detail::cat_dump_resume(currarg + 1u, dumpfn, results, buf, more...);
    179 }
    180 } // namespace detail
    181 /// @endcond
    182 
    183 
    184 template<DumperPfn dumpfn, class Arg, class... Args>
    185 C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    186 {
    187     if(results.bufsize > buf.len)
    188         return results;
    189     return detail::cat_dump_resume<dumpfn>(0u, results, buf, a, more...);
    190 }
    191 
    192 template<class DumperFn, class Arg, class... Args>
    193 C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    194 {
    195     if(results.bufsize > buf.len)
    196         return results;
    197     return detail::cat_dump_resume(0u, dumpfn, results, buf, a, more...);
    198 }
    199 
    200 template<DumperPfn dumpfn, class Arg, class... Args>
    201 C4_ALWAYS_INLINE DumpResults cat_dump_resume(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    202 {
    203     return detail::cat_dump_resume<dumpfn>(0u, DumpResults{}, buf, a, more...);
    204 }
    205 
    206 template<class DumperFn, class Arg, class... Args>
    207 C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    208 {
    209     return detail::cat_dump_resume(0u, dumpfn, DumpResults{}, buf, a, more...);
    210 }
    211 
    212 
    213 //-----------------------------------------------------------------------------
    214 //-----------------------------------------------------------------------------
    215 //-----------------------------------------------------------------------------
    216 
    217 /// @cond dev
    218 // terminate the recursion
    219 template<class DumperFn, class Sep>
    220 size_t catsep_dump(DumperFn &&, substr, Sep const& C4_RESTRICT)
    221 {
    222     return 0;
    223 }
    224 
    225 // terminate the recursion
    226 template<DumperPfn dumpfn, class Sep>
    227 size_t catsep_dump(substr, Sep const& C4_RESTRICT)
    228 {
    229     return 0;
    230 }
    231 /// @endcond
    232 
    233 /** take the function pointer as a function argument */
    234 template<class DumperFn, class Sep, class Arg, class... Args>
    235 size_t catsep_dump(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    236 {
    237     size_t sz = dump(dumpfn, buf, a);
    238     if(C4_UNLIKELY(sz > buf.len))
    239         buf = buf.first(0); // ensure no more calls
    240     if C4_IF_CONSTEXPR (sizeof...(more) > 0)
    241     {
    242         size_t szsep = dump(dumpfn, buf, sep);
    243         if(C4_UNLIKELY(szsep > buf.len))
    244             buf = buf.first(0); // ensure no more calls
    245         sz = sz > szsep ? sz : szsep;
    246     }
    247     size_t size_for_more = catsep_dump(dumpfn, buf, sep, more...);
    248     return size_for_more > sz ? size_for_more : sz;
    249 }
    250 
    251 /** take the function pointer as a template argument */
    252 template<DumperPfn dumpfn, class Sep, class Arg, class... Args>
    253 size_t catsep_dump(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    254 {
    255     size_t sz = dump<dumpfn>(buf, a);
    256     if(C4_UNLIKELY(sz > buf.len))
    257         buf = buf.first(0); // ensure no more calls
    258     if C4_IF_CONSTEXPR (sizeof...(more) > 0)
    259     {
    260         size_t szsep = dump<dumpfn>(buf, sep);
    261         if(C4_UNLIKELY(szsep > buf.len))
    262             buf = buf.first(0); // ensure no more calls
    263         sz = sz > szsep ? sz : szsep;
    264     }
    265     size_t size_for_more = catsep_dump<dumpfn>(buf, sep, more...);
    266     return size_for_more > sz ? size_for_more : sz;
    267 }
    268 
    269 
    270 //-----------------------------------------------------------------------------
    271 //-----------------------------------------------------------------------------
    272 //-----------------------------------------------------------------------------
    273 
    274 /// @cond dev
    275 namespace detail {
    276 template<DumperPfn dumpfn, class Arg>
    277 void catsep_dump_resume_(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
    278 {
    279     if(C4_LIKELY(results->write_arg(currarg)))
    280     {
    281         size_t sz = dump<dumpfn>(*buf, a);
    282         results->bufsize = sz > results->bufsize ? sz : results->bufsize;
    283         if(C4_LIKELY(sz <= buf->len))
    284             results->lastok = currarg;
    285         else
    286             buf->len = 0;
    287     }
    288 }
    289 
    290 template<class DumperFn, class Arg>
    291 void catsep_dump_resume_(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
    292 {
    293     if(C4_LIKELY(results->write_arg(currarg)))
    294     {
    295         size_t sz = dump(dumpfn, *buf, a);
    296         results->bufsize = sz > results->bufsize ? sz : results->bufsize;
    297         if(C4_LIKELY(sz <= buf->len))
    298             results->lastok = currarg;
    299         else
    300             buf->len = 0;
    301     }
    302 }
    303 
    304 template<DumperPfn dumpfn, class Sep, class Arg>
    305 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)
    306 {
    307     detail::catsep_dump_resume_<dumpfn>(currarg, results, buf, a);
    308 }
    309 
    310 template<class DumperFn, class Sep, class Arg>
    311 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)
    312 {
    313     detail::catsep_dump_resume_(currarg, dumpfn, results, buf, a);
    314 }
    315 
    316 template<DumperPfn dumpfn, class Sep, class Arg, class... Args>
    317 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)
    318 {
    319     detail::catsep_dump_resume_<dumpfn>(currarg     , results, buf, a);
    320     detail::catsep_dump_resume_<dumpfn>(currarg + 1u, results, buf, sep);
    321     detail::catsep_dump_resume <dumpfn>(currarg + 2u, results, buf, sep, more...);
    322 }
    323 
    324 template<class DumperFn, class Sep, class Arg, class... Args>
    325 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)
    326 {
    327     detail::catsep_dump_resume_(currarg     , dumpfn, results, buf, a);
    328     detail::catsep_dump_resume_(currarg + 1u, dumpfn, results, buf, sep);
    329     detail::catsep_dump_resume (currarg + 2u, dumpfn, results, buf, sep, more...);
    330 }
    331 } // namespace detail
    332 /// @endcond
    333 
    334 
    335 template<DumperPfn dumpfn, class Sep, class... Args>
    336 C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
    337 {
    338     detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...);
    339     return results;
    340 }
    341 
    342 template<class DumperFn, class Sep, class... Args>
    343 C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
    344 {
    345     detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...);
    346     return results;
    347 }
    348 
    349 template<DumperPfn dumpfn, class Sep, class... Args>
    350 C4_ALWAYS_INLINE DumpResults catsep_dump_resume(substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
    351 {
    352     DumpResults results;
    353     detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...);
    354     return results;
    355 }
    356 
    357 template<class DumperFn, class Sep, class... Args>
    358 C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
    359 {
    360     DumpResults results;
    361     detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...);
    362     return results;
    363 }
    364 
    365 
    366 //-----------------------------------------------------------------------------
    367 //-----------------------------------------------------------------------------
    368 //-----------------------------------------------------------------------------
    369 
    370 /** take the function pointer as a function argument */
    371 template<class DumperFn>
    372 C4_ALWAYS_INLINE size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt)
    373 {
    374     // we can dump without using buf
    375     // but we'll only dump if the buffer is ok
    376     if(C4_LIKELY(buf.len > 0 && fmt.len))
    377         dumpfn(fmt);
    378     return 0u;
    379 }
    380 
    381 /** take the function pointer as a function argument */
    382 template<DumperPfn dumpfn>
    383 C4_ALWAYS_INLINE size_t format_dump(substr buf, csubstr fmt)
    384 {
    385     // we can dump without using buf
    386     // but we'll only dump if the buffer is ok
    387     if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
    388         dumpfn(fmt);
    389     return 0u;
    390 }
    391 
    392 /** take the function pointer as a function argument */
    393 template<class DumperFn, class Arg, class... Args>
    394 size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    395 {
    396     // we can dump without using buf
    397     // but we'll only dump if the buffer is ok
    398     size_t pos = fmt.find("{}"); // @todo use _find_fmt()
    399     if(C4_UNLIKELY(pos == csubstr::npos))
    400     {
    401         if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
    402             dumpfn(fmt);
    403         return 0u;
    404     }
    405     if(C4_LIKELY(buf.len > 0 && pos > 0))
    406         dumpfn(fmt.first(pos)); // we can dump without using buf
    407     fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
    408     pos = dump(dumpfn, buf, a);
    409     if(C4_UNLIKELY(pos > buf.len))
    410         buf.len = 0; // ensure no more calls to dump
    411     size_t size_for_more = format_dump(dumpfn, buf, fmt, more...);
    412     return size_for_more > pos ? size_for_more : pos;
    413 }
    414 
    415 /** take the function pointer as a template argument */
    416 template<DumperPfn dumpfn, class Arg, class... Args>
    417 size_t format_dump(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    418 {
    419     // we can dump without using buf
    420     // but we'll only dump if the buffer is ok
    421     size_t pos = fmt.find("{}"); // @todo use _find_fmt()
    422     if(C4_UNLIKELY(pos == csubstr::npos))
    423     {
    424         if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
    425             dumpfn(fmt);
    426         return 0u;
    427     }
    428     if(C4_LIKELY(buf.len > 0 && pos > 0))
    429         dumpfn(fmt.first(pos)); // we can dump without using buf
    430     fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
    431     pos = dump<dumpfn>(buf, a);
    432     if(C4_UNLIKELY(pos > buf.len))
    433         buf.len = 0; // ensure no more calls to dump
    434     size_t size_for_more = format_dump<dumpfn>(buf, fmt, more...);
    435     return size_for_more > pos ? size_for_more : pos;
    436 }
    437 
    438 
    439 //-----------------------------------------------------------------------------
    440 //-----------------------------------------------------------------------------
    441 //-----------------------------------------------------------------------------
    442 
    443 /// @cond dev
    444 namespace detail {
    445 
    446 template<DumperPfn dumpfn>
    447 DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt)
    448 {
    449     // we can dump without using buf
    450     // but we'll only dump if the buffer is ok
    451     if(C4_LIKELY(buf.len > 0))
    452     {
    453         dumpfn(fmt);
    454         results.lastok = currarg;
    455     }
    456     return results;
    457 }
    458 
    459 template<class DumperFn>
    460 DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt)
    461 {
    462     // we can dump without using buf
    463     // but we'll only dump if the buffer is ok
    464     if(C4_LIKELY(buf.len > 0))
    465     {
    466         dumpfn(fmt);
    467         results.lastok = currarg;
    468     }
    469     return results;
    470 }
    471 
    472 template<DumperPfn dumpfn, class Arg, class... Args>
    473 DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
    474 {
    475     // we need to process the format even if we're not
    476     // going to print the first arguments because we're resuming
    477     size_t pos = fmt.find("{}"); // @todo use _find_fmt()
    478     // we can dump without using buf
    479     // but we'll only dump if the buffer is ok
    480     if(C4_LIKELY(results.write_arg(currarg)))
    481     {
    482         if(C4_UNLIKELY(pos == csubstr::npos))
    483         {
    484             if(C4_LIKELY(buf.len > 0))
    485             {
    486                 results.lastok = currarg;
    487                 dumpfn(fmt);
    488             }
    489             return results;
    490         }
    491         if(C4_LIKELY(buf.len > 0))
    492         {
    493             results.lastok = currarg;
    494             dumpfn(fmt.first(pos));
    495         }
    496     }
    497     fmt = fmt.sub(pos + 2);
    498     if(C4_LIKELY(results.write_arg(currarg + 1)))
    499     {
    500         pos = dump<dumpfn>(buf, a);
    501         results.bufsize = pos > results.bufsize ? pos : results.bufsize;
    502         if(C4_LIKELY(pos <= buf.len))
    503             results.lastok = currarg + 1;
    504         else
    505             buf.len = 0;
    506     }
    507     return detail::format_dump_resume<dumpfn>(currarg + 2u, results, buf, fmt, more...);
    508 }
    509 /// @endcond
    510 
    511 
    512 template<class DumperFn, class Arg, class... Args>
    513 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)
    514 {
    515     // we need to process the format even if we're not
    516     // going to print the first arguments because we're resuming
    517     size_t pos = fmt.find("{}"); // @todo use _find_fmt()
    518     // we can dump without using buf
    519     // but we'll only dump if the buffer is ok
    520     if(C4_LIKELY(results.write_arg(currarg)))
    521     {
    522         if(C4_UNLIKELY(pos == csubstr::npos))
    523         {
    524             if(C4_LIKELY(buf.len > 0))
    525             {
    526                 results.lastok = currarg;
    527                 dumpfn(fmt);
    528             }
    529             return results;
    530         }
    531         if(C4_LIKELY(buf.len > 0))
    532         {
    533             results.lastok = currarg;
    534             dumpfn(fmt.first(pos));
    535         }
    536     }
    537     fmt = fmt.sub(pos + 2);
    538     if(C4_LIKELY(results.write_arg(currarg + 1)))
    539     {
    540         pos = dump(dumpfn, buf, a);
    541         results.bufsize = pos > results.bufsize ? pos : results.bufsize;
    542         if(C4_LIKELY(pos <= buf.len))
    543             results.lastok = currarg + 1;
    544         else
    545             buf.len = 0;
    546     }
    547     return detail::format_dump_resume(currarg + 2u, dumpfn, results, buf, fmt, more...);
    548 }
    549 } // namespace detail
    550 
    551 
    552 template<DumperPfn dumpfn, class... Args>
    553 C4_ALWAYS_INLINE DumpResults format_dump_resume(DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
    554 {
    555     return detail::format_dump_resume<dumpfn>(0u, results, buf, fmt, more...);
    556 }
    557 
    558 template<class DumperFn, class... Args>
    559 C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
    560 {
    561     return detail::format_dump_resume(0u, dumpfn, results, buf, fmt, more...);
    562 }
    563 
    564 
    565 template<DumperPfn dumpfn, class... Args>
    566 C4_ALWAYS_INLINE DumpResults format_dump_resume(substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
    567 {
    568     return detail::format_dump_resume<dumpfn>(0u, DumpResults{}, buf, fmt, more...);
    569 }
    570 
    571 template<class DumperFn, class... Args>
    572 C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
    573 {
    574     return detail::format_dump_resume(0u, dumpfn, DumpResults{}, buf, fmt, more...);
    575 }
    576 
    577 C4_SUPPRESS_WARNING_GCC_CLANG_POP
    578 
    579 } // namespace c4
    580 
    581 
    582 #endif /* C4_DUMP_HPP_ */