Critique my non-intrusive heap debugger - c++

This is a follow-up to Critique my heap debugger from yesterday. As suggested by bitc, I now keep metadata about the allocated blocks in a separate handwritten hashtable.
The heap debugger now detects the following kinds of errors:
memory leaks (now with more verbose debugging output)
illegal pointers passed to delete (that also takes care of double deletes)
wrong form of delete (array vs. non-array)
buffer overflows
buffer underflows
Feel free to discuss and thanks in advance!
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <new>
namespace
{
// I don't want to #include <algorithm> for a single function template :)
template <typename T>
void my_swap(T& x, T& y)
{
T z(x);
x = y;
y = z;
}
typedef unsigned char byte;
const byte CANARY[] = {0x5A, 0xFE, 0x6A, 0x8D,
0x5A, 0xFE, 0x6A, 0x8D,
0x5A, 0xFE, 0x6A, 0x8D,
0x5A, 0xFE, 0x6A, 0x8D};
bool canary_dead(const byte* cage)
{
bool dead = memcmp(cage, CANARY, sizeof CANARY);
if (dead)
{
for (size_t i = 0; i < sizeof CANARY; ++i)
{
byte b = cage[i];
printf(b == CANARY[i] ? "__ " : "%2X ", b);
}
putchar('\n');
}
return dead;
}
enum kind_of_memory {AVAILABLE, TOMBSTONE, NON_ARRAY_MEMORY, ARRAY_MEMORY};
const char* kind_string[] = {0, 0, "non-array memory", " array memory"};
struct metadata
{
byte* address;
size_t size;
kind_of_memory kind;
bool in_use() const
{
return kind & 2;
}
void print() const
{
printf("%s at %p (%d bytes)\n", kind_string[kind], address, size);
}
bool must_keep_searching_for(void* address)
{
return kind == TOMBSTONE || (in_use() && address != this->address);
}
bool canaries_alive() const
{
bool alive = true;
if (canary_dead(address - sizeof CANARY))
{
printf("ERROR: buffer underflow at %p\n", address);
alive = false;
}
if (canary_dead(address + size))
{
printf("ERROR: buffer overflow at %p\n", address);
alive = false;
}
return alive;
}
};
const size_t MINIMUM_CAPACITY = 11;
class hashtable
{
metadata* data;
size_t used;
size_t capacity;
size_t tombstones;
public:
size_t size() const
{
return used - tombstones;
}
void print() const
{
for (size_t i = 0; i < capacity; ++i)
{
if (data[i].in_use())
{
printf(":( leaked ");
data[i].print();
}
}
}
hashtable()
{
used = 0;
capacity = MINIMUM_CAPACITY;
data = static_cast<metadata*>(calloc(capacity, sizeof(metadata)));
tombstones = 0;
}
~hashtable()
{
free(data);
}
hashtable(const hashtable& that)
{
used = 0;
capacity = 3 * that.size() | 1;
if (capacity < MINIMUM_CAPACITY) capacity = MINIMUM_CAPACITY;
data = static_cast<metadata*>(calloc(capacity, sizeof(metadata)));
tombstones = 0;
for (size_t i = 0; i < that.capacity; ++i)
{
if (that.data[i].in_use())
{
insert_unsafe(that.data[i]);
}
}
}
hashtable& operator=(hashtable copy)
{
swap(copy);
return *this;
}
void swap(hashtable& that)
{
my_swap(data, that.data);
my_swap(used, that.used);
my_swap(capacity, that.capacity);
my_swap(tombstones, that.tombstones);
}
void insert_unsafe(const metadata& x)
{
*find(x.address) = x;
++used;
}
void insert(const metadata& x)
{
if (2 * used >= capacity)
{
hashtable copy(*this);
swap(copy);
}
insert_unsafe(x);
}
metadata* find(void* address)
{
size_t index = reinterpret_cast<size_t>(address) % capacity;
while (data[index].must_keep_searching_for(address))
{
++index;
if (index == capacity) index = 0;
}
return &data[index];
}
void erase(metadata* it)
{
it->kind = TOMBSTONE;
++tombstones;
}
} the_hashset;
struct heap_debugger
{
heap_debugger()
{
puts("heap debugger started");
}
~heap_debugger()
{
the_hashset.print();
puts("heap debugger shutting down");
}
} the_heap_debugger;
void* allocate(size_t size, kind_of_memory kind) throw (std::bad_alloc)
{
byte* raw = static_cast<byte*>(malloc(size + 2 * sizeof CANARY));
if (raw == 0) throw std::bad_alloc();
memcpy(raw, CANARY, sizeof CANARY);
byte* payload = raw + sizeof CANARY;
memcpy(payload + size, CANARY, sizeof CANARY);
metadata md = {payload, size, kind};
the_hashset.insert(md);
printf("allocated ");
md.print();
return payload;
}
void release(void* payload, kind_of_memory kind) throw ()
{
if (payload == 0) return;
metadata* p = the_hashset.find(payload);
if (!p->in_use())
{
printf("ERROR: no dynamic memory at %p\n", payload);
}
else if (p->kind != kind)
{
printf("ERROR:wrong form of delete at %p\n", payload);
}
else if (p->canaries_alive())
{
printf("releasing ");
p->print();
free(static_cast<byte*>(payload) - sizeof CANARY);
the_hashset.erase(p);
}
}
}
void* operator new(size_t size) throw (std::bad_alloc)
{
return allocate(size, NON_ARRAY_MEMORY);
}
void* operator new[](size_t size) throw (std::bad_alloc)
{
return allocate(size, ARRAY_MEMORY);
}
void operator delete(void* payload) throw ()
{
release(payload, NON_ARRAY_MEMORY);
}
void operator delete[](void* payload) throw ()
{
release(payload, ARRAY_MEMORY);
}
int main()
{
int* p = new int[1];
delete p; // wrong form of delete
delete[] p; // ok
delete p; // no dynamic memory (double delete)
p = new int[1];
p[-1] = 0xcafebabe;
p[+1] = 0x12345678;
delete[] p; // underflow and overflow prevent release
// p is not released, hence leak
}

Very nice, indeed. Your canaries could actually reveal some real cases of overflow/underflow (though not all of them as Matthieu pointed out).
What more. You might run into some problems with a multi-threaded application. Perhaps protect the hashtable from concurrent access?
Now that you log every allocation and deallocation, you can (if you like) provide more information about the program being tested. It might be interesting to know the total and average number of allocations at any given time? The total, max, min and average bytes allocated, and the average lifespan of allocations.
If you want to compare different threads, at least with Pthreads you can identify them with pthread_self(). This heap debugger could become a quite useful analysis tool.

Are you using a very weak malloc that doesn't already have this sort of stuff built into it? Because if it's there, you are doubling the overhead for little gain. Also, this kind of system really hurts when doing small object allocation or is ineffective with them as people do 1 alloc and manage the memory themselves.
As far as the code is concerned, it looks like it will do what you say it will do and it looks well designed and is easy to read. But, if you are going to go through the trouble of doing this though, why not catch your buffer over/under flows at the source by using managed containers/pointers/operator[] thingies. That way, you can debug on the spot of the failure instead of finding out at free that something evil has occured.
There are efficiencies to be had that I'm sure others will find, but these are just some thoughts off the top of my head after looking over your code for a few minutes.

I wonder about the detection of underflows / overflows.
I mean, if I have a 10 elements arrays, then it seems you'll detect if I write at -1 and 10, but what if I write at 20 ? Underflow or Overflow are not necessarily done as part of a buffer overrun (contiguous).
Furthermore, what's the point of preventing release of the block ? This block is (relatively) fine, it's the neighbors you've (unfortunately) corrupted.
Anyway, it seems pretty fine to me, though I would probably have more than one return per function because there's no point in Single Exit. You seem more of a C programmer than a C++ one :)

Related

My vector works sometimes and othertimes it doesn't

