mirror of
https://github.com/dlang/phobos.git
synced 2025-05-01 23:50:31 +03:00
Add alignedAllocate/Reallocate, improve documentation
This commit is contained in:
parent
0d6ae0459d
commit
2e435bd4d0
1 changed files with 134 additions and 39 deletions
|
@ -40,8 +40,6 @@ blockSize) parameter as in $(D HeapBlock!(Allocator, 4096)). To choose a block
|
||||||
size parameter, use $(D HeapBlock!(Allocator, chooseAtRuntime)) and pass the
|
size parameter, use $(D HeapBlock!(Allocator, chooseAtRuntime)) and pass the
|
||||||
block size to the constructor.
|
block size to the constructor.
|
||||||
|
|
||||||
TODO: implement $(D alignedAllocate) and $(D alignedReallocate).
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
ParentAllocator = NullAllocator)
|
ParentAllocator = NullAllocator)
|
||||||
|
@ -66,9 +64,11 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
If $(D blockSize == chooseAtRuntime), $(D HeapBlock) offers a read/write
|
If $(D blockSize == chooseAtRuntime), $(D HeapBlock) offers a read/write
|
||||||
property $(D blockSize). It must be set to a power of two before any use
|
property $(D blockSize). It must be set before any use of the allocator.
|
||||||
of the allocator. Otherwise, $(D blockSize) is an alias for $(D
|
Otherwise (i.e. $(D theBlockSize) is a legit constant), $(D blockSize) is
|
||||||
theBlockSize).
|
an alias for $(D theBlockSize). Whether constant or variable, must also be
|
||||||
|
a multiple of $(D alignment). This constraint is $(D assert)ed statically
|
||||||
|
and dynamically.
|
||||||
*/
|
*/
|
||||||
static if (theBlockSize != chooseAtRuntime)
|
static if (theBlockSize != chooseAtRuntime)
|
||||||
{
|
{
|
||||||
|
@ -96,14 +96,14 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The alignment offered is user-configurable statically through parameter
|
The _alignment offered is user-configurable statically through parameter
|
||||||
$(D theAlignment), defaulted to $(D platformAlignment).
|
$(D theAlignment), defaulted to $(D platformAlignment).
|
||||||
*/
|
*/
|
||||||
alias alignment = theAlignment;
|
alias alignment = theAlignment;
|
||||||
|
|
||||||
// state {
|
// state {
|
||||||
/**
|
/**
|
||||||
The parent allocator. Depending on whether $(D ParentAllocator) holds state
|
The _parent allocator. Depending on whether $(D ParentAllocator) holds state
|
||||||
or not, this is a member variable or an alias for $(D ParentAllocator.it).
|
or not, this is a member variable or an alias for $(D ParentAllocator.it).
|
||||||
*/
|
*/
|
||||||
static if (stateSize!ParentAllocator)
|
static if (stateSize!ParentAllocator)
|
||||||
|
@ -135,20 +135,20 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Constructs a block allocator given desired capacity in bytes.
|
Constructs a block allocator given a hunk of memory, or a desired capacity
|
||||||
*/
|
in bytes.
|
||||||
static if (!is(ParentAllocator == NullAllocator))
|
|
||||||
this(size_t capacity)
|
|
||||||
{
|
|
||||||
size_t toAllocate = totalAllocation(capacity);
|
|
||||||
auto data = parent.allocate(toAllocate);
|
|
||||||
this(data);
|
|
||||||
assert(_blocks * blockSize >= capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
$(UL
|
||||||
Constructs a block allocator given a hunk of memory. The layout puts the
|
$(LI If $(D ParentAllocator) is $(D NullAllocator), only the constructor
|
||||||
bitmap at the front followed immediately by the payload.
|
taking $(D data) is defined and the user is responsible for freeing $(D
|
||||||
|
data) if desired.)
|
||||||
|
$(LI Otherwise, both constructors are defined. The $(D data)-based
|
||||||
|
constructor assumes memory has been allocated with the parent allocator.
|
||||||
|
The $(D capacity)-based constructor uses $(D ParentAllocator) to allocate
|
||||||
|
an appropriate contiguous hunk of memory. Regardless of the constructor
|
||||||
|
used, the destructor releases the memory by using $(D
|
||||||
|
ParentAllocator.deallocate).)
|
||||||
|
)
|
||||||
*/
|
*/
|
||||||
this(void[] data)
|
this(void[] data)
|
||||||
{
|
{
|
||||||
|
@ -181,6 +181,16 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ditto
|
||||||
|
static if (!is(ParentAllocator == NullAllocator))
|
||||||
|
this(size_t capacity)
|
||||||
|
{
|
||||||
|
size_t toAllocate = totalAllocation(capacity);
|
||||||
|
auto data = parent.allocate(toAllocate);
|
||||||
|
this(data);
|
||||||
|
assert(_blocks * blockSize >= capacity);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
If $(D ParentAllocator) is not $(D NullAllocator) and defines $(D
|
If $(D ParentAllocator) is not $(D NullAllocator) and defines $(D
|
||||||
deallocate), the destructor is defined to deallocate the block held.
|
deallocate), the destructor is defined to deallocate the block held.
|
||||||
|
@ -224,12 +234,16 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Standard allocator methods per the semantics defined above. The $(D
|
Allocates $(D s) bytes of memory and returns it, or $(D null) if memory
|
||||||
deallocate) and $(D reallocate) methods are $(D @system) because they may
|
could not be allocated.
|
||||||
move memory around, leaving dangling pointers in user code.
|
|
||||||
|
|
||||||
BUGS: Neither $(D deallocateAll) nor the destructor free the original memory
|
The following information might be of help with choosing the appropriate
|
||||||
block. Either user code or the parent allocator should carry that.
|
block size. Actual allocation occurs in sizes multiple of the block size.
|
||||||
|
Allocating one block is the fastest because only one 0 bit needs to be
|
||||||
|
found in the metadata. Allocating 2 through 64 blocks is the next cheapest
|
||||||
|
because it affects a maximum of two $(D ulong)s in the metadata.
|
||||||
|
Allocations greater than 64 blocks require a multiword search through the
|
||||||
|
metadata.
|
||||||
*/
|
*/
|
||||||
@trusted void[] allocate(const size_t s)
|
@trusted void[] allocate(const size_t s)
|
||||||
{
|
{
|
||||||
|
@ -261,7 +275,7 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
goto case 0; // fall through
|
goto case 0; // fall through
|
||||||
case 0:
|
case 0:
|
||||||
return null;
|
return null;
|
||||||
case 2: .. case 63:
|
case 2: .. case 64:
|
||||||
result = smallAlloc(cast(uint) blocks);
|
result = smallAlloc(cast(uint) blocks);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -271,7 +285,47 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
return result.ptr ? result.ptr[0 .. s] : null;
|
return result.ptr ? result.ptr[0 .. s] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/**
|
||||||
|
Allocates a block with specified alignment $(D a). The alignment must be a
|
||||||
|
power of 2. If $(D a <= alignment), function forwards to $(D allocate).
|
||||||
|
Otherwise, it attempts to overallocate and then adjust the result for
|
||||||
|
proper alignment. In the worst case the slack memory is around two blocks.
|
||||||
|
*/
|
||||||
|
void[] alignedAllocate(size_t n, uint a)
|
||||||
|
{
|
||||||
|
assert(a.isPowerOf2);
|
||||||
|
if (a <= alignment) return allocate(n);
|
||||||
|
|
||||||
|
// Overallocate to make sure we can get an aligned block
|
||||||
|
auto b = allocate((n + a - alignment).roundUpToMultipleOf(blockSize));
|
||||||
|
if (!b.ptr) return null;
|
||||||
|
auto result = b.roundStartToMultipleOf(a);
|
||||||
|
assert(result.length >= n);
|
||||||
|
result = result.ptr[0 .. n]; // final result
|
||||||
|
|
||||||
|
// Free any blocks that might be slack at the beginning
|
||||||
|
auto slackHeadingBlocks = (result.ptr - b.ptr) / blockSize;
|
||||||
|
if (slackHeadingBlocks)
|
||||||
|
{
|
||||||
|
deallocate(b[0 .. slackHeadingBlocks * blockSize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free any blocks that might be slack at the end
|
||||||
|
auto slackTrailingBlocks = ((b.ptr + b.length)
|
||||||
|
- (result.ptr + result.length)) / blockSize;
|
||||||
|
if (slackTrailingBlocks)
|
||||||
|
{
|
||||||
|
deallocate(b[$ - slackTrailingBlocks * blockSize .. $]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
If the $(D HeapBlock) object is empty (has no active allocation), allocates
|
||||||
|
all memory within and returns a slice to it. Otherwise, returns $(D null)
|
||||||
|
(i.e. no attempt is made to allocate the largest available block).
|
||||||
|
*/
|
||||||
void[] allocateAll()
|
void[] allocateAll()
|
||||||
{
|
{
|
||||||
if (!empty) return null;
|
if (!empty) return null;
|
||||||
|
@ -279,7 +333,10 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
return _payload;
|
return _payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/**
|
||||||
|
Returns $(D true) if $(D b) belongs to the $(D HeapBlock) object. This
|
||||||
|
method is somewhat tolerant in that accepts an interior slice.
|
||||||
|
*/
|
||||||
bool owns(void[] b) const
|
bool owns(void[] b) const
|
||||||
{
|
{
|
||||||
assert(b.ptr !is null || b.length == 0, "Corrupt block.");
|
assert(b.ptr !is null || b.length == 0, "Corrupt block.");
|
||||||
|
@ -433,9 +490,12 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/**
|
||||||
|
Expands an allocated block in place.
|
||||||
|
*/
|
||||||
@trusted bool expand(ref void[] b, immutable size_t delta)
|
@trusted bool expand(ref void[] b, immutable size_t delta)
|
||||||
{
|
{
|
||||||
|
// Dispose with trivial corner cases
|
||||||
if (delta == 0) return true;
|
if (delta == 0) return true;
|
||||||
if (b is null)
|
if (b is null)
|
||||||
{
|
{
|
||||||
|
@ -443,6 +503,10 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
return b !is null;
|
return b !is null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* To simplify matters, refuse to expand buffers that don't start at a block start (this may be the case for blocks allocated with alignedAllocate).
|
||||||
|
*/
|
||||||
|
if ((b.ptr - _payload.ptr) % blockSize) return false;
|
||||||
|
|
||||||
const blocksOld = bytes2blocks(b.length);
|
const blocksOld = bytes2blocks(b.length);
|
||||||
const blocksNew = bytes2blocks(b.length + delta);
|
const blocksNew = bytes2blocks(b.length + delta);
|
||||||
assert(blocksOld <= blocksNew);
|
assert(blocksOld <= blocksNew);
|
||||||
|
@ -475,7 +539,9 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/**
|
||||||
|
Reallocates a previously-allocated block. Contractions occur in place.
|
||||||
|
*/
|
||||||
@system bool reallocate(ref void[] b, size_t newSize)
|
@system bool reallocate(ref void[] b, size_t newSize)
|
||||||
{
|
{
|
||||||
if (newSize == 0)
|
if (newSize == 0)
|
||||||
|
@ -492,23 +558,46 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
b = b[0 .. newSize];
|
b = b[0 .. newSize];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Attempt an in-place expansion first
|
|
||||||
const delta = newSize - b.length;
|
|
||||||
if (expand(b, delta)) return true;
|
|
||||||
// Go the slow route
|
// Go the slow route
|
||||||
return .reallocate(this, b, newSize);
|
return .reallocate(this, b, newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/**
|
||||||
|
Reallocates a block previously allocated with $(D alignedAllocate). Contractions do not occur in place.
|
||||||
|
*/
|
||||||
|
@system bool alignedReallocate(ref void[] b, size_t newSize, uint a)
|
||||||
|
{
|
||||||
|
if (newSize == 0)
|
||||||
|
{
|
||||||
|
deallocate(b);
|
||||||
|
b = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Go the slow route
|
||||||
|
return .alignedReallocate(this, b, newSize, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Deallocates a block previously allocated with this allocator.
|
||||||
|
*/
|
||||||
void deallocate(void[] b)
|
void deallocate(void[] b)
|
||||||
{
|
{
|
||||||
if (b is null) return;
|
if (b is null) return;
|
||||||
// Round up size to multiple of block size
|
// Adjust pointer, might be inside a block after alignedAllocate
|
||||||
auto blocks = b.length.divideRoundUp(blockSize);
|
//auto p = (b.ptr - _payload.ptr) / blockSize
|
||||||
|
|
||||||
// Locate position
|
// Locate position
|
||||||
auto pos = b.ptr - _payload.ptr;
|
auto pos = b.ptr - _payload.ptr;
|
||||||
assert(pos % blockSize == 0);
|
|
||||||
auto blockIdx = pos / blockSize;
|
auto blockIdx = pos / blockSize;
|
||||||
|
|
||||||
|
// Adjust pointer, might be inside a block due to alignedAllocate
|
||||||
|
auto begin = _payload.ptr + blockIdx * blockSize,
|
||||||
|
end = b.ptr + b.length;
|
||||||
|
b = begin[0 .. end - begin];
|
||||||
|
// Round up size to multiple of block size
|
||||||
|
auto blocks = b.length.divideRoundUp(blockSize);
|
||||||
|
|
||||||
|
// Get into details
|
||||||
auto wordIdx = blockIdx / 64, msbIdx = cast(uint) (blockIdx % 64);
|
auto wordIdx = blockIdx / 64, msbIdx = cast(uint) (blockIdx % 64);
|
||||||
if (_startIdx > wordIdx) _startIdx = wordIdx;
|
if (_startIdx > wordIdx) _startIdx = wordIdx;
|
||||||
|
|
||||||
|
@ -545,14 +634,20 @@ struct HeapBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/**
|
||||||
|
Forcibly deallocates all memory allocated by this allocator, making it
|
||||||
|
available for further allocations. Does not return memory to $(D
|
||||||
|
ParentAllocator).
|
||||||
|
*/
|
||||||
void deallocateAll()
|
void deallocateAll()
|
||||||
{
|
{
|
||||||
_control[] = 0;
|
_control[] = 0;
|
||||||
_startIdx = 0;
|
_startIdx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/**
|
||||||
|
Returns $(D true) iff no memory is currently allocated with this allocator.
|
||||||
|
*/
|
||||||
bool empty()
|
bool empty()
|
||||||
{
|
{
|
||||||
return _control.allAre0();
|
return _control.allAre0();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue