This is an automated email from the ASF dual-hosted git repository.

amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new cf32494  BufferWriter: Documentation and some API cleanup.
cf32494 is described below

commit cf324942778ebf082da1fbf37e66234d6e8733b9
Author: Alan M. Carroll <a...@apache.org>
AuthorDate: Fri Feb 16 14:57:57 2018 -0600

    BufferWriter: Documentation and some API cleanup.
---
 .../internal-libraries/buffer-writer.en.rst        | 302 +++++++++++++++++++
 .../internal-libraries/index.en.rst                |   1 +
 iocore/eventsystem/I_MIOBufferWriter.h             |   4 +-
 .../eventsystem/unit-tests/test_MIOBufferWriter.cc |   4 +-
 lib/ts/BufferWriter.h                              | 319 +++++++++++++++------
 lib/ts/Diags.cc                                    |   9 +-
 lib/ts/unit-tests/test_BufferWriter.cc             |  45 ++-
 proxy/http/HttpTransactHeaders.cc                  |   8 +-
 8 files changed, 579 insertions(+), 113 deletions(-)

diff --git a/doc/developer-guide/internal-libraries/buffer-writer.en.rst 
b/doc/developer-guide/internal-libraries/buffer-writer.en.rst
new file mode 100644
index 0000000..d72d7ba
--- /dev/null
+++ b/doc/developer-guide/internal-libraries/buffer-writer.en.rst
@@ -0,0 +1,302 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you under the Apache License, Version 2.0 (the
+   "License"); you may not use this file except in compliance
+   with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing,
+   software distributed under the License is distributed on an
+   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+   KIND, either express or implied.  See the License for the
+   specific language governing permissions and limitations
+   under the License.
+
+.. include:: ../../common.defs
+
+.. highlight:: cpp
+.. default-domain:: cpp
+
+.. _BufferWriter:
+
+BufferWriter
+*************
+
+:class:`BufferWriter` is designed to make writing text to a buffer fast and 
safe. The output buffer
+can have a size and :class:`BufferWriter` will prevent writing past the end, 
while tracking the
+theoretical output to enable buffer resizing after the fact. This also lets a 
:class:`BufferWriter`
+instance write into the middle of a larger buffer, making nested output logic 
easy to build.
+
+Synopsis
+++++++++
+
+.. code-block:: cpp
+
+   #include <ts/BufferWriter.h>
+
+Description
++++++++++++
+
+:class:`BufferWriter` is an abstract base class, in the style of 
:code:`std::ostream`. There are
+several subclasses for various use cases. When passing around this is the 
common type.
+
+:class:`FixedBufferWriter` writes to an externally provided buffer of a fixed 
length. The buffer must
+be provided to the constructor. This will generally be used in a function 
where the target buffer is
+external to the function or already exists.
+
+:class:`LocalBufferWriter` is a templated class whose template argument is the 
size of an internal
+buffer. This is useful when the buffer is local to a function and the results 
will be transferred
+from the buffer to other storage after the output is assembled. Rather than 
having code like
+
+.. code-block:: cpp
+
+   char buff[1024];
+   ts::FixedBufferWriter w(buff, sizeof(buff));
+
+can be more compactly and robustly done as:
+
+.. code-block:: cpp
+
+   ts::LocalBufferWriter<1024> w;
+
+In many cases using :class:`LocalBufferWriter` this is the only place the size 
of the buffer needs
+to be specified, and therefore can simply be a constant without the overhead 
of defining a size to
+maintain consistency.
+
+Writing
+-------
+
+The basic mechanism for writing to a :class:`BufferWriter` is 
:func:`BufferWriter::write`.
+This is an overloaded method for a character (:code:`char`), a buffer 
(:code:`void *, size_t`)
+and a string view (:class:`string_view`). Because there is a constructor for 
:class:`string_view`
+that takes a :code:`const char*` as a C string, passing a literal string works 
as expected.
+
+There are also stream operators in the style of C++ stream I/O. The basic 
template is
+
+.. code-block:: cpp
+
+   template < typename T > ts::BufferWriter& operator << (ts::BufferWriter& w, 
T const& t);
+
+Several basic types are overloaded and it is easy to extend to additional 
types. For instance, to make :code:`ts::TextView` work with 
:class:`BufferWriter`, the code would be
+
+.. code-block:: cpp
+
+   ts::BufferWriter & operator << (ts::BufferWriter & w, ts::TextView const & 
sv) {
+      w.write(sv.data(), sv.size());
+      return w;
+   }
+
+Reading
+-------
+
+The data in the buffer can be extracted using :func:`BufferWriter::data`. This 
and
+:func:`BufferWriter::size` return a pointer to the start of the buffer and the 
amount of data
+written to the buffer. This is very similar to :func:`BufferWriter::view` 
which returns a
+:class:`string_view` which covers the output data. Calling 
:func:`BufferWriter::error` will indicate
+if more data than space available was written. :func:`BufferWriter::extent` 
returns the amount of
+data written to the :class:`BufferWriter`. This can be used in a two pass 
style with a null / size 0
+buffer to determine the buffer size required for the full output.
+
+Advanced
+--------
+
+The :func:`BufferWriter::clip` and :func:`BufferWriter::extend` methods can be 
used to reserve space
+in the buffer. A common use case for this is to guarantee matching delimiters 
in output if buffer
+space is exhausted. :func:`BufferWriter::clip` can be used to temporarily 
reduce the buffer size by
+an amount large enough to hold the terminal delimiter. After writing the 
contained output,
+:func:`BufferWriter::extend` can be used to restore the capacity and then 
output the terminal
+delimiter.
+
+.. warning:: **Never** call :func:`BufferWriter::extend` without previously 
calling :func:`BufferWriter::clip` and always pass the same argument value.
+
+:func:`BufferWriter::remaining` returns the amount of buffer space not yet 
consumed.
+
+:func:`BufferWriter::auxBuffer` returns a pointer to the first byte of the 
buffer not yet used. This
+is useful to do speculative output, or do bounded output in a manner similar 
to use
+:func:`BufferWriter::clip` and :func:`BufferWriter::extend`. A new 
:class:`BufferWriter` instance
+can be constructed with
+
+.. code-block:: cpp
+
+   ts::FixedBufferWriter subw(w.auxBuffer(), w.remaining());
+
+Output can be written to :arg:`subw`. If successful, then 
:code:`w.fill(subw.size())` will add that
+output to the main buffer. Depending on the purpose, 
:code:`w.fill(subw.extent())` can be used -
+this will track the attempted output if sizing is important. Note that space 
for any terminal
+markers can be reserved by bumping down the size from 
:func:`BufferWriter::remaining`. Be careful of
+underrun as the argument is an unsigned type.
+
+If there is an error then :arg:`subw` can be ignored and some suitable error 
output written to
+:arg:`w` instead. A common use case is to verify there is sufficient space in 
the buffer and create
+a "not enough space" message if not. E.g.
+
+.. code-block:: cpp
+
+   ts::FixedBufferWriter subw(w.auxBuffer(), w.remaining());
+   this->write_some_output(subw);
+   if (!subw.error()) w.fill(subw.size());
+   else w << "Insufficient space"_sv;
+
+Examples
+++++++++
+
+For example, error prone code that looks like
+
+.. code-block:: cpp
+
+   char new_via_string[1024]; // 512-bytes for hostname+via string, 512-bytes 
for the debug info
+   char * via_string = new_via_string;
+   char * via_limit  = via_string + sizeof(new_via_string);
+
+   // ...
+
+   * via_string++ = ' ';
+   * via_string++ = '[';
+
+   // incoming_via can be max MAX_VIA_INDICES+1 long (i.e. around 25 or so)
+   if (s->txn_conf->insert_request_via_string > 2) { // Highest verbosity
+      via_string += nstrcpy(via_string, incoming_via);
+   } else {
+      memcpy(via_string, incoming_via + VIA_CLIENT, VIA_SERVER - VIA_CLIENT);
+      via_string += VIA_SERVER - VIA_CLIENT;
+   }
+   *via_string++ = ']';
+
+becomes
+
+.. code-block:: cpp
+
+   ts::LocalBufferWriter<1024> w; // 1K internal buffer.
+
+   // ...
+
+   w << " [";
+   if (s->txn_conf->insert_request_via_string > 2) { // Highest verbosity
+      w << incoming_via;
+   } else {
+      w << ts::string_view{incoming_via + VIA_CLIENT, VIA_SERVER - VIA_CLIENT};
+   }
+   w << ']';
+
+Note that in addition there will be no overrun on the memory buffer in 
:arg:`w`, in strong contrast
+to the original code.
+
+Reference
++++++++++
+
+.. class:: BufferWriter
+
+   :class:`BufferWriter` is the abstract base class which defines the basic 
client interface. This
+   is intended to be the reference type used when passing concrete instances 
rather than having to
+   support the distinct types.
+
+   .. function:: BufferWriter & write(void * data, size_t length)
+
+      Write to the buffer starting at :arg:`data` for at most :arg:`length` 
bytes. If there is not
+      enough room to fit all the data, none is written.
+
+   .. function:: BufferWriter & write(string_view str)
+
+      Write the string :arg:`str` to the buffer. If there is not enough room 
to write the string no
+      data is written.
+
+   .. function:: BufferWriter & write(char c)
+
+      Write the character :arg:`c` to the buffer. If there is no space in the 
buffer the character
+      is not written.
+
+   .. function:: fill(size_t n)
+
+      Increase the output size by :arg:`n` without changing the buffer 
contents. This is used in
+      conjuction with :func:`BufferWriter::auxBuffer` after writing output to 
the buffer returned by
+      that method. If this method is not called then such output will not be 
counted by
+      :func:`BufferWriter::size` and will be overwritten by subsequent output.
+
+   .. function:: char * data() const
+
+      Return a pointer to start of the buffer.
+
+   .. function:: size_t size() const
+
+      Return the number of valid (written) bytes in the buffer.
+
+   .. function:: string_view view() const
+
+      Return a :class:`string_view` that covers the valid data in the buffer.
+
+   .. function:: size_t remaining() const
+
+      Return the number of available remaining bytes that could be written to 
the buffer.
+
+   .. function:: size_t capacity() const
+
+      Return the number of bytes in the buffer.
+
+   .. function:: char * auxBuffer() const
+
+      Return a pointer to the first byte in the buffer not yet consumed.
+
+   .. function:: BufferWriter & clip(size_t n)
+
+      Reduce the available space by :arg:`n` bytes.
+
+   .. function:: BufferWriter & extend(size_t n)
+
+      Increase the available space by :arg:`n` bytes. Extreme care must be 
used with this method as
+      :class:`BufferWriter` will trust the argument, having no way to verify 
it. In general this
+      should only be used after calling :func:`BufferWriter::clip` and passing 
the same value.
+      Together these allow the buffer to be temporarily reduced to reserve 
space for the trailing
+      element of a required pair of output strings, e.g. making sure a closing 
quote can be written
+      even if part of the string is not.
+
+   .. function:: bool error() const
+
+      Return :code:`true` if the buffer has overflowed from writing, 
:code:`false` if not.
+
+   .. function:: size_t extent() const
+
+      Return the total number of bytes in all attempted writes to this buffer. 
This value allows a
+      successful retry in case of overflow, presuming the output data doesn't 
change. This works
+      well with the standard "try before you buy" approach of attempting to 
write output, counting
+      the characters needed, then allocating a sufficiently sized buffer and 
actually writing.
+
+.. class:: FixedBufferWriter : public BufferWriter
+
+   This is a class that implements :class:`BufferWriter` on a fixed buffer, 
passed in to the constructor.
+
+   .. function:: FixedBufferWriter(void * buffer, size_t length)
+
+      Construct an instance that will write to :arg:`buffer` at most 
:arg:`length` bytes. If more
+      data is written, all data past the maximum size is discarded.
+
+   .. function:: reduce(size_t n)
+
+      Roll back the output to :arg:`n` bytes. This is useful primarily for 
clearing the buffer by
+      calling :code:`reduce(0)`.
+
+   .. function:: FixedBufferWriter auxWriter(size_t reserve = 0)
+
+      Create a new instance of :class:`FixedBufferWriter` for the remaining 
output buffer. If
+      :arg:`reserve` is non-zero then if possible the capacity of the returned 
instance is reduced
+      by :arg:`reserve` bytes, in effect reserving that amount of space at the 
end. Note the space will
+      not be reserved if :arg:`reserve` is larger than the remaining output 
space.
+
+.. class:: template < size_t N > LocalBufferWriter : public BufferWriter
+
+   This is a convenience class which is a subclass of 
:class:`FixedBufferWriter`. It which creates a
+   buffer as a member rather than having an external buffer that is passed to 
the instance. The
+   buffer is :arg:`N` bytes long.
+
+   .. function:: LocalBufferWriter::LocalBufferWriter()
+
+      Construct an instance with a capacity of :arg:`N`.
+
+Futures
++++++++
+
+A planned future extension is a variant of :class:`BufferWriter` that operates 
on a
+:code:`MIOBuffer`. This would be very useful in many places that work with 
:code:`MIOBuffer`
+instances, most specifically in the body factory logic.
diff --git a/doc/developer-guide/internal-libraries/index.en.rst 
b/doc/developer-guide/internal-libraries/index.en.rst
index 1e569e3..0542b07 100644
--- a/doc/developer-guide/internal-libraries/index.en.rst
+++ b/doc/developer-guide/internal-libraries/index.en.rst
@@ -31,3 +31,4 @@ development team.
    string_view.en
    memview.en
    scalar.en
