windows-nt/Source/XPSP1/NT/base/fs/sis/sisbkup/pool.cpp

311 lines
6 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1993-1999 Microsoft Corporation
Module Name:
pool.cpp
Abstract:
Fixed size memory allocator.
Author:
Bill Bolosky [bolosky] 1993
Revision History:
--*/
#include "sibp.h"
struct PoolEntry {
void *object;
struct PoolEntry *next;
};
struct PoolBlob {
struct PoolBlob *next;
void *data;
};
Pool::Pool(
unsigned objectSize,
void *(*allocator)(unsigned),
unsigned blobSize,
void (*destructor)(void *))
{
assert(objectSize > 0);
assert(!destructor || allocator); // Can't have a destructor without an allocator. Allocator w/o destructor leaks objects on pool destruct.
this->countAllocator = allocator;
this->singleAllocator = NULL;
this->destructor = destructor;
this->objectSize = objectSize;
entries = NULL;
freeEntries = NULL;
entriesBlobHead = NULL;
objectsBlobHead = NULL;
entriesPerBlob = blobSize / sizeof(PoolEntry);
assert(entriesPerBlob > 0);
objectsPerBlob = blobSize / objectSize;
if (!objectsPerBlob) {
objectsPerBlob = 1;
}
allocations = 0;
frees = 0;
news = 0;
numFree = 0;
}
// This version of the pool constructor uses the old kind of allocator function that only returns one object. Object blobs have one object,
// and the blob size for entry blobs is smaller so that we have finer grain memory allocation.
Pool::Pool(
unsigned objectSize,
void *(*allocator)(void))
{
assert(objectSize > 0);
assert(!destructor || allocator); // Can't have a destructor without an allocator; allocator w/o destructor leaks objects on pool destruct
this->singleAllocator = allocator;
this->countAllocator = NULL;
this->destructor = NULL;
this->objectSize = objectSize;
entries = NULL;
freeEntries = NULL;
entriesBlobHead = NULL;
objectsBlobHead = NULL;
unsigned blobSize = 1024 - 50; // Our default allocation size; we leave the 50 byte headroom for the underlying allocator
entriesPerBlob = blobSize / sizeof(PoolEntry);
assert(entriesPerBlob > 0);
objectsPerBlob = 1;
allocations = 0;
frees = 0;
news = 0;
numFree = 0;
}
Pool::~Pool(void)
{
// Just delete the blob lists. All objects that have been allocated from this pool will be destroyed.
while (entriesBlobHead) {
PoolBlob *blob = entriesBlobHead;
assert(blob->data);
delete [] blob->data;
entriesBlobHead = blob->next;
delete blob;
}
while (objectsBlobHead) {
PoolBlob *blob = objectsBlobHead;
assert(blob->data);
if (destructor) {
(*destructor)(blob->data);
} else if (!singleAllocator && !countAllocator) {
delete [] blob->data;
} // else leak the objects
objectsBlobHead = blob->next;
delete blob;
}
}
void
Pool::allocateMoreObjects(void)
{
assert(objectsPerBlob);
PoolBlob *blob = new PoolBlob;
if (!blob) {
return;
}
if (countAllocator) {
blob->data = (*countAllocator)(objectsPerBlob);
} else if (singleAllocator) {
assert(objectsPerBlob == 1);
blob->data = (*singleAllocator)();
} else {
blob->data = (void *)new char[objectSize * objectsPerBlob];
}
if (!blob->data) {
delete blob;
return;
}
blob->next = objectsBlobHead;
objectsBlobHead = blob;
// Now put them on the free list.
for (unsigned i = 0; i < objectsPerBlob; i++) {
PoolEntry *entry = getEntry();
if (!entry) {
return; // This is kinda bogus, because it might leave some allocated objects unreachable.
}
entry->object = (void *)(((char *)blob->data) + i * objectSize);
entry->next = entries;
entries = entry;
}
news += objectsPerBlob;
numFree += objectsPerBlob;
}
// Allocate entries until the free list is of size n (or until an allocation fails).
void
Pool::preAllocate(
unsigned n)
{
assert(n);
while (numFree < n) {
unsigned oldNumFree = numFree;
allocateMoreObjects();
if (oldNumFree == numFree) {
// We can't allocate more; punt
return;
}
}
}
PoolEntry *
Pool::getEntry(void)
{
PoolEntry *entry = NULL;
if (freeEntries) {
entry = freeEntries;
freeEntries = entry->next;
assert(entry->object == NULL);
} else {
// Allocate a new entry blob and fill it in.
PoolBlob *blob = new PoolBlob;
if (blob) {
PoolEntry *blobEntries = new PoolEntry[entriesPerBlob];
if (blobEntries) {
blob->data = (void *)blobEntries;
// Release all of the newly allocated entries except the first one, which we'll return.
for (unsigned i = 1; i < entriesPerBlob; i++) {
releaseEntry(&blobEntries[i]);
}
entry = &blobEntries[0];
// Stick the new blob on the entries blob list.
blob->next = entriesBlobHead;
entriesBlobHead = blob;
} else {
// Give up; we couldn't get memory
delete blob;
}
}
}
return(entry);
}
void
Pool::releaseEntry(
PoolEntry *entry)
{
assert(entry);
entry->object = NULL;
entry->next = freeEntries;
freeEntries = entry;
}
void *
Pool::allocate(void)
{
allocations++;
assert((numFree == 0) == (entries == NULL));
if (!entries) {
allocateMoreObjects();
}
if (entries) {
// We've got something
struct PoolEntry *thisEntry = entries;
entries = entries->next;
void *object = thisEntry->object;
assert(object);
releaseEntry(thisEntry);
assert(numFree);
numFree--;
return(object);
} else {
// Coudn't allocate more, we're out of memory.
assert(numFree == 0);
return NULL;
}
}
void
Pool::free(
void *object)
{
assert(object);
frees++;
// No way to assert that this is the right kind (size) of object...
// Get a PoolEntry.
struct PoolEntry *entry = getEntry();
if (!entry) {
// We couldn't get an entry, so we can't add this object to the free list. Leak it.
return;
}
numFree++;
entry->object = object;
entry->next = entries;
entries = entry;
}
unsigned
Pool::numAllocations(void)
{
return(allocations);
}
unsigned
Pool::numFrees(void)
{
return(frees);
}
unsigned
Pool::numNews(void)
{
return(news);
}
unsigned
Pool::getObjectSize(void)
{
return(objectSize);
}