File fixed_size_allocator.hpp¶
File List > base > fixed_size_allocator.hpp
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _FIXED_SIZE_ALLOCATOR_HPP_
#define _FIXED_SIZE_ALLOCATOR_HPP_
#include "thread_compat.hpp"
#include "memory_private_allocator.hpp"
namespace cubmem
{
namespace fixed_size_alloc
{
template <typename T>
using private_vector = std::vector<T, private_allocator<T>>;
constexpr size_t align_size = 16;
constexpr size_t allocate_node_unit = 256;
template <typename T, bool is_private>
class allocator
{
};
template <typename T>
struct node
{
node<T> *m_next;
alignas (align_size) char m_data[sizeof (T)];
static constexpr size_t get_size()
{
constexpr size_t size = sizeof (node<T> *) + sizeof (T);
return (size + align_size-1) & ~static_cast<size_t> (align_size-1);
}
static node<T> *from_data (void *data_ptr)
{
return reinterpret_cast<node<T> *> (
reinterpret_cast<char *> (data_ptr) - offsetof (node<T>, m_data)
);
}
};
template <typename T>
struct block
{
node<T> nodes[allocate_node_unit];
};
template <typename T>
class allocator<T, false>
{
public:
allocator();
~allocator();
void *allocate ();
void deallocate (void *ptr);
void expand();
private:
std::vector<std::unique_ptr<block<T>>> m_blocks;
node<T> *m_free_head;
};
template <typename T>
class allocator<T, true>
{
public:
allocator (THREAD_ENTRY *thread_p);
~allocator();
void *allocate ();
void deallocate (void *ptr);
void expand();
private:
THREAD_ENTRY *m_thread_p;
private_allocator<block<T>> m_allocator;
std::vector<std::shared_ptr<block<T>>> m_blocks;
node<T> *m_free_head;
};
template <typename T>
allocator<T, false>::allocator()
: m_blocks ()
, m_free_head (nullptr)
{
}
template <typename T>
allocator<T, false>::~allocator()
{
m_blocks.clear();
m_free_head = nullptr;
}
template <typename T>
void allocator<T, false>::expand()
{
size_t old_size = m_blocks.size(), new_size = 0;
if (old_size == 0)
{
new_size = 1;
}
else
{
new_size = old_size * 2;
}
size_t blocks_to_add = new_size - old_size;
m_blocks.reserve (new_size);
node<T> *prev_node = nullptr;
node<T> *new_free_head = nullptr;
for (size_t i = 0; i < blocks_to_add; i++)
{
m_blocks.push_back (std::make_unique<block<T>>());
for (node<T> &node : m_blocks.back()->nodes)
{
if (new_free_head == nullptr)
{
new_free_head = &node;
}
if (prev_node == nullptr)
{
prev_node = &node;
}
else
{
prev_node->m_next = &node;
prev_node = &node;
}
}
}
/* last one */
if (prev_node != nullptr)
{
prev_node->m_next = nullptr;
}
assert (m_free_head == nullptr);
m_free_head = new_free_head;
}
template <typename T>
void *allocator<T, false>::allocate()
{
if (m_free_head == nullptr)
{
expand();
}
assert (m_free_head != nullptr);
node<T> *node = m_free_head;
m_free_head = m_free_head->m_next;
return static_cast<void *> (node->m_data);
}
template <typename T>
void allocator<T, false>::deallocate (void *ptr)
{
assert (ptr != nullptr);
node<T> *freed_node = node<T>::from_data (ptr);
freed_node->m_next = m_free_head;
m_free_head = freed_node;
}
template <typename T>
allocator<T, true>::allocator (THREAD_ENTRY *thread_p)
: m_thread_p (thread_p)
, m_allocator (thread_p)
, m_blocks ()
, m_free_head (nullptr)
{
}
template <typename T>
allocator<T, true>::~allocator()
{
m_blocks.clear();
m_free_head = nullptr;
}
template <typename T>
void allocator<T, true>::expand()
{
size_t old_size = m_blocks.size(), new_size = 0;
if (old_size == 0)
{
new_size = 1;
}
else
{
new_size = old_size * 2;
}
size_t blocks_to_add = new_size - old_size;
m_blocks.reserve (new_size);
node<T> *prev_node = nullptr;
node<T> *new_free_head = nullptr;
for (size_t i = 0; i < blocks_to_add; i++)
{
void *raw_mem = m_allocator.allocate (1);
auto deleter = [alloc = &m_allocator] (block<T> *ptr)
{
ptr->~block();
alloc->deallocate (ptr);
};
m_blocks.push_back (std::shared_ptr<block<T>> (new (raw_mem) block<T>(), deleter));
for (node<T> &node : m_blocks.back()->nodes)
{
if (new_free_head == nullptr)
{
new_free_head = &node;
}
if (prev_node == nullptr)
{
prev_node = &node;
}
else
{
prev_node->m_next = &node;
prev_node = &node;
}
}
}
/* last one */
if (prev_node != nullptr)
{
prev_node->m_next = m_free_head;
}
m_free_head = new_free_head;
}
template <typename T>
void *allocator<T, true>::allocate()
{
if (m_free_head == nullptr)
{
expand();
}
assert (m_free_head != nullptr);
node<T> *node = m_free_head;
m_free_head = m_free_head->m_next;
return static_cast<void *> (node->m_data);
}
template <typename T>
void allocator<T, true>::deallocate (void *ptr)
{
assert (ptr != nullptr);
node<T> *freed_node = node<T>::from_data (ptr);
freed_node->m_next = m_free_head;
m_free_head = freed_node;
}
}
}
#endif