+   buffer-writer.en
diff --git a/iocore/eventsystem/I_MIOBufferWriter.h 
b/iocore/eventsystem/I_MIOBufferWriter.h
index 3943fc8..213255b 100644
--- a/iocore/eventsystem/I_MIOBufferWriter.h
+++ b/iocore/eventsystem/I_MIOBufferWriter.h
@@ -112,7 +112,7 @@ public:
   // This function should not be called if no auxiliary buffer is available.
   //
   MIOBufferWriter &
-  write(size_t n) override
+  fill(size_t n) override
   {
     if (n) {
       IOBufferBlock *iobbPtr = _miob.first_write_block();
@@ -169,6 +169,8 @@ private:
   {
     _miob.add_block();
   }
+  // INTERNAL - Overload removed, make sure it's not used.
+  MIOBufferWriter &write(size_t n);
 };
 
 #endif // include once
diff --git a/iocore/eventsystem/unit-tests/test_MIOBufferWriter.cc 
b/iocore/eventsystem/unit-tests/test_MIOBufferWriter.cc
index 34a4084..cb5b901 100644
--- a/iocore/eventsystem/unit-tests/test_MIOBufferWriter.cc
+++ b/iocore/eventsystem/unit-tests/test_MIOBufferWriter.cc
@@ -141,11 +141,11 @@ writeOnce(MIOBufferWriter &bw, std::size_t len)
 
     if (cap >= len) {
       memcpy(bw.auxBuffer(), s.data(), len);
-      bw.write(len);
+      bw.fill(len);
 
     } else {
       memcpy(bw.auxBuffer(), s.data(), cap);
-      bw.write(cap);
+      bw.fill(cap);
       bw.write(s.data() + cap, len - cap);
     }
   } else {
diff --git a/lib/ts/BufferWriter.h b/lib/ts/BufferWriter.h
index 8853a2d..ab6a6fa 100644
--- a/lib/ts/BufferWriter.h
+++ b/lib/ts/BufferWriter.h
@@ -1,9 +1,6 @@
-#if !defined TS_BUFFERWRITER_H_
-#define TS_BUFFERWRITER_H_
-
 /** @file
 
-    Utilities for generating character sequences.
+    Utilities for generating character sequences in buffers.
 
     @section license License
 
@@ -24,6 +21,10 @@
     limitations under the License.
  */
 
+#if !defined TS_BUFFERWRITER_H_
+#define TS_BUFFERWRITER_H_
+
+#include <stdlib.h>
 #include <utility>
 #include <cstring>
 
@@ -32,17 +33,36 @@
 
 namespace ts
 {
-// Abstract class.
-//
+/** Base (abstract) class for concrete buffer writers.
+ */
 class BufferWriter
 {
 public:
-  // The write() functions "add" characters at the end.  If these functions 
discard any characters, this must put the instance
-  // in an error state (indicated by the override of error() ).  Derived 
classes must not assume the write() functions will
-  // not be called when the instance is in an error state.
+  /** Add the character @a c to the buffer.
+
+      @a c is added only if there is room in the buffer. If not, the instance 
is put in to an error
+      state. In either case the value for @c extent is incremented.
+
+      @internal If any variant of @c write discards any characters, the 
instance must be put in an
+      error state (indicated by the override of @c error).  Derived classes 
must not assume the
+      write() functions will not be called when the instance is in an error 
state.
 
+      @return @c *this
+  */
   virtual BufferWriter &write(char c) = 0;
 
+  /** Add @a data to the buffer, up to @a length bytes.
+
+      Data is added only up to the remaining room in the buffer. If the 
remaining capacity is
+      exceeded (i.e. data is not written to the output), the instance is put 
in to an error
+      state. In either case the value for @c extent is incremented by @a 
length.
+
+      @internal This uses the single character write to output the data. It is 
presumed concrete
+      subclasses will override this method to use more efficient mechanisms, 
dependent on the type
+      of output buffer.
+
+      @return @c *this
+  */
   virtual BufferWriter &
   write(const void *data, size_t length)
   {
@@ -54,93 +74,129 @@ public:
     return *this;
   }
 
+  /** Add the contents of @a sv to the buffer, up to the size of the view.
+
+      Data is added only up to the remaining room in the buffer. If the 
remaining capacity is
+      exceeded (i.e. data is not written to the output), the instance is put 
in to an error
+      state. In either case the value for @c extent is incremented by the size 
of @a sv.
+
+      @return @c *this
+  */
   BufferWriter &
-  write(const string_view &sV)
+  write(const string_view &sv)
   {
-    return write(sV.data(), sV.size());
+    return write(sv.data(), sv.size());
   }
 
-  /// Return the written buffer.
+  /// Get the address of the first byte in the output buffer.
   virtual const char *data() const = 0;
 
-  // Returns true if the instance is in an error state.
-  //
+  /// Get the error state.
+  /// @return @c true if in an error state, @c false if not.
   virtual bool error() const = 0;
 
-  // Returns pointer to an auxiliary buffer (or nullptr if none is available). 
 Succeeding calls to non-const member functions,
-  // other than auxBuffer(), must be presumed to invalidate the current 
auxiliary buffer (contents and address).  Results
-  // are UNDEFINED if character locations at or beyond 
auxBuffer()[remaining()] are written.
-  //
+  /** Get the address of the next output byte in the buffer.
+
+      Succeeding calls to non-const member functions, other than this method, 
must be presumed to
+      invalidate the current auxiliary buffer (contents and address).
+
+      Care must be taken to not write to data beyond this plus @c remaining 
bytes. Usually the
+      safest mechanism is to create a @c FixedBufferWriter on the auxillary 
buffer and write to that.
+
+      @code
+      ts::FixedBufferWriter subw(w.auxBuffer(), w.remaining());
+      write_some_stuff(subw); // generate output into the buffer.
+      w.fill(subw.extent()); // update main buffer writer.
+      @endcode
+
+      @return Address of the next output byte, or @c nullptr if there is no 
remaining capacity.
+   */
   virtual char *
   auxBuffer()
   {
     return nullptr;
   }
 
-  // Write the first n characters that have been placed in the auxiliary 
buffer.  This call invalidates the auxiliary buffer.
-  // This function should not be called if no auxiliary buffer is available.
-  //
+  /** Advance the buffer position @a n bytes.
+
+      This treats the next @a n bytes as being written without changing the 
content. This is useful
+      only in conjuction with @a auxBuffer to indicate that @a n bytes of the 
auxillary buffer has
+      been written by some other mechanism.
+
+      @internal Concrete subclasses @b must override this to advance in a way 
consistent with the
+      specific buffer type.
+
+      @return @c *this
+  */
   virtual BufferWriter &
-  write(size_t n)
+  fill(size_t n)
   {
     return *this;
   }
 
-  // Returns number of total characters that can be written without causing an 
error condidtion.
-  //
+  /// Get the total capacity.
+  /// @return The total number of bytes that can be written without causing an 
error condition.
   virtual size_t capacity() const = 0;
 
-  // Total number of characters that have been written, including those 
discarded due to an error condition.
-  //
+  /// Get the extent.
+  /// @return Total number of characters that have been written, including 
those discarded due to an error condition.
   virtual size_t extent() const = 0;
 
-  // Total number of characters that are in the buffer (successfully written 
and not discarded).
-  //
+  /// Get the output size.
+  /// @return Total number of characters that are in the buffer (successfully 
written and not discarded)
   size_t
   size() const
   {
-    size_t e = extent(), c = capacity();
-
-    return e < c ? e : c;
+    return std::min(this->extent(), this->capacity());
   }
 
-  // Returns number of additional characters that can be written without 
causing an error condidtion.
-  //
+  /// Get the remaining buffer space.
+  /// @return Number of additional characters that can be written without 
causing an error condidtion.
   size_t
   remaining() const
   {
     return capacity() - size();
   }
 
-  // Reduce the capacity by n characters, potentially creating an error 
condition.
-  //
+  /// Reduce the capacity by @a n bytes
+  /// If the capacity is reduced below the current @c size the instance goes 
in to an error state.
+  /// @return @c *this
   virtual BufferWriter &clip(size_t n) = 0;
 
-  // If there is an error condition, this function clears it and sets the 
extent to the size.  It then increases the
-  // capacity by n characters.
-  //
+  /// Increase the capacity by @a n bytes.
+  /// If there is an error condition, this function clears it and sets the 
extent to the size.  It
+  /// then increases the capacity by n characters.
   virtual BufferWriter &extend(size_t n) = 0;
 
-  // Make destructor virtual.
-  //
+  // Force virtual destructor.
   virtual ~BufferWriter() {}
 };
 
