Pool allocator implementation - c++

I have two questions about pool allocator implementation:
In given pool allocator implementation how can I check that void*
pointer that I pass to deallocate function is exactly one of those
that I allocated previously?
What should I do to allocate size
of memory bigger than size of block? Just calculate amount of blocks
that would be enough to fit given size and move my next free element pointer x blocks forward, where x is amount of blocks I need ?
class Pool_c { // Basic type define
typedef unsigned int uint;
typedef unsigned char uchar;
uint m_numOfBlocks; // Num of blocks
uint m_sizeOfEachBlock; // Size of each block
uint m_numFreeBlocks; // Num of remaining blocks
uint m_numInitialized; // Num of initialized blocks
uchar* m_memStart; // Beginning of memory pool
uchar* m_next; // Num of next free block
public:
Pool_c()
{
m_numOfBlocks = 0;
m_sizeOfEachBlock = 0;
m_numFreeBlocks = 0;
m_numInitialized = 0;
m_memStart = NULL;
m_next = 0;
}
~Pool_c() { DestroyPool(); }
void CreatePool(size_t sizeOfEachBlock,
uint numOfBlocks)
{
m_numOfBlocks = numOfBlocks;
m_sizeOfEachBlock = sizeOfEachBlock;
m_memStart = new uchar[m_sizeOfEachBlock * m_numOfBlocks];
m_numFreeBlocks = numOfBlocks;
m_next = m_memStart;
}
void DestroyPool()
{
delete[] m_memStart;
m_memStart = NULL;
}
uchar* AddrFromIndex(uint i) const
{
return m_memStart + (i * m_sizeOfEachBlock);
}
uint IndexFromAddr(const uchar* p) const
{
return (((uint)(p - m_memStart)) / m_sizeOfEachBlock);
}
void* Allocate()
{
if (m_numInitialized < m_numOfBlocks) {
uint* p = (uint*)AddrFromIndex(m_numInitialized);
*p = m_numInitialized + 1;
m_numInitialized++;
}
void* ret = NULL;
if (m_numFreeBlocks > 0) {
ret = (void*)m_next;
--m_numFreeBlocks;
if (m_numFreeBlocks != 0) {
m_next = AddrFromIndex(*((uint*)m_next));
}
else {
m_next = NULL;
}
}
return ret;
}
void DeAllocate(void* p)
{
if (m_next != NULL) {
(*(uint*)p) = IndexFromAddr(m_next);
m_next = (uchar*)p;
}
else {
*((uint*)p) = m_numOfBlocks;
m_next = (uchar*)p;
}
++m_numFreeBlocks;
}

Related

unique_ptr with custom allocator

I wrote a simple B-Tree, with Node is defined as:
class Node {
Node* parent = nullptr;
std::uint32_t index = 0;
std::uint32_t height = 1;
std::vector<T> key;
std::vector<unique_alloc_ptr<Node>> child;
// ... details ...
};
where unique_alloc_ptr is the unique_ptr using my custom allocator.
template <typename T>
using unique_alloc_ptr = std::unique_ptr<T, std::function<void(T*)>>;
template <typename T>
unique_alloc_ptr<T> make_unique_fixed(FixedAllocator<T>& alloc) {
T* ptr = alloc.allocate(1);
alloc.construct(ptr);
std::function<void(T*)> deleter;
auto deleter_ = [](T* p, FixedAllocator<T>& alloc) {
alloc.destroy(p);
alloc.deallocate(p, 1);
};
deleter = [&deleter_, &alloc](auto&& PH1) { return deleter_(std::forward<decltype(PH1)>(PH1), alloc); };
return std::unique_ptr<T, decltype(deleter)>(ptr, deleter);
}
The custom allocator uses the memory pool as:
template <typename T>
class FixedAllocator {
struct Chunk {
// details ...
unsigned char data_[blockSize_ * numBlocks_];
};
// details ...
std::vector<Chunk> chunks_;
and my B-Tree uses the memory pool as member variable:
class BTree {
class Node {
// ...
};
// details ...
FixedAllocator<Node> alloc;
unique_alloc_ptr<Node> root;
};
But this gives segfault. As I guess, double free is the problem.
When lifetime of BTree ends, FixedAllocator<Node> is destroyed,
and its internal buffer std::vector<Chunk> is also destroyed.
The problem is, unique_alloc_ptr<Node> is also destroyed as well,
calling destructor of std::vector<unique_alloc_ptr<Node>> child,
which uses FixedAllocator<Node> as internal memory pool,
so double free problem occurs.
How can I solve this problem?
EDIT: Detailed implementation of FixedAllocator
template <typename T>
class FixedAllocator {
struct Chunk {
static constexpr std::size_t blockSize_ = sizeof(T);
static constexpr unsigned char numBlocks_ = FixedAllocator::numBlocks_;
Chunk() {
unsigned char i = 0;
for (unsigned char * p = &data_[0]; i != numBlocks_; p += blockSize_) {
*p = ++i;
}
}
void* allocate() {
unsigned char* result = &data_[firstAvailableBlock_ * blockSize_];
firstAvailableBlock_ = *result;
--blocksAvailable_;
return result;
}
void deallocate(void* p) {
assert(p >= &data_[0]);
auto* toRelease = static_cast<unsigned char*>(p);
assert((toRelease - &data_[0]) % blockSize_ == 0);
*toRelease = firstAvailableBlock_;
firstAvailableBlock_ = static_cast<unsigned char>((toRelease - &data_[0]) / blockSize_);
assert(firstAvailableBlock_ == (toRelease - &data_[0]) / blockSize_);
++blocksAvailable_;
}
bool hasBlock(void* p, std::size_t chunkLength) const {
auto* pc = static_cast<unsigned char*>(p);
return (&data_[0] <= pc) && (pc < &data_[chunkLength]);
}
[[nodiscard]] bool hasAvailable() const {
return (blocksAvailable_ == numBlocks_);
}
[[nodiscard]] bool isFilled() const {
return !blocksAvailable_;
}
unsigned char data_[blockSize_ * numBlocks_];
unsigned char firstAvailableBlock_ = 0;
unsigned char blocksAvailable_ = numBlocks_;
};
private:
static constexpr std::size_t blockSize_ = sizeof(T);
static constexpr unsigned char numBlocks_ = std::numeric_limits<unsigned char>::max();
std::vector<Chunk> chunks_;
Chunk* allocChunk_ = nullptr;
Chunk* deallocChunk_ = nullptr;
Chunk* emptyChunk_ = nullptr;
public:
using value_type = T;
void* doAllocate() {
if (!allocChunk_ || !allocChunk_->blocksAvailable_) {
auto it = chunks_.begin();
for (; ; ++it) {
if (it == chunks_.end()) {
chunks_.emplace_back();
allocChunk_ = &chunks_.back();
deallocChunk_ = &chunks_.front();
break;
}
if (it->blocksAvailable_) {
allocChunk_ = &*it;
break;
}
}
}
assert(allocChunk_);
assert(allocChunk_->blocksAvailable_);
return allocChunk_->allocate();
}
value_type* allocate(std::size_t n) {
assert(n == 1);
auto* p = static_cast<value_type*>(doAllocate());
return p;
}
Chunk* findVicinity(void* p) const {
assert(!chunks_.empty() && deallocChunk_);
const std::size_t chunkLength = numBlocks_ * blockSize_;
// bidirectional search
Chunk* lo = deallocChunk_;
Chunk* hi = deallocChunk_ + 1;
const Chunk* lbound = &chunks_.front();
const Chunk* hbound = &chunks_.back() + 1;
if (hi == hbound) {
hi = nullptr;
}
for (;;) {
if (lo) {
if (lo->hasBlock(p, chunkLength)) {
return lo;
}
if (lo == lbound) {
lo = nullptr;
if (!hi) {
break;
}
} else {
--lo;
}
}
if (hi) {
if (hi->hasBlock(p, chunkLength)) {
return hi;
}
if (++hi == hbound) {
hi = nullptr;
if (!lo) {
break;
}
}
}
}
return nullptr;
}
void deallocate(void* p, std::size_t n) noexcept {
assert(n == 1);
assert(!chunks_.empty());
assert(&chunks_.front() <= deallocChunk_);
assert(&chunks_.back() >= deallocChunk_);
assert(&chunks_.front() <= allocChunk_);
assert(&chunks_.back() >= allocChunk_);
Chunk* foundChunk = nullptr;
const std::size_t chunkLength = numBlocks_ * blockSize_;
if (deallocChunk_->hasBlock(p, chunkLength)) {
foundChunk = deallocChunk_;
} else {
foundChunk = findVicinity(p);
}
assert(foundChunk && foundChunk->hasBlock(p, chunkLength));
deallocChunk_ = foundChunk;
// double free check
assert(emptyChunk_ != deallocChunk_);
assert(!deallocChunk_->hasAvailable());
assert(!emptyChunk_ || emptyChunk_->hasAvailable());
deallocChunk_->deallocate(p);
if (deallocChunk_->hasAvailable()) {
// only release chunk if there are 2 empty chunks.
if (emptyChunk_) {
// if last chunk is empty, just let deallocChunk_ points
// to empty chunk, and release the last.
// otherwise, swap two and release an empty chunk
Chunk* lastChunk = &chunks_.back();
if (lastChunk == deallocChunk_) {
deallocChunk_ = emptyChunk_;
} else if (lastChunk != emptyChunk_) {
std::swap(*emptyChunk_, *lastChunk);
}
assert(lastChunk->hasAvailable());
chunks_.pop_back();
if ((allocChunk_ == lastChunk) || (allocChunk_->isFilled())) {
allocChunk_ = deallocChunk_;
}
}
emptyChunk_ = deallocChunk_;
}
}
template <typename... Args>
void construct (value_type* p, Args&&... args) {
std::construct_at(p, std::forward<Args>(args)...);
}
void destroy(value_type* p) {
std::destroy_at(p);
}
};

GCC "AddressSanitizer: heap-buffer-overflow" when initializing struct

I've been writing an VM/Interpreter combination thingy, I don't know how to exactly describe it.
Everything behaved as it should, now before I have hundreds of lines of code, I wanted to go into Garba Collection, because there were some pointers which somehow got lost, in some way. Not that I didn't delete pointers, I created, but they somehow got lost in the proccess of interpreting/running the code.
So, I wanted to track them. I wrote my own "Memory Manager" in some way, it's just a std::vector, where I collect all pointers in.
To track and allocate pointers, I have following code:
struct MemBlock {
bool free;
void* ptr;
size_t size;
};
std::vector<MemBlock*> mem;
size_t max_size;
size_t mem_size;
int count = 0;
void mem_init(size_t maxSize) {
max_size = size/sizeof(MemBlock*);
}
void* mem_alloc(size_t size) {
for (int i = 0; i < count; i++) {
MemBlock* block = mem[i];
if (block->free) {
mem_size -= block->size;
mem_size += size;
block->free = false;
block->ptr = malloc(size);
block->size = size;
if (block->ptr == nullptr) {
throw std::exception();
}
return block->ptr;
}
}
void* ptr = malloc(sizeof(size));
if (ptr == nullptr) {
throw PointerNullException();
}
MemBlock* block = (MemBlock*) malloc(sizeof(MemBlock));
*block = (MemBlock) {
false,
ptr,
size
};
mem_size += size;
count++;
mem.push_back(block);
return block->ptr;
}
But as soon, as I use mem_alloc() and initialize the object inside of the pointer:
Int* i = (Int*) mem_alloc(sizeof(Int));
*i = (Int) {}; // -- Here
i->value = atoi(advance().c_str());
The GCC AdressSanitizer shows following error:
==5939==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000098 at pc 0x555963d82fc5 bp 0x7fff4ec39070 sp 0x7fff4ec39060
WRITE of size 4 at 0x602000000098 thread T0
If I remove said line, then it just occurs on the nex line. The the pointer does point to a valid memory location, if not it should've had thrown an exception.
I'm sure that I missed something/did something wrong, of course.
But I don't know what. This is how I learned it, or at least what I understood...
Edit:
This would be a minimal reproducible Example:
#include <iostream>
#include <stdlib.h>
#include <vector>
struct Object {
const char* type;
};
template <typename T>
struct Primitive : Object {
T value;
};
struct Int : Primitive<int> {
const char* type = "int";
};
struct MemBlock {
bool free;
void* ptr;
size_t size;
};
std::vector<MemBlock*> mem;
size_t mem_size = 0;
int count = 0;
void* mem_alloc(size_t size) {
for (int i = 0; i < count; i++) {
MemBlock* block = mem[i];
if (block->free) {
mem_size -= block->size;
mem_size += size;
block->free = false;
block->ptr = malloc(size);
block->size = size;
if (block->ptr == nullptr) {
throw std::exception();
}
return block->ptr;
}
}
void* ptr = malloc(sizeof(size));
MemBlock* block = (MemBlock*) malloc(sizeof(MemBlock));
*block = (MemBlock) {
false,
ptr,
size
};
mem_size += size;
count++;
mem.push_back(block);
std::cout << "HI" << std::endl;
return block->ptr;
}
void mem_free(void* ptr) {
for (int i = 0; i < count; i++) {
MemBlock* block = mem[i];
if (block->ptr == ptr) {
free(ptr);
mem_size -= block->size;
block->size = 0;
block->ptr = nullptr;
block->free = true;
}
}
}
int main() {
// Create new Integer-Object
Int* i = (Int*) mem_alloc(sizeof(Int));
std::cout << "[Pointer]: " << i << std::endl;
*i = (Int) {};
i->value = 5;
std::cout << "[Value]: " << i->value << std::endl;
}
Well, thanks to Retired Ninja and Richar Critten, I've got the solution.
In mem_alloc() I've used sizeof(size) to allocate memory to the pointer, which of course is wrong. I guess my head was pretty much off after hours of coding.
But I guess this problem is now solved.

(FUZZING) Get a pointer to data range of dynamic array

EDIT: Clarification:
If I have an array int* a = new int[10], I want to get a pointer to a, but only the values from 0 to 5, without having to allocate another array for those values.
Original post:
I created a small class to fuzz my functions, but the thing is that it is painfully slow. It takes roughly 10-20 seconds to run my function 1000 times.
I decided to improve my code by allocating a very large array at first, then filling it from 0 to a randomly generated number and then just returning a pointer to that range to use in my function instead of allocating memory and deleting it each time.
Below is my code.
I attempt to allocate 1 million bytes at first, then I want to return a range from 0 to whatever size my class generated. Currently I allocate memory once more for returning it, but that's not efficient.
I use Xorshift to generate random numbers, which should be much faster than rand() so I think besides memory allocation it's pretty good, but any suggestions are very much welcome!
Note: if you do not understand part of my code ask me (it's written quickly, so it might be unintelligible at certain parts) ;)
class fuzz {
public:
fuzz() {
this->alloc_init_buff();
}
~fuzz() {
this->dealloc_init_buff();
}
int fill_buff(unsigned int size) {
if (size > this->m_buffsize) { size = this->m_buffsize; }
for (int i = 0; i < size; ++i) {
this->m_buff[i] = this->rand_xor();
}
return size;
}
int fill_buff() {
int size = this->rand_xor(1, this->m_buffsize);
if (size > this->m_buffsize) { size = this->m_buffsize; }
for (int i = 0; i < size; ++i) {
this->m_buff[i] = this->rand_xor();
}
return size;
}
unsigned char*& get_buff(int size) {
unsigned char* temp = new unsigned char[size];
memcpy((void*)temp, (void*)this->m_buff, size);
return temp;
}
private:
struct xr_xorshift_state {
unsigned int a = 123456789, b = 362436069, c = 521288629, d = 88675123;
};
unsigned int xorshift(xr_xorshift_state* state) {
unsigned int res = 0;
res = state->a ^ (state->a << 11);
state->a = state->b; state->b = state->c; state->c = state->d;
state->d = state->d ^ (state->d >> 19) ^ (res ^ (res >> 8));
res &= 0x7fffffff;
return res;
}
unsigned int rand_xor() {
return this->xorshift(&this->m_state);
}
unsigned int rand_xor(unsigned int min, unsigned int max) {
return (min + (this->rand_xor() % (max - min)));
}
void alloc_init_buff() {
this->m_buff = new unsigned char[this->m_buffsize];
}
void dealloc_init_buff() {
delete[] this->m_buff;
}
xr_xorshift_state m_state = { 0 };
unsigned char* m_buff = { 0 };
unsigned int m_buffsize = { 1000000 };
};
int find_newline(const char* text, int size) {
int pos = 0;
while (*text != '\n') {
if (pos == size) { return 0; }
++text; ++pos;
}
return pos;
}
int main() {
fuzz fz = {};
unsigned char* randdata = nullptr;
int lap = 0;
int th = 0;
for (;;) {
if (lap == 1000) {
lap = 0;
++th;
printf("%d thousand laps done!\n", th);
}
try {
int size = fz.fill_buff();
randdata = fz.get_buff(size);
const char* d = (const char*)randdata;
find_newline(d, size);
delete[] randdata;
++lap;
}
catch (...) {
printf("error!\n");
++lap;
}
}
getchar();
return 0;
}

unsigned char variable is not incremented

I came across one strange behaviour. In my code one variable is decremented, but not incremented and as a result my algorithm does not work. The variable name is blocksAvailable, it is defined in Chunk class, initiated with Chunk::init method, decremented with Chunk::allocate method and must be incremented with Chunk::deallocate method. So, there are just two places where this variable is mentioned - allocate and deallocate methods. In one place it gets decremented (and it works) and in other place it gets incremented and it does not work. This is the completely minimized and reproducible code:
#include <cstddef>
#include <iostream>
#include <vector>
using uchar = unsigned char;
class Chunk
{
private:
friend class FixedAllocator;
void init(size_t blockSize, uchar blocks);
void release();
void* allocate(size_t blockSize);
void deallocate(void* p, size_t blockSize);
inline bool hasBlock(void* p, size_t chunkLen) const
{
uchar * pc = static_cast<uchar*>(p);
return (pData <= pc) && (pc <= (pData + chunkLen));
}
inline bool releasable(uchar numBlocks) const
{
return blocksAvailable == numBlocks;
}
uchar* pData;
uchar firstAvailableBlock, blocksAvailable;
};
void Chunk::init(size_t blockSize, uchar blocks)
{
// for n of Ts it will allocate n * sizeof(T) memory
pData = new uchar[blockSize * blocks];
firstAvailableBlock = 0;
blocksAvailable = blocks;
uchar i = 0;
uchar* p = pData;
// used by allocate method to move forward firstAvailableBlock
for (; i != blocks; p += blockSize)
{
*p = ++i;
}
}
void Chunk::release()
{
::operator delete(pData);
}
void* Chunk::allocate(size_t blockSize)
{
if (!blocksAvailable) return 0;
// move firstAvailableBlock one block ahead
uchar* pResult = pData + firstAvailableBlock * blockSize;
firstAvailableBlock = *pResult;
--blocksAvailable;
std::cout << "blocksAvailable after allocate " << blocksAvailable << std::endl;
return pResult;
}
void Chunk::deallocate(void* p, size_t blockSize)
{
uchar* toRelease = static_cast<uchar*>(p);
// find last but one available block
firstAvailableBlock = static_cast<uchar>((toRelease - pData) / blockSize);
++blocksAvailable;
std::cout << "blocksAvailable after deallocate " << blocksAvailable << std::endl;
}
class FixedAllocator
{
private:
size_t blockSize;
uchar blocks;
using Chunks = std::vector<Chunk>;
Chunks chunks;
Chunk* allocChunk;
public:
FixedAllocator();
~FixedAllocator();
void init(size_t blockSize, size_t pageSize);
const int blockOwner(void* p) const;
void * allocate();
void deallocate(void* p);
};
FixedAllocator::FixedAllocator()
:blockSize(0),
blocks(0),
chunks(0),
allocChunk(nullptr)
{
}
FixedAllocator::~FixedAllocator()
{
Chunks::iterator it;
for (it = chunks.begin(); it != chunks.end(); ++it)
{
it->release();
}
}
void FixedAllocator::init(size_t blockSize_, size_t pageSize)
{
blockSize = blockSize_;
size_t numBlocks = pageSize / blockSize;
blocks = static_cast<uchar>(numBlocks);
}
const int FixedAllocator::blockOwner(void* p) const
{
size_t chunkLen = blocks * blockSize;
std::vector<int>::size_type i = 0, sz = chunks.size();
for (; i < sz; i++)
{
if (chunks[i].hasBlock(p, chunkLen))
{
return i;
}
}
return -1;
}
void* FixedAllocator::allocate()
{
if (!allocChunk || allocChunk->blocksAvailable == 0)
{
Chunks::iterator i = chunks.begin();
for (;;++i)
{
if (i == chunks.end())
{
// allocate memory for one more chunk
chunks.reserve(chunks.size() + 1);
Chunk newChunk;
newChunk.init(blockSize, blocks);
// add new chunk to memory pool
chunks.push_back(newChunk);
// points to new just initiated chunk
allocChunk = &chunks.back();
break;
}
if (i->blocksAvailable > 0)
{
// points to chunk with available blocks
allocChunk = &*i;
break;
}
}
}
return allocChunk->allocate(blockSize);
}
void FixedAllocator::deallocate(void* p)
{
// TODO. Optimize. Now very bruteforce and non-efficient
const int chunkPos = blockOwner(p);
if (chunkPos != -1)
{
Chunk chunk = chunks[chunkPos];
chunk.deallocate(p, blockSize);
// if chunk is releasable, release memory
if (chunk.releasable(blocks))
{
chunk.release();
chunks.erase(chunks.begin() + chunkPos);
// allocChunk may point to deleted chunk
// so, reset it
allocChunk = &chunks.back();
} else {
// there are free blocks in chunk
// so, reset allocChunk for faster future allocation
allocChunk = &chunk;
}
}
}
int main() {
FixedAllocator* alloc = new FixedAllocator();
alloc->init(4, 12);
void* p = alloc->allocate();
void* q = alloc->allocate();
void* r = alloc->allocate();
alloc->deallocate(p);
alloc->deallocate(q);
alloc->deallocate(r);
return 0;
}
As you can see, I have two debug statements in my code. One which prints blocksAvailable value after increment and one which prints its value after decrement.
But this is what I have on my screen, when I compile and run my code:
As you can see, blocksAvailable is initiated with value 3, then it gets decremented three times (three calls to allocate method), but after each decrement (call to deallocate) its value stays the same - 1. It really drives me crazy and looks like some ghost in my code. You can easily reproduce it, compile and run as simply as:
$ g++ main.cpp
$ ./a.out
I hope, someone can help me to find where this ghost appeared from.
Here is the only place in your code where you call Chunk::deallocate:
Chunk chunk = chunks[chunkPos];
chunk.deallocate(p, blockSize);
The first line makes a copy of your Chunk; the second line calls deallocate on it, which increments chunk.blocksAvailable. But chunk is just a copy of the data. Modifying it has no lasting effect.
In particular, chunks[chunkPos] is unaffected and still contains blocksAvailable = 0.

Error 'identifier not found' when using heap function

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
typedef struct{
void** heapAry; //
int last; //
int size; //
int (*compare) (void* argu1, void* argu2); // compare argumentations
int maxSize; //
} HEAP; //stuct HEAP
int compare(void *a, void *b) {
if (a < b)
return -1; // (return minus if former smaller)
if (a > b) // (return plus if former smaller)
return 1;
else
return 0; // (return zero if former smaller)
}
HEAP* heapCreate (int maxSize)
{
HEAP* heap = (HEAP*)malloc(sizeof (HEAP)); //creating heap
if (!heap)
return NULL;
heap->last = -1; // start of last; pre-existing
heap->size = 0; // start of size is zero
// Force heap size to power of 2 -1
heap->maxSize = (int) pow(2, ceil(log((double)maxSize)/log(2.0))) - 1;
heap->heapAry = (void**)calloc(heap->maxSize, sizeof(void*));
return heap;
} // createHeap
bool heapInsert (HEAP* heap, void* dataPtr)
{
if (heap->size >= heap->maxSize) // size cannot be bigger thant maxsize
return false;
++(heap->last); // increment last if insertion is true
++(heap->size); // increment size if insertion is true
heap->heapAry[heap->last] = dataPtr; // The data lies in last of heap
_reheapUp (heap, heap->last); // And arrange the data in order of heap
return true;
}
void _reheapUp (HEAP* heap, int childLoc)
{
int parent = 0;
void** heapAry = NULL;
void* hold = NULL;
if (childLoc){ // if not at root of heap -- index 0
heapAry = heap->heapAry;
parent = (childLoc - 1)/ 2;
if (heap->compare(heapAry[childLoc], heapAry[parent]) > 0) {
// child is greater than parent -- swap
hold = heapAry[parent];
heapAry[parent] = heapAry[childLoc];
heapAry[childLoc] = hold;
_reheapUp (heap, parent);
} // if heap[]
} // if newNode
}
bool heapDelete (HEAP* heap, void** dataOutPtr)
{
if (heap->size == 0) // heap empty
return false;
*dataOutPtr = heap->heapAry[0];
heap->heapAry[0] = heap->heapAry[heap->last];
(heap->last)--;
(heap->size)--;
_reheapDown (heap, 0);
return true;
}
void _reheapDown (HEAP* heap, int root)
{
void* hold = NULL;
void* leftData = NULL;
void* rightData = NULL;
int largeLoc = 0;
int last = 0;
last = heap->last;
if ((root * 2 + 1) <= last){
leftData = heap->heapAry[root * 2 + 1];
if ((root * 2 + 2) <= last) // right subtree
rightData = heap->heapAry[root * 2 + 2];
else
rightData = NULL;
// Determine which child is larger
if ((!rightData) ||
heap->compare (leftData, rightData) > 0){
largeLoc = root * 2 + 1;
} else { // if no right key or leftKey greater
largeLoc = root * 2 + 2;
} // else
// Test if root > larger subtree
if (heap->compare (heap->heapAry[root],
heap->heapAry[largeLoc]) < 0){
// parent < children
hold = heap->heapAry[root];
heap->heapAry[root] = heap->heapAry[largeLoc];
heap->heapAry[largeLoc] = hold;
_reheapDown (heap, largeLoc);
} // if root <
} // if root
} // reheapDown
void* selectK(HEAP *heap, int k){
if(k>heap->size)
return false; // k shouldnt be larger than size
heap->size = heap->last+1;
for(int i=0; i<k; i++){
void * temp = heap-> heapAry[0];
heapDelete(heap, heap->heapAry);
temp = heap->heapAry[heap->last + 1];
}
void * holdout = heap->heapAry[heap->last];
while(heap->last<heap->size){
heap->last++;
_reheapUp(heap, heap->last);
}
return holdout;
}
int main(){
HEAP * heap = heapCreate(256);
heapInsert(heap, (int*)1);
heapInsert(heap, (int*)2);
heapInsert(heap, (int*)3);
heapInsert(heap, (int*)4);
heapInsert(heap, (int*)5);
int *x = (int*) selectK(heap, 3);
printf("%d", *x); //print
}
from above source, I debugged but found two errors '_reheapUp': identifier not found, '_reheapDown': identifier not found. The source code lacks connectivity, as I guess. How may change the heap function? I don't know what to do, this is just on my book, and doesn't make sense