#include #include #include "vtr_assert.h" #include "vtr_list.h" #include "vtr_memory.h" #include "vtr_error.h" #include "vtr_util.h" #ifndef __GLIBC__ #include #else #include #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