-// A buffer writer that writes to an array of char that is external to the 
writer instance.
-//
+/** A @c BufferWrite concrete subclass to write to a fixed size buffer.
+ */
 class FixedBufferWriter : public BufferWriter
 {
-protected:
-  FixedBufferWriter(char *buf, size_t capacity, size_t attempted) : _buf(buf), 
_capacity(capacity), _attempted(attempted) {}
+  using super_type = BufferWriter;
 
 public:
-  // 'buf' is a pointer to the external array of char to write to.  'capacity' 
is the number of bytes in the array.
-  //
-  // If you create a instance of this class with capacity == 0 (and a nullptr 
buffer), you can use it to measure the number of
-  // characters a series of writes would result it (from the extent() value) 
without actually writing.
-  //
-  FixedBufferWriter(char *buf, size_t capacity) : FixedBufferWriter(buf, 
capacity, 0) {}
+  /** Construct a buffer writer on a fixed @a buffer of size @a capacity.
+
+      If writing goes past the end of the buffer, the excess is dropped.
+
+      @note If you create a instance of this class with capacity == 0 (and a 
nullptr buffer), you
+      can use it to measure the number of characters a series of writes would 
result it (from the
+      extent() value) without actually writing.
+   */
+  FixedBufferWriter(char *buffer, size_t capacity) : _buf(buffer), 
_capacity(capacity) {}
+
+  FixedBufferWriter(const FixedBufferWriter &) = delete;
+  FixedBufferWriter &operator=(const FixedBufferWriter &) = delete;
+  /// Move constructor.
+  FixedBufferWriter(FixedBufferWriter &&) = default;
+  /// Move assignment.
+  FixedBufferWriter &operator=(FixedBufferWriter &&) = default;
 
