// Copyright (c) 2009 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.

#include "net/tools/flip_server/simple_buffer.h"
#include "base/logging.h"

// Some of the following member functions are marked inlined, even though they
// are virtual. This may seem counter-intuitive, since virtual functions are
// generally not eligible for inlining. Profiling results indicate that these
// large amount of runtime is spent on virtual function dispatch on these
// simple functions. They are virtual because of the interface this class
// inherits from. However, it is very unlikely that anyone will sub-class
// SimpleBuffer and change their implementation. To get rid of this baggage,
// internal implementation (e.g., Write) explicitly use SimpleBuffer:: to
// qualify the method calls, thus disabling the virtual dispatch and enable
// inlining.

namespace net {

static const int kInitialSimpleBufferSize = 10;

SimpleBuffer::SimpleBuffer()
  : storage_(new char[kInitialSimpleBufferSize]),
    write_idx_(0),
    read_idx_(0),
    storage_size_(kInitialSimpleBufferSize) {
}

SimpleBuffer::SimpleBuffer(int size)
  : write_idx_(0),
    read_idx_(0),
    storage_size_(size) {
  // Callers may try to allocate overly large blocks, but negative sizes are
  // obviously wrong.
  CHECK_GE(size, 0);
  storage_ = new char[size];
}

////////////////////////////////////////////////////////////////////////////////

int SimpleBuffer::ReadableBytes() const {
  return write_idx_ - read_idx_;
}

////////////////////////////////////////////////////////////////////////////////

std::string SimpleBuffer::str() const {
  std::string s;
  char * readable_ptr;
  int readable_size;
  GetReadablePtr(&readable_ptr, &readable_size);
  s.append(readable_ptr, readable_ptr + readable_size);
  return s;
}

////////////////////////////////////////////////////////////////////////////////

int SimpleBuffer::BufferSize() const {
  return storage_size_;
}

////////////////////////////////////////////////////////////////////////////////

inline int SimpleBuffer::BytesFree() const {
  return (storage_size_ - write_idx_);
}

////////////////////////////////////////////////////////////////////////////////

bool SimpleBuffer::Empty() const {
  return (read_idx_ == write_idx_);
}

////////////////////////////////////////////////////////////////////////////////

bool SimpleBuffer::Full() const {
  return ((write_idx_ == storage_size_) && (read_idx_ != write_idx_));
}

////////////////////////////////////////////////////////////////////////////////

// returns the number of characters written.
// appends up-to-'size' bytes to the simplebuffer.
int SimpleBuffer::Write(const char* bytes, int size) {
  bool has_room = ((storage_size_ - write_idx_) >= size);
  if (!has_room) {
    (void)Reserve(size);
  }
  memcpy(storage_ + write_idx_, bytes, size);
  SimpleBuffer::AdvanceWritablePtr(size);
  return size;
}

////////////////////////////////////////////////////////////////////////////////

// stores a pointer into the simple buffer in *ptr,
// and stores the number of characters which are allowed
// to be written in *size.
inline void SimpleBuffer::GetWritablePtr(char **ptr, int* size) const {
  *ptr = storage_ + write_idx_;
  *size = SimpleBuffer::BytesFree();
}

////////////////////////////////////////////////////////////////////////////////

// stores a pointer into the simple buffer in *ptr,
// and stores the number of characters which are allowed
// to be read in *size.
void SimpleBuffer::GetReadablePtr(char **ptr, int* size) const {
  *ptr = storage_ + read_idx_;
  *size = write_idx_ - read_idx_;
}

////////////////////////////////////////////////////////////////////////////////

// returns the number of bytes read into 'bytes'
int SimpleBuffer::Read(char* bytes, int size) {
  char * read_ptr = NULL;
  int read_size = 0;
  GetReadablePtr(&read_ptr, &read_size);
  if (read_size > size) {
    read_size = size;
  }
  memcpy(bytes, read_ptr, read_size);
  AdvanceReadablePtr(read_size);
  return read_size;
}

////////////////////////////////////////////////////////////////////////////////

// removes all data from the simple buffer
void SimpleBuffer::Clear() {
  read_idx_ = write_idx_ = 0;
}

////////////////////////////////////////////////////////////////////////////////

// Attempts to reserve a contiguous block of buffer space by either reclaiming
// old data that is already read, and reallocate large storage as needed.
bool SimpleBuffer::Reserve(int size) {
  if (size > 0 && BytesFree() < size) {
    char * read_ptr = NULL;
    int read_size = 0;
    GetReadablePtr(&read_ptr, &read_size);

    if (read_size + size <= BufferSize()) {
      // Can reclaim space from already read bytes by shifting
      memmove(storage_, read_ptr, read_size);
      read_idx_ = 0;
      write_idx_ = read_size;
      CHECK_GE(BytesFree(), size);
    } else {
      // what we need is to have at least size bytes available for writing.
      // This implies that the buffer needs to be at least size bytes +
      // read_size bytes long. Since we want linear time extensions in the case
      // that we're extending this thing repeatedly, we should extend to twice
      // the current size (if that is big enough), or the size + read_size
      // bytes, whichever is larger.
      int new_storage_size = 2 * storage_size_;
      if (new_storage_size < size + read_size) {
        new_storage_size = size + read_size;
      }

      // have to extend the thing
      char* new_storage = new char[new_storage_size];

      // copy still useful info to the new buffer.
      memcpy(new_storage, read_ptr, read_size);
      // reset pointers.
      read_idx_ = 0;
      write_idx_ = read_size;
      delete[] storage_;
      storage_ = new_storage;
      storage_size_ = new_storage_size;
    }
  }
  return true;
}

////////////////////////////////////////////////////////////////////////////////

// removes the oldest 'amount_to_consume' characters.
void SimpleBuffer::AdvanceReadablePtr(int amount_to_advance) {
  read_idx_ += amount_to_advance;
  if (read_idx_ > storage_size_) {
    read_idx_ = storage_size_;
  }
}

////////////////////////////////////////////////////////////////////////////////

// Moves the internal pointers around such that the
// amount of data specified here is expected to
// already be resident (as if it was Written)
inline void SimpleBuffer::AdvanceWritablePtr(int amount_to_advance) {
  write_idx_ += amount_to_advance;
  if (write_idx_ > storage_size_) {
    write_idx_ = storage_size_;
  }
}

}  // namespace net