// Filename: simpleAllocator.h
// Created by:  drose (12May07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#ifndef SIMPLEALLOCATOR_H
#define SIMPLEALLOCATOR_H

#include "pandabase.h"
#include "linkedListNode.h"
#include "pmutex.h"
#include "mutexHolder.h"

class SimpleAllocatorBlock;

////////////////////////////////////////////////////////////////////
//       Class : SimpleAllocator
// Description : An implementation of a very simple block allocator.
//               This class can allocate ranges of nonnegative
//               integers within a specified upper limit; it uses a
//               simple first-fit algorithm to find the next available
//               space.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_GOBJ SimpleAllocator : public LinkedListNode {
PUBLISHED:
  INLINE SimpleAllocator(size_t max_size, Mutex &lock);
  virtual ~SimpleAllocator();

  INLINE SimpleAllocatorBlock *alloc(size_t size);

  INLINE bool is_empty() const;
  INLINE size_t get_total_size() const;
  INLINE size_t get_max_size() const;
  INLINE void set_max_size(size_t max_size);
  INLINE size_t get_contiguous() const;

  INLINE SimpleAllocatorBlock *get_first_block() const;

  void output(ostream &out) const;
  void write(ostream &out) const;

protected:
  SimpleAllocatorBlock *do_alloc(size_t size);
  INLINE bool do_is_empty() const;

  virtual SimpleAllocatorBlock *make_block(size_t start, size_t size);
  INLINE void mark_contiguous(const LinkedListNode *block);
  virtual void changed_contiguous();

protected:
  // This is implemented as a linked-list chain of allocated blocks.
  // Free blocks are implicit.  Blocks are kept in sorted order from
  // beginning to end.  Allocating a block means creating a new entry
  // in the chain wherever it may fit; freeing a block means simply
  // removing the allocated block from the chain.  With this simple
  // approach, there is no need to merge adjacent free blocks to
  // straighten out fragmentation, since free blocks are not stored.
  // However, it does mean we have to walk through a list of adjacent
  // allocated blocks in order to find the free blocks.
  size_t _total_size;
  size_t _max_size;

  // This is what we currently believe our max contiguous space to be.
  // This guess might be larger than the actual available space, but
  // it will not be smaller.
  size_t _contiguous;

  // This mutex protects all operations within this class.  The caller
  // must pass the reference to a mutex in to the constructor, and the
  // caller remains responsible for owning the mutex.  This allows the
  // mutex to be shared where appropriate.

  // A derived class may also use it to protect itself as well, but
  // take care to call do_alloc() instead of alloc() etc. as
  // necessary.
  Mutex &_lock;

  friend class SimpleAllocatorBlock;
};

////////////////////////////////////////////////////////////////////
//       Class : SimpleAllocatorBlock
// Description : A single block as returned from
//               SimpleAllocator::alloc().
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_GOBJ SimpleAllocatorBlock : public LinkedListNode {
protected:
  INLINE SimpleAllocatorBlock(SimpleAllocator *alloc,
                              size_t start, size_t size);

PUBLISHED:
  INLINE ~SimpleAllocatorBlock();
  INLINE void free();

  INLINE SimpleAllocator *get_allocator() const;

  INLINE size_t get_start() const;
  INLINE size_t get_size() const;
  INLINE bool is_free() const;

  INLINE size_t get_max_size() const;
  INLINE bool realloc(size_t size);

  INLINE SimpleAllocatorBlock *get_next_block() const;

  void output(ostream &out) const;

protected:
  INLINE void do_free();
  INLINE size_t do_get_max_size() const;
  INLINE bool do_realloc(size_t size);

private:
  SimpleAllocator *_allocator;
  size_t _start;
  size_t _size;

  friend class SimpleAllocator;
};

INLINE ostream &operator << (ostream &out, const SimpleAllocator &obj) {
  obj.output(out);
  return out;
}

INLINE ostream &operator << (ostream &out, const SimpleAllocatorBlock &obj) {
  obj.output(out);
  return out;
}

#include "simpleAllocator.I"

#endif
