TEST(insert_many_ids_into_same_list, then_only_few_mallocs_must_be_called) { int i; int expectedMallocCalls = 1000 / 36 + 1; uint expectedMemLen = AlignDefault(sizeof(SHashItem)) + AlignDefault(sizeof(int)) + AlignDefault(34); TEST_ASSERT_TRUE(fakeMemStorageCount > 0); TEST_ASSERT_EQUAL_UINT32(fakeMemStorageCount, expectedMallocCalls); for (i = 0; i < expectedMallocCalls; i++) { TEST_ASSERT_EQUAL_UINT32(fakeMemStorage[i].memLen, expectedMemLen * 36); } }
/* This is an auxiliary function for allocateMemory method. * It allocates a whole new block, inserts it into * the head of the set's block list * and takes the whole memory from the entire block * for a chunk. */ void* allocateChunkBlock( void* self, size_t chunkSize, MemoryContainer container) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = (IErrorLogger)_->errorLogger; size_t blockSize = chunkSize + MEM_BLOCK_SIZE + MEM_CHUNK_SIZE; MemorySet set = (MemorySet)container; MemoryBlock block; MemoryChunk chunk; void* chunkPtr; size_t size = AlignDefault(chunkSize); ASSERT(elog, funcMalloc != NULL, NULL); block = (MemoryBlock)funcMalloc(blockSize); /* Report malloc error */ if (block == NULL) { showMemStat(_, topMemCont, 0); elog->log(LOG_ERROR, ERROR_CODE_OUT_OF_MEMORY, "Out of memory. Failed request size: %lu", chunkSize); } block->memset = set; block->freeStart = (char*)block + blockSize; block->freeEnd = block->freeStart; chunk = (MemoryChunk)((char*)block + MEM_BLOCK_SIZE); chunk->memsetorchunk = set; chunk->size = size; chunk->sizeRequested = chunkSize; chunkPtr = MemoryChunkGetPointer(chunk); /* Insert the block into the head of * the set's block list. */ if (set->blockList != NULL) { block->next = set->blockList->next; set->blockList->next = block; return chunkPtr; } block->next = NULL; set->blockList = block; return chunkPtr; }
TEST(allocate_large_chunk, then_chunk_block_must_be_created) { int chunk_size_requested = size_alc; int chunk_size = AlignDefault(chunk_size_requested); void* chunkMem = mem_alc; MemoryChunk chunk = (MemoryChunk)((char*)chunkMem - MEM_CHUNK_SIZE); TEST_ASSERT_NOT_NULL(chunkMem); TEST_ASSERT_NOT_NULL(chunk); TEST_ASSERT_EQUAL(chunk->memsetorchunk, mc_alc); TEST_ASSERT_EQUAL_INT32(chunk->size, chunk_size); TEST_ASSERT_EQUAL_INT32(chunk->sizeRequested, chunk_size_requested); }
void initializePage( void* self, void* page, size_t size, size_t suplSize) { PageHeader p = (PageHeader)page; suplSize = AlignDefault(suplSize); memset(p, 0, size); p->freeStart = SizeOfPageHeader; p->freeEnd = size - suplSize; p->special = size - suplSize; }
uint16 addItemToPage( void* page, void* item, size_t size, uint16 itemnum, Bool overwrite) { ItemPointer itemId; PageHeader pageHdr = (PageHeader)page; uint16 maxItemNum = RowsCountOnPage(page) + 1; Bool needShuffle = False; int num; int newFreeStart, newFreeEnd; size_t alignedSize; if (IsItemIdValid(itemnum)) { if (overwrite) { itemId = PageGetItemId(page, itemnum); /* If the item is in use or has not empty storage * we can not use it. Return 0. */ if (ItemIdIsInUse(itemId) || ItemIdHasStorage(itemId)) return 0; } else { /* If we are not overwriting an item * and the offset number in less than max number * Our page needs shuffle to free a room for the item. */ if (itemnum < maxItemNum) needShuffle = True; } } else { /* If itemnum is not passed in or is invalid, * we need to find a free item. If there does not exist * a free item, we will put it at the end of the page. */ if (PageHasFreeItems(pageHdr)) { /* Look for unused items */ for (num = 1; num < maxItemNum; num++) { itemId = PageGetItemId(page, itemnum); /* If the item is unused we break. */ if (!ItemIdIsInUse(itemId) && !ItemIdHasStorage(itemId)) break; } /* If we have reached the maximum offset, there are not free items. */ if (num >= maxItemNum) { /* the hint is wrong, so reset it */ PageClearHasFreeLinePointers(page); } } else { /* If we have not a free item we try to insert it * into the end of a page. */ itemnum = maxItemNum; } } /* If we are inserting an item into the end or with a shuffle * we need to insert an item into items array. We pull the free start * right on one position. */ if (itemnum == maxItemNum || needShuffle) newFreeStart = pageHdr->freeStart + sizeof(ItemPointerData); else newFreeStart = pageHdr->freeStart; alignedSize = AlignDefault(size); /* Calculate new free end. */ newFreeEnd = (int)pageHdr->freeEnd - (int)alignedSize; /* if newFreeStart exceeds newFreeEnd we do not have * a free room to put the item to */ if (newFreeStart > newFreeEnd) return 0; itemId = PageGetItemId(page, itemnum); if (needShuffle) memmove(itemId + 1, itemId, (maxItemNum - itemnum) * sizeof(ItemPointerData)); itemId->flags = ITEM_NORMAL; itemId->off = newFreeEnd; itemId->len = size; /* copy the item's data onto the page */ memcpy((char*)page + newFreeEnd, item, size); /* Change free space range, freeStart and freeEnd. */ pageHdr->freeStart = (uint16)newFreeStart; pageHdr->freeEnd = (uint16)newFreeEnd; return itemnum; }
/* Reallocate memory. When we need more memory * we allocate a piece of memory with an appropriate size, * copy an old memory and then free it. */ void* reallocateMemory( void* self, MemoryContainer container, void* old_mem, size_t new_size) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = _->errorLogger; MemorySet set = (MemorySet)container; MemoryChunk chunk = (MemoryChunk)((char*)old_mem - MEM_CHUNK_SIZE); size_t oldsize = chunk->size; void* chunkPtr; void* new_mem; /* Always return when a requested size is a decrease */ if (oldsize >= new_size) return old_mem; /* Check if for the chunk there was allocate a block */ if (oldsize > set->chunkMaxSize) { /* Try to find the corresponding block first */ MemoryBlock block = set->blockList; MemoryBlock prevblock = NULL; size_t chunk_size; size_t block_size; char* expected_block_end; while (block != NULL) { if (chunk == (MemoryChunk)((char*)block + MEM_BLOCK_SIZE)) break; prevblock = block; block = block->next; } /* Could not find the block. We should report an error. */ if (block == NULL) { elog->log(LOG_ERROR, ERROR_CODE_BLOCK_NOT_FOUND, "Could not find block containing chunk %p", chunk); return NULL; } /* We should check that the chunk is only one * on the block. */ expected_block_end = (char*)block + chunk->size + MEM_BLOCK_SIZE + MEM_CHUNK_SIZE; ASSERT(elog, block->freeEnd == expected_block_end, NULL); /* Do the realloc */ chunk_size = AlignDefault(new_size); block_size = chunk_size + MEM_BLOCK_SIZE + MEM_CHUNK_SIZE; ASSERT(elog, funcRealloc != NULL, NULL); block = (MemoryBlock)funcRealloc(block, block_size); if (block == NULL) { showMemStat(_, topMemCont, 0); elog->log(LOG_ERROR, ERROR_CODE_OUT_OF_MEMORY, "Out of memory. Failed request size: %lu", block_size); return NULL; } block->freeStart = block->freeEnd = (char*)block + block_size; chunk = (MemoryChunk)((char*)block + MEM_BLOCK_SIZE); /* Change block pointer to newly allocated block. */ if (prevblock == NULL) set->blockList = block; else prevblock->next = block; chunk->size = chunk_size; chunkPtr = MemoryChunkGetPointer(chunk); return chunkPtr; } /* If we are here that this small block * was taken from the free list. We allocate * a new free chunk and the old chunk add to * the free list. */ new_mem = allocateMemory(_, container, new_size); /* copy existing memory to a new memory. */ memcpy(new_mem, old_mem, oldsize); /* free old chunk */ freeChunk(self, old_mem); return new_mem; }
/* Creates new memory set object. */ MemorySet memSetCreate( void* self, MemoryContainer container, MemoryContainer parent, char* name, size_t minContainerSize, size_t initBlockSize, size_t maxBlockSize) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = _->errorLogger; size_t blockMaxChunkSize; size_t blockSize; MemoryBlock block; /* MemorySet is derived from MemoryContainer. * First we create a base object. */ MemorySet set = (MemorySet)memContCreate( self, container, parent, MCT_MemorySet, sizeof(SMemorySet), name); ASSERT(elog, set != NULL, NULL); initBlockSize = AlignDefault(initBlockSize); /* Check if initBlockSize is less than * the minimum allowed value. */ if (initBlockSize < MEM_BLOCK_INIT_MIN_SIZE) initBlockSize = MEM_BLOCK_INIT_MIN_SIZE; maxBlockSize = AlignDefault(maxBlockSize); /* maxBlock should be bigger than initBlockSize. */ if (maxBlockSize < initBlockSize) maxBlockSize = initBlockSize; set->initBlockSize = initBlockSize; set->maxBlockSize = maxBlockSize; set->nextBlockSize = initBlockSize; /* chunkMaxSize can't be more than chunk_max_size * because the number of free lists is restricted. */ set->chunkMaxSize = MEMORY_CHUNK_MAX_SIZE; /* Calculate the max chunk size in comparison * to the max block size. * The chunk size limit is at most 1/8 of the max block size * In the case when all chunks have the maximum size * only 1/8 of the block space will be wasted. */ blockMaxChunkSize = (maxBlockSize - MEM_BLOCK_SIZE) / MAX_BLOCK_CHUNKS_NUM; /* There can be a situation when memory chunk max size is more than * the max chunk size to block. So we should syncronize this and * we divide it on 2 until chunk max size becomes less than maxChunkSizeToBlock. */ while (set->chunkMaxSize + MEM_CHUNK_SIZE > blockMaxChunkSize) set->chunkMaxSize >>= 1; if (minContainerSize <= MEM_BLOCK_SIZE + MEM_CHUNK_SIZE) return (MemoryContainer)set; /* Here minContextSize is more than the block size. * In this case we allocate the first block. */ blockSize = AlignDefault(minContainerSize); ASSERT(elog, funcMalloc != NULL, NULL); block = (MemoryBlock)funcMalloc(blockSize); /* An error has happened. We should report it. */ if (block == NULL) { showMemStat(_, topMemCont, 0); elog->log(LOG_ERROR, ERROR_CODE_OUT_OF_MEMORY, "Out of memory. Failed request size: %lu", blockSize); return NULL; } block->memset = set; block->freeStart = ((char*)block) + MEM_BLOCK_SIZE; block->freeEnd = ((char*)block) + blockSize; /* Insert this block into the head of the blocks list. */ block->next = set->blockList; set->blockList = block; //set->keeperBlock = block; return (MemoryContainer)set; }