#ifndef BMCALLOC__H__INCLUDED__
#define BMCALLOC__H__INCLUDED__
#include <stdlib.h>
#include "try_throw_catch.h"
#include "bmfunc.h"
namespace libbm
{
#if defined(BMSSE2OPT) || defined(BMSSE42OPT)
#define BM_ALLOC_ALIGN 16
#endif
#if defined(BMAVX2OPT)
#define BM_ALLOC_ALIGN 32
#endif
class block_allocator
{
public:
static bm::word_t* allocate(size_t n, const void *)
{
bm::word_t* ptr;
#if defined(BM_ALLOC_ALIGN)
#ifdef _MSC_VER
ptr = (bm::word_t*) ::_aligned_malloc(n * sizeof(bm::word_t), BM_ALLOC_ALIGN);
#else
ptr = (bm::word_t*) ::_mm_malloc(n * sizeof(bm::word_t), BM_ALLOC_ALIGN);
#endif
#else
ptr = (bm::word_t*) ::malloc(n * sizeof(bm::word_t));
#endif
if (!ptr)
{
BM_THROW( BM_ERR_BADALLOC );
}
return ptr;
}
static void deallocate(bm::word_t* p, size_t)
{
#ifdef BM_ALLOC_ALIGN
# ifdef _MSC_VER
::_aligned_free(p);
#else
::_mm_free(p);
# endif
#else
::free(p);
#endif
}
};
class ptr_allocator
{
public:
static void* allocate(size_t n, const void *)
{
void* ptr = ::malloc(n * sizeof(void*));
if (!ptr)
{
BM_THROW( BM_ERR_BADALLOC );
}
return ptr;
}
static void deallocate(void* p, size_t)
{
::free(p);
}
};
class pointer_pool_array
{
public:
enum params
{
n_pool_max_size = BM_DEFAULT_POOL_SIZE
};
pointer_pool_array() : size_(0)
{
allocate_pool(n_pool_max_size);
}
pointer_pool_array(const pointer_pool_array&) = delete;
pointer_pool_array& operator=(const pointer_pool_array&) = delete;
~pointer_pool_array()
{
BM_ASSERT(size_ == 0); free_pool();
}
unsigned push(void* ptr)
{
if (size_ == n_pool_max_size - 1)
return 0;
pool_ptr_[size_++] = ptr;
return size_;
}
void* pop()
{
if (size_ == 0)
return 0;
return pool_ptr_[--size_];
}
private:
void allocate_pool(size_t pool_size)
{
pool_ptr_ = (void**)::malloc(sizeof(void*) * pool_size);
if (!pool_ptr_)
BM_THROW(BM_ERR_BADALLOC);
}
void free_pool()
{
::free(pool_ptr_);
}
private:
void** pool_ptr_; unsigned size_; };
template<class BA, class PA>
class alloc_pool
{
public:
typedef BA block_allocator_type;
typedef PA ptr_allocator_type;
public:
alloc_pool() {}
~alloc_pool()
{
free_pools();
}
bm::word_t* alloc_bit_block()
{
bm::word_t* ptr = (bm::word_t*)block_pool_.pop();
if (ptr == 0)
ptr = block_alloc_.allocate(bm::set_block_size, 0);
return ptr;
}
void free_bit_block(bm::word_t* block)
{
BM_ASSERT(IS_VALID_ADDR(block));
if (!block_pool_.push(block))
{
block_alloc_.deallocate(block, bm::set_block_size);
}
}
void free_pools()
{
bm::word_t* block;
do
{
block = (bm::word_t*)block_pool_.pop();
if (block)
block_alloc_.deallocate(block, bm::set_block_size);
} while (block);
}
protected:
pointer_pool_array block_pool_;
BA block_alloc_;
};
template<class BA, class PA, class APool>
class mem_alloc
{
public:
typedef BA block_allocator_type;
typedef PA ptr_allocator_type;
typedef APool allocator_pool_type;
public:
mem_alloc(const BA& block_alloc = BA(), const PA& ptr_alloc = PA())
: block_alloc_(block_alloc),
ptr_alloc_(ptr_alloc),
alloc_pool_p_(0)
{}
mem_alloc(const mem_alloc& ma)
: block_alloc_(ma.block_alloc_),
ptr_alloc_(ma.ptr_alloc_),
alloc_pool_p_(0) {}
mem_alloc& operator=(const mem_alloc& ma)
{
block_alloc_ = ma.block_alloc_;
ptr_alloc_ = ma.ptr_alloc_;
return *this;
}
block_allocator_type get_block_allocator() const
{
return BA(block_alloc_);
}
ptr_allocator_type get_ptr_allocator() const
{
return PA(block_alloc_);
}
void set_pool(allocator_pool_type* pool) BMNOEXCEPT
{
alloc_pool_p_ = pool;
}
allocator_pool_type* get_pool() BMNOEXCEPT
{
return alloc_pool_p_;
}
bm::word_t* alloc_bit_block(unsigned alloc_factor = 1)
{
if (alloc_pool_p_ && alloc_factor == 1)
return alloc_pool_p_->alloc_bit_block();
return block_alloc_.allocate(bm::set_block_size * alloc_factor, 0);
}
void free_bit_block(bm::word_t* block, unsigned alloc_factor = 1) BMNOEXCEPT
{
BM_ASSERT(IS_VALID_ADDR(block));
if (alloc_pool_p_ && alloc_factor == 1)
alloc_pool_p_->free_bit_block(block);
else
block_alloc_.deallocate(block, bm::set_block_size * alloc_factor);
}
bm::gap_word_t* alloc_gap_block(unsigned level,
const bm::gap_word_t* glevel_len)
{
BM_ASSERT(level < bm::gap_levels);
unsigned len =
(unsigned)(glevel_len[level] / (sizeof(bm::word_t) / sizeof(bm::gap_word_t)));
return (bm::gap_word_t*)block_alloc_.allocate(len, 0);
}
void free_gap_block(bm::gap_word_t* block,
const bm::gap_word_t* glevel_len)
{
BM_ASSERT(IS_VALID_ADDR((bm::word_t*)block));
unsigned len = bm::gap_capacity(block, glevel_len);
len /= (unsigned)(sizeof(bm::word_t) / sizeof(bm::gap_word_t));
block_alloc_.deallocate((bm::word_t*)block, len);
}
void* alloc_ptr(unsigned size)
{
return ptr_alloc_.allocate(size, 0);
}
void free_ptr(void* p, unsigned size) BMNOEXCEPT
{
if (p)
ptr_alloc_.deallocate(p, size);
}
BA& get_block_alloc() BMNOEXCEPT { return block_alloc_; }
public:
BA block_alloc_;
PA ptr_alloc_;
allocator_pool_type* alloc_pool_p_;
};
typedef libbm::alloc_pool<block_allocator, ptr_allocator> standard_alloc_pool;
typedef libbm::mem_alloc<block_allocator, ptr_allocator, standard_alloc_pool> standard_allocator;
inline
void* aligned_new_malloc(size_t size)
{
void* ptr;
#ifdef BM_ALLOC_ALIGN
#ifdef _MSC_VER
ptr = ::_aligned_malloc(size, BM_ALLOC_ALIGN);
#else
ptr = ::_mm_malloc(size, BM_ALLOC_ALIGN);
#endif
#else
ptr = ::malloc(size);
#endif
if (!ptr)
{
#ifndef BM_NO_STL
throw std::bad_alloc();
#else
BM_THROW(BM_ERR_BADALLOC);
#endif
}
return ptr;
}
inline
void aligned_free(void* ptr)
{
if (!ptr)
return;
#ifdef BM_ALLOC_ALIGN
# ifdef _MSC_VER
::_aligned_free(ptr);
#else
::_mm_free(ptr);
# endif
#else
::free(ptr);
#endif
}
}
#endif