+  /// Write a single character @a c to the buffer.
   FixedBufferWriter &
   write(char c) override
   {
@@ -152,6 +208,7 @@ public:
     return *this;
   }
 
+  /// Write @a data to the buffer, up to @a length bytes.
   FixedBufferWriter &
   write(const void *data, size_t length) override
   {
@@ -168,50 +225,54 @@ public:
     return *this;
   }
 
-  // It's not clear to my why g++ needs this using declaration in order to 
consider the inherited versions of 'write' when
-  // resolving calls to a 'write' member ( wka...@oath.com ).
-  //
-  using BufferWriter::write;
+  // Bring in non-overridden methods.
+  using super_type::write;
 
-  /// Return the written buffer.
+  /// Return the output buffer.
   const char *
   data() const override
   {
     return _buf;
   }
 
+  /// Return whether there has been an error.
   bool
   error() const override
   {
     return _attempted > _capacity;
   }
 
+  /// Get the start of the unused output buffer.
   char *
   auxBuffer() override
   {
     return error() ? nullptr : _buf + _attempted;
   }
 
+  /// Advance the used part of the output buffer.
   FixedBufferWriter &
-  write(size_t n) override
+  fill(size_t n) override
   {
     _attempted += n;
 
     return *this;
   }
 
+  /// Get the total capacity of the output buffer.
   size_t
   capacity() const override
   {
     return _capacity;
   }
 
+  /// Get the total output sent to the writer.
   size_t
   extent() const override
   {
     return _attempted;
   }
 
+  /// Reduce the capacity by @a n.
   FixedBufferWriter &
   clip(size_t n) override
   {
@@ -222,6 +283,7 @@ public:
     return *this;
   }
 
+  /// Extend the capacity by @a n.
   FixedBufferWriter &
   extend(size_t n) override
   {
@@ -234,71 +296,104 @@ public:
     return *this;
   }
 
-  // Reduce extent.  If extent is less than capacity, error condition is 
cleared.
-  //
+  /// Reduce extent to @a n.
+  /// If @a n is less than the capacity the error condition, if any, is 
cleared.
+  /// This can be used to clear the output by calling @c reduce(0)
   void
-  reduce(size_t smallerExtent)
+  reduce(size_t n)
   {
-    ink_assert(smallerExtent <= _attempted);
+    ink_assert(n <= _attempted);
 
-    _attempted = smallerExtent;
+    _attempted = n;
   }
 
-  // Provide a string_view of all successfully written characters.
-  //
+  /// Provide a string_view of all successfully written characters.
   string_view
   view() const
   {
     return string_view(_buf, size());
   }
 
+  /// Provide a @c string_view of all successfully written characters as a 
user conversion.
   operator string_view() const { return view(); }
 
