178 lines
5.0 KiB
C++
178 lines
5.0 KiB
C++
#include <cstddef>
|
|
#include <cstdlib>
|
|
|
|
#include "vtr_assert.h"
|
|
#include "vtr_list.h"
|
|
#include "vtr_memory.h"
|
|
#include "vtr_error.h"
|
|
#include "vtr_util.h"
|
|
|
|
#ifndef __GLIBC__
|
|
#include <stdlib.h>
|
|
#else
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
|
|
namespace vtr {
|
|
|
|
|
|
#ifndef __GLIBC__
|
|
int malloc_trim(size_t /*pad*/) {
|
|
return 0;
|
|
}
|
|
#else
|
|
int malloc_trim(size_t pad) {
|
|
return ::malloc_trim(pad);
|
|
}
|
|
#endif
|
|
|
|
void* free(void *some){
|
|
if(some){
|
|
std::free(some);
|
|
some = nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void* calloc(size_t nelem, size_t size) {
|
|
void *ret;
|
|
if (nelem == 0) {
|
|
return nullptr ;
|
|
}
|
|
|
|
if ((ret = std::calloc(nelem, size)) == nullptr ) {
|
|
throw VtrError("Unable to calloc memory.", __FILE__, __LINE__);
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
void* malloc(size_t size) {
|
|
void *ret;
|
|
if (size == 0) {
|
|
return nullptr ;
|
|
}
|
|
|
|
if ((ret = std::malloc(size)) == nullptr && size != 0) {
|
|
throw VtrError("Unable to malloc memory.", __FILE__, __LINE__);
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
void* realloc(void *ptr, size_t size) {
|
|
void *ret;
|
|
|
|
ret = std::realloc(ptr, size);
|
|
if (nullptr == ret && size != 0) {
|
|
throw VtrError(string_fmt("Unable to realloc memory (ptr=%p, size=%d).", ptr, size),
|
|
__FILE__, __LINE__);
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
void* chunk_malloc(size_t size, t_chunk *chunk_info) {
|
|
|
|
/* This routine should be used for allocating fairly small data *
|
|
* structures where memory-efficiency is crucial. This routine allocates *
|
|
* large "chunks" of data, and parcels them out as requested. Whenever *
|
|
* it mallocs a new chunk it adds it to the linked list pointed to by *
|
|
* chunk_info->chunk_ptr_head. This list can be used to free the *
|
|
* chunked memory. *
|
|
* Information about the currently open "chunk" must be stored by the *
|
|
* user program. chunk_info->mem_avail_ptr points to an int storing *
|
|
* how many bytes are left in the current chunk, while *
|
|
* chunk_info->next_mem_loc_ptr is the address of a pointer to the *
|
|
* next free bytes in the chunk. To start a new chunk, simply set *
|
|
* chunk_info->mem_avail_ptr = 0. Each independent set of data *
|
|
* structures should use a new chunk. */
|
|
|
|
/* To make sure the memory passed back is properly aligned, I must *
|
|
* only send back chunks in multiples of the worst-case alignment *
|
|
* restriction of the machine. On most machines this should be *
|
|
* a long, but on 64-bit machines it might be a long long or a *
|
|
* double. Change the typedef below if this is the case. */
|
|
|
|
typedef size_t Align;
|
|
|
|
constexpr int CHUNK_SIZE = 32768;
|
|
constexpr int FRAGMENT_THRESHOLD = 100;
|
|
|
|
char *tmp_ptr;
|
|
int aligned_size;
|
|
|
|
VTR_ASSERT(chunk_info->mem_avail >= 0);
|
|
|
|
if ((size_t) (chunk_info->mem_avail) < size) { /* Need to malloc more memory. */
|
|
if (size > CHUNK_SIZE) { /* Too big, use standard routine. */
|
|
tmp_ptr = (char *) vtr::malloc(size);
|
|
|
|
/* When debugging, uncomment the code below to see if memory allocation size */
|
|
/* makes sense */
|
|
//#ifdef DEBUG
|
|
// vtr_printf("NB: my_chunk_malloc got a request for %d bytes.\n", size);
|
|
// vtr_printf("You should consider using vtr::malloc for such big requests.\n");
|
|
// #endif
|
|
|
|
VTR_ASSERT(chunk_info != nullptr);
|
|
chunk_info->chunk_ptr_head = insert_in_vptr_list(
|
|
chunk_info->chunk_ptr_head, tmp_ptr);
|
|
return (tmp_ptr);
|
|
}
|
|
|
|
if (chunk_info->mem_avail < FRAGMENT_THRESHOLD) { /* Only a small scrap left. */
|
|
chunk_info->next_mem_loc_ptr = (char *) vtr::malloc(CHUNK_SIZE);
|
|
chunk_info->mem_avail = CHUNK_SIZE;
|
|
VTR_ASSERT(chunk_info != nullptr);
|
|
chunk_info->chunk_ptr_head = insert_in_vptr_list(
|
|
chunk_info->chunk_ptr_head, chunk_info->next_mem_loc_ptr);
|
|
}
|
|
|
|
/* Execute else clause only when the chunk we want is pretty big, *
|
|
* and would leave too big an unused fragment. Then we use malloc *
|
|
* to allocate normally. */
|
|
|
|
else {
|
|
tmp_ptr = (char *) vtr::malloc(size);
|
|
VTR_ASSERT(chunk_info != nullptr);
|
|
chunk_info->chunk_ptr_head = insert_in_vptr_list(
|
|
chunk_info->chunk_ptr_head, tmp_ptr);
|
|
return (tmp_ptr);
|
|
}
|
|
}
|
|
|
|
/* Find the smallest distance to advance the memory pointer and keep *
|
|
* everything aligned. */
|
|
|
|
if (size % sizeof(Align) == 0) {
|
|
aligned_size = size;
|
|
} else {
|
|
aligned_size = size + sizeof(Align) - size % sizeof(Align);
|
|
}
|
|
|
|
tmp_ptr = chunk_info->next_mem_loc_ptr;
|
|
chunk_info->next_mem_loc_ptr += aligned_size;
|
|
chunk_info->mem_avail -= aligned_size;
|
|
return (tmp_ptr);
|
|
}
|
|
|
|
void free_chunk_memory(t_chunk *chunk_info) {
|
|
|
|
/* Frees the memory allocated by a sequence of calls to my_chunk_malloc. */
|
|
|
|
t_linked_vptr *curr_ptr, *prev_ptr;
|
|
|
|
curr_ptr = chunk_info->chunk_ptr_head;
|
|
|
|
while (curr_ptr != nullptr ) {
|
|
free(curr_ptr->data_vptr); /* Free memory "chunk". */
|
|
prev_ptr = curr_ptr;
|
|
curr_ptr = curr_ptr->next;
|
|
free(prev_ptr); /* Free memory used to track "chunk". */
|
|
}
|
|
chunk_info->chunk_ptr_head = nullptr;
|
|
chunk_info->mem_avail = 0;
|
|
chunk_info->next_mem_loc_ptr = nullptr;
|
|
}
|
|
|
|
} //namespace
|