static void * wmem_block_alloc(void *private_data, const size_t size) { wmem_block_allocator_t *allocator = (wmem_block_allocator_t*) private_data; wmem_block_chunk_t *chunk; if (size > WMEM_BLOCK_MAX_ALLOC_SIZE) { return wmem_block_alloc_jumbo(allocator, size); } if (allocator->recycler_head && WMEM_CHUNK_DATA_LEN(allocator->recycler_head) >= size) { /* If we can serve it from the recycler, do so. */ chunk = allocator->recycler_head; } else { if (allocator->master_head && WMEM_CHUNK_DATA_LEN(allocator->master_head) < size) { /* Recycle the head of the master list if necessary. */ chunk = allocator->master_head; wmem_block_pop_master(allocator); wmem_block_add_to_recycler(allocator, chunk); } if (!allocator->master_head) { /* Allocate a new block if necessary. */ wmem_block_new_block(allocator); } chunk = allocator->master_head; } /* if our chunk is used, something is wrong */ g_assert(! chunk->used); /* if we still don't have the space at this point, something is wrong */ g_assert(size <= WMEM_CHUNK_DATA_LEN(chunk)); /* Split our chunk into two to preserve any trailing free space */ wmem_block_split_free_chunk(allocator, chunk, size); /* if our split reduced our size too much, something went wrong */ g_assert(size <= WMEM_CHUNK_DATA_LEN(chunk)); /* the resulting chunk should not be in either free list */ g_assert(chunk != allocator->master_head); g_assert(chunk != allocator->recycler_head); /* Now cycle the recycler */ wmem_block_cycle_recycler(allocator); /* mark it as used */ chunk->used = TRUE; /* and return the user's pointer */ return WMEM_CHUNK_TO_DATA(chunk); }
static void * wmem_block_alloc(void *private_data, const size_t size) { wmem_block_allocator_t *allocator = (wmem_block_allocator_t*) private_data; wmem_block_chunk_t *chunk; if (size > WMEM_BLOCK_MAX_ALLOC_SIZE) { return wmem_block_alloc_jumbo(allocator, size); } if (allocator->recycler_head && WMEM_CHUNK_DATA_LEN(allocator->recycler_head) >= size) { /* If we can serve it from the recycler, do so. */ chunk = allocator->recycler_head; } else { if (allocator->master_head && WMEM_CHUNK_DATA_LEN(allocator->master_head) < size) { /* Recycle the head of the master list if necessary. */ chunk = allocator->master_head; wmem_block_pop_master(allocator); wmem_block_add_to_recycler(allocator, chunk); } if (!allocator->master_head) { /* Allocate a new block if necessary. */ wmem_block_new_block(allocator); } chunk = allocator->master_head; } /* Split our chunk into two to preserve any trailing free space */ wmem_block_split_free_chunk(allocator, chunk, size); /* Now cycle the recycler */ wmem_block_cycle_recycler(allocator); /* mark it as used */ chunk->used = TRUE; /* and return the user's pointer */ return WMEM_CHUNK_TO_DATA(chunk); }
/* Takes an unused chunk and a size, and splits it into two chunks if possible. * The first chunk (at the same address as the input chunk) is guaranteed to * hold at least `size` bytes of data, and to not be in either the master or * recycler lists. * * The second chunk gets whatever data is left over. It is marked unused and * replaces the input chunk in whichever list it originally inhabited. */ static void wmem_block_split_free_chunk(wmem_block_allocator_t *allocator, wmem_block_chunk_t *chunk, const size_t size) { wmem_block_chunk_t *extra; wmem_block_free_t *old_blk, *new_blk; size_t aligned_size, available; gboolean last; aligned_size = WMEM_ALIGN_SIZE(size) + WMEM_CHUNK_HEADER_SIZE; if (WMEM_CHUNK_DATA_LEN(chunk) < aligned_size + sizeof(wmem_block_free_t)) { /* If the available space is not enought to store all of * (hdr + requested size + alignment padding + hdr + free-header) then * just remove the current chunk from the free list and return, since we * can't usefully split it. */ if (chunk == allocator->master_head) { wmem_block_pop_master(allocator); } else if (WMEM_CHUNK_DATA_LEN(chunk) >= sizeof(wmem_block_free_t)) { wmem_block_remove_from_recycler(allocator, chunk); } return; } /* preserve a few values from chunk that we'll need to manipulate */ last = chunk->last; available = chunk->len - aligned_size; /* set new values for chunk */ chunk->len = (guint32) aligned_size; chunk->last = FALSE; /* with chunk's values set, we can use the standard macro to calculate * the location and size of the new free chunk */ extra = WMEM_CHUNK_NEXT(chunk); /* Now we move the free chunk's address without changing its location * in whichever list it is in. * * Note that the new chunk header 'extra' may overlap the old free header, * so we have to copy the free header before we write anything to extra. */ old_blk = WMEM_GET_FREE(chunk); new_blk = WMEM_GET_FREE(extra); if (allocator->master_head == chunk) { new_blk->prev = old_blk->prev; new_blk->next = old_blk->next; if (old_blk->next) { WMEM_GET_FREE(old_blk->next)->prev = extra; } allocator->master_head = extra; } else { if (old_blk->prev == chunk) { new_blk->prev = extra; new_blk->next = extra; } else { new_blk->prev = old_blk->prev; new_blk->next = old_blk->next; WMEM_GET_FREE(old_blk->prev)->next = extra; WMEM_GET_FREE(old_blk->next)->prev = extra; } if (allocator->recycler_head == chunk) { allocator->recycler_head = extra; } } /* Now that we've copied over the free-list stuff (which may have overlapped * with our new chunk header) we can safely write our new chunk header. */ extra->len = (guint32) available; extra->last = last; extra->prev = chunk->len; extra->used = FALSE; extra->jumbo = FALSE; /* Correctly update the following chunk's back-pointer */ if (!last) { WMEM_CHUNK_NEXT(extra)->prev = extra->len; } }