I need help in understanding the code snipped below...allocate is a function that would be called by the overloaded new operator to allocate memory. I am having problems trying to understand the following casts in particular:
*static_cast<std::size_t*>(mem) = pAmount; //please explain?
return static_cast<char*>(mem) + sizeof(std::size_t); //?
and..
// get original block
void* mem = static_cast<char*>(pMemory) - sizeof(std::size_t); //?
the code is shown below:
const std::size_t allocation_limit = 1073741824; // 1G
std::size_t totalAllocation = 0;
void* allocate(std::size_t pAmount)
{
// make sure we're within bounds
assert(totalAllocation + pAmount < allocation_limit);
// over allocate to store size
void* mem = std::malloc(pAmount + sizeof(std::size_t));
if (!mem)
return 0;
// track amount, return remainder
totalAllocation += pAmount;
*static_cast<std::size_t*>(mem) = pAmount;
return static_cast<char*>(mem) + sizeof(std::size_t);
}
void deallocate(void* pMemory)
{
// get original block
void* mem = static_cast<char*>(pMemory) - sizeof(std::size_t);
// track amount
std::size_t amount = *static_cast<std::size_t*>(mem);
totalAllocation -= pAmount;
// free
std::free(mem);
}
The allocator keeps track of the size of allocations by keeping them along with the blocks it serves to client code. When asked for a block of pAmount bytes, it allocates an extra sizeof(size_t) bytes at the beginning and stores the size there. To get to this size, it interprets the mem pointer it gets from malloc as a size_t* and dereferences that (*static_cast<std::size_t*>(mem) = pAmount;). It then returns the rest of the block, which starts at mem + sizeof(size_t), since that is the part that the client may use.
When deallocating, it must pass the exact pointer it got from malloc to free. To get this pointer, it subtracts the sizeof(size_t) bytes it added in the allocate member function.
In both cases, the casts to char* are needed because pointer arithmetic is not allowed on void pointers.
void* allocate(std::size_t pAmount)
allocates pAmount of memory plus space to store the size
|-size-|---- pAmount of memory-----|
^
|
"allocate" will return a pointer just pasted the size field.
void deallocate(void* pMemory)
will move the pointer back to the beginning
|-size-|---- pAmount of memory-----|
^
|
and free it.
1.)
std::size_t mySize = 0;
void * men = & mySize;
// same as: mySize = 42;
*static_cast<std::size_t*>(mem) = 42;
std::cout << mySize;
// prints "42"
2.)
`return static_cast<char*>(mem) + sizeof(std::size_t);
// casts void pointer mem to a char* so that you can do pointer arithmetic.
// same as
char *myPointer = (char*)mem;
// increment myPointer by the size of size_t
return myPointer + sizeof(std::size_t);
3.)
`void* mem = static_cast<char*>(pMemory) - sizeof(std::size_t);`
// mem points size of size_t before pMemory
In order to know how much memory to clean up when you delete it (and provide some diagnostics) the allocator stores off the size in extra allocated memory.
*static_cast(mem) = pAmount; //please explain?
This takes the allocated memory and stores the number of allocated bytes into this location. The cast treats the raw memory as a size_t for storage purposes.
return static_cast(mem) +
sizeof(std::size_t); //?
This moves forward past the size bytes to the actual memory that your application will use and returns that pointer.
void* mem =
static_cast(pMemory) -
sizeof(std::size_t); //?
This is taking the block previously returned to the user and advancing back to the "real" allocated block that stored the size earlier. It's needed to do checks and reclaim the memory.
the cast is needed in order to get the proper offset since void* is not a type with a size.
when you write
return static_cast(mem) + sizeof(std::size_t);
the pointer is cast to a char* before the offset bytes is added.
ditto subtract when deallocating.
Related
I am currently implementing my own pool allocator to store n chunks of the same size in one big block of memory. I am linking all the chunks together using a *next pointer stored in the struct chunk like this
struct Chunk{
Chunk* next;
};
so I would expect to make a linked list like this given that i have a variable num_chunks which stores the number of chunks in the block
Chunk* allocate_block(size_t chunk_size){
alloc_pointer = (Chunk*) malloc(chunk_size * num_chunks);
Chunk* chunk = alloc_pointer;
for (int i = 0; i < num_chunks; ++i){
/*
I need to solve the problem of how to
link all these chunks together.
So I know that I have to work using the next pointer.
This next pointer must point to an address chunk_size
away from the current pointer and so on so forth.
So basically:
chunk -> next = alloc_pointer + chunk_size
and chunk is going to be this chunk -> next on the
successive call.
*/
chunk -> next = chunk + chunk_size;
chunk = chunk -> next;}
chunk -> next = nullptr;
return chunk;
}
However looking at a blog post I have this implementation which makes sense but still do not understand why mine should be wrong
/**
* Allocates a new block from OS.
*
* Returns a Chunk pointer set to the beginning of the block.
*/
Chunk *PoolAllocator::allocateBlock(size_t chunkSize) {
cout << "\nAllocating block (" << mChunksPerBlock << " chunks):\n\n";
size_t blockSize = mChunksPerBlock * chunkSize;
// The first chunk of the new block.
Chunk *blockBegin = reinterpret_cast<Chunk *>(malloc(blockSize));
// Once the block is allocated, we need to chain all
// the chunks in this block:
Chunk *chunk = blockBegin;
for (int i = 0; i < mChunksPerBlock - 1; ++i) {
chunk->next =
reinterpret_cast<Chunk *>(reinterpret_cast<char *>(chunk) + chunkSize);
chunk = chunk->next;
}
chunk->next = nullptr;
return blockBegin;
}
I don’t really understand why I should convert the type of chunk to char and then add that to the size of the chunk. Thanks in advance
When you add to pointers, pointer arithmetic is used. With pointer arithmetic, the memory address result depends on the size of the pointer being added to.
Let's break down this expression:
reinterpret_cast<Chunk *>(reinterpret_cast<char *>(chunk) + chunkSize);
The first part of this expression to be evaluated is
reinterpret_cast<char *>(chunk)
This will take the chunk pointer and tell the compiler to treat it as a char* rather than as Chunk*. This means that when pointer arithmetic is performed on the pointer, it will be in terms of 1 byte offsets (since a char has a size of 1 byte), instead of in offsets of sizeof(Chunk) bytes.
Next, chunkSize is added to this pointer. Because we are doing pointer arithmetic on a char* pointer, we will take the memory address of chunk, and add chunkSize*sizeof(char) = chunkSize*1 to that memory address.
That covers everything inside the outer set of brackets.
The problem now is that the result of our pointer arithmetic is still understood by the compiler to be a char* pointer, but we really want it to be a Chunk*. To fix this, we cast back to Chunk*. This effectively undoes the temporary cast to char*.
You can find more info on pointer arithmetic in the answers to this question.
How do I call new operator with alignment?
auto foo = new(std::align_val_t(32)) Foo; //?
and then, how to delete it properly?
delete(std::align_val_t(32), foo); //?
If this is the right form of using these overloads, why valgring complaining about mismatched free()/delete/delete[]?
exist very basic principle - the memory free routine always must match to allocate routine. if we use mismatch allocate and free - run time behavior can be any: all can be random ok, or crash by run-time, or memory leak, or heap corruption.
if we allocate memory with aligned version of operator new
void* operator new ( std::size_t count, std::align_val_t al);
we must use the corresponding aligned version of operator delete
void operator delete ( void* ptr, std::align_val_t al );
call void operator delete ( void* ptr ); here always must lead to run-time error. let simply test
std::align_val_t al = (std::align_val_t)256;
if (void* pv = operator new(8, al))
{
operator delete(pv, al);
//operator delete(pv); this line crash, or silently corrupt heap
}
why is aligned and not aligned version of operator delete always incompatible ? let think - how is possible allocate align on some value memory ? we initially always allocate some memory block. for return align pointer to use - we need adjust allocated memory pointer to be multiple align. ok. this is possible by allocate more memory than requested and adjust pointer. but now question - how free this block ? in general user got pointer not to the begin of allocated memory - how from this user pointer jump back to begin of allocated block ? without additional info this is impossible. we need store pointer to actual allocated memory before user returned pointer. may be this will be more visible in code typical implementation for aligned new and delete use _aligned_malloc and _aligned_free
void* operator new(size_t size, std::align_val_t al)
{
return _aligned_malloc(size, static_cast<size_t>(al));
}
void operator delete (void * p, std::align_val_t al)
{
_aligned_free(p);
}
when not aligned new and delete use malloc and free
void* operator new(size_t size)
{
return malloc(size);
}
void operator delete (void * p)
{
free(p);
}
now let look for internal implementation of _aligned_malloc and _aligned_free
void* __cdecl _aligned_malloc(size_t size, size_t alignment)
{
if (!alignment || ((alignment - 1) & alignment))
{
// alignment is not a power of 2 or is zero
return 0;
}
union {
void* pv;
void** ppv;
uintptr_t up;
};
if (void* buf = malloc(size + sizeof(void*) + --alignment))
{
pv = buf;
up = (up + sizeof(void*) + alignment) & ~alignment;
ppv[-1] = buf;
return pv;
}
return 0;
}
void __cdecl _aligned_free(void * pv)
{
if (pv)
{
free(((void**)pv)[-1]);
}
}
in general words _aligned_malloc allocate size + sizeof(void*) + alignment - 1 instead requested by caller size. adjust allocated pointer to fit alignment , and store originally allocated memory before pointer returned to caller.
and _aligned_free(pv) call not free(pv) but free(((void**)pv)[-1]); - for always another pointer. because this effect of _aligned_free(pv) always another compare free(pv). and operator delete(pv, al); always not compatible with operator delete(pv); if say delete [] usual have the same effect as delete but align vs not align always run time different.
The below syntax was the only one that worked for me to create and destroy an overaligned array, using clang-cl 13 on Windows 10 x64:
int* arr = new (std::align_val_t(64)) int[555];
::operator delete[] (arr, std::align_val_t(64));
For the same new operation, the below delete expression would not compile ("cannot delete expression of type 'std::align_val_t'):
delete[] (arr, std::align_val_t(64));
The below delete expression will compile, but then throws a runtime error ("
Critical error detected c0000374"):
delete[](std::align_val_t(64), blocks);
I'm trying to allocate an array of struct and I want each struct to be aligned to 64 bytes.
I tried this (it's for Windows only for now), but it doesn't work (I tried with VS2012 and VS2013):
struct __declspec(align(64)) A
{
std::vector<int> v;
A()
{
assert(sizeof(A) == 64);
assert((size_t)this % 64 == 0);
}
void* operator new[] (size_t size)
{
void* ptr = _aligned_malloc(size, 64);
assert((size_t)ptr % 64 == 0);
return ptr;
}
void operator delete[] (void* p)
{
_aligned_free(p);
}
};
int main(int argc, char* argv[])
{
A* arr = new A[200];
return 0;
}
The assert ((size_t)this % 64 == 0) breaks (the modulo returns 16). It looks like it works if the struct only contains simple types though, but breaks when it contains an std container (or some other std classes).
Am I doing something wrong? Is there a way of doing this properly? (Preferably c++03 compatible, but any solution that works in VS2012 is fine).
Edit:
As hinted by Shokwav, this works:
A* arr = (A*)new std::aligned_storage<sizeof(A), 64>::type[200];
// this works too actually:
//A* arr = (A*)_aligned_malloc(sizeof(A) * 200, 64);
for (int i=0; i<200; ++i)
new (&arr[i]) A();
So it looks like it's related to the use of new[]... I'm very curious if anybody has an explanation.
I wonder why you need such a huge alignment requirement, moreover to store a dynamic heap allocated object in the struct. But you can do this:
struct __declspec(align(64)) A
{
unsigned char ___padding[64 - sizeof(std::vector<int>)];
std::vector<int> v;
void* operator new[] (size_t size)
{
// Make sure the buffer will fit even in the worst case
unsigned char* ptr = (unsigned char*)malloc(size + 63);
// Find out the next aligned position in the buffer
unsigned char* endptr = (unsigned char*)(((intptr_t)ptr + 63) & ~63ULL);
// Also store the misalignment in the first padding of the structure
unsigned char misalign = (unsigned char)(endptr - ptr);
*endptr = misalign;
return endptr;
}
void operator delete[] (void* p)
{
unsigned char * ptr = (unsigned char*)p;
// It's required to call back with the original pointer, so subtract the misalignment offset
ptr -= *ptr;
free(ptr);
}
};
int main()
{
A * a = new A[2];
printf("%p - %p = %d\n", &a[1], &a[0], int((char*)&a[1] - (char*)&a[0]));
return 0;
}
I did not have your align_malloc and free function, so the implementation I'm providing is doing this:
It allocates larger to make sure it will fit in 64-bytes boundaries
It computes the offset from the allocation to the closest 64-bytes boundary
It stores the "offset" in the padding of the first structure (else I would have required a larger allocation space each time)
This is used to compute back the original pointer to the free()
Outputs:
0x7fff57b1ca40 - 0x7fff57b1ca00 = 64
Warning: If there is no padding in your structure, then the scheme above will corrupt data, since I'll be storing the misalignement offset in a place that'll be overwritten by the constructor of the internal members.
Remember that when you do "new X[n]", "n" has to be stored "somewhere" so when calling delete[], "n" calls to the destructors will be done. Usually, it's stored before the returned memory buffer (new will likely allocate the required size + 4 for storing the number of elements). The scheme here avoid this.
Another warning: Because C++ calls this operator with some additional padding included in the size for storing the array's number of elements, you'll might still get a "shift" in the returned pointer address for your objects. You might need to account for it. This is what the std::align does, it takes the extra space, compute the alignment like I did and return the aligned pointer. However, you can not get both done in the new[] overload, because of the "count storage" shift that happens after returning from new(). However, you can figure out the "count storage" space once by a single allocation, and adjust the offset accordingly in the new[] implementation.
I'm building my own string class in c++ 11 and I have a memory problem.
in main:
MyString str1; //Works ok, constructor creates empty char array.
const char* pointer1 = str1.c_str(); //Return the pointer to the array.
str1.Reserve(5);
// Now, when I use the Reverse method in string1, Pointer1 is
// pointing to the old memory address.
How to I change the array data in str1, but to the memory address?
With aother words, How do I fix this so that:
pointer1 == str1.c_str();
Reserve method:
void reserve(int res)
{
capacity = NewSize(size + res,0 , capacity); //Method to find the best cap.
char* oldData = data;
data = new char[capacity];
memcpy(data, oldData, capacity);
oldData = data;
//delete[] data;
data[(size)] = '\0';
}
This returns all the right data, but when I do "oldData = data", the memory address is lost.
I appreciate all help, thanks!
I think what you are asking is if there is a way to get a return value from your string class which will always point to the current string array. There are a number of ways to do this but generally this indicates bad design/implementation.
The more normal way to do this would be to advise API users that the result of c_str() is invalidated by any subsequent modifications to the object: don't keep the pointer, just call c_str() again.
Two obvious options are: a) a pointer to the pointer, very dangerous because now someone outside your class can tweak it, b) provide a wrapper class which encapsulates a pointer-to-pointer without allowing modifications.
template<typename T>
struct ReadOnlyPointer {
T* m_ptr;
... operator * ...
... operator -> ...
... operator T ...
};
ReadOnlyPointer<const char*> pointer = str1.pointer();
There also appear to be at least a couple of issues with your "reserve" function.
You push a '\0' at data[0] even though size might be zero.
MyString a;
a.reserve(0); // crash? you wrote to the first byte of a zero length array.
After copying the data from oldData to data, for some reason you assign the value of 'data' to 'oldData' and then never use 'oldData' again - this is a memory leak.
Your memcpy uses 'capacity' instead of 'size' so it may be over-copying.
Consider instead:
// ensure we have an additional 'res' bytes.
// caution: unlike stl and boost reserve, these are
// additional bytes, not total bytes.
void reserve(int res)
{
int newCapacity = NewSize(m_size + res, 0, m_capacity); //Method to find the best cap.
if(newCapacity <= m_capacity)
return;
char* newData = new char[newCapacity];
memcpy(newData, m_data, m_size);
delete[] m_data; // release the old allocation
m_data = newData;
m_capacity = newCapacity;
}
The extra data[(size)] = '\0'; could be the cause of your string becoming truncated if you are not changing the value of size elsewhere in your code.
I am currently writing a small custom memory Allocator in C++, and want to use it together with operator overloading of new/ delete. Anyways, my memory Allocator basically checks if the requested memory is over a certain threshold, and if so uses malloc to allocate the requested memory chunk. Otherwise the memory will be provided by some fixedPool allocators. that generally works, but for my deallocation function looks like this:
void MemoryManager::deallocate(void * _ptr, size_t _size){
if(_size > heapThreshold)
deallocHeap(_ptr);
else
deallocFixedPool(_ptr, _size);
}
So I need to provide the size of the chunk pointed to, to deallocate from the right place.
Now the problem is that the delete keyword does not provide any hint on the size of the deleted chunk, so I would need something like this:
void operator delete(void * _ptr, size_t _size){
MemoryManager::deallocate(_ptr, _size);
}
But as far as I can see, there is no way to determine the size inside the delete operator.- If I want to keep things the way it is right now, would I have to save the size of the memory chunks myself?
allocate more memory than neccessary and store the size information there. That's what your system allocator probably does already. Something like this (demonstrate with malloc for simplicity):
void *allocate(size_t size) {
size_t *p = malloc(size + sizeof(size_t));
p[0] = size; // store the size in the first few bytes
return (void*)(&p[1]); // return the memory just after the size we stored
}
void deallocate(void *ptr) {
size_t *p = (size_t*)ptr; // make the pointer the right type
size_t size = p[-1]; // get the data we stored at the beginning of this block
// do what you need with size here...
void *p2 = (void*)(&p[-1]); // get a pointer to the memory we originally really allocated
free(p2); // free it
}
You could keep a map of memory address to size for your pool-allocated memory. When you delete, check if the pointer is in the map, if it is delete that size, if it isn't call regular delete.
For class type, C++ already supports it directly. For nonclass types, you need to store the size manually like the other solution shows.
struct MyClass {
void operator delete(void *p, size_t size) {
MemoryManager::deallocate(p, size);
}
};
As of C++14 the Standard supports the second size parameter in the global delete allocation function. So want you want to do is possible natively now.
http://en.cppreference.com/w/cpp/memory/new/operator_delete