Skip to content
Snippets Groups Projects
Commit 003daabb authored by Alexandr's avatar Alexandr
Browse files

added pt_alloc to small library

parent 29ada348
No related branches found
No related tags found
No related merge requests found
set(lib_sources slab_cache.c region.c mempool.c slab_arena.c small.c)
set(lib_sources slab_cache.c region.c mempool.c slab_arena.c small.c pt_alloc.c)
set_source_files_compile_flags(${lib_sources})
add_library(small ${lib_sources})
/*
* pt_alloc implementation
*/
#include "pt_alloc.h"
#include <limits.h>
#ifndef __OPTIMIZE__ /* Assert is not used in optimized build */
#include <assert.h>
#endif
/*
* Assert is turned off in optimized build
*/
#ifndef __OPTIMIZE__
#define pt_assert(e) assert(e)
#else
#define pt_assert(e) do {} while(0)
#endif
/*
* Binary logarithm of value (exact if the value is a power of 2,
* appoximate otherwise)
*/
static PT_ID_T
pt_log2(PT_ID_T val)
{
pt_assert(val > 0);
return sizeof(unsigned long) * CHAR_BIT -
__builtin_clzl((unsigned long) val) - 1;
}
/*
* Construction
*/
void
pt3_construct(pt3 *p, PT_ID_T extent_size, PT_ID_T block_size,
prov_alloc_func alloc_func, prov_free_func free_func)
{
/*extent_size must be power of 2 */
pt_assert((extent_size & (extent_size - 1)) == 0);
/*block_size must be power of 2 */
pt_assert((block_size & (block_size - 1)) == 0);
p->extent = 0;
p->created = 0;
p->extent_size = extent_size;
p->block_size = block_size;
PT_ID_T log1 = pt_log2(extent_size);
PT_ID_T log2 = pt_log2(block_size);
PT_ID_T log3 = pt_log2(sizeof(void *));
p->log2_capacity = log1 * 3 - log2 - log3 * 2;
p->shift1 = log1 * 2 - log2 - log3;
p->shift2 = log1 - log2;
p->mask1 = (((PT_ID_T)1) << p->shift1) - ((PT_ID_T)1);
p->mask2 = (((PT_ID_T)1) << p->shift2) - ((PT_ID_T)1);
p->alloc_func = alloc_func;
p->free_func = free_func;
}
void
pt2_construct(pt2 *p, PT_ID_T extent_size, PT_ID_T block_size,
prov_alloc_func alloc_func, prov_free_func free_func)
{
/*extent_size must be power of 2 */
pt_assert((extent_size & (extent_size - 1)) == 0);
/*block_size must be power of 2 */
pt_assert((block_size & (block_size - 1)) == 0);
p->extent = 0;
p->created = 0;
p->extent_size = extent_size;
p->block_size = block_size;
PT_ID_T log1 = pt_log2(extent_size);
PT_ID_T log2 = pt_log2(block_size);
PT_ID_T log3 = pt_log2(sizeof(void *));
p->log2_capacity = log1 * 2 - log2 - log3;
p->shift = log1 - log2;
p->mask = (((PT_ID_T)1) << p->shift) - ((PT_ID_T)1);
p->alloc_func = alloc_func;
p->free_func = free_func;
}
/*
* Destruction
*/
void
pt3_destroy(pt3 *p)
{
if (p->created) {
void* extent1 = p->extent;
PT_ID_T id = p->created;
PT_ID_T index1 = id >> p->shift1;
id &= p->mask1;
if (id) {
PT_ID_T index2 = id >> p->shift2;
id &= p->mask2;
if (id)
index2++;
void *extent2 = ((void **)extent1)[index1];
for (PT_ID_T j = 0; j < index2; j++) {
void *extent3 = ((void **)extent2)[j];
p->free_func(extent3);
}
p->free_func(extent2);
}
PT_ID_T index2 = p->extent_size / sizeof(void *);
for (PT_ID_T i = 0; i < index1; i++) {
void *extent2 = ((void **)extent1)[i];
for (PT_ID_T j = 0; j < index2; j++) {
void *extent3 = ((void **)extent2)[j];
p->free_func(extent3);
}
p->free_func(extent2);
}
p->free_func(extent1);
p->created = 0;
}
#ifndef __OPTIMIZE__
p->extent = (void *)0xDEADBEEF;
#endif
}
void
pt2_destroy(pt2 *p)
{
if (p->created) {
PT_ID_T id = p->created;
PT_ID_T index1 = id >> p->shift;
id &= p->mask;
if (id)
index1++;
void* extent1 = p->extent;
for (PT_ID_T i = 0; i < index1; i++) {
void *extent2 = ((void **)extent1)[i];
p->free_func(extent2);
}
p->free_func(extent1);
p->created = 0;
}
#ifndef __OPTIMIZE__
p->extent = (void *)0xDEADBEEF;
#endif
}
/*
* Allocation
*/
void *
pt3_alloc(pt3 *p, PT_ID_T *result_id)
{
if (p->created)
pt_assert(pt_log2(p->created) < p->log2_capacity);
PT_ID_T id = p->created;
PT_ID_T extent1_not_empty = id;
PT_ID_T index1 = id >> p->shift1;
id &= p->mask1;
PT_ID_T extent2_not_empty = id;
PT_ID_T index2 = id >> p->shift2;
id &= p->mask2;
PT_ID_T extent3_not_empty = id;
PT_ID_T index3 = id;
void *extent1, *extent2, *extent3;
if (extent1_not_empty) {
extent1 = p->extent;
} else {
extent1 = p->alloc_func();
if (!extent1)
return 0;
p->extent = extent1;
}
if (extent2_not_empty) {
extent2 = ((void **)extent1)[index1];
} else {
extent2 = p->alloc_func();
if (!extent2) {
if (!extent1_not_empty) /* means - was empty */
p->free_func(extent1);
return 0;
}
((void **)extent1)[index1] = extent2;
}
if (extent3_not_empty) {
extent3 = ((void **)extent2)[index2];
} else {
extent3 = p->alloc_func();
if (!extent3) {
if (!extent1_not_empty) /* means - was empty */
p->free_func(extent1);
if (!extent2_not_empty) /* means - was empty */
p->free_func(extent2);
return 0;
}
((void **)extent2)[index2] = extent3;
}
*result_id = p->created++;
return (void*)((char*)extent3 + index3 * p->block_size);
}
void *
pt2_alloc(pt2 *p, PT_ID_T *result_id)
{
if (p->created)
pt_assert(pt_log2(p->created) < p->log2_capacity);
PT_ID_T id = p->created;
PT_ID_T extent1_not_empty = id;
PT_ID_T index1 = id >> p->shift;
id &= p->mask;
PT_ID_T extent2_not_empty = id;
PT_ID_T index2 = id;
void *extent1, *extent2;
if (extent1_not_empty) {
extent1 = p->extent;
} else {
extent1 = p->alloc_func();
if (!extent1)
return 0;
p->extent = extent1;
}
if (extent2_not_empty) {
extent2 = ((void **)extent1)[index1];
} else {
extent2 = p->alloc_func();
if (!extent2) {
if (!extent1_not_empty) /* means - was empty */
p->free_func(extent1);
return 0;
}
((void **)extent1)[index1] = extent2;
}
*result_id = p->created++;
return (void*)((char*)extent2 + index2 * p->block_size);
}
/*
* Restoration
*/
void *
pt3_get(pt3 *p, PT_ID_T id)
{
pt_assert(id < p->created);
PT_ID_T index1 = id >> p->shift1;
id &= p->mask1;
PT_ID_T index2 = id >> p->shift2;
id &= p->mask2;
PT_ID_T index3 = id;
return (((char***)p->extent)[index1][index2] + index3 * p->block_size);
}
void *
pt2_get(pt2 *p, PT_ID_T id)
{
pt_assert(id < p->created);
PT_ID_T index1 = id >> p->shift;
id &= p->mask;
PT_ID_T index2 = id;
return (((char**)p->extent)[index1] + index2 * p->block_size);
}
/*
* Getting number of allocated chunks (of size p->chunk_size each)
*/
PT_ID_T
pt3_extents_count(pt3 *p)
{
PT_ID_T c = (p->created + (p->extent_size / p->block_size - 1))
/ (p->extent_size / p->block_size);
PT_ID_T res = c;
for (PT_ID_T i = 0; i < 2; i++) {
c = (c + (p->extent_size / sizeof(void *) - 1))
/ (p->extent_size / sizeof(void *));
res += c;
}
return res;
}
PT_ID_T
pt2_extents_count(pt2 *p)
{
PT_ID_T c = (p->created + (p->extent_size / p->block_size - 1))
/ (p->extent_size / p->block_size);
PT_ID_T res = c;
for (PT_ID_T i = 0; i < 1; i++) {
c = (c + (p->extent_size / sizeof(void *) - 1))
/ (p->extent_size / sizeof(void *));
res += c;
}
return res;
}
#pragma once
/*
* pt_alloc: pointer translation allocator
* pt_alloc is as an allocator, that provides blocks of specified size (N),
* and provides an integer incrementally-growning ID for each provided block.
* Block size must be power of 2 (checked by assert in debug build).
* pt_alloc can restore pointer to block by it's ID, so
* one can store such ID instead of storing pointer to block.
* Since block IDs are generated incrementally from 0 and pt instance stores
* the number of provided blocks, one can simply iterate all provided blocks.
* pt_alloc in it's turn allocates extents of memory with given allocator,
* and strictly with specified size (M).
* Extent size must be power of 2.
* No block freeing provided, but destoying pt_alloc instance frees all blocks.
* There is an impotant compile-time setting - recurse level (L).
* Actually pt_alloc defined several times, with different L as suffix of "pt"
* For example pt3_alloc is a pt_alloc with L = 3
* Briefly, with a given N, M and L (see above) the pt_alloc instance:
* 1) can provide not more than POW(M / sizeof(void*), L - 1) * (M / N) blocks
* 2) costs L memory reading for providing new block and restoring block ptr
* 3) has approximate memory overhead of size (L * M)
* Of course, ID integer type limit also limits maximum capability of pt_alloc.
*/
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif /* defined(__cplusplus) */
typedef uint32_t PT_ID_T;
typedef void *(*prov_alloc_func)();
typedef void (*prov_free_func)(void *);
/*
* pt_alloc struct definition
*/
typedef struct tag_pt3 {
void *extent;
PT_ID_T created;
PT_ID_T extent_size;
PT_ID_T block_size;
PT_ID_T log2_capacity;
PT_ID_T shift1, shift2;
PT_ID_T mask1, mask2;
prov_alloc_func alloc_func;
prov_free_func free_func;
} pt3;
typedef struct tag_pt2 {
void *extent;
PT_ID_T created;
PT_ID_T extent_size;
PT_ID_T block_size;
PT_ID_T log2_capacity;
PT_ID_T shift;
PT_ID_T mask;
prov_alloc_func alloc_func;
prov_free_func free_func;
} pt2;
/*
* pt_alloc API declaration
*/
/*
* Construction
*/
void
pt3_construct(pt3 *p, PT_ID_T extent_size, PT_ID_T block_size,
prov_alloc_func alloc_func, prov_free_func free_func);
void
pt2_construct(pt2 *p, PT_ID_T extent_size, PT_ID_T block_size,
prov_alloc_func alloc_func, prov_free_func free_func);
/*
* Destruction
*/
void
pt3_destroy(pt3 *p);
void
pt2_destroy(pt2 *p);
/*
* Allocation
*/
void *
pt3_alloc(pt3 *p, PT_ID_T *id);
void *
pt2_alloc(pt2 *p, PT_ID_T *id);
/*
* Restoration
*/
void *
pt3_get(pt3 *p, PT_ID_T id);
void *
pt2_get(pt2 *p, PT_ID_T id);
/*
* Getting number of allocated extents (of size extent_size each)
*/
PT_ID_T
pt3_extents_count(pt3 *p);
PT_ID_T
pt2_extents_count(pt2 *p);
#if defined(__cplusplus)
} /* extern "C" */
#endif /* defined(__cplusplus) */
......@@ -34,6 +34,8 @@ add_executable(slab_arena.test slab_arena.c)
target_link_libraries(slab_arena.test small)
add_executable(arena_mt.test arena_mt.c)
target_link_libraries(arena_mt.test small pthread)
add_executable(pt_alloc.test pt_alloc.cc)
target_link_libraries(pt_alloc.test small)
set(MSGPUCK_DIR ${PROJECT_SOURCE_DIR}/src/lib/msgpuck/)
add_executable(msgpack.test
......
#include "small/pt_alloc.h"
#include <set>
#include <vector>
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <iostream>
static void *
pta_alloc();
static void
pta_free(void *p);
#define PROV_BLOCK_SIZE 16
#define PROV_EXTENT_SIZE 64
static size_t AllocatedCount = 0;
static std::set<void*> AllocatedBlocks;
static std::set<void*> AllocatedItems;
static void
check_file_line(bool expr, const char *err_message, const char *file, int line)
{
if (!expr) {
std::cout << " ****************************************\n"
<< " * " << file << ":" << line
<< " ERROR: " << err_message << "\n";
}
assert(expr);
if (!expr) {
throw err_message;
}
}
#define check(e, m) check_file_line(e, m, __FILE__, __LINE__)
bool alloc_err_inj_enabled = false;
unsigned int alloc_err_inj_countdown = 0;
static void *
pta_alloc()
{
if (alloc_err_inj_enabled) {
if (alloc_err_inj_countdown == 0)
return 0;
alloc_err_inj_countdown--;
}
void *p = new char[PROV_EXTENT_SIZE];
AllocatedCount++;
AllocatedBlocks.insert(p);
return p;
}
static void
pta_free(void *p)
{
check(AllocatedBlocks.find(p) != AllocatedBlocks.end(), "Bad free");
AllocatedBlocks.erase(p);
delete [] static_cast<char *>(p);
AllocatedCount--;
}
void pt3_alloc_test()
{
std::cout << "Testing pt3_alloc..." << std::endl;
unsigned int maxCapacity = PROV_EXTENT_SIZE / PROV_BLOCK_SIZE;
maxCapacity *= PROV_EXTENT_SIZE / sizeof(void *);
maxCapacity *= PROV_EXTENT_SIZE / sizeof(void *);
std::cout << "pt3 capacity : " << maxCapacity << std::endl;
pt3 pta;
alloc_err_inj_enabled = false;
for (unsigned int i = 0; i <= maxCapacity; i++) {
pt3_construct(&pta, PROV_EXTENT_SIZE, PROV_BLOCK_SIZE, pta_alloc, pta_free);
check(1u << pta.log2_capacity == maxCapacity, "Wrong capacity!");
AllocatedItems.clear();
for (unsigned int j = 0; j < i; j++) {
unsigned int res = 0;
void *data = pt3_alloc(&pta, &res);
check(data, "Alloc returned NULL");
void *test_data = pt3_get(&pta, res);
check(data == test_data, "Alloc and Get mismatch");
size_t provConsumedMemory = (size_t)pt3_extents_count(&pta) * PROV_EXTENT_SIZE;
check(provConsumedMemory == AllocatedCount * PROV_EXTENT_SIZE, "ConsumedMemory counter failed (1)");
check(res == j, "Index mismatch");
{
check(!AllocatedBlocks.empty(), "Alloc w/o alloc!");
std::set<void*>::iterator itr = AllocatedBlocks.lower_bound(data);
if (itr == AllocatedBlocks.end() || *itr != data) {
check(itr != AllocatedBlocks.begin(), "Pointer to not allocatead region! (1)");
--itr;
}
check (itr != AllocatedBlocks.end(), "Pointer to not allocatead region! (2)");
check(data <= (void*)( ((char*)(*itr)) + PROV_EXTENT_SIZE - PROV_BLOCK_SIZE), "Pointer to not allocatead region! (3)");
}
{
if (!AllocatedItems.empty()) {
std::set<void*>::iterator itr = AllocatedItems.lower_bound(data);
if (itr != AllocatedItems.end()) {
check(*itr >= (void*)(((char*)data) + PROV_BLOCK_SIZE), "Data regions overlaps! (1)");
}
if (itr != AllocatedItems.begin()) {
--itr;
check(data >= (void*)(((char*)(*itr)) + PROV_BLOCK_SIZE), "Data regions overlaps! (2)");
}
}
}
AllocatedItems.insert(data);
}
size_t provConsumedMemory = (size_t)pt3_extents_count(&pta) * PROV_EXTENT_SIZE;
check(provConsumedMemory == AllocatedCount * PROV_EXTENT_SIZE, "ConsumedMemory counter failed (2)");
pt3_destroy(&pta);
check(AllocatedCount == 0, "Not all memory freeed");
}
alloc_err_inj_enabled = true;
for (unsigned int i = 0; i <= maxCapacity; i++) {
pt3_construct(&pta, PROV_EXTENT_SIZE, PROV_BLOCK_SIZE, pta_alloc, pta_free);
alloc_err_inj_countdown = i;
for (unsigned int j = 0; j < maxCapacity; j++) {
unsigned int res = 0;
unsigned int prev_created = pta.created;
void *data = pt3_alloc(&pta, &res);
if (!data) {
check(prev_created == pta.created, "Created count changed during memory fail!");
break;
}
}
pt3_destroy(&pta);
check(AllocatedCount == 0, "Not all memory freeed after memory fail!");
}
std::cout << "Testing pt3_alloc successfully finished" << std::endl;
}
void pt2_alloc_test()
{
std::cout << "Testing pt2_alloc..." << std::endl;
unsigned int maxCapacity = PROV_EXTENT_SIZE / PROV_BLOCK_SIZE;
maxCapacity *= PROV_EXTENT_SIZE / sizeof(void *);
std::cout << "pt2 capacity : " << maxCapacity << std::endl;
pt2 pta;
alloc_err_inj_enabled = false;
for (unsigned int i = 0; i <= maxCapacity; i++) {
pt2_construct(&pta, PROV_EXTENT_SIZE, PROV_BLOCK_SIZE, pta_alloc, pta_free);
check(1u << pta.log2_capacity == maxCapacity, "Wrong capacity!");
AllocatedItems.clear();
for (unsigned int j = 0; j < i; j++) {
unsigned int res = 0;
void *data = pt2_alloc(&pta, &res);
check(data, "Alloc returned NULL");
void *test_data = pt2_get(&pta, res);
check(data == test_data, "Alloc and Get mismatch");
size_t provConsumedMemory = (size_t)pt2_extents_count(&pta) * PROV_EXTENT_SIZE;
check(provConsumedMemory == AllocatedCount * PROV_EXTENT_SIZE, "ConsumedMemory counter failed (1)");
check(res == j, "Index mismatch");
{
check(!AllocatedBlocks.empty(), "Alloc w/o alloc!");
std::set<void*>::iterator itr = AllocatedBlocks.lower_bound(data);
if (itr == AllocatedBlocks.end() || *itr != data) {
check(itr != AllocatedBlocks.begin(), "Pointer to not allocatead region! (1)");
--itr;
}
check (itr != AllocatedBlocks.end(), "Pointer to not allocatead region! (2)");
check(data <= (void*)( ((char*)(*itr)) + PROV_EXTENT_SIZE - PROV_BLOCK_SIZE), "Pointer to not allocatead region! (3)");
}
{
if (!AllocatedItems.empty()) {
std::set<void*>::iterator itr = AllocatedItems.lower_bound(data);
if (itr != AllocatedItems.end()) {
check(*itr >= (void*)(((char*)data) + PROV_BLOCK_SIZE), "Data regions overlaps! (1)");
}
if (itr != AllocatedItems.begin()) {
--itr;
check(data >= (void*)(((char*)(*itr)) + PROV_BLOCK_SIZE), "Data regions overlaps! (2)");
}
}
}
AllocatedItems.insert(data);
}
size_t provConsumedMemory = (size_t)pt2_extents_count(&pta) * PROV_EXTENT_SIZE;
check(provConsumedMemory == AllocatedCount * PROV_EXTENT_SIZE, "ConsumedMemory counter failed (2)");
pt2_destroy(&pta);
check(AllocatedCount == 0, "Not all memory freeed");
}
alloc_err_inj_enabled = true;
for (unsigned int i = 0; i <= maxCapacity; i++) {
pt2_construct(&pta, PROV_EXTENT_SIZE, PROV_BLOCK_SIZE, pta_alloc, pta_free);
alloc_err_inj_countdown = i;
for (unsigned int j = 0; j < maxCapacity; j++) {
unsigned int res = 0;
unsigned int prev_created = pta.created;
void *data = pt2_alloc(&pta, &res);
if (!data) {
check(prev_created == pta.created, "Created count changed during memory fail!");
break;
}
}
pt2_destroy(&pta);
check(AllocatedCount == 0, "Not all memory freeed after memory fail!");
}
std::cout << "Testing pt2_alloc successfully finished" << std::endl;
}
int
main(int, const char **)
{
pt2_alloc_test();
pt3_alloc_test();
}
\ No newline at end of file
Testing pt2_alloc...
pt2 capacity : 32
Testing pt2_alloc successfully finished
Testing pt3_alloc...
pt3 capacity : 256
Testing pt3_alloc successfully finished
Subproject commit 4fba08a9aca4ce99a6a51f5faca2e1e091ad1422
Subproject commit 880ca300e8fb7b432b9d25ed377db2102e4cb63d
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment