text: /*
   *
   *    Copyright (c) 2020-2021 Project CHIP Authors
   *    Copyright (c) 2016-2017 Nest Labs, Inc.
   *
   *    Licensed 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.
   */
  
  /**
   *    @file
   *      This file defines the chip::System::PacketBuffer class,
   *      which provides the mechanisms for manipulating packets of *
   *      octet-serialized data.
   */
  
  #pragma once
  
  // Include configuration header
  #include <system/SystemConfig.h>
  
  // Include dependent headers
  #include <support/BufferWriter.h>
  #include <support/CodeUtils.h>
  #include <support/DLLUtil.h>
  #include <system/SystemAlignSize.h>
  #include <system/SystemError.h>
  
  #include <stddef.h>
  #include <utility>
  
  #if CHIP_SYSTEM_CONFIG_USE_LWIP
  #include <lwip/mem.h>
  #include <lwip/memp.h>
  #include <lwip/pbuf.h>
  #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
  
  class PacketBufferTest;
  
  /*
   * Preprocessor definitions related to packet buffer memory configuration.
   * These are not part of the public PacketBuffer interface; they are present
   * here so that certain public functions can have definitions optimized for
   * particular configurations.
   */
  
  #undef CHIP_SYSTEM_PACKETBUFFER_STORE                // One of the following constants:
  #define CHIP_SYSTEM_PACKETBUFFER_STORE_LWIP_POOL 1   //   Default lwIP allocation
  #define CHIP_SYSTEM_PACKETBUFFER_STORE_LWIP_CUSTOM 2 //   Custom lwIP allocation
  #define CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_POOL 3   //   Internal fixed pool
  #define CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_HEAP 4   //   Platform::MemoryAlloc
  
  #undef CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHT_SIZE // True if RightSize() has a nontrivial implementation
  #undef CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK      // True if Check() has a nontrivial implementation
  #if CHIP_SYSTEM_CONFIG_USE_LWIP
  #if LWIP_PBUF_FROM_CUSTOM_POOLS
  #define CHIP_SYSTEM_PACKETBUFFER_STORE CHIP_SYSTEM_PACKETBUFFER_STORE_LWIP_CUSTOM
  #define CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHT_SIZE 1
  #define CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK 0
  #else
  #define CHIP_SYSTEM_PACKETBUFFER_STORE CHIP_SYSTEM_PACKETBUFFER_STORE_LWIP_POOL
  #define CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHT_SIZE 0
  #define CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK 0
  #endif
  #else
  #if CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE
  #define CHIP_SYSTEM_PACKETBUFFER_STORE CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_POOL
  #define CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHT_SIZE 0
  #define CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK 0
  #else
  #define CHIP_SYSTEM_PACKETBUFFER_STORE CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_HEAP
  #define CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHT_SIZE 1
  #define CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK CHIP_CONFIG_MEMORY_DEBUG_CHECKS
  #endif
  #endif
  
  namespace chip {
  namespace System {
  
  class PacketBufferHandle;
  
  #if !CHIP_SYSTEM_CONFIG_USE_LWIP
  struct pbuf
  {
      struct pbuf * next;
      void * payload;
      uint16_t tot_len;
      uint16_t len;
      uint16_t ref;
  #if CHIP_SYSTEM_PACKETBUFFER_STORE == CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_HEAP
      uint16_t alloc_size;
  #endif
  };
  #endif // !CHIP_SYSTEM_CONFIG_USE_LWIP
  
  /**    @class PacketBuffer
   *
   *     @brief
   *      The packet buffer class is the core structure used for manipulating packets of octet-serialized data, usually in the
   *      context of a data communications network, like Bluetooth or the Internet protocol.
   *
   *      In LwIP-based environments, this class is built on top of the pbuf structure defined in that library. In the absence of
   *      LwIP, chip provides either a malloc-based implementation, or a pool-based implementation that closely approximates the
   *      memory challenges of deeply embedded devices.
   *
   *      The PacketBuffer class, like many similar structures used in layered network stacks, provide a mechanism to reserve space
   *      for protocol headers at each layer of a configurable communication stack.  For details, see `PacketBufferHandle::New()`
   *      as well as LwIP documentation.
   *
   *      PacketBuffer objects are reference-counted, and normally held and used through a PacketBufferHandle that owns one of the
   *      counted references. When a PacketBufferHandle goes out of scope, its reference is released. To take ownership, a function
   *      takes a PacketBufferHandle by value. To borrow ownership, a function takes a `const PacketBufferHandle &`.
   *
   *      New objects of PacketBuffer class are initialized at the beginning of an allocation of memory obtained from the underlying
   *      environment, e.g. from LwIP pbuf target pools, from the standard C library heap, from an internal buffer pool. In the
   *      simple pool case, the size of the data buffer is PacketBuffer::kBlockSize.
   *
   *      PacketBuffer objects may be chained to accomodate larger payloads.  Chaining, however, is not transparent, and users of the
   *      class must explicitly decide to support chaining.  Examples of classes written with chaining support are as follows:
   *
   *          @ref chip::chipTLVReader
   *          @ref chip::chipTLVWriter
   *
   */
  class DLL_EXPORT PacketBuffer : private pbuf
  {
  private:
      // The effective size of the packet buffer structure.
  #if CHIP_SYSTEM_CONFIG_USE_LWIP
      static constexpr uint16_t kStructureSize = LWIP_MEM_ALIGN_SIZE(sizeof(struct ::pbuf));
  #else  // CHIP_SYSTEM_CONFIG_USE_LWIP
      static constexpr uint16_t kStructureSize         = CHIP_SYSTEM_ALIGN_SIZE(sizeof(::chip::System::pbuf), 4u);
  #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
  
  public:
      /**
       * The maximum size buffer an application can allocate with no protocol header reserve.
       */
  #if CHIP_SYSTEM_CONFIG_USE_LWIP
      static constexpr uint16_t kMaxSizeWithoutReserve = LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE);
  #else
      static constexpr uint16_t kMaxSizeWithoutReserve = CHIP_SYSTEM_CONFIG_PACKETBUFFER_CAPACITY_MAX;
  #endif
  
      /**
       * The number of bytes to reserve in a network packet buffer to contain all the possible protocol encapsulation headers
       * before the application data.
       */
      static constexpr uint16_t kDefaultHeaderReserve = CHIP_SYSTEM_CONFIG_HEADER_RESERVE_SIZE;
  
      /**
       * The maximum size buffer an application can allocate with the default protocol header reserve.
       */
      static constexpr uint16_t kMaxSize = kMaxSizeWithoutReserve - kDefaultHeaderReserve;
  
      /**
       * Return the size of the allocation including the reserved and payload data spaces but not including space
       * allocated for the PacketBuffer structure.
       *
       *  @note    The allocation size is equal to or greater than the \c aAllocSize parameter to the \c Create method).
       *
       *  @return     size of the allocation
       */
      uint16_t AllocSize() const
      {
  #if CHIP_SYSTEM_PACKETBUFFER_STORE == CHIP_SYSTEM_PACKETBUFFER_STORE_LWIP_POOL ||                                                  \
      CHIP_SYSTEM_PACKETBUFFER_STORE == CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_POOL
          return kMaxSizeWithoutReserve;
  #elif CHIP_SYSTEM_PACKETBUFFER_STORE == CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_HEAP
          return this->alloc_size;
  #elif CHIP_SYSTEM_PACKETBUFFER_STORE == CHIP_SYSTEM_PACKETBUFFER_STORE_LWIP_CUSTOM
          // Temporary workaround for custom pbufs by assuming size to be PBUF_POOL_BUFSIZE
          if (this->flags & PBUF_FLAG_IS_CUSTOM)
              return LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) - kStructureSize;
          else
              return LWIP_MEM_ALIGN_SIZE(memp_sizes[this->pool]) - kStructureSize;
  #else
  #error "Unimplemented CHIP_SYSTEM_PACKETBUFFER_STORE case"
  #endif // CHIP_SYSTEM_PACKETBUFFER_STORE
      }
  
      /**
       * Get a pointer to the start of data in a buffer.
       *
       *  @return pointer to the start of data.
       */
      uint8_t * Start() const { return static_cast<uint8_t *>(this->payload); }
  
      /**
       *  Set the the start of data in a buffer, adjusting length and total length accordingly.
       *
       *  @note The data within the buffer is not moved, only accounting information is changed.  The function is commonly used to
       *      either strip or prepend protocol headers in a zero-copy way.
       *
       *  @note This call should not be used on any buffer that is not the head of a buffer chain, as it only alters the current
       *      buffer.
       *
       *  @param[in] aNewStart - A pointer to where the new payload should start.  newStart will be adjusted internally to fall within
       *      the boundaries of the first buffer in the PacketBuffer chain.
       */
      void SetStart(uint8_t * aNewStart);
  
      /**
       * Get the length, in bytes, of data in a packet buffer.
       *
       *  @return length, in bytes (current payload length).
       */
      uint16_t DataLength() const { return this->len; }
  
      /**
       * Set the length, in bytes, of data in a packet buffer, adjusting total length accordingly.
       *
       *  The function sets the length, in bytes, of the data in the buffer, adjusting the total length appropriately. When the buffer
       *  is not the head of the buffer chain (common case: the caller adds data to the last buffer in the PacketBuffer chain prior to
       *  calling higher layers), the aChainHead __must__ be passed in to properly adjust the total lengths of each buffer ahead of
       *  the current buffer.
       *
       *  @param[in] aNewLen - new length, in bytes, of this buffer.
       *
       *  @param[in,out] aChainHead - the head of the buffer chain the current buffer belongs to.  May be \c nullptr if the current
       *      buffer is the head of the buffer chain.
       */
      void SetDataLength(uint16_t aNewLen, const PacketBufferHandle & aChainHead);
      void SetDataLength(uint16_t aNewLen) { SetDataLength(aNewLen, nullptr); }
  
      /**
       * Get the total length of packet data in the buffer chain.
       *
       *  @return total length, in octets.
       */
      uint16_t TotalLength() const { return this->tot_len; }
  
      /**
       * Get the maximum amount, in bytes, of data that will fit in the buffer given the current start position and buffer size.
       *
       *  @return number of bytes that fits in the buffer given the current start position.
       */
      uint16_t MaxDataLength() const;
  
      /**
       * Get the number of bytes of data that can be added to the current buffer given the current start position and data length.
       *
       *  @return the length, in bytes, of data that will fit in the current buffer given the current start position and data length.
       */
      uint16_t AvailableDataLength() const;
  
      /**
       * Get the number of bytes within the current buffer between the start of the buffer and the current data start position.
       *
       *  @return the amount, in bytes, of space between the start of the buffer and the current data start position.
       */
      uint16_t ReservedSize() const;
  
      /**
       * Determine whether there are any additional buffers chained to the current buffer.
       *
       *  @return \c true if there is a chained buffer.
       */
      bool HasChainedBuffer() const { return ChainedBuffer() != nullptr; }
  
      /**
       * Add the given packet buffer to the end of the buffer chain, adjusting the total length of each buffer in the chain
       * accordingly.
       *
       *  @note The current packet buffer must be the head of the buffer chain for the lengths to be adjusted properly.
       *
       *  @note Ownership is transferred from the argument to the `next` link at the end of the current chain.
       *
       *  @param[in] aPacket - the packet buffer to be added to the end of the current chain.
       */
      void AddToEnd(PacketBufferHandle && aPacket);
  
      /**
       * Move data from subsequent buffers in the chain into the current buffer until it is full.
       *
       *  Only the current buffer is compacted: the data within the current buffer is moved to the front of the buffer, eliminating
       *  any reserved space. The remaining available space is filled with data moved from subsequent buffers in the chain, until the
       *  current buffer is full. If a subsequent buffer in the chain is moved into the current buffer in its entirety, it is removed
       *  from the chain and freed. The method takes no parameters, returns no results and cannot fail.
       */
      void CompactHead();
  
      /**
       * Adjust the current buffer to indicate the amount of data consumed.
       *
       *  Advance the data start position in the current buffer by the specified amount, in bytes, up to the length of data in the
       *  buffer. Decrease the length and total length by the amount consumed.
       *
       *  @param[in] aConsumeLength - number of bytes to consume from the current buffer.
       */
      void ConsumeHead(uint16_t aConsumeLength);
  
      /**
       * Ensure the buffer has at least the specified amount of reserved space.
       *
       *  Ensure the buffer has at least the specified amount of reserved space, moving the data in the buffer forward to make room if
       *  necessary.
       *
       *  @param[in] aReservedSize - number of bytes desired for the headers.
       *
       *  @return \c true if the requested reserved size is available, \c false if there's not enough room in the buffer.
       */
      CHECK_RETURN_VALUE bool EnsureReservedSize(uint16_t aReservedSize);
  
      /**
       * Align the buffer payload on the specified bytes boundary.
       *
       *  Moving the payload in the buffer forward if necessary.
       *
       *  @param[in] aAlignBytes - specifies number of bytes alignment for the payload start pointer.
       *
       *  @return \c true if alignment is successful, \c false if there's not enough room in the buffer.
       */
      bool AlignPayload(uint16_t aAlignBytes);
  
      /**
       * Return the next buffer in a buffer chain.
       *
       *  If there is no next buffer, the handle will have \c IsNull() \c true.
       *
       *  @return a handle to the next buffer in the buffer chain.
       */
      CHECK_RETURN_VALUE PacketBufferHandle Next();
  
      /**
       * Return the last buffer in a buffer chain.
       *
       *  @return a handle to the last buffer in the buffer chain.
       */
      CHECK_RETURN_VALUE PacketBufferHandle Last();
  
      /**
       * Copies data from the payloads of a chain of packet buffers until a given amount of data has been copied.
       *
       * @param[in]  buf             Destination buffer; must be at least @a length bytes.
       * @param[in]  length          Destination buffer length.
       *
       * @retval #CHIP_ERROR_BUFFER_TOO_SMALL If the total length of the payloads in the chain is less than the requested @a length.
       * @retval #CHIP_ERROR_INTERNAL         In case of an inconsistency in the buffer chain.
       * @retval #CHIP_NO_ERROR               If the requested payload has been copied.
       */
      CHIP_ERROR Read(uint8_t * buf, size_t length) const;
      template <size_t N>
      inline CHIP_ERROR Read(uint8_t (&buf)[N]) const
      {
          return Read(buf, N);
      }
  
      /**
       * Perform an implementation-defined check on the validity of a PacketBuffer pointer.
       *
       * Unless enabled by #CHIP_CONFIG_MEMORY_DEBUG_CHECKS == 1, this function does nothing.
       *
       * When enabled, it performs an implementation- and configuration-defined check on
       * the validity of the packet buffer. It MAY log an error and/or abort the program
       * if the packet buffer or the implementation-defined memory management system is in
       * a faulty state. (Some configurations may not actually perform any check.)
       *
       * @note  A null pointer is not considered faulty.
       *
       *  @param[in] buffer - the packet buffer to check.
       */
      static void Check(const PacketBuffer * buffer)
      {
  #if CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK
          InternalCheck(buffer);
  #endif
      }
  
  private:
      // Memory required for a maximum-size PacketBuffer.
      static constexpr uint16_t kBlockSize = PacketBuffer::kStructureSize + PacketBuffer::kMaxSizeWithoutReserve;
  
      // Note: this condition includes DOXYGEN to work around a Doxygen error. DOXYGEN is never defined in any actual build.
  #if CHIP_SYSTEM_PACKETBUFFER_STORE == CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_POOL || defined(DOXYGEN)
      typedef union
      {
          pbuf Header;
          uint8_t Block[PacketBuffer::kBlockSize];
      } BufferPoolElement;
      static BufferPoolElement sBufferPool[CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE];
      static PacketBuffer * sFreeList;
      static PacketBuffer * BuildFreeList();
  #endif // CHIP_SYSTEM_PACKETBUFFER_STORE == CHIP_SYSTEM_PACKETBUFFER_STORE_CHIP_POOL || defined(DOXYGEN)
  
  #if CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK
      static void InternalCheck(const PacketBuffer * buffer);
  #endif
  
      void AddRef();
      bool HasSoleOwnership() const { return (this->ref == 1); }
      static void Free(PacketBuffer * aPacket);
      static PacketBuffer * FreeHead(PacketBuffer * aPacket);
  
      PacketBuffer * ChainedBuffer() const { return static_cast<PacketBuffer *>(this->next); }
      PacketBuffer * Consume(uint16_t aConsumeLength);
      void Clear();
      void SetDataLength(uint16_t aNewLen, PacketBuffer * aChainHead);
  
      friend class PacketBufferHandle;
      friend class ::PacketBufferTest;
  };
  
  static_assert(sizeof(pbuf) == sizeof(PacketBuffer), "PacketBuffer must not have additional members");
  
  /**
   * @class PacketBufferHandle
   *
   * @brief
   *  Tracks ownership of a PacketBuffer.
   *
   *  PacketBuffer objects are reference-counted, and normally held and used through a PacketBufferHandle that owns one of the
   *  counted references. When a PacketBufferHandle goes out of scope, its reference is released. To take ownership, a function
   *  takes a PacketBufferHandle by value. To borrow ownership, a function takes a `const PacketBufferHandle &`.
   */
  class DLL_EXPORT PacketBufferHandle
  {
  public:
      /**
       * Construct an empty PacketBufferHandle.
       */
      PacketBufferHandle() : mBuffer(nullptr) {}
      PacketBufferHandle(decltype(nullptr)) : mBuffer(nullptr) {}
  
      /**
       * Construct a PacketBufferHandle that takes ownership of a PacketBuffer from another.
       */
      PacketBufferHandle(PacketBufferHandle && aOther)
      {
          mBuffer        = aOther.mBuffer;
          aOther.mBuffer = nullptr;
      }
  
      ~PacketBufferHandle() { *this = nullptr; }
  
      /**
       * Take ownership of a PacketBuffer from another PacketBufferHandle, freeing any existing owned buffer.
       */
      PacketBufferHandle & operator=(PacketBufferHandle && aOther)
      {
          if (mBuffer != nullptr)
          {
              PacketBuffer::Free(mBuffer);
          }
          mBuffer        = aOther.mBuffer;
          aOther.mBuffer = nullptr;
          return *this;
      }
  
      /**
       * Free any buffer owned by this handle.
       */
      PacketBufferHandle & operator=(decltype(nullptr))
      {
          if (mBuffer != nullptr)
          {
              PacketBuffer::Free(mBuffer);
          }
          mBuffer = nullptr;
          return *this;
      }
  
      /**
       * Get a new handle to an existing buffer.
       *
       * @return a PacketBufferHandle that shares ownership with this.
       */
      PacketBufferHandle Retain() const
      {
          mBuffer->AddRef();
          return PacketBufferHandle(mBuffer);
      }
  
      /**
       * Access a PackerBuffer's public methods.
       */
      PacketBuffer * operator->() const { return mBuffer; }
  
      /**
       * Test whether this PacketBufferHandle is empty, or conversely owns a PacketBuffer.
       *
       * @return \c true if this PacketBufferHandle is empty; return \c false if it owns a PacketBuffer.
       */
      bool IsNull() const { return mBuffer == nullptr; }
  
      /**
       * Test whether the PacketBuffer owned by this PacketBufferHandle has unique ownership.
       *
       * @return \c true if the PacketBuffer owned by this PacketBufferHandle is solely owned; return \c false if
       * it has more than one ownership.
       */
      bool HasSoleOwnership() const { return mBuffer->HasSoleOwnership(); }
  
      /**
       *  Detach and return the head of a buffer chain while updating this handle to point to the remaining buffers.
       *  The current buffer must be the head of the chain.
       *
       *  This PacketBufferHandle now holds the ownership formerly held by the head of the chain.
       *  The returned PacketBufferHandle holds the ownership formerly held by this.
       *
       *  @return the detached buffer formerly at the head of the buffer chain.
       */
      CHECK_RETURN_VALUE PacketBufferHandle PopHead();
  
      /**
       * Free the first buffer in a chain.
       *
       *  @note When the buffer chain is referenced by multiple handles, `FreeHead()` will detach the head, but will not forcibly
       *  deallocate the head buffer.
       */
      void FreeHead()
      {
          // `PacketBuffer::FreeHead()` frees the current head; this takes ownership from the `next` link.
          mBuffer = PacketBuffer::FreeHead(mBuffer);
      }
  
      /**
       * Add the given packet buffer to the end of the buffer chain, adjusting the total length of each buffer in the chain
       * accordingly.
       *
       *  @note The current packet buffer handle must either be the head of the buffer chain for the lengths to be adjusted properly,
       *        or be null (in which case it becomes the head).
       *
       *  @note Ownership is transferred from the argument to the `next` link at the end of the current chain,
       *        or to the handle if it's currently null.
       *
       *  @param[in] aPacket - the packet buffer to be added to the end of the current chain.
       */
      void AddToEnd(PacketBufferHandle && aPacket)
      {
          if (IsNull())
          {
              mBuffer         = aPacket.mBuffer;
              aPacket.mBuffer = nullptr;
          }
          else
          {
              mBuffer->AddToEnd(std::move(aPacket));
          }
      }
  
      /**
       * Consume data in a chain of buffers.
       *
       *  Consume data in a chain of buffers starting with the current buffer and proceeding through the remaining buffers in the
       *  chain. Each buffer that is completely consumed is freed and the handle holds the first buffer (if any) containing the
       *  remaining data. The current buffer must be the head of the buffer chain.
       *
       *  @param[in] aConsumeLength - number of bytes to consume from the current chain.
       */
      void Consume(uint16_t aConsumeLength) { mBuffer = mBuffer->Consume(aConsumeLength); }
  
      /**
       * Copy the given buffer to a right-sized buffer if applicable.
       *
       * Only operates on single buffers (for chains, use \c CompactHead() and RightSize the tail).
       * Requires that this handle be the only reference to the underlying buffer.
       */
      void RightSize()
      {
  #if CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHT_SIZE
          InternalRightSize();
  #endif
      }
  
      /**
       * Get a new handle to a raw PacketBuffer pointer.
       *
       * @brief The caller's ownership is transferred to this.
       *
       * @note This should only be used in low-level code, e.g. to import buffers from LwIP or a similar stack.
       */
      static PacketBufferHandle Adopt(PacketBuffer * buffer) { return PacketBufferHandle(buffer); }
  #if CHIP_SYSTEM_CONFIG_USE_LWIP
      static PacketBufferHandle Adopt(pbuf * buffer) { return Adopt(reinterpret_cast<PacketBuffer *>(buffer)); }
  #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
  
      /**
       * Advance this PacketBufferHandle to the next buffer in a chain.
       *
       *  @note This differs from `FreeHead()` in that it does not touch any content in the currently referenced packet buffer;
       *      it only changes which buffer this handle owns. (Note that this could result in the previous buffer being freed,
       *      if there is no other owner.) `Advance()` is designed to be used with an additional handle to traverse a buffer chain,
       *      whereas `FreeHead()` modifies a chain.
       */
      void Advance() { *this = Hold(mBuffer->ChainedBuffer()); }
  
      /**
       * Export a raw PacketBuffer pointer.
       *
       * @brief The PacketBufferHandle's ownership is transferred to the caller.
       *
       * @note This should only be used in low-level code. The caller owns one counted reference to the \c PacketBuffer
       *       and is reponsible for managing it safely.
       *
       * @note The ref-qualifier `&&` requires the caller to use `std::move` to emphasize that ownership is
       *       moved out of this handle.
       */
      CHECK_RETURN_VALUE PacketBuffer * UnsafeRelease() &&
      {
          PacketBuffer::Check(mBuffer);
          PacketBuffer * buffer = mBuffer;
          mBuffer               = nullptr;
          return buffer;
      }
  
      /**
       * Allocates a packet buffer.
       *
       *  A packet buffer is conceptually divided into two parts:
       *  @li  Space reserved for network protocol headers. The size of this space normally defaults to a value determined
       *       by the network layer configuration, but can be given explicity by \c aReservedSize for special cases.
       *  @li  Space for application data. The minimum size of this space is given by \c aAvailableSize, and then \c Start()
       *       provides a pointer to the start of this space.
       *
       *  Fails and returns \c nullptr if no memory is available, or if the size requested is too large.
       *  When the sum of \a aAvailableSize and \a aReservedSize is no greater than \c PacketBuffer::kMaxSizeWithoutReserve,
       *  that is guaranteed not to be too large.
       *
       *  On success, it is guaranteed that \c AvailableDataSize() is no less than \a aAvailableSize.
       *
       *  @param[in]  aAvailableSize  Minimum number of octets to for application data (at `Start()`).
       *  @param[in]  aReservedSize   Number of octets to reserve for protocol headers (before `Start()`).
       *
       *  @return     On success, a PacketBufferHandle to the allocated buffer. On fail, \c nullptr.
       */
      static PacketBufferHandle New(size_t aAvailableSize, uint16_t aReservedSize = PacketBuffer::kDefaultHeaderReserve);
  
      /**
       * Allocates a packet buffer with initial contents.
       *
       *  @param[in]  aData           Initial buffer contents.
       *  @param[in]  aDataSize       Size of initial buffer contents.
       *  @param[in]  aAdditionalSize Size of additional application data space after the initial contents.
       *  @param[in]  aReservedSize   Number of octets to reserve for protocol headers.
       *
       *  @return     On success, a PacketBufferHandle to the allocated buffer. On fail, \c nullptr.
       */
      static PacketBufferHandle NewWithData(const void * aData, size_t aDataSize, uint16_t aAdditionalSize = 0,
                                            uint16_t aReservedSize = PacketBuffer::kDefaultHeaderReserve);
  
      /**
       * Creates a copy of a packet buffer (or chain).
       *
       * @returns empty handle on allocation failure. Otherwise, the returned buffer has the same sizes and contents as the original.
       */
      PacketBufferHandle CloneData() const;
  
      /**
       * Perform an implementation-defined check on the validity of a PacketBufferHandle.
       *
       * Unless enabled by #CHIP_CONFIG_MEMORY_DEBUG_CHECKS == 1, this function does nothing.
       *
       * When enabled, it performs an implementation- and configuration-defined check on
       * the validity of the packet buffer. It MAY log an error and/or abort the program
       * if the packet buffer or the implementation-defined memory management system is in
       * a faulty state. (Some configurations may not actually perform any check.)
       *
       * @note  A null handle is not considered faulty.
       */
      void Check() const
      {
  #if CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK
          PacketBuffer::Check(mBuffer);
  #endif
      }
  
  protected:
  #if CHIP_SYSTEM_CONFIG_USE_LWIP
      // For use via LwIPPacketBufferView only.
      static struct pbuf * GetLwIPpbuf(const PacketBufferHandle & handle)
      {
          PacketBuffer::Check(handle.mBuffer);
          return static_cast<struct pbuf *>(handle.mBuffer);
      }
  #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
  
  private:
      PacketBufferHandle(const PacketBufferHandle &) = delete;
      PacketBufferHandle & operator=(const PacketBufferHandle &) = delete;
  
      // The caller's ownership is transferred to this.
      explicit PacketBufferHandle(PacketBuffer * buffer) : mBuffer(buffer) {}
  
      static PacketBufferHandle Hold(PacketBuffer * buffer)
      {
          if (buffer != nullptr)
          {
              buffer->AddRef();
          }
          return PacketBufferHandle(buffer);
      }
  
      PacketBuffer * Get() const { return mBuffer; }
  
      bool operator==(const PacketBufferHandle & aOther) { return mBuffer == aOther.mBuffer; }
  
  #if CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHT_SIZE
      void InternalRightSize();
  #endif
  
      PacketBuffer * mBuffer;
  
      friend class PacketBuffer;
      friend class ::PacketBufferTest;
  };
  
  inline void PacketBuffer::SetDataLength(uint16_t aNewLen, const PacketBufferHandle & aChainHead)
  {
      SetDataLength(aNewLen, aChainHead.mBuffer);
  }
  
  inline PacketBufferHandle PacketBuffer::Next()
  {
      return PacketBufferHandle::Hold(ChainedBuffer());
  }
  
  inline PacketBufferHandle PacketBuffer::Last()
  {
      PacketBuffer * p = this;
      while (p->HasChainedBuffer())
          p = p->ChainedBuffer();
      return PacketBufferHandle::Hold(p);
  }
  
  } // namespace System
  
  namespace Encoding {
  
  class PacketBufferWriterUtil
  {
  private:
      template <typename>
      friend class PacketBufferWriterBase;
      static System::PacketBufferHandle Finalize(BufferWriter & aBufferWriter, System::PacketBufferHandle & aPacket);
  };
  
  /**
   * BufferWriter backed by packet buffer.
   *
   * Typical use:
   *  @code
   *      PacketBufferWriter buf(maximumLength);
   *      if (buf.IsNull()) { return CHIP_ERROR_NO_MEMORY; }
   *      buf.Put(...);
   *      ...
   *      PacketBufferHandle handle = buf.Finalize();
   *      if (buf.IsNull()) { return CHIP_ERROR_BUFFER_TOO_SMALL; }
   *      // valid data
   *  @endcode
   */
  template <class Writer>
  class PacketBufferWriterBase : public Writer
  {
  public:
      /**
       * Constructs a BufferWriter that writes into a packet buffer, using all avaiable space.
       *
       *  @param[in]  aPacket  A handle to PacketBuffer, to be used as backing store for the BufferWriter.
       */
      PacketBufferWriterBase(System::PacketBufferHandle && aPacket) :
          Writer(aPacket->Start() + aPacket->DataLength(), aPacket->AvailableDataLength())
      {
          mPacket = std::move(aPacket);
      }
  
      /**
       * Constructs a BufferWriter that writes into a packet buffer, using no more than the requested space.
       *
       *  @param[in]  aPacket A handle to PacketBuffer, to be used as backing store for the BufferWriter.
       *  @param[in]  aSize   Maximum number of octects to write into the packet buffer.
       */
      PacketBufferWriterBase(System::PacketBufferHandle && aPacket, size_t aSize) :
          Writer(aPacket->Start() + aPacket->DataLength(), chip::min(aSize, static_cast<size_t>(aPacket->AvailableDataLength())))
      {
          mPacket = std::move(aPacket);
      }
  
      /**
       * Test whether this PacketBufferWriter is null, or conversely owns a PacketBuffer.
       *
       * @retval true     The PacketBufferWriter is null; it does not own a PacketBuffer. This implies either that
       *                  construction failed, or that \c Finalize() has previously been called to release the buffer.
       * @retval false    The PacketBufferWriter owns a PacketBuffer, which can be written using BufferWriter \c Put() methods,
       *                  and (assuming no overflow) obtained by calling \c Finalize().
       */
      bool IsNull() const { return mPacket.IsNull(); }
  
      /**
       * Obtain the backing packet buffer, if it is valid.
       *
       *  If construction succeeded, \c Finalize() has not already been called, and \c BufferWriter::Fit() is true,
       *  the caller takes ownership of a buffer containing the desired data. Otherwise, the returned handle tests null,
       *  and any underlying storage has been released.
       *
       *  @return     A packet buffer handle.
       */
      System::PacketBufferHandle Finalize() { return PacketBufferWriterUtil::Finalize(*this, mPacket); }
  
  private:
      System::PacketBufferHandle mPacket;
  };
  
  using PacketBufferWriter = PacketBufferWriterBase<chip::Encoding::BufferWriter>;
  
  namespace LittleEndian {
  using PacketBufferWriter = PacketBufferWriterBase<chip::Encoding::LittleEndian::BufferWriter>;
  } // namespace LittleEndian
  
  namespace BigEndian {
  using PacketBufferWriter = PacketBufferWriterBase<chip::Encoding::BigEndian::BufferWriter>;
  } // namespace BigEndian
  
  } // namespace Encoding
  
  } // namespace chip
  
  #if CHIP_SYSTEM_CONFIG_USE_LWIP
  
  namespace chip {
  
  namespace Inet {
  class RawEndPoint;
  class UDPEndPoint;
  class IPEndPointBasis;
  } // namespace Inet
  
  namespace System {
  
  /**
   * Provide low-level access to a raw `pbuf *`, limited to specific classes that interface with LwIP.
   */
  class LwIPPacketBufferView : public PacketBufferHandle
  {
  private:
      /**
       * Borrow a raw LwIP `pbuf *`.
       *
       * @brief The caller has access but no ownership.
       *
       * @note This should be used ONLY by low-level code interfacing with LwIP.
       */
      static struct pbuf * UnsafeGetLwIPpbuf(const PacketBufferHandle & handle) { return PacketBufferHandle::GetLwIPpbuf(handle); }
      friend class Inet::RawEndPoint;
      friend class Inet::UDPEndPoint;
      friend class Inet::IPEndPointBasis;
  };
  
  } // namespace System
  } // namespace chip
  
  #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
  