I'm trying to make my own vector, but i've got the following problem: When I push_back 100 times there's no problem. When I push_back 1000 the program does not work
#include <iostream>
#include <stdlib.h>
#include <conio.h>
struct Exception {
static const char* out_of_range;
};
const char* Exception::out_of_range = "[Error]: Out of range";
template < typename T >
struct vector {
typedef T myType;
public:
vector() {
m_vector = (myType*) malloc ( sizeof( myType ) );
m_position = 0;
}
template < typename ... Ts >
vector(myType head, Ts ... tail) {
m_position = 0;
m_vector = (myType*) malloc( (sizeof ...( tail ) + 1) * sizeof( myType ) );
this->push_back(head);
(this->push_back(tail),...);
}
~vector() {
free(m_vector);
m_vector = NULL;
}
void push_back( myType value ) {
m_vector[ m_position ] = value;
++m_position;
m_vector = (myType*) realloc(m_vector, m_position * sizeof(myType));
}
void pop_back() {
--m_position;
m_vector = (myType*)realloc( m_vector, m_position * sizeof (myType) );
}
myType at( size_t pos ) {
try {
if (pos < m_position)
return m_vector[ pos ];
else throw Exception::out_of_range;
} catch (const char* e) {
printf("%s", e);
return (myType){};
}
}
inline myType& front() { return *m_vector; }
inline myType& back() { return *(m_vector + size() -1); }
inline myType* data() { return m_vector; }
inline myType* begin() { return m_vector; }
inline myType* end() { return (m_vector + size()); }
inline myType operator[](size_t pos) { return m_vector[ pos ]; }
inline size_t size() { return m_position; }
inline bool empty () { return (begin() == end()? true:false); }
private:
myType* m_vector;
size_t m_position;
};
Here is my main that use push_back by 100 times:
int main() {
vector<int> v;
for(int i = 0; i < 100; ++i) v.push_back(i);
for(int i = 0; i < 100; ++i) std::cout << v[i];
}
And here the hunted code ahah:
int main() {
vector<int> v;
for(int i = 0; i < 1000; ++i) v.push_back(i);
for(int i = 0; i < 1000; ++i) std::cout << v[i];
}
With "doesn't work" I'm trying to say that when I have 100 values inserted by push_back the program show me all the values from 0 to 99... but when I've got 1000 values (I don't know why) the program show only a black screen and nothing more
Consider the first call of
void push_back(myType value) {
m_vector[m_position] = value; // Store into 0
++m_position; // set `m_position` to 1
m_vector = (myType*)realloc(m_vector, m_position * sizeof(myType)); // Allocate more space.
}
How much more space is allocated on that last line? m_position * sizeof(myType). This resolves to 1 * sizeof(myType). Enough space for 1 myType. In other words the same amount of space the program already had. This is not useful.
Let's look at the next push_back
void push_back(myType value) {
m_vector[m_position] = value; // Store into 1. There is no 1. Program now broken
++m_position; // set `m_position` to 2
m_vector = (myType*)realloc(m_vector, m_position * sizeof(myType)); // Allocate more space.
}
The next push_back writes into invalid storage. Program now officially broken and no further point debugging.
How do we fix this?
Let's ignore the fact that malloc and family don't handle complex data structures and vector does not observe the Rules of Three and Five. Those are best handled in other questions. How do we fix this with realloc?
m_vector = (myType*) realloc(m_vector, (m_position +1) * sizeof(myType));
smooths over the immediate rough spot. But this is inefficient as hell. Every addition triggers a realloc. This really, really hurts performance. Aggregate O(1) goes right out the window replaced by O(n), copy every time, plus a potentially very expensive memory allocation.1
Worse, what happens when you remove an item? You lose track of how much was in the vector and may find yourself reallocing smaller buffers. Yuck.
To do this right, first add a m_capacity member to track how much data can be stored so that we don't have to reallocate if the amount needed is less than the amount required.
Then we test for amount of space and possibly reallocate before trying to store.
void push_back( myType value ) {
if (m_position >= m_capacity)
{ // need to reallocate
m_capacity *= 2;
myType * temp = (myType*) realloc(m_vector, m_capacity *sizeof(myType));
// ask for more than is needed. Reduce number of reallocations needed
// do not overwrite m_vector. realloc can fail to allocate and then where are you?
if (temp != NULL)
{
m_vector = temp;
}
else
{
// handle error. Probably throw exception. Definitely exit function
// before trying to add new element
}
}
m_vector[ m_position ] = value; // now guarantied to have space.
++m_position;
}
1This isn't strictly true. One of the things you'll find is that memory provided often isn't as granular as what you asked for. When the program asks for X bytes, it might get a convenient block of free memory larger than X bytes. You ever noticed that sometimes you can run off the end of a buffer and the program doesn't notice and immediately crash? This extra space is one of the reasons. Quite often realloc can take advantage of this and keep using the same allocation over and over, allowing the program to legally see more of it. You can't count on this, though.
I assume the idea behind your code is that m_vector should always be able to hold one more value than it currently does. Your push_back funtion is wrong then, it should realloc for m_position + 1.

segfault, but not in valgrind or gdb

In my project, there is a library that has code to load an fbx using the FBX SDK 2017.1 from Autodesk.
Loading the fbx crashes in debug & release. The crash occurs in 2 different ways and what seems to be at random:
the crash is either simply "Segmentation fault" (most of the time)
the crash is a dump of all the libraries that may be involved in the crash, and the allusion of a problem with a realloc() call. (every once in a while) From the context of the message, I haven't been able to make out which realloc that may be (the message is followed by a dump of all the libs that are linked).
The code does contain realloc() calls, specifically in the allocation of buffers used in a custom implementation of an FbxStream
Most of the code path is entirely identical for windows, only a number of platform specific sections have been re-implemented. On windows, it runs as expected.
What strikes me is that if I run the program in either gdb or valgrind, the crash disappears! So I set out to find uninitialized members/values, but so far I could not find anything suspicious. I used CppDepend/CppCheck and VS2012 code analysis, but both came up empty on un-initialized variables/members
To give some background on FBX loading; the FBX SDK has a number of ways to deal with different types of resources (obj, 3ds, fbx,..). They can be loaded from file or from stream. To support large files, the stream option is the more relevant option. The code below is far from perfect, but what interests me mostly at present is the reason why valgrind/gdb would not crash. I've left the SDK documentation on top of ReadString, since it's the most complex one.
class MyFbxStream : public FbxStream{
uint32 m_FormatID;
uint32 m_Error;
EState m_State;
size_t m_Pos;
size_t m_Size;
const Engine::Buffer* const m_Buffer;
MyFbxStream& operator = (const MyFbxStream& other) const;
public:
MyFbxStream(const Engine::Buffer* const buffer)
: m_FormatID(0)
, m_Error(0)
, m_State(eClosed)
, m_Pos(0)
, m_Size(0)
, m_Buffer(buffer) {};
virtual ~MyFbxStream() {};
virtual bool Open(void* pStreamData) {
m_FormatID = *(uint32*)pStreamData;
m_Pos = 0;
m_State = eOpen;
m_Size = m_Buffer->GetSize();
return true;
}
virtual bool Close() {
m_Pos = m_Size = 0;
m_State = eClosed;
return true;
}
virtual int Read(void* pData, int pSize) const {
const unsigned char* data = (m_Buffer->GetBase(m_Pos));
const size_t bytesRead = m_Pos + pSize > m_Buffer->GetSize() ? (m_Buffer->GetSize() - m_Pos) : pSize;
const_cast<MyFbxStream*>(this)->m_Pos += bytesRead;
memcpy(pData, data, bytesRead);
return (int)bytesRead;
}
/** Read a string from the stream.
* The default implementation is written in terms of Read() but does not cope with DOS line endings.
* Subclasses may need to override this if DOS line endings are to be supported.
* \param pBuffer Pointer to the memory block where the read bytes are stored.
* \param pMaxSize Maximum number of bytes to be read from the stream.
* \param pStopAtFirstWhiteSpace Stop reading when any whitespace is encountered. Otherwise read to end of line (like fgets()).
* \return pBuffer, if successful, else NULL.
* \remark The default implementation terminates the \e pBuffer with a null character and assumes there is enough room for it.
* For example, a call with \e pMaxSize = 1 will fill \e pBuffer with the null character only. */
virtual char* ReadString(char* pBuffer, int pMaxSize, bool pStopAtFirstWhiteSpace = false) {
assert(!pStopAtFirstWhiteSpace); // "Not supported"
const size_t pSize = pMaxSize - 1;
if (pSize) {
const char* const base = (const char* const)m_Buffer->GetBase();
char* cBuffer = pBuffer;
const size_t totalSize = std::min(m_Buffer->GetSize(), (m_Pos + pSize));
const char* const maxSize = base + totalSize;
const char* sum = base + m_Pos;
bool done = false;
// first align the copy on alignment boundary (4byte)
while ((((size_t)sum & 0x3) != 0) && (sum < maxSize)) {
const unsigned char c = *sum++;
*cBuffer++ = c;
if ((c == '\n') || (c == '\r')) {
done = true;
break;
} }
// copy from alignment boundary to boundary (4byte)
if (!done) {
int64 newBytesRead = 0;
uint32* dBuffer = (uint32*)cBuffer;
const uint32* dBase = (uint32*)sum;
const uint32* const dmaxSize = ((uint32*)maxSize) - 1;
while (dBase < dmaxSize) {
const uint32 data = *(const uint32*const)dBase++;
*dBuffer++ = data;
if (((data & 0xff) == 0x0a) || ((data & 0xff) == 0x0d)) { // third bytes, 4 bytes read..
newBytesRead -= 3;
done = true;
break;
} else {
const uint32 shiftedData8 = data & 0xff00;
if ((shiftedData8 == 0x0a00) || (shiftedData8 == 0x0d00)) { // third bytes, 3 bytes read..
newBytesRead -= 2;
done = true;
break;
} else {
const uint32 shiftedData16 = data & 0xff0000;
if ((shiftedData16 == 0x0a0000) || (shiftedData16 == 0x0d0000)) { // second byte, 2 bytes read..
newBytesRead -= 1;
done = true;
break;
} else {
const uint32 shiftedData24 = data & 0xff000000;
if ((shiftedData24 == 0x0a000000) || (shiftedData24 == 0x0d000000)) { // first byte, 1 bytes read..
done = true;
break;
} } } } }
newBytesRead += (int64)dBuffer - (int64)cBuffer;
if (newBytesRead) {
sum += newBytesRead;
cBuffer += newBytesRead;
} }
// copy anything beyond the last alignment boundary (4byte)
if (!done) {
while (sum < maxSize) {
const unsigned char c = *sum++;
*cBuffer++ = c;
if ((c == '\n') || (c == '\r')) {
done = true;
break;
} } }
const size_t bytesRead = cBuffer - pBuffer;
if (bytesRead) {
const_cast<MyFbxStream*>(this)->m_Pos += bytesRead;
pBuffer[bytesRead] = 0;
return pBuffer;
} }
pBuffer = NULL;
return NULL;
}
virtual void Seek(const FbxInt64& pOffset, const FbxFile::ESeekPos& pSeekPos) {
switch (pSeekPos) {
case FbxFile::ESeekPos::eBegin: m_Pos = pOffset; break;
case FbxFile::ESeekPos::eCurrent: m_Pos += pOffset; break;
case FbxFile::ESeekPos::eEnd: m_Pos = m_Size - pOffset; break;
}
}
virtual long GetPosition() const { return (long)m_Pos; }
virtual void SetPosition(long position) { m_Pos = position; }
virtual void ClearError() { m_Error = 0; }
virtual int GetError() const { return m_Error; }
virtual EState GetState() { return m_State; }
virtual int GetReaderID() const { return m_FormatID; }
virtual int GetWriterID() const { return -1; } // readonly stream
virtual bool Flush() { return true; } // readonly stream
virtual int Write(const void* /*d*/, int /*s*/) { assert(false); return 0; } // readonly stream
};
I assume that there may be undefined behavior related to malloc/free/realloc operations that somehow do not occur in gdb. But if this is the case, I also expect the Windows binaries to have problems.
Also, I don't know if this is relevant, but the when I trace into the Open() function and print the "m_Buffer" pointer's value (or the "this"), I get a pointer value starting with 0xfffffff.. which for a Windows programmer looks like a problem. However, can I pull the same conclusion in linux, since I also saw this happening in static function calls etc.
if I run the program in either gdb or valgrind, the crash disappears!
There are two possible explanations:
There are multiple threads, the code exhibits a data race, and both GDB and Valgrind significantly affect execution timing.
GDB disables address randomization; Valgrind significantly affects program layout, and the crash is sensitive to the exact layout.
The steps I would take:
Set ulimit -c unlimited, run the program and get it to dump core, then use post-mortem analysis in GDB.
Run the program under GDB, use set disable-randomization off and see if you can get to crash point that way.
Run the program with Helgrind or DRD, Valgrind's thread error detectors.

c++ incorrect checksum for freed object-

I am getting this error (memory location varies between runs):
Freeing memory!
Image_Processing(6282,0x100091000) malloc: * error for object 0x1212121212121212: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
in this point it crashes : //delete m_data;
class Uint8Image {
public:
uint32_t m_w;
uint32_t m_h;
uint8_t *m_data;
Uint8Image(uint32_t w, uint32_t h): m_w(w), m_h(h), m_data(0)
{
m_data = new uint8_t(w*h);
}
Uint8Image(const Uint8Image &obj) ;
Uint8Image& operator = (const Uint8Image &D ) {
if(this != &D)
{
delete [] m_data;
m_w= D.m_w;
m_h = D.m_h;
m_data=new uint8_t(m_w * m_h); // deep copy the pointer data
}
return *this;
}
~Uint8Image()
{
std::cout << "Freeing memory!"<< std::endl;
delete m_data; // it crashes here
m_data = NULL;
}
};
class MeniscusFinderContext {
public:
MeniscusFinderContext( uint32_t m_a, uint32_t m_b):
{
m_input_image = new Uint8Image(m_a,m_b);
}
~MeniscusFinderContext()
{
delete m_input_image;
m_input_image = NULL;
}
Uint8Image m_input_image;};
//The function that calls:
// Taking input through option-parsing,
int main(int argc, char *argv[]{
const char *file_name = options[INPUT].arg;
std::ifstream file_stream(file_name,
std::ifstream::in | std::ifstream::binary);
char buf[256];
char *sEnd;
file_stream.getline(buf, sizeof(buf));
if(buf[0] != 'P' || buf[1] != '5') {
std::cerr << "invalid input PGM file" << std::endl;
return 1;
}
file_stream.getline(buf, sizeof(buf));
while(buf[0] == '#') file_stream.getline(buf, sizeof(buf));
uint32_t m_a = strtol(buf, &sEnd, 10);
uint32_t m_b = strtol(sEnd, &sEnd, 10);
MeniscusFinderContext M(m_a,m_b);
file_stream.getline(buf, sizeof(buf));
while(buf[0] == '#') file_stream.getline(buf, sizeof(buf));
if(atoi(buf) != 255) return 3;
file_stream.read((char *)M.m_input_image->m_data ,m_a * m_b);
if(!file_stream) {
std::cerr << "only got " << file_stream.gcount() << std::endl;
return 2;
}
file_stream.close();
return 0;
}
Edit: I'm running it and sometimes it runs while others it gives me the error. Seems to be at a random order. Any hints would be really helpful.
I already have checked all the related answers in stack overflow, but could nt figure it out.
new uint8_t(w*h);
This allocates exactly one uint8_t, whose initial value is w*h.
You probably intended:
new uint8_t[w*h];
Otherwise, this:
file_stream.read((char *)M.m_input_image->m_data ,m_a * m_b);
will immediately overrun this buffer. The same bug occurs several times in the shown code.
delete m_data;
Stuff allocated with new[] should be deallocated with delete[].
In general, your overall approach is very error-prone. Instead of manually handling memory, in this fashion, you should be using std::vector, and iterators. Correct usage of C++'s containers greatly reduces the possibility of making these kinds of bugs.

C++ Memory Leak With STL Vector

I am building a templated Max Heap class in C++ for a datastructures class. The implementation demonstrates a Max Heap with a vector under the hood. There is an online submission associated with the assignment and when I submit mine, all the tests (push, pop, top, and size) pass and work (for the online unknown unit tests as well as all the ones I wrote) and I have no memory leaks with any of my tests, however I am failing the memory leak section with the online submission, indicating to me that my Bubble Up (Reheap Up) or Bubble Down (Reheap Down) algorithms are doing something funny with vector indices.
I noticed that I used the bracket operator a lot to mess with the vector, so I went through and changed all the brackets to .at() so I could see any suppressed out of bounds errors. Flying colors again, except for the memory leaks allegedly. I then figured well maybe one of the unit tests is adding sooo many values the vector fails to clear them all for some unknown reason...wasn't the case because I added so many values to a vector in my max heap class in my unit tests it took 90 seconds to finish and after all 52K allocations were made 52K deallocations were made as well and valgrind reported no errors.
Below is some of the main code for the class, if anyone could decide where some code is written that in some situation may warrant a memory leak that would be great!
template <class T>
class MaxHeap {
public:
MaxHeap(){
// TODO: Fill me in
}
~MaxHeap() {
data.clear();
}
void push(T value){
data.push_back(value);
bubbleUp(data.size()-1, value);
}
void pop(){
if(!size()) {
return;
}
T val = data.at(size()-1);
data.pop_back();
if(!size()) {
return;
}
data.at(0) = val;
bubbleDown(0, val);
}
T top(){
if(!data.size()) throw logic_error("Empty Heap");
return data.at(0);
}
unsigned int size(){
return data.size();
}
void print_vec() {
for (int i = 0; i < size(); ++i) {
cout << data.at(i) << " ";
}
cout << endl;
}
vector<T> getVec() {
return data;
}
private:
vector<T> data;
void bubbleUp(int idx, T value) {
int position = idx;
int parent_idx = parent(position);
while (data.at(parent_idx) < value) {
data.at(position) = data.at(parent_idx);
data.at(parent_idx) = value;
position = parent_idx;
parent_idx = parent(position);
}
}
void bubbleDown(int idx, T value) {
int left_child_idx = left_child(idx);
int right_child_idx = right_child(idx);
int max_child_idx;
if(left_child_idx <= size()-1) { // left child (consequently right child) in bounds of vector
if(left_child_idx == size()-1) { // no right child, left is maxchild
max_child_idx = left_child_idx;
} else {
max_child_idx = (data.at(left_child_idx) <= data.at(right_child_idx)) ? right_child_idx : left_child_idx;
}
if(data.at(idx) < data.at(max_child_idx)) {
data.at(idx) = data.at(max_child_idx);
data.at(max_child_idx) = value;
bubbleDown(max_child_idx, value);
}
}
}
int left_child(int idx) {return (idx*2+1);}
int right_child(int idx) {return (idx*2+2);}
int parent(int idx) {return ((idx-1)/2);}
};
Warning: this is only a theory, since it is improbable that the source of leak is in the code shown here.
If T is a malformed type, that does not release it's memory when using the assignment operator, then this might be the part that trigger this bad behvior:
T swap; // initialized to something. perhaps using new
while (data.at(parent_idx) < value) {
swap = data.at(parent_idx); //assume no delete in T.operator=()
data.at(parent_idx) = value;
data.at(position) = swap;
position = parent_idx;
parent_idx = parent(position);
}
This is not a problem in this code. However, you might still be able to patch it here. Why is T defined outside the loop?
while (data.at(parent_idx) < value) {
T swap = data.at(parent_idx); // no assignment here
data.at(parent_idx) = value;
data.at(position) = swap;
position = parent_idx;
parent_idx = parent(position);
}
===
Unrelated but better - don't use the unnecessary intermediate variable, and mix in move semantics:
while (data.at(parent_idx) < value) {
data.at(position) = std::move(data.at(parent_idx));
data.at(parent_idx) = value;
position = parent_idx;
parent_idx = parent(position);
}

Creating the biggest array of chars which can be allocated

I have tried to check programmatically how big an array I can allocate but my code does not seem to check it. How to make it faster? In the end I would like to get an exception.
#include "stdafx.h"
#include "iostream"
using namespace std;
int ASCENDING = 1, DESCENDING = 2;
int tworzTablice(int rozmiar, char* t){
try{
t = new char[rozmiar];
delete []t;
}catch (std::bad_alloc& e){
tworzTablice(rozmiar - 1,t);
return -1;
}
return rozmiar;
}
int f(long p, long skok){
char* t;
try{
while(true){
t = new char[p];
delete []t;
p = p + skok;
}
}
catch (std::bad_alloc& ba){
p = tworzTablice(p-1, t);
cout<<"blad";
}
return p;
}
int main(){
cout<<f(0, 100000000)<<endl;;
cout<<"koniec"<<endl;
system("pause");
return 0;
}
As I noted, there is a way to query the OS in order to determine the maximal size of heap-allocated memory, but I can't for the heck of it remember its name.
However, you can easily find out yourself. However, you should use malloc/free instead of new/delete in order to avoid the unnecessary initialisation of all cells;
#include <cstdlib>
#include <cstdio>
size_t maxMem() {
static size_t size = 0;
if (!size) {
size_t m = 0;
for (void* p = 0; (p = malloc(1<<m)); m++)
free(p);
while (m) {
size_t const testSize = size + (1<<(--m));
if (void* const p = malloc(testSize)) {
size = testSize;
free(p);
}
}
}
return size;
}
int main() {
// forgive me for using printf, but I couldn't remember how to hex-format in std::cout
printf("%u (hex %X)\n",int(maxMem()),int(maxMem()));
}
On my 64 bit machine I get
2147483647 (hex 7FFFFFFF)
while on another 32 system I get
2140700660 (hex 7F987FF4)
You can then go ahead and new an array of that size if you really have to. Note however, that this is the largest consecutive chunk you can request. The total memory your process might allocate is larger and depends on the installed RAM and the reserved swap space.
Allocating all available memory is probably a bad idea, but if you really want to:
vector<char*> ptrs;
int avail;
try {
while (true)
ptrs.push_back(new char[1000]);
}
catch (bad_alloc& b)
{
avail = ptrs.size() * 1000;
for (int i = 0; i < ptrs.size(); i++)
delete[] ptrs[i];
}