-  // No copying
-  //
-  FixedBufferWriter(const FixedBufferWriter &) = delete;
-  FixedBufferWriter &operator=(const FixedBufferWriter &) = delete;
+  /** Get a @c FixedBufferWriter for the unused output buffer.
 
-  // Moving is OK.
-  //
-  FixedBufferWriter(FixedBufferWriter &&) = default;
-  FixedBufferWriter &operator=(FixedBufferWriter &&) = default;
+      If @a reserve is non-zero then the buffer size for the auxillary writer 
will be @a reserve bytes
+      smaller than the remaining buffer. This "reserves" space for additional 
output after writing
+      to the auxillary buffer, in a manner similar to @c clip / @c extend.
+   */
+  FixedBufferWriter
+  auxWriter(size_t reserve = 0)
+  {
+    return {this->auxBuffer(), reserve < this->remaining() ? this->remaining() 
- reserve : 0};
+  }
 
 protected:
-  char *const _buf;
-
-  size_t _capacity;
-
-  size_t _attempted; // Number of characters written, including those 
discarded due error condition.
+  char *const _buf;      ///< Output buffer.
+  size_t _capacity;      ///< Size of output buffer.
+  size_t _attempted = 0; ///< Number of characters written, including those 
discarded due error condition.
+private:
+  // INTERNAL - Overload removed, make sure it's not used.
+  BufferWriter &write(size_t n);
 };
 
-// A buffer writer that writes to an array of char (of fixed dimension N) that 
is internal to the writer instance.
-// It's called 'local' because instances are typically declared as 
stack-allocated, local function variables.
-//
+/** A buffer writer that writes to an array of char (of fixed size N) that is 
internal to the writer instance.
+
+    It's called 'local' because instances are typically declared as 
stack-allocated, local function
+    variables.
+*/
 template <size_t N> class LocalBufferWriter : public FixedBufferWriter
 {
 public:
+  /// Construct an empty writer.
   LocalBufferWriter() : FixedBufferWriter(_arr, N) {}
 
-  LocalBufferWriter(const LocalBufferWriter &that) : FixedBufferWriter(_arr, 
that._capacity, that._attempted)
+  /// Copy another writer.
+  /// Any data in @a that is copied over.
+  LocalBufferWriter(const LocalBufferWriter &that) : FixedBufferWriter(_arr, N)
   {
-    std::memcpy(_arr, that._arr, size());
+    std::memcpy(_arr, that._arr, that.size());
+    _attempted = that._attempted;
   }
 
+  /// Copy another writer.
+  /// Any data in @a that is copied over.
+  template <size_t K> LocalBufferWriter(const LocalBufferWriter<K> &that) : 
FixedBufferWriter(_arr, N)
+  {
+    size_t n = std::min(N, that.size());
+    std::memcpy(_arr, that.data(), n);
+    // if a bigger space here, don't leave a gap between size and attempted.
+    _attempted = N > K ? n : that.extent();
+  }
+
+  /// Copy another writer.
+  /// Any data in @a that is copied over.
   LocalBufferWriter &
   operator=(const LocalBufferWriter &that)
   {
     if (this != &that) {
-      _capacity = that._capacity;
-
-      _attempted = that._attempted;
-
-      std::memcpy(_buf, that._buf, size());
+      _attempted = that.extent();
+      std::memcpy(_buf, that._buf, that.size());
     }
 
     return *this;
   }
 
+  /// Copy another writer.
+  /// Any data in @a that is copied over.
+  template <size_t K>
+  LocalBufferWriter &
+  operator=(const LocalBufferWriter<K> &that)
+  {
+    size_t n = std::min(N, that.size());
+    // if a bigger space here, don't leave a gap between size and attempted.
+    _attempted = N > K ? n : that.extent();
+    std::memcpy(_arr, that.data(), n);
+    return *this;
+  }
+
+  /// Increase capacity by @a n.
   LocalBufferWriter &
   extend(size_t n) override
   {
@@ -313,12 +408,12 @@ public:
     return *this;
   }
 
-  // Move construction/assignment intentionally defaulted to copying.
-
 protected:
-  char _arr[N];
+  char _arr[N]; ///< output buffer.
 };
 
