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 a free chunk and checks the chunks to its immediate right and left in * the block. If they are also free, the contigous free chunks are merged into * a single free chunk. The resulting chunk ends up in either the master list or * the recycler, depending on where the merged chunks were originally. */ static void wmem_block_merge_free(wmem_block_allocator_t *allocator, wmem_block_chunk_t *chunk) { wmem_block_chunk_t *tmp; wmem_block_chunk_t *left_free = NULL; wmem_block_chunk_t *right_free = NULL; /* Check the chunk to our right. If it is free, merge it into our current * chunk. If it is big enough to hold a free-header, save it for later (we * need to know about the left chunk before we decide what goes where). */ tmp = WMEM_CHUNK_NEXT(chunk); if (tmp && !tmp->used) { if (WMEM_CHUNK_DATA_LEN(tmp) >= sizeof(wmem_block_free_t)) { right_free = tmp; } chunk->len += tmp->len; chunk->last = tmp->last; } /* Check the chunk to our left. If it is free, merge our current chunk into * it (thus chunk = tmp). As before, save it if it has enough space to * hold a free-header. */ tmp = WMEM_CHUNK_PREV(chunk); if (tmp && !tmp->used) { if (WMEM_CHUNK_DATA_LEN(tmp) >= sizeof(wmem_block_free_t)) { left_free = tmp; } tmp->len += chunk->len; tmp->last = chunk->last; chunk = tmp; } /* The length of our chunk may have changed. If we have a chunk following, * update its 'prev' count. */ if (!chunk->last) { WMEM_CHUNK_NEXT(chunk)->prev = chunk->len; } /* Now that the chunk headers are merged and consistent, we need to figure * out what goes where in which free list. */ if (right_free && right_free == allocator->master_head) { /* If we merged right, and that chunk was the head of the master list, * then we leave the resulting chunk at the head of the master list. */ wmem_block_free_t *moved; if (left_free) { wmem_block_remove_from_recycler(allocator, left_free); } moved = WMEM_GET_FREE(chunk); moved->prev = NULL; moved->next = WMEM_GET_FREE(right_free)->next; allocator->master_head = chunk; if (moved->next) { WMEM_GET_FREE(moved->next)->prev = chunk; } } else { /* Otherwise, we remove the right-merged chunk (if there was one) from * the recycler. Then, if we merged left we have nothing to do, since * that recycler entry is still valid. If not, we add the chunk. */ if (right_free) { wmem_block_remove_from_recycler(allocator, right_free); } if (!left_free) { wmem_block_add_to_recycler(allocator, chunk); } } }