1 /** @file
2 
3     Utilities for generating character sequences in buffers.
4 
5     @section license License
6 
7     Licensed to the Apache Software Foundation (ASF) under one
8     or more contributor license agreements.  See the NOTICE file
9     distributed with this work for additional information
10     regarding copyright ownership.  The ASF licenses this file
11     to you under the Apache License, Version 2.0 (the
12     "License"); you may not use this file except in compliance
13     with the License.  You may obtain a copy of the License at
14 
15     http://www.apache.org/licenses/LICENSE-2.0
16 
17     Unless required by applicable law or agreed to in writing, software
18     distributed under the License is distributed on an "AS IS" BASIS,
19     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20     See the License for the specific language governing permissions and
21     limitations under the License.
22  */
23 
24 #pragma once
25 
26 #include <cstdlib>
27 #include <utility>
28 #include <cstring>
29 #include <vector>
30 #include <string>
31 #include <iosfwd>
32 #include <string_view>
33 
34 #include "tscpp/util/TextView.h"
35 #include "tscpp/util/MemSpan.h"
36 #include "tscore/BufferWriterForward.h"
37 
38 namespace ts
39 {
40 /** Base (abstract) class for concrete buffer writers.
41  */
42 class BufferWriter
43 {
44 public:
45   /** Add the character @a c to the buffer.
46 
47       @a c is added only if there is room in the buffer. If not, the instance is put in to an error
48       state. In either case the value for @c extent is incremented.
49 
50       @internal If any variant of @c write discards any characters, the instance must be put in an
51       error state (indicated by the override of @c error).  Derived classes must not assume the
52       write() functions will not be called when the instance is in an error state.
53 
54       @return @c *this
55   */
56   virtual BufferWriter &write(char c) = 0;
57 
58   /** Add @a data to the buffer, up to @a length bytes.
59 
60       Data is added only up to the remaining room in the buffer. If the remaining capacity is
61       exceeded (i.e. data is not written to the output), the instance is put in to an error
62       state. In either case the value for @c extent is incremented by @a length.
63 
64       @internal This uses the single character write to output the data. It is presumed concrete
65       subclasses will override this method to use more efficient mechanisms, dependent on the type
66       of output buffer.
67 
68       @return @c *this
69   */
70   virtual BufferWriter &
write(const void * data,size_t length)71   write(const void *data, size_t length)
72   {
73     const char *d = static_cast<const char *>(data);
74 
75     while (length--) {
76       write(*(d++));
77     }
78     return *this;
79   }
80 
81   /** Add the contents of @a sv to the buffer, up to the size of the view.
82 
83       Data is added only up to the remaining room in the buffer. If the remaining capacity is
84       exceeded (i.e. data is not written to the output), the instance is put in to an error
85       state. In either case the value for @c extent is incremented by the size of @a sv.
86 
87       @return @c *this
88   */
89   BufferWriter &
write(const std::string_view & sv)90   write(const std::string_view &sv)
91   {
92     return write(sv.data(), sv.size());
93   }
94 
95   /// Get the address of the first byte in the output buffer.
96   virtual const char *data() const = 0;
97 
98   /// Get the error state.
99   /// @return @c true if in an error state, @c false if not.
100   virtual bool error() const = 0;
101 
102   /** Get the address of the next output byte in the buffer.
103 
104       Succeeding calls to non-const member functions, other than this method, must be presumed to
105       invalidate the current auxiliary buffer (contents and address).
106 
107       Care must be taken to not write to data beyond this plus @c remaining bytes. Usually the
108       safest mechanism is to create a @c FixedBufferWriter on the auxiliary buffer and write to that.
109 
110       @code
111       ts::FixedBufferWriter subw(w.auxBuffer(), w.remaining());
112       write_some_stuff(subw); // generate output into the buffer.
113       w.fill(subw.extent()); // update main buffer writer.
114       @endcode
115 
116       @return Address of the next output byte, or @c nullptr if there is no remaining capacity.
117    */
118   virtual char *
auxBuffer()119   auxBuffer()
120   {
121     return nullptr;
122   }
123 
124   /** Advance the buffer position @a n bytes.
125 
126       This treats the next @a n bytes as being written without changing the content. This is useful
127       only in conjunction with @a auxBuffer to indicate that @a n bytes of the auxiliary buffer has
128       been written by some other mechanism.
129 
130       @internal Concrete subclasses @b must override this to advance in a way consistent with the
131       specific buffer type.
132 
133       @return @c *this
134   */
135   virtual BufferWriter &
fill(size_t n)136   fill(size_t n)
137   {
138     return *this;
139   }
140 
141   /// Get the total capacity.
142   /// @return The total number of bytes that can be written without causing an error condition.
143   virtual size_t capacity() const = 0;
144 
145   /// Get the extent.
146   /// @return Total number of characters that have been written, including those discarded due to an error condition.
147   virtual size_t extent() const = 0;
148 
149   /// Get the output size.
150   /// @return Total number of characters that are in the buffer (successfully written and not discarded)
151   size_t
size() const152   size() const
153   {
154     return std::min(this->extent(), this->capacity());
155   }
156 
157   /// Get the remaining buffer space.
158   /// @return Number of additional characters that can be written without causing an error condition.
159   size_t
remaining() const160   remaining() const
161   {
162     return capacity() - size();
163   }
164 
165   /// Reduce the capacity by @a n bytes
166   /// If the capacity is reduced below the current @c size the instance goes in to an error state.
167   /// @return @c *this
168   virtual BufferWriter &clip(size_t n) = 0;
169 
170   /// Increase the capacity by @a n bytes.
171   /// If there is an error condition, this function clears it and sets the extent to the size.  It
172   /// then increases the capacity by n characters.
173   virtual BufferWriter &extend(size_t n) = 0;
174 
175   // Force virtual destructor.
~BufferWriter()176   virtual ~BufferWriter() {}
177 
178   /** BufferWriter print.
179 
180       This prints its arguments to the @c BufferWriter @a w according to the format @a fmt. The format
181       string is based on Python style formatting, each argument substitution marked by braces, {}. Each
182       specification has three parts, a @a name, a @a specifier, and an @a extension. These are
183       separated by colons. The name should be either omitted or a number, the index of the argument to
184       use. If omitted the place in the format string is used as the argument index. E.g. "{} {} {}",
185       "{} {1} {}", and "{0} {1} {2}" are equivalent. Using an explicit index does not reset the
186       position of subsequent substitutions, therefore "{} {0} {}" is equivalent to "{0} {0} {2}".
187   */
188   template <typename... Rest> BufferWriter &print(TextView fmt, Rest &&... rest);
189   /** Print overload to take arguments as a tuple instead of explicitly.
190       This is useful for forwarding variable arguments from other functions / methods.
191   */
192   template <typename... Args> BufferWriter &printv(TextView fmt, std::tuple<Args...> const &args);
193 
194   /// Print using a preparsed @a fmt.
195   template <typename... Args> BufferWriter &print(BWFormat const &fmt, Args &&... args);
196   /** Print overload to take arguments as a tuple instead of explicitly.
197       This is useful for forwarding variable arguments from other functions / methods.
198   */
199   template <typename... Args> BufferWriter &printv(BWFormat const &fmt, std::tuple<Args...> const &args);
200 
201   /// Output the buffer contents to the @a stream.
202   /// @return The destination stream.
203   virtual std::ostream &operator>>(std::ostream &stream) const = 0;
204   /// Output the buffer contents to the file for file descriptor @a fd.
205   /// @return The number of bytes written.
206   virtual ssize_t operator>>(int fd) const = 0;
207 };
208 
209 /** A @c BufferWrite concrete subclass to write to a fixed size buffer.
210  *
211  * Copies and moves are forbidden because that leaves the original in a potentially bad state. An
212  * instance is cheap to construct and should be done explicitly when needed.
213  */
214 class FixedBufferWriter : public BufferWriter
215 {
216   using super_type = BufferWriter;
217   using self_type  = FixedBufferWriter;
218 
219 public:
220   /** Construct a buffer writer on a fixed @a buffer of size @a capacity.
221 
222       If writing goes past the end of the buffer, the excess is dropped.
223 
224       @note If you create a instance of this class with capacity == 0 (and a nullptr buffer), you
225       can use it to measure the number of characters a series of writes would result it (from the
226       extent() value) without actually writing.
227    */
228   FixedBufferWriter(char *buffer, size_t capacity);
229 
230   /** Construct empty buffer.
231    * This is useful for doing sizing before allocating a buffer.
232    */
233   FixedBufferWriter(std::nullptr_t);
234 
235   FixedBufferWriter(const FixedBufferWriter &) = delete;
236   FixedBufferWriter &operator=(const FixedBufferWriter &) = delete;
237   FixedBufferWriter(FixedBufferWriter &&)                 = delete;
238   FixedBufferWriter &operator=(FixedBufferWriter &&) = delete;
239 
FixedBufferWriter(MemSpan<char> & span)240   FixedBufferWriter(MemSpan<char> &span) : _buf(span.begin()), _capacity(static_cast<size_t>(span.size())) {}
241 
242   /// Write a single character @a c to the buffer.
243   FixedBufferWriter &
write(char c)244   write(char c) override
245   {
246     if (_attempted < _capacity) {
247       _buf[_attempted] = c;
248     }
249     ++_attempted;
250 
251     return *this;
252   }
253 
254   /// Write @a data to the buffer, up to @a length bytes.
255   FixedBufferWriter &
write(const void * data,size_t length)256   write(const void *data, size_t length) override
257   {
258     const size_t newSize = _attempted + length;
259 
260     if (_buf) {
261       if (newSize <= _capacity) {
262         std::memcpy(_buf + _attempted, data, length);
263       } else if (_attempted < _capacity) {
264         std::memcpy(_buf + _attempted, data, _capacity - _attempted);
265       }
266     }
267     _attempted = newSize;
268 
269     return *this;
270   }
271 
272   // Bring in non-overridden methods.
273   using super_type::write;
274 
275   /// Return the output buffer.
276   const char *
data() const277   data() const override
278   {
279     return _buf;
280   }
281 
282   /// Return whether there has been an error.
283   bool
error() const284   error() const override
285   {
286     return _attempted > _capacity;
287   }
288 
289   /// Get the start of the unused output buffer.
290   char *
auxBuffer()291   auxBuffer() override
292   {
293     return error() ? nullptr : _buf + _attempted;
294   }
295 
296   /// Advance the used part of the output buffer.
297   FixedBufferWriter &
fill(size_t n)298   fill(size_t n) override
299   {
300     _attempted += n;
301 
302     return *this;
303   }
304 
305   /// Get the total capacity of the output buffer.
306   size_t
capacity() const307   capacity() const override
308   {
309     return _capacity;
310   }
311 
312   /// Get the total output sent to the writer.
313   size_t
extent() const314   extent() const override
315   {
316     return _attempted;
317   }
318 
319   /// Reduce the capacity by @a n.
320   FixedBufferWriter &
clip(size_t n)321   clip(size_t n) override
322   {
323     ink_assert(n <= _capacity);
324 
325     _capacity -= n;
326 
327     return *this;
328   }
329 
330   /// Extend the capacity by @a n.
331   FixedBufferWriter &
extend(size_t n)332   extend(size_t n) override
333   {
334     if (error()) {
335       _attempted = _capacity;
336     }
337 
338     _capacity += n;
339 
340     return *this;
341   }
342 
343   /// Reduce extent to @a n.
344   /// If @a n is less than the capacity the error condition, if any, is cleared.
345   /// This can be used to clear the output by calling @c reduce(0). In contrast
346   /// to @c clip this reduces the data in the buffer, rather than the capacity.
347   self_type &
reduce(size_t n)348   reduce(size_t n)
349   {
350     ink_assert(n <= _attempted);
351 
352     _attempted = n;
353     return *this;
354   }
355 
356   /// Clear the buffer, reset to empty (no data).
357   /// This is a convenience for reusing a buffer. For instance
358   /// @code
359   ///   bw.reset().print("....."); // clear old data and print new data.
360   /// @endcode
361   /// This is equivalent to @c reduce(0) but clearer for that case.
362   self_type &
reset()363   reset()
364   {
365     _attempted = 0;
366     return *this;
367   }
368 
369   /// Provide a string_view of all successfully written characters.
370   std::string_view
view() const371   view() const
372   {
373     return std::string_view(_buf, size());
374   }
375 
376   /// Provide a @c string_view of all successfully written characters as a user conversion.
377   operator std::string_view() const { return view(); }
378 
379   /** Get a @c FixedBufferWriter for the unused output buffer.
380 
381       If @a reserve is non-zero then the buffer size for the auxiliary writer will be @a reserve bytes
382       smaller than the remaining buffer. This "reserves" space for additional output after writing
383       to the auxiliary buffer, in a manner similar to @c clip / @c extend.
384    */
385   FixedBufferWriter
auxWriter(size_t reserve=0)386   auxWriter(size_t reserve = 0)
387   {
388     return {this->auxBuffer(), reserve < this->remaining() ? this->remaining() - reserve : 0};
389   }
390 
391   /// Output the buffer contents to the @a stream.
392   std::ostream &operator>>(std::ostream &stream) const override;
393   /// Output the buffer contents to the file for file descriptor @a fd.
394   ssize_t operator>>(int fd) const override;
395 
396   // Overrides for co-variance
397   template <typename... Rest> self_type &print(TextView fmt, Rest &&... rest);
398   template <typename... Args> self_type &printv(TextView fmt, std::tuple<Args...> const &args);
399   template <typename... Args> self_type &print(BWFormat const &fmt, Args &&... args);
400   template <typename... Args> self_type &printv(BWFormat const &fmt, std::tuple<Args...> const &args);
401 
402 protected:
403   char *const _buf;      ///< Output buffer.
404   size_t _capacity;      ///< Size of output buffer.
405   size_t _attempted = 0; ///< Number of characters written, including those discarded due error condition.
406 private:
407   // INTERNAL - Overload removed, make sure it's not used.
408   BufferWriter &write(size_t n);
409 };
410 
411 /** A buffer writer that writes to an array of char (of fixed size N) that is internal to the writer instance.
412 
413     It's called 'local' because instances are typically declared as stack-allocated, local function
414     variables.
415 */
416 template <size_t N> class LocalBufferWriter : public FixedBufferWriter
417 {
418   using self_type  = LocalBufferWriter;
419   using super_type = FixedBufferWriter;
420 
421 public:
422   /// Construct an empty writer.
LocalBufferWriter()423   LocalBufferWriter() : FixedBufferWriter(_arr, N) {}
424 
425   /// Copy another writer.
426   /// Any data in @a that is copied over.
LocalBufferWriter(const LocalBufferWriter & that)427   LocalBufferWriter(const LocalBufferWriter &that) : FixedBufferWriter(_arr, N)
428   {
429     std::memcpy(_arr, that._arr, that.size());
430     _attempted = that._attempted;
431   }
432 
433   /// Copy another writer.
434   /// Any data in @a that is copied over.
LocalBufferWriter(const LocalBufferWriter<K> & that)435   template <size_t K> LocalBufferWriter(const LocalBufferWriter<K> &that) : FixedBufferWriter(_arr, N)
436   {
437     size_t n = std::min(N, that.size());
438     std::memcpy(_arr, that.data(), n);
439     // if a bigger space here, don't leave a gap between size and attempted.
440     _attempted = N > K ? n : that.extent();
441   }
442 
443   /// Copy another writer.
444   /// Any data in @a that is copied over.
445   LocalBufferWriter &
operator =(const LocalBufferWriter & that)446   operator=(const LocalBufferWriter &that)
447   {
448     if (this != &that) {
449       _attempted = that.extent();
450       std::memcpy(_buf, that._buf, that.size());
451     }
452 
453     return *this;
454   }
455 
456   /// Copy another writer.
457   /// Any data in @a that is copied over.
458   template <size_t K>
459   LocalBufferWriter &
operator =(const LocalBufferWriter<K> & that)460   operator=(const LocalBufferWriter<K> &that)
461   {
462     size_t n = std::min(N, that.size());
463     // if a bigger space here, don't leave a gap between size and attempted.
464     _attempted = N > K ? n : that.extent();
465     std::memcpy(_arr, that.data(), n);
466     return *this;
467   }
468 
469   /// Increase capacity by @a n.
470   LocalBufferWriter &
extend(size_t n)471   extend(size_t n) override
472   {
473     if (error()) {
474       _attempted = _capacity;
475     }
476 
477     _capacity += n;
478 
479     ink_assert(_capacity <= N);
480 
481     return *this;
482   }
483 
484 protected:
485   char _arr[N]; ///< output buffer.
486 };
487 
488 // --------------- Implementation --------------------
489 /** Overridable formatting for type @a V.
490 
491     This is the output generator for data to a @c BufferWriter. Default stream operators call this with
492     the default format specification (although those can be overloaded specifically for performance).
493     User types should overload this function to format output for that type.
494 
495     @code
496       BufferWriter &
497       bwformat(BufferWriter &w, BWFSpec  &, V const &v)
498       {
499         // generate output on @a w
500       }
501     @endcode
502 
503     The argument can be passed by value if that would be more efficient.
504   */
505 
506 namespace bw_fmt
507 {
508   /// Internal signature for template generated formatting.
509   /// @a args is a forwarded tuple of arguments to be processed.
510   template <typename TUPLE> using ArgFormatterSignature = BufferWriter &(*)(BufferWriter &w, BWFSpec const &, TUPLE const &args);
511 
512   /// Internal error / reporting message generators
513   void Err_Bad_Arg_Index(BufferWriter &w, int i, size_t n);
514 
515   // MSVC will expand the parameter pack inside a lambda but not gcc, so this indirection is required.
516 
517   /// This selects the @a I th argument in the @a TUPLE arg pack and calls the formatter on it. This
518   /// (or the equivalent lambda) is needed because the array of formatters must have a homogenous
519   /// signature, not vary per argument. Effectively this indirection erases the type of the specific
520   /// argument being formatted. Instances of this have the signature @c ArgFormatterSignature.
521   template <typename TUPLE, size_t I>
522   BufferWriter &
Arg_Formatter(BufferWriter & w,BWFSpec const & spec,TUPLE const & args)523   Arg_Formatter(BufferWriter &w, BWFSpec const &spec, TUPLE const &args)
524   {
525     return bwformat(w, spec, std::get<I>(args));
526   }
527 
528   /// This exists only to expand the index sequence into an array of formatters for the tuple type
529   /// @a TUPLE.  Due to language limitations it cannot be done directly. The formatters can be
530   /// accessed via standard array access in contrast to templated tuple access. The actual array is
531   /// static and therefore at run time the only operation is loading the address of the array.
Get_Arg_Formatter_Array(std::index_sequence<N...>)532   template <typename TUPLE, size_t... N> ArgFormatterSignature<TUPLE> *Get_Arg_Formatter_Array(std::index_sequence<N...>)
533   {
534     static ArgFormatterSignature<TUPLE> fa[sizeof...(N)] = {&bw_fmt::Arg_Formatter<TUPLE, N>...};
535     return fa;
536   }
537 
538   /// Perform alignment adjustments / fill on @a w of the content in @a lw.
539   /// This is the normal mechanism, but a number of the builtin types handle this internally
540   /// for performance reasons.
541   void Do_Alignment(BWFSpec const &spec, BufferWriter &w, BufferWriter &lw);
542 
543   /// Global named argument table.
544   using GlobalSignature = void (*)(BufferWriter &, BWFSpec const &);
545   using GlobalTable     = std::map<std::string_view, GlobalSignature>;
546   extern GlobalTable BWF_GLOBAL_TABLE;
547   extern GlobalSignature Global_Table_Find(std::string_view name);
548 
549   /// Generic integral conversion.
550   BufferWriter &Format_Integer(BufferWriter &w, BWFSpec const &spec, uintmax_t n, bool negative_p);
551 
552   /// Generic floating point conversion.
553   BufferWriter &Format_Floating(BufferWriter &w, BWFSpec const &spec, double n, bool negative_p);
554 
555 } // namespace bw_fmt
556 
557 using BWGlobalNameSignature = bw_fmt::GlobalSignature;
558 /// Add a global @a name to BufferWriter formatting, output generated by @a formatter.
559 /// @return @c true if the name was register, @c false if not (name already in use).
560 bool bwf_register_global(std::string_view name, BWGlobalNameSignature formatter);
561 
562 /** Compiled BufferWriter format.
563 
564     @note This is not as useful as hoped, the performance is not much better using this than parsing
565     on the fly (about 30% better, which is fine for tight loops but not for general use).
566  */
567 class BWFormat
568 {
569 public:
570   /// Construct from a format string @a fmt.
571   BWFormat(TextView fmt);
572   ~BWFormat();
573 
574   /** Parse elements of a format string.
575 
576       @param fmt The format string [in|out]
577       @param literal A literal if found
578       @param spec A specifier if found (less enclosing braces)
579       @return @c true if a specifier was found, @c false if not.
580 
581       Pull off the next literal and/or specifier from @a fmt. The return value distinguishes
582       the case of no specifier found (@c false) or an empty specifier (@c true).
583 
584    */
585   static bool parse(TextView &fmt, std::string_view &literal, std::string_view &spec);
586 
587   /** Parsed items from the format string.
588 
589       Literals are handled by putting the literal text in the extension field and setting the
590       global formatter @a _gf to @c LiteralFormatter, which writes out the extension as a literal.
591    */
592   struct Item {
593     BWFSpec _spec; ///< Specification.
594     /// If the spec has a global formatter name, cache it here.
595     mutable bw_fmt::GlobalSignature _gf = nullptr;
596 
Itemts::BWFormat::Item597     Item() {}
Itemts::BWFormat::Item598     Item(BWFSpec const &spec, bw_fmt::GlobalSignature gf) : _spec(spec), _gf(gf) {}
599   };
600 
601   using Items = std::vector<Item>;
602   Items _items; ///< Items from format string.
603 
604 protected:
605   /// Handles literals by writing the contents of the extension directly to @a w.
606   static void Format_Literal(BufferWriter &w, BWFSpec const &spec);
607 };
608 
609 template <typename... Args>
610 BufferWriter &
print(TextView fmt,Args &&...args)611 BufferWriter::print(TextView fmt, Args &&... args)
612 {
613   return this->printv(fmt, std::forward_as_tuple(args...));
614 }
615 
616 template <typename... Args>
617 BufferWriter &
printv(TextView fmt,std::tuple<Args...> const & args)618 BufferWriter::printv(TextView fmt, std::tuple<Args...> const &args)
619 {
620   using namespace std::literals;
621   static constexpr int N = sizeof...(Args); // used as loop limit
622   static const auto fa   = bw_fmt::Get_Arg_Formatter_Array<decltype(args)>(std::index_sequence_for<Args...>{});
623   int arg_idx            = 0; // the next argument index to be processed.
624 
625   while (fmt.size()) {
626     // Next string piece of interest is an (optional) literal and then an (optional) format specifier.
627     // There will always be a specifier except for the possible trailing literal.
628     std::string_view lit_v;
629     std::string_view spec_v;
630     bool spec_p = BWFormat::parse(fmt, lit_v, spec_v);
631 
632     if (lit_v.size()) {
633       this->write(lit_v);
634     }
635 
636     if (spec_p) {
637       BWFSpec spec{spec_v}; // parse the specifier.
638       size_t width = this->remaining();
639       if (spec._max < width) {
640         width = spec._max;
641       }
642       FixedBufferWriter lw{this->auxBuffer(), width};
643 
644       if (spec._name.size() == 0) {
645         spec._idx = arg_idx;
646       }
647       if (0 <= spec._idx) {
648         if (spec._idx < N) {
649           fa[spec._idx](lw, spec, args);
650         } else {
651           bw_fmt::Err_Bad_Arg_Index(lw, spec._idx, N);
652         }
653         ++arg_idx;
654       } else if (spec._name.size()) {
655         auto gf = bw_fmt::Global_Table_Find(spec._name);
656         if (gf) {
657           gf(lw, spec);
658         } else {
659           lw.write("{~"sv).write(spec._name).write("~}"sv);
660         }
661       }
662       if (lw.extent()) {
663         bw_fmt::Do_Alignment(spec, *this, lw);
664       }
665     }
666   }
667   return *this;
668 }
669 
670 template <typename... Args>
671 BufferWriter &
print(BWFormat const & fmt,Args &&...args)672 BufferWriter::print(BWFormat const &fmt, Args &&... args)
673 {
674   return this->printv(fmt, std::forward_as_tuple(args...));
675 }
676 
677 template <typename... Args>
678 BufferWriter &
printv(BWFormat const & fmt,std::tuple<Args...> const & args)679 BufferWriter::printv(BWFormat const &fmt, std::tuple<Args...> const &args)
680 {
681   using namespace std::literals;
682   static constexpr int N = sizeof...(Args);
683   static const auto fa   = bw_fmt::Get_Arg_Formatter_Array<decltype(args)>(std::index_sequence_for<Args...>{});
684 
685   for (BWFormat::Item const &item : fmt._items) {
686     size_t width = this->remaining();
687     if (item._spec._max < width) {
688       width = item._spec._max;
689     }
690     FixedBufferWriter lw{this->auxBuffer(), width};
691     if (item._gf) {
692       item._gf(lw, item._spec);
693     } else {
694       auto idx = item._spec._idx;
695       if (0 <= idx && idx < N) {
696         fa[idx](lw, item._spec, args);
697       } else if (item._spec._name.size()) {
698         lw.write("{~"sv).write(item._spec._name).write("~}"sv);
699       }
700     }
701     bw_fmt::Do_Alignment(item._spec, *this, lw);
702   }
703   return *this;
704 }
705 
706 // Must be first so that other inline formatters can use it.
707 BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, std::string_view sv);
708 
709 // Pointers that are not specialized.
710 inline BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,const void * ptr)711 bwformat(BufferWriter &w, BWFSpec const &spec, const void *ptr)
712 {
713   BWFSpec ptr_spec{spec};
714   ptr_spec._radix_lead_p = true;
715 
716   if (ptr == nullptr) {
717     if (spec._type == 's' || spec._type == 'S') {
718       ptr_spec._type = BWFSpec::DEFAULT_TYPE;
719       ptr_spec._ext  = ""_sv; // clear any extension.
720       return bwformat(w, spec, spec._type == 's' ? "null"_sv : "NULL"_sv);
721     } else if (spec._type == BWFSpec::DEFAULT_TYPE) {
722       return w; // print nothing if there is no format character override.
723     }
724   }
725 
726   if (ptr_spec._type == BWFSpec::DEFAULT_TYPE || ptr_spec._type == 'p') {
727     ptr_spec._type = 'x'; // if default or 'p;, switch to lower hex.
728   } else if (ptr_spec._type == 'P') {
729     ptr_spec._type = 'X'; // P means upper hex, overriding other specializations.
730   }
731   return bw_fmt::Format_Integer(w, ptr_spec, reinterpret_cast<intptr_t>(ptr), false);
732 }
733 
734 // MemSpan
735 BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, MemSpan<void> const &span);
736 
737 // -- Common formatters --
738 
739 // Capture this explicitly so it doesn't go to any other pointer type.
740 inline BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,std::nullptr_t)741 bwformat(BufferWriter &w, BWFSpec const &spec, std::nullptr_t)
742 {
743   return bwformat(w, spec, static_cast<void *>(nullptr));
744 }
745 
746 template <size_t N>
747 BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,const char (& a)[N])748 bwformat(BufferWriter &w, BWFSpec const &spec, const char (&a)[N])
749 {
750   return bwformat(w, spec, std::string_view(a, N - 1));
751 }
752 
753 inline BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,const char * v)754 bwformat(BufferWriter &w, BWFSpec const &spec, const char *v)
755 {
756   if (spec._type == 'x' || spec._type == 'X' || spec._type == 'p') {
757     bwformat(w, spec, static_cast<const void *>(v));
758   } else if (v != nullptr) {
759     bwformat(w, spec, std::string_view(v));
760   } else {
761     bwformat(w, spec, nullptr);
762   }
763   return w;
764 }
765 
766 inline BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,TextView tv)767 bwformat(BufferWriter &w, BWFSpec const &spec, TextView tv)
768 {
769   return bwformat(w, spec, static_cast<std::string_view>(tv));
770 }
771 
772 inline BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,std::string const & s)773 bwformat(BufferWriter &w, BWFSpec const &spec, std::string const &s)
774 {
775   return bwformat(w, spec, std::string_view{s});
776 }
777 
778 template <typename F>
779 auto
bwformat(BufferWriter & w,BWFSpec const & spec,F && f)780 bwformat(BufferWriter &w, BWFSpec const &spec, F &&f) ->
781   typename std::enable_if<std::is_floating_point<typename std::remove_reference<F>::type>::value, BufferWriter &>::type
782 {
783   return f < 0 ? bw_fmt::Format_Floating(w, spec, -f, true) : bw_fmt::Format_Floating(w, spec, f, false);
784 }
785 
786 /* Integer types.
787 
788    Due to some oddities for MacOS building, need a bit more template magic here. The underlying
789    integer rendering is in @c Format_Integer which takes @c intmax_t or @c uintmax_t. For @c
790    bwformat templates are defined, one for signed and one for unsigned. These forward their argument
791    to the internal renderer. To avoid additional ambiguity the template argument is checked with @c
792    std::enable_if to invalidate the overload if the argument type isn't a signed / unsigned
793    integer. One exception to this is @c char which is handled by a previous overload in order to
794    treat the value as a character and not an integer. The overall benefit is this works for any set
795    of integer types, rather tuning and hoping to get just the right set of overloads.
796  */
797 
798 template <typename I>
799 auto
bwformat(BufferWriter & w,BWFSpec const & spec,I && i)800 bwformat(BufferWriter &w, BWFSpec const &spec, I &&i) ->
801   typename std::enable_if<std::is_unsigned<typename std::remove_reference<I>::type>::value &&
802                             std::is_integral<typename std::remove_reference<I>::type>::value,
803                           BufferWriter &>::type
804 {
805   return bw_fmt::Format_Integer(w, spec, i, false);
806 }
807 
808 template <typename I>
809 auto
bwformat(BufferWriter & w,BWFSpec const & spec,I && i)810 bwformat(BufferWriter &w, BWFSpec const &spec, I &&i) ->
811   typename std::enable_if<std::is_signed<typename std::remove_reference<I>::type>::value &&
812                             std::is_integral<typename std::remove_reference<I>::type>::value,
813                           BufferWriter &>::type
814 {
815   return i < 0 ? bw_fmt::Format_Integer(w, spec, -i, true) : bw_fmt::Format_Integer(w, spec, i, false);
816 }
817 
818 inline BufferWriter &
bwformat(BufferWriter & w,BWFSpec const &,char c)819 bwformat(BufferWriter &w, BWFSpec const &, char c)
820 {
821   return w.write(c);
822 }
823 
824 inline BufferWriter &
bwformat(BufferWriter & w,BWFSpec const & spec,bool f)825 bwformat(BufferWriter &w, BWFSpec const &spec, bool f)
826 {
827   using namespace std::literals;
828   if ('s' == spec._type) {
829     w.write(f ? "true"sv : "false"sv);
830   } else if ('S' == spec._type) {
831     w.write(f ? "TRUE"sv : "FALSE"sv);
832   } else {
833     bw_fmt::Format_Integer(w, spec, static_cast<uintmax_t>(f), false);
834   }
835   return w;
836 }
837 
838 // Generically a stream operator is a formatter with the default specification.
839 template <typename V>
840 BufferWriter &
operator <<(BufferWriter & w,V && v)841 operator<<(BufferWriter &w, V &&v)
842 {
843   return bwformat(w, BWFSpec::DEFAULT, std::forward<V>(v));
844 }
845 
846 // std::string support
847 /** Print to a @c std::string
848 
849     Print to the string @a s. If there is overflow then resize the string sufficiently to hold the output
850     and print again. The effect is the string is resized only as needed to hold the output.
851  */
852 template <typename... Args>
853 std::string &
bwprintv(std::string & s,ts::TextView fmt,std::tuple<Args...> const & args)854 bwprintv(std::string &s, ts::TextView fmt, std::tuple<Args...> const &args)
855 {
856   auto len = s.size(); // remember initial size
857   size_t n = ts::FixedBufferWriter(const_cast<char *>(s.data()), s.size()).printv(fmt, std::move(args)).extent();
858   s.resize(n);   // always need to resize - if shorter, must clip pre-existing text.
859   if (n > len) { // dropped data, try again.
860     ts::FixedBufferWriter(const_cast<char *>(s.data()), s.size()).printv(fmt, std::move(args));
861   }
862   return s;
863 }
864 
865 template <typename... Args>
866 std::string &
bwprint(std::string & s,ts::TextView fmt,Args &&...args)867 bwprint(std::string &s, ts::TextView fmt, Args &&... args)
868 {
869   return bwprintv(s, fmt, std::forward_as_tuple(args...));
870 }
871 
872 // -- FixedBufferWriter --
FixedBufferWriter(std::nullptr_t)873 inline FixedBufferWriter::FixedBufferWriter(std::nullptr_t) : _buf(nullptr), _capacity(0) {}
874 
FixedBufferWriter(char * buffer,size_t capacity)875 inline FixedBufferWriter::FixedBufferWriter(char *buffer, size_t capacity) : _buf(buffer), _capacity(capacity)
876 {
877   ink_assert(_capacity == 0 || buffer != nullptr);
878 }
879 
880 template <typename... Args>
881 inline auto
print(TextView fmt,Args &&...args)882 FixedBufferWriter::print(TextView fmt, Args &&... args) -> self_type &
883 {
884   return static_cast<self_type &>(this->super_type::printv(fmt, std::forward_as_tuple(args...)));
885 }
886 
887 template <typename... Args>
888 inline auto
printv(TextView fmt,std::tuple<Args...> const & args)889 FixedBufferWriter::printv(TextView fmt, std::tuple<Args...> const &args) -> self_type &
890 {
891   return static_cast<self_type &>(this->super_type::printv(fmt, args));
892 }
893 
894 template <typename... Args>
895 inline auto
print(BWFormat const & fmt,Args &&...args)896 FixedBufferWriter::print(BWFormat const &fmt, Args &&... args) -> self_type &
897 {
898   return static_cast<self_type &>(this->super_type::printv(fmt, std::forward_as_tuple(args...)));
899 }
900 
901 template <typename... Args>
902 inline auto
printv(BWFormat const & fmt,std::tuple<Args...> const & args)903 FixedBufferWriter::printv(BWFormat const &fmt, std::tuple<Args...> const &args) -> self_type &
904 {
905   return static_cast<self_type &>(this->super_type::printv(fmt, args));
906 }
907 
908 // Basic format wrappers - these are here because they're used internally.
909 namespace bwf
910 {
911   namespace detail
912   {
913     /** Write out raw memory in hexadecimal wrapper.
914      *
915      * This wrapper indicates the contained view should be dumped as raw memory in hexadecimal format.
916      * This is intended primarily for internal use by other formatting logic.
917      *
918      * @see Hex_Dump
919      */
920     struct MemDump {
921       std::string_view _view;
922 
923       /** Dump @a n bytes starting at @a mem as hex.
924        *
925        * @param mem First byte of memory to dump.
926        * @param n Number of bytes.
927        */
MemDumpts::bwf::detail::MemDump928       MemDump(void const *mem, size_t n) : _view(static_cast<char const *>(mem), n) {}
929     };
930   } // namespace detail
931 
932   /** Treat @a t as raw memory and dump the memory as hexadecimal.
933    *
934    * @tparam T Type of argument.
935    * @param t Object to dump.
936    * @return @a A wrapper to do a hex dump.
937    *
938    * This is the standard way to do a hexadecimal memory dump of an object.
939    *
940    * @internal This function exists so that other types can overload it for special processing,
941    * which would not be possible with just @c HexDump.
942    */
943   template <typename T>
944   detail::MemDump
Hex_Dump(T const & t)945   Hex_Dump(T const &t)
946   {
947     return {&t, sizeof(T)};
948   }
949 } // namespace bwf
950 
951 BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::detail::MemDump const &hex);
952 
953 } // end namespace ts
954 
955 namespace std
956 {
957 inline ostream &
operator <<(ostream & s,ts::BufferWriter const & w)958 operator<<(ostream &s, ts::BufferWriter const &w)
959 {
960   return w >> s;
961 }
962 } // end namespace std
963