+// Define stream operators for built in @c write overloads.
+
 inline BufferWriter &
 operator<<(BufferWriter &b, char c)
 {
@@ -331,6 +426,52 @@ operator<<(BufferWriter &b, const string_view &sv)
   return b.write(sv);
 }
 
+inline BufferWriter &
+operator<<(BufferWriter &w, intmax_t i)
+{
+  if (i) {
+    char txt[std::numeric_limits<intmax_t>::digits10 + 1];
+    int n = sizeof(txt);
+    while (i) {
+      txt[--n] = '0' + i % 10;
+      i /= 10;
+    }
+    return w.write(txt + n, sizeof(txt) - n);
+  } else {
+    return w.write('0');
+  }
+}
+
+// Annoying but otherwise ambiguous.
+inline BufferWriter &
+operator<<(BufferWriter &w, int i)
+{
+  return w << static_cast<intmax_t>(i);
+}
+
+inline BufferWriter &
+operator<<(BufferWriter &w, uintmax_t i)
+{
+  if (i) {
+    char txt[std::numeric_limits<uintmax_t>::digits10 + 1];
+    int n = sizeof(txt);
+    while (i) {
+      txt[--n] = '0' + i % 10;
+      i /= 10;
+    }
+    return w.write(txt + n, sizeof(txt) - n);
+  } else {
+    return w.write('0');
+  }
+}
+
+// Annoying but otherwise ambiguous.
+inline BufferWriter &
+operator<<(BufferWriter &w, unsigned int i)
+{
+  return w << static_cast<uintmax_t>(i);
+}
+
 } // end namespace ts
 
 #endif // include once
diff --git a/lib/ts/Diags.cc b/lib/ts/Diags.cc
index abfa480..a9e4cfb 100644
--- a/lib/ts/Diags.cc
+++ b/lib/ts/Diags.cc
@@ -245,13 +245,8 @@ Diags::print_va(const char *debug_tag, DiagsLevel 
diags_level, const SourceLocat
   ///////////////////////
   // add the thread id //
   ///////////////////////
-  {
-    int num_bytes_written =
-      snprintf(format_writer.auxBuffer(), format_writer.remaining(), "{0x%" 
PRIx64 "} ", (uint64_t)ink_thread_self());
-    if (num_bytes_written > 0) {
-      format_writer.write(static_cast<size_t>(num_bytes_written));
-    }
-  }
+  format_writer.fill(
+    snprintf(format_writer.auxBuffer(), format_writer.remaining(), "{0x%" 
PRIx64 "} ", (uint64_t)ink_thread_self()));
 
   //////////////////////////////////
   // append the diag level prefix //
diff --git a/lib/ts/unit-tests/test_BufferWriter.cc 
b/lib/ts/unit-tests/test_BufferWriter.cc
index fb7011a..3d6b1c7 100644
--- a/lib/ts/unit-tests/test_BufferWriter.cc
+++ b/lib/ts/unit-tests/test_BufferWriter.cc
@@ -178,7 +178,7 @@ twice(BWType &bw)
   }
 
   std::strcpy(bw.auxBuffer(), " fox");
