// Copyright 2013 The Chromium 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 MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_

#include <stddef.h>
#include <stdint.h>

#include <vector>

#include "base/component_export.h"
#include "base/macros.h"
#include "mojo/public/cpp/system/handle.h"
#include "mojo/public/cpp/system/message.h"

namespace mojo {
namespace internal {

// Buffer provides an interface to allocate memory blocks which are 8-byte
// aligned. It doesn't own the underlying memory. Users must ensure that the
// memory stays valid while using the allocated blocks from Buffer.
//
// A Buffer may be moved around. A moved-from Buffer is reset and may no longer
// be used to Allocate memory unless re-Initialized.
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Buffer {
 public:
  // Constructs an invalid Buffer. May not call Allocate().
  Buffer();

  // Constructs a Buffer which can Allocate() blocks from a buffer of fixed size
  // |size| at |data|. Allocations start at |cursor|, so if |cursor| == |size|
  // then no allocations are allowed.
  //
  // |data| is not owned.
  Buffer(void* data, size_t size, size_t cursor);

  // Like above, but gives the Buffer an underlying message object which can
  // have its payload extended to acquire more storage capacity on Allocate().
  //
  // |data| and |size| must correspond to |message|'s data buffer at the time of
  // construction.
  //
  // |payload_size| is the length of the payload as known by |message|, and it
  // must be less than or equal to |size|.
  //
  // |message| is NOT owned and must outlive this Buffer.
  Buffer(MessageHandle message,
         size_t message_payload_size,
         void* data,
         size_t size);

  Buffer(Buffer&& other);
  ~Buffer();

  Buffer& operator=(Buffer&& other);

  void* data() const { return data_; }
  size_t size() const { return size_; }
  size_t cursor() const { return cursor_; }

  bool is_valid() const {
    return data_ != nullptr || (size_ == 0 && !message_.is_valid());
  }

  // Allocates |num_bytes| from the buffer and returns an index to the start of
  // the allocated block. The resulting index is 8-byte aligned and can be
  // resolved to an address using Get<T>() below.
  size_t Allocate(size_t num_bytes);

  // Returns a typed address within the Buffer corresponding to |index|. Note
  // that this address is NOT stable across calls to |Allocate()| and thus must
  // not be cached accordingly.
  template <typename T>
  T* Get(size_t index) {
    DCHECK_LT(index, cursor_);
    return reinterpret_cast<T*>(static_cast<uint8_t*>(data_) + index);
  }

  // A template helper combining Allocate() and Get<T>() above to allocate and
  // return a block of size |sizeof(T)|.
  template <typename T>
  T* AllocateAndGet() {
    return Get<T>(Allocate(sizeof(T)));
  }

  // A helper which combines Allocate() and Get<void>() for a specified number
  // of bytes.
  void* AllocateAndGet(size_t num_bytes) {
    return Get<void>(Allocate(num_bytes));
  }

  // Serializes |handles| into the buffer object. Only valid to call when this
  // Buffer is backed by a message object.
  void AttachHandles(std::vector<ScopedHandle>* handles);

  // Seals this Buffer so it can no longer be used for allocation, and ensures
  // the backing message object has a complete accounting of the size of the
  // meaningful payload bytes.
  void Seal();

  // Resets the buffer to an invalid state. Can no longer be used to Allocate().
  void Reset();

 private:
  MessageHandle message_;

  // The payload size from the message's internal perspective. This differs from
  // |size_| as Mojo may intentionally over-allocate space to account for future
  // growth. It differs from |cursor_| because we don't push payload size
  // updates to the message object as frequently as we update |cursor_|, for
  // performance.
  size_t message_payload_size_ = 0;

  // The storage location and capacity currently backing |message_|. Owned by
  // the message object internally, not by this Buffer.
  void* data_ = nullptr;
  size_t size_ = 0;

  // The current write offset into |data_| if this Buffer is being used for
  // message creation.
  size_t cursor_ = 0;

  DISALLOW_COPY_AND_ASSIGN(Buffer);
};

}  // namespace internal
}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
