338 lines
7.7 KiB
Text
338 lines
7.7 KiB
Text
|
/**
|
||
|
* 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."
|
||
|
*
|
||
|
* @file simpleAllocator.I
|
||
|
* @author drose
|
||
|
* @date 2007-05-12
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
INLINE SimpleAllocator::
|
||
|
SimpleAllocator(size_t max_size, Mutex &lock) :
|
||
|
LinkedListNode(true),
|
||
|
_total_size(0),
|
||
|
_max_size(max_size),
|
||
|
_contiguous(max_size),
|
||
|
_lock(lock)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Allocates a new block. Returns NULL if a block of the requested size
|
||
|
* cannot be allocated.
|
||
|
*
|
||
|
* To free the allocated block, call block->free(), or simply delete the block
|
||
|
* pointer.
|
||
|
*/
|
||
|
SimpleAllocatorBlock *SimpleAllocator::
|
||
|
alloc(size_t size, size_t alignment) {
|
||
|
MutexHolder holder(_lock);
|
||
|
return do_alloc(size, alignment);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if there are no blocks allocated on this page, or false if
|
||
|
* there is at least one.
|
||
|
*/
|
||
|
INLINE bool SimpleAllocator::
|
||
|
is_empty() const {
|
||
|
MutexHolder holder(_lock);
|
||
|
return do_is_empty();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the total size of allocated objects.
|
||
|
*/
|
||
|
INLINE size_t SimpleAllocator::
|
||
|
get_total_size() const {
|
||
|
MutexHolder holder(_lock);
|
||
|
return _total_size;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the available space for allocated objects.
|
||
|
*/
|
||
|
INLINE size_t SimpleAllocator::
|
||
|
get_max_size() const {
|
||
|
MutexHolder holder(_lock);
|
||
|
return _max_size;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Changes the available space for allocated objects. This will not affect
|
||
|
* any already-allocated objects, but will have an effect on future calls to
|
||
|
* alloc().
|
||
|
*/
|
||
|
INLINE void SimpleAllocator::
|
||
|
set_max_size(size_t max_size) {
|
||
|
MutexHolder holder(_lock);
|
||
|
_max_size = max_size;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns an upper-bound estimate of the size of the largest contiguous block
|
||
|
* that may be allocated. It is guaranteed that an attempt to allocate a
|
||
|
* block larger than this will fail, though it is not guaranteed that an
|
||
|
* attempt to allocate a block this size or smaller will succeed.
|
||
|
*/
|
||
|
INLINE size_t SimpleAllocator::
|
||
|
get_contiguous() const {
|
||
|
MutexHolder holder(_lock);
|
||
|
return _contiguous;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a pointer to the first allocated block, or NULL if there are no
|
||
|
* allocated blocks.
|
||
|
*/
|
||
|
INLINE SimpleAllocatorBlock *SimpleAllocator::
|
||
|
get_first_block() const {
|
||
|
MutexHolder holder(_lock);
|
||
|
return (_next == this) ? nullptr : (SimpleAllocatorBlock *)_next;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if there are no blocks allocated on this page, or false if
|
||
|
* there is at least one.
|
||
|
*
|
||
|
* Assumes the lock is already held.
|
||
|
*/
|
||
|
INLINE bool SimpleAllocator::
|
||
|
do_is_empty() const {
|
||
|
return (_next == this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Some space has been made available following the indicated block. Increase
|
||
|
* the contiguous space accordingly.
|
||
|
*
|
||
|
* Assumes the lock is already held.
|
||
|
*/
|
||
|
INLINE void SimpleAllocator::
|
||
|
mark_contiguous(const LinkedListNode *block) {
|
||
|
size_t space;
|
||
|
if (block == this) {
|
||
|
// This is the beginning of the list.
|
||
|
if (_next == this) {
|
||
|
// And the list is empty.
|
||
|
space = _max_size;
|
||
|
} else {
|
||
|
space = ((SimpleAllocatorBlock *)_next)->get_start();
|
||
|
}
|
||
|
} else {
|
||
|
space = ((SimpleAllocatorBlock *)block)->do_get_max_size() - ((SimpleAllocatorBlock *)block)->get_size();
|
||
|
}
|
||
|
if (space > _contiguous) {
|
||
|
_contiguous = space;
|
||
|
changed_contiguous();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A SimpleAllocatorBlock must be constructed via the SimpleAllocator::alloc()
|
||
|
* call.
|
||
|
*/
|
||
|
INLINE SimpleAllocatorBlock::
|
||
|
SimpleAllocatorBlock(SimpleAllocator *alloc,
|
||
|
size_t start, size_t size) :
|
||
|
_allocator(alloc),
|
||
|
_start(start),
|
||
|
_size(size)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Transfers ownership from the given SimpleAllocatorBlock to this one.
|
||
|
*/
|
||
|
INLINE SimpleAllocatorBlock::
|
||
|
SimpleAllocatorBlock(SimpleAllocatorBlock &&from) :
|
||
|
_allocator(from._allocator)
|
||
|
{
|
||
|
if (_allocator == nullptr) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
MutexHolder holder(_allocator->_lock);
|
||
|
_start = from._start;
|
||
|
_size = from._size;
|
||
|
LinkedListNode::operator = (std::move(from));
|
||
|
from._allocator = nullptr;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The block automatically frees itself when it destructs.
|
||
|
*/
|
||
|
INLINE SimpleAllocatorBlock::
|
||
|
~SimpleAllocatorBlock() {
|
||
|
free();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Frees this block and instead takes ownership of the given other block.
|
||
|
*/
|
||
|
INLINE SimpleAllocatorBlock &SimpleAllocatorBlock::
|
||
|
operator = (SimpleAllocatorBlock &&from) {
|
||
|
free();
|
||
|
|
||
|
_allocator = from._allocator;
|
||
|
if (_allocator == nullptr) {
|
||
|
_start = 0;
|
||
|
_size = 0;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
MutexHolder holder(_allocator->_lock);
|
||
|
_start = from._start;
|
||
|
_size = from._size;
|
||
|
LinkedListNode::operator = (std::move(from));
|
||
|
from._allocator = nullptr;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Releases the allocated space.
|
||
|
*/
|
||
|
INLINE void SimpleAllocatorBlock::
|
||
|
free() {
|
||
|
if (_allocator != nullptr) {
|
||
|
MutexHolder holder(_allocator->_lock);
|
||
|
do_free();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the SimpleAllocator object that owns this block. Returns NULL if
|
||
|
* the block has been freed.
|
||
|
*/
|
||
|
INLINE SimpleAllocator *SimpleAllocatorBlock::
|
||
|
get_allocator() const {
|
||
|
return _allocator;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the starting point of this block. It is an error to call this if
|
||
|
* the block has been freed.
|
||
|
*/
|
||
|
INLINE size_t SimpleAllocatorBlock::
|
||
|
get_start() const {
|
||
|
nassertr(_allocator != nullptr, 0);
|
||
|
return _start;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the size of this block. It is an error to call this if the block
|
||
|
* has been freed.
|
||
|
*/
|
||
|
INLINE size_t SimpleAllocatorBlock::
|
||
|
get_size() const {
|
||
|
nassertr(_allocator != nullptr, 0);
|
||
|
return _size;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the block has been freed, false if it is still valid.
|
||
|
*/
|
||
|
INLINE bool SimpleAllocatorBlock::
|
||
|
is_free() const {
|
||
|
return (_allocator != nullptr);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the maximum size this block can be reallocated to, as limited by
|
||
|
* the following block.
|
||
|
*/
|
||
|
INLINE size_t SimpleAllocatorBlock::
|
||
|
get_max_size() const {
|
||
|
nassertr(_allocator != nullptr, 0);
|
||
|
MutexHolder holder(_allocator->_lock);
|
||
|
return do_get_max_size();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Changes the size of this block to the specified size. Returns true if the
|
||
|
* change is accepted, false if there was not enough room.
|
||
|
*/
|
||
|
INLINE bool SimpleAllocatorBlock::
|
||
|
realloc(size_t size) {
|
||
|
nassertr(_allocator != nullptr, false);
|
||
|
MutexHolder holder(_allocator->_lock);
|
||
|
return do_realloc(size);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a pointer to the next allocated block in the chain, or NULL if
|
||
|
* there are no more allocated blocks.
|
||
|
*/
|
||
|
INLINE SimpleAllocatorBlock *SimpleAllocatorBlock::
|
||
|
get_next_block() const {
|
||
|
nassertr(_allocator != nullptr, nullptr);
|
||
|
MutexHolder holder(_allocator->_lock);
|
||
|
return (_next == _allocator) ? nullptr : (SimpleAllocatorBlock *)_next;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Releases the allocated space.
|
||
|
*
|
||
|
* Assumes the lock is already held.
|
||
|
*/
|
||
|
INLINE void SimpleAllocatorBlock::
|
||
|
do_free() {
|
||
|
nassertv(_allocator != nullptr);
|
||
|
|
||
|
_allocator->_total_size -= _size;
|
||
|
LinkedListNode *prev = _prev;
|
||
|
remove_from_list();
|
||
|
_allocator->mark_contiguous(prev);
|
||
|
_allocator = nullptr;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the maximum size this block can be reallocated to, as limited by
|
||
|
* the following block.
|
||
|
*
|
||
|
* Assumes the lock is already held.
|
||
|
*/
|
||
|
INLINE size_t SimpleAllocatorBlock::
|
||
|
do_get_max_size() const {
|
||
|
size_t end;
|
||
|
if (_next == _allocator) {
|
||
|
end = _allocator->_max_size;
|
||
|
} else {
|
||
|
end = ((SimpleAllocatorBlock *)_next)->_start;
|
||
|
}
|
||
|
return end - _start;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Changes the size of this block to the specified size. Returns true if the
|
||
|
* change is accepted, false if there was not enough room.
|
||
|
*
|
||
|
* Assumes the lock is already held.
|
||
|
*/
|
||
|
INLINE bool SimpleAllocatorBlock::
|
||
|
do_realloc(size_t size) {
|
||
|
if (size > do_get_max_size()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
_allocator->_total_size -= _size;
|
||
|
_allocator->_total_size += size;
|
||
|
|
||
|
if (size < _size) {
|
||
|
// We're decreasing the block size.
|
||
|
_size = size;
|
||
|
_allocator->mark_contiguous(this);
|
||
|
} else {
|
||
|
// We're increasing the block size.
|
||
|
_size = size;
|
||
|
}
|
||
|
return true;
|
||
|
}
|