// Copyright (c) 2012 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 CONTENT_BROWSER_LOADER_RESOURCE_BUFFER_H_
#define CONTENT_BROWSER_LOADER_RESOURCE_BUFFER_H_

#include <queue>

#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/shared_memory.h"
#include "content/common/content_export.h"

namespace content {

// ResourceBuffer implements a simple "circular buffer" allocation strategy.
// Allocations are recycled in FIFO order.
//
// You can think of the ResourceBuffer as a FIFO.  The Allocate method reserves
// space in the buffer.  Allocate may be called multiple times until the buffer
// is fully reserved (at which point CanAllocate returns false).  Allocations
// are freed in FIFO order via a call to RecycleLeastRecentlyAllocated.
//
// ResourceBuffer is reference-counted for the benefit of consumers, who need
// to ensure that ResourceBuffer stays alive while they are using its memory.
//
// EXAMPLE USAGE:
//
//   // Writes data into the ResourceBuffer, and returns the location (byte
//   // offset and count) of the bytes written into the ResourceBuffer's shared
//   // memory buffer.
//   void WriteToBuffer(ResourceBuffer* buf, int* offset, int* count) {
//     DCHECK(buf->CanAllocate());
//
//     *offset = -1;
//     *count = 0;
//
//     int size;
//     char* ptr = buf->Allocate(&size);
//     if (!ptr) { /* handle error */ }
//
//     int bytes_read = static_cast<int>(fread(ptr, 1, size, file_pointer_));
//     if (!bytes_read) { /* handle error */ }
//
//     if (bytes_read < size)
//       buf->ShrinkLastAllocation(bytes_read);
//
//     *offset = buf->GetLastAllocationOffset();
//     *count = bytes_read;
//   }
//
// NOTE: As the above example illustrates, the ResourceBuffer keeps track of
// the last allocation made.  Calling ShrinkLastAllocation is optional, as it
// just helps the ResourceBuffer optimize storage and be more aggressive about
// returning larger allocations from the Allocate method.
//
class CONTENT_EXPORT ResourceBuffer
    : public base::RefCountedThreadSafe<ResourceBuffer> {
 public:
  ResourceBuffer();

  // Initialize the shared memory buffer.  It will be buffer_size bytes in
  // length.  The min/max_allocation_size parameters control the behavior of
  // the Allocate method.  It will prefer to return segments that are
  // max_allocation_size in length, but will return segments less than that if
  // space is limited.  It will not return allocations smaller than
  // min_allocation_size.
  bool Initialize(int buffer_size,
                  int min_allocation_size,
                  int max_allocation_size);
  bool IsInitialized() const;

  // Returns a shared memory handle that can be passed to the given process.
  // The shared memory handle is only intended to be interpretted by code
  // running in the specified process.  NOTE: The caller should ensure that
  // this memory eventually be returned to the operating system.
  bool ShareToProcess(base::ProcessHandle process_handle,
                      base::SharedMemoryHandle* shared_memory_handle,
                      int* shared_memory_size);

  // Returns true if Allocate will succeed.
  bool CanAllocate() const;

  // Returns a pointer into the shared memory buffer or NULL if the buffer is
  // already fully allocated.  The returned size will be max_allocation_size
  // unless the buffer is close to being full.
  char* Allocate(int* size);

  // Returns the offset into the shared memory buffer where the last allocation
  // returned by Allocate can be found.
  int GetLastAllocationOffset() const;

  // Called to reduce the size of the last allocation returned by Allocate.  It
  // is OK for new_size to match the current size of the last allocation.
  void ShrinkLastAllocation(int new_size);

  // Called to allow reuse of memory that was previously allocated.  See notes
  // above the class for more details about this method.
  void RecycleLeastRecentlyAllocated();

 private:
  friend class base::RefCountedThreadSafe<ResourceBuffer>;
  ~ResourceBuffer();

  base::SharedMemory shared_mem_;

  int buf_size_;
  int min_alloc_size_;
  int max_alloc_size_;

  // These point to the range of the shared memory that is currently allocated.
  // If alloc_start_ is -1, then the range is empty and nothing is allocated.
  // Otherwise, alloc_start_ points to the start of the allocated range, and
  // alloc_end_ points just beyond the end of the previous allocation.  In the
  // wraparound case, alloc_end_ <= alloc_start_.  See resource_buffer.cc for
  // more details about these members.
  int alloc_start_;
  int alloc_end_;

  std::queue<int> alloc_sizes_;

  DISALLOW_COPY_AND_ASSIGN(ResourceBuffer);
};

}  // namespace content

#endif  // CONTENT_BROWSER_LOADER_RESOURCE_BUFFER_H_
