text: // Copyright 2017 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_PACKET_VIEW_H_
  #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_PACKET_VIEW_H_
  
  #include <zircon/assert.h>
  
  #include <cstdint>
  
  #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
  
  namespace bt {
  
  // Base class-template for generic packets that contain a header and a payload.
  // A PacketView is a light-weight object that operates over a previously
  // allocated ByteBuffer without taking ownership of it. The PacketView
  // class-template provides a read-only view over the underlying buffer while
  // MutablePacketView allows modification of the underlying buffer.
  //
  // Example usage:
  //
  //   // Allocate a buffer
  //   StaticByteBuffer<512> buffer;
  //
  //   // Receive some data on the buffer.
  //   foo::WriteMyPacket(buffer.mutable_data(), ...);
  //
  //   // Read packet header contents:
  //   struct MyHeaderType {
  //     uint8_t field0;
  //   };
  //
  //   PacketView<MyHeaderType> packet(&buffer, 0);
  //   std::cout << "My header field is: " << packet.header().field0;
  //
  //   // If the packet has an expected payload size, pass that into the
  //   // constructor:
  //   struct MyPayloadType {
  //     uint8_t byte_field;
  //     uint16_t uint16_field;
  //     uint8_t array_field[];
  //   } __PACKED;
  //
  //   MutablePacketView<MyHeaderType> packet(&buffer, sizeof(MyPayloadType) + 2);
  //   packet.mutable_payload<MyPayloadType>().byte_field = 0xFF;
  //   packet.mutable_payload<MyPayloadType>().uint16_field = 0xFFFF;
  //   packet.mutable_payload<MyPayloadType>().array_field[0] = 0x00;
  //   packet.mutable_payload<MyPayloadType>().array_field[1] = 0x01;
  //
  // MutablePacketView allows itself to be resized at any time. This is useful
  // when the complete packet payload is unknown prior to reading the header
  // contents. For example:
  //
  //   MutablePacketView<MyHeaderType view(&buffer, my_max_payload_length);
  //   view.mutable_data().Write(data);
  //   view.Resize(view.header().payload_size);
  template <typename HeaderType>
  class PacketView {
   public:
    // The default constructor initializes an empty packet view. This is to enable
    // PacketView to be used by value in structures and stl containers.
    PacketView() : buffer_(nullptr), size_(0u) {}
  
    // Initializes this Packet to operate over |buffer|. |payload_size| is the
    // size of the packet payload not including the packet header. A
    // |payload_size| value of 0 indicates that the packet contains no payload.
    explicit PacketView(const ByteBuffer* buffer, size_t payload_size = 0u)
        : buffer_(buffer), size_(sizeof(HeaderType) + payload_size) {
      ZX_ASSERT(buffer_);
      ZX_ASSERT(buffer_->size() >= size_);
    }
  
    const BufferView data() const { return buffer_->view(0, size_); }
    const BufferView payload_data() const {
      return buffer_->view(sizeof(HeaderType), size_ - sizeof(HeaderType));
    }
  
    size_t size() const { return size_; }
    size_t payload_size() const { return size() - sizeof(HeaderType); }
  
    const uint8_t* payload_bytes() const {
      ZX_ASSERT(is_valid());
      return payload_size() ? buffer_->data() + sizeof(HeaderType) : nullptr;
    }
  
    const HeaderType& header() const {
      ZX_ASSERT(is_valid());
      return *reinterpret_cast<const HeaderType*>(buffer_->data());
    }
  
    template <typename PayloadType>
    const PayloadType& payload() const {
      ZX_ASSERT(sizeof(PayloadType) <= payload_size());
      return *reinterpret_cast<const PayloadType*>(payload_bytes());
    }
  
    // A PacketView that contains no backing buffer is considered invalid. A
    // PacketView that was initialized with a buffer that is too small is invalid.
    bool is_valid() const { return buffer_ && size_ >= sizeof(HeaderType); }
  
    // Adjusts the size of this PacketView to match the given |payload_size|. This
    // is useful when the exact packet size is not known during construction.
    //
    // This performs runtime checks to make sure that the underlying buffer is
    // approriately sized.
    void Resize(size_t payload_size) { this->set_size(sizeof(HeaderType) + payload_size); }
  
   protected:
    void set_size(size_t size) {
      ZX_ASSERT(buffer_);
      ZX_ASSERT(buffer_->size() >= size);
      ZX_ASSERT(size >= sizeof(HeaderType));
      size_ = size;
    }
  
    const ByteBuffer* buffer() const { return buffer_; }
  
   private:
    const ByteBuffer* buffer_;  // weak
    size_t size_;
  };
  
  template <typename HeaderType>
  class MutablePacketView : public PacketView<HeaderType> {
   public:
    MutablePacketView() = default;
  
    explicit MutablePacketView(MutableByteBuffer* buffer, size_t payload_size = 0u)
        : PacketView<HeaderType>(buffer, payload_size) {}
  
    MutableBufferView mutable_data() { return mutable_buffer()->mutable_view(0, this->size()); }
  
    MutableBufferView mutable_payload_data() const {
      return mutable_buffer()->mutable_view(sizeof(HeaderType), this->size() - sizeof(HeaderType));
    }
  
    uint8_t* mutable_payload_bytes() {
      return this->payload_size() ? mutable_buffer()->mutable_data() + sizeof(HeaderType) : nullptr;
    }
  
    HeaderType* mutable_header() {
      return reinterpret_cast<HeaderType*>(mutable_buffer()->mutable_data());
    }
  
    template <typename PayloadType>
    PayloadType* mutable_payload() {
      ZX_ASSERT(sizeof(PayloadType) <= this->payload_size());
      return reinterpret_cast<PayloadType*>(mutable_payload_bytes());
    }
  
   private:
    MutableByteBuffer* mutable_buffer() const {
      // Cast-away the const. This is OK in this case since we're storing our
      // buffer in the parent class instead of duplicating a non-const version in
      // this class.
      return const_cast<MutableByteBuffer*>(static_cast<const MutableByteBuffer*>(this->buffer()));
    }
  };
  
  }  // namespace bt
  
  #endif  // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_PACKET_VIEW_H_
  