-  bw.write(sizeof(" fox") - 1);
+  bw.fill(sizeof(" fox") - 1);
 
   if (bw.error()) {
     return false;
@@ -258,13 +258,34 @@ TEST_CASE("Concrete Buffer Writers 2", "[BWC2]")
 
   REQUIRE(space[20] == '!');
 
-  LBW<20> bw2(bw), bw3;
-
-  REQUIRE(bw2.view() == "The quick brown fox");
-
-  bw3 = bw2;
-
-  REQUIRE(bw3.view() == "The quick brown fox");
+  LBW<20> bw20(bw);
+  LBW<30> bw30(bw); // test cross length constructors
+  LBW<10> bw10(bw);
+
+  REQUIRE(bw20.view() == "The quick brown fox");
+
+  bw30 = bw20;
+  REQUIRE(bw30.view() == "The quick brown fox");
+
+  bw10 = bw20;
+  REQUIRE(bw10.view() == "The quick ");
+  bw10.reduce(0);
+  bw10.write("01234567890123456789");
+  REQUIRE(bw10.extent() == 20);
+  REQUIRE(bw10.view() == "0123456789");
+  REQUIRE(bw10.remaining() == 0);
+  bw20 = bw10;
+  REQUIRE(bw20.view() == "0123456789");
+  REQUIRE(bw20.extent() == 10);
+  REQUIRE(bw20.size() == 10);
+
+  auto abw = bw20.auxWriter();
+  REQUIRE(abw.remaining() == 10);
+  abw.write("abcdefghijklmnopqrstuvwxyz");
+  bw20.fill(abw.extent());
+  REQUIRE(bw20.size() == 20);
+  REQUIRE(bw20.extent() == 36);
+  REQUIRE(bw20.view() == "0123456789abcdefghij");
 }
 
 TEST_CASE("Discard Buffer Writer", "[BWD]")
@@ -292,7 +313,7 @@ TEST_CASE("Discard Buffer Writer", "[BWD]")
   REQUIRE(bw.size() == 0);
   REQUIRE(bw.extent() == (sizeof("The quick brown") - 1));
 
-  bw.write(sizeof(" fox") - 1);
+  bw.fill(sizeof(" fox") - 1);
 
   REQUIRE(bw.size() == 0);
   REQUIRE(bw.extent() == (sizeof("The quick brown fox") - 1));
@@ -319,6 +340,10 @@ TEST_CASE("Buffer Writer << operator", "[BW<<]")
   bw << "The" << ' ' << "quick" << ' ' << "brown fox";
 
   REQUIRE(bw.view() == "The quick brown fox");
+
+  bw.reduce(0);
+  bw << "x=" << bw.capacity();
+  REQUIRE(bw.view() == "x=50");
 }
 
 TEST_CASE("LocalBufferWriter clip and extend")
@@ -334,6 +359,6 @@ TEST_CASE("LocalBufferWriter clip and extend")
   REQUIRE(bw.view() == "aaabbb");
 
   bw.extend(4);
-  bw.write(static_cast<size_t>(snprintf(bw.auxBuffer(), bw.remaining(), 
"ccc")));
+  bw.fill(static_cast<size_t>(snprintf(bw.auxBuffer(), bw.remaining(), 
"ccc")));
   REQUIRE(bw.view() == "aaabbbccc");
 }
diff --git a/proxy/http/HttpTransactHeaders.cc 
b/proxy/http/HttpTransactHeaders.cc
index 4a75db1..c0d7998 100644
--- a/proxy/http/HttpTransactHeaders.cc
+++ b/proxy/http/HttpTransactHeaders.cc
@@ -1040,7 +1040,7 @@ 
HttpTransactHeaders::add_forwarded_field_to_request(HttpTransact::State *s, HTTP
       // Fail-safe.
       hdr.auxBuffer()[hdr.remaining() - 1] = '\0';
 
-      hdr.write(strlen(hdr.auxBuffer()));
+      hdr.fill(strlen(hdr.auxBuffer()));
 
       if (is_ipv6) {
         hdr << "]\"";
@@ -1094,7 +1094,7 @@ 
HttpTransactHeaders::add_forwarded_field_to_request(HttpTransact::State *s, HTTP
       // Fail-safe.
       hdr.auxBuffer()[hdr.remaining() - 1] = '\0';
 
-      hdr.write(strlen(hdr.auxBuffer()));
+      hdr.fill(strlen(hdr.auxBuffer()));
 
       if (is_ipv6) {
         hdr << "]\"";
@@ -1124,7 +1124,7 @@ 
HttpTransactHeaders::add_forwarded_field_to_request(HttpTransact::State *s, HTTP
       int numChars = 
HttpTransactHeaders::write_hdr_protocol_stack(hdr.auxBuffer(), hdr.remaining(), 
ProtocolStackDetail::Compact,
                                                                    
protoBuf.data(), n_proto, '-');
       if (numChars > 0) {
-        hdr.write(size_t(numChars));
+        hdr.fill(size_t(numChars));
       }
     }
 
@@ -1165,7 +1165,7 @@ 
HttpTransactHeaders::add_forwarded_field_to_request(HttpTransact::State *s, HTTP
           int numChars =
             HttpTransactHeaders::write_hdr_protocol_stack(hdr.auxBuffer(), 
hdr.remaining(), detail, protoBuf.data(), n_proto, '-');
           if (numChars > 0) {
-            hdr.write(size_t(numChars));
+            hdr.fill(size_t(numChars));
           }
 
           if ((numChars <= 0) or (hdr.size() >= hdr.capacity())) {

-- 
To stop receiving notification emails like this one, please contact
a...@apache.org.

Reply via email to