ClickHouse/dbms/include/DB/Common/AutoArray.h
2015-09-29 22:19:54 +03:00

263 lines
6.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <cstddef>
#include <common/likely.h>
namespace DB
{
/** Массив (почти) неизменяемого размера:
* размер задаётся в конструкторе;
* метод resize приводит к удалению старых данных и нужен лишь для того,
* чтобы можно было сначала создать пустой объект, используя конструктор по-умолчанию,
* а потом уже определиться с размером.
*
* Есть возможность не инициализировать элементы по-умолчанию, а создавать их inplace.
* Деструкторы элементов вызываются автоматически.
*
* sizeof равен размеру одного указателя.
*
* Не exception-safe.
* Копирование не поддерживается. Перемещение опустошает исходный объект.
* То есть, использовать этот массив во многих случаях неудобно.
*
* Предназначен для ситуаций, в которых создаётся много массивов одинакового небольшого размера,
* но при этом размер не известен во время компиляции.
* Также даёт существенное преимущество в случаях, когда важно, чтобы sizeof был минимальным.
* Например, если массивы кладутся в open-addressing хэш-таблицу с inplace хранением значений (как HashMap)
*
* В этом случае, по сравнению с std::vector:
* - для массивов размером в 1 элемент - преимущество примерно в 2 раза;
* - для массивов размером в 5 элементов - преимущество примерно в 1.5 раза
* (в качестве T использовались DB::Field, содержащие UInt64 и String);
*/
const size_t empty_auto_array_helper = 0;
struct DontInitElemsTag {};
template <typename T>
class AutoArray
{
public:
/// Для отложенного создания.
AutoArray()
{
setEmpty();
}
AutoArray(size_t size_)
{
init(size_, false);
}
/** Не будут вызваны конструкторы по-умолчанию для элементов.
* В этом случае, вы должны вставить все элементы с помощью функции place и placement new,
* так как для них потом будут вызваны деструкторы.
*/
AutoArray(size_t size_, const DontInitElemsTag & tag)
{
init(size_, true);
}
/** Инициализирует все элементы копирующим конструктором с параметром value.
*/
AutoArray(size_t size_, const T & value)
{
init(size_, true);
for (size_t i = 0; i < size_; ++i)
{
new (place(i)) T(value);
}
}
/** resize удаляет все существующие элементы.
*/
void resize(size_t size_, bool dont_init_elems = false)
{
uninit();
init(size_, dont_init_elems);
}
/** Премещение.
*/
AutoArray(AutoArray && src)
{
if (this == &src)
return;
setEmpty();
data = src.data;
src.setEmpty();
}
AutoArray & operator= (AutoArray && src)
{
if (this == &src)
return *this;
uninit();
data = src.data;
src.setEmpty();
return *this;
}
~AutoArray()
{
uninit();
}
size_t size() const
{
return m_size();
}
bool empty() const
{
return size() == 0;
}
void clear()
{
uninit();
setEmpty();
}
/** Можно читать и модифицировать элементы с помощью оператора []
* только если элементы были инициализированы
* (то есть, в конструктор не был передан DontInitElemsTag,
* или вы их инициализировали с помощью place и placement new).
*/
T & operator[](size_t i)
{
return elem(i);
}
const T & operator[](size_t i) const
{
return elem(i);
}
/** Получить кусок памяти, в котором должен быть расположен элемент.
* Функция предназначена, чтобы инициализировать элемент,
* который ещё не был инициализирован:
* new (arr.place(i)) T(args);
*/
char * place(size_t i)
{
return data + sizeof(T) * i;
}
typedef T * iterator;
typedef const T * const_iterator;
iterator begin() { return &elem(0); }
iterator end() { return &elem(size()); }
const_iterator begin() const { return &elem(0); }
const_iterator end() const { return &elem(size()); }
bool operator== (const AutoArray<T> & rhs) const
{
size_t s = size();
if (s != rhs.size())
return false;
for (size_t i = 0; i < s; ++i)
if (elem(i) != rhs.elem(i))
return false;
return true;
}
bool operator!= (const AutoArray<T> & rhs) const
{
return !(*this == rhs);
}
bool operator< (const AutoArray<T> & rhs) const
{
size_t s = size();
size_t rhs_s = rhs.size();
if (s < rhs_s)
return true;
if (s > rhs_s)
return false;
for (size_t i = 0; i < s; ++i)
{
if (elem(i) < rhs.elem(i))
return true;
if (elem(i) > rhs.elem(i))
return false;
}
return false;
}
private:
char * data;
size_t & m_size()
{
return reinterpret_cast<size_t *>(data)[-1];
}
size_t m_size() const
{
return reinterpret_cast<const size_t *>(data)[-1];
}
T & elem(size_t i)
{
return reinterpret_cast<T *>(data)[i];
}
const T & elem(size_t i) const
{
return reinterpret_cast<const T *>(data)[i];
}
void setEmpty()
{
data = const_cast<char *>(reinterpret_cast<const char *>(&empty_auto_array_helper)) + sizeof(size_t);
}
void init(size_t size_, bool dont_init_elems)
{
if (!size_)
{
setEmpty();
return;
}
data = new char[size_ * sizeof(T) + sizeof(size_t)];
data += sizeof(size_t);
m_size() = size_;
if (!dont_init_elems)
for (size_t i = 0; i < size_; ++i)
new (place(i)) T();
}
void uninit()
{
size_t s = size();
if (likely(s))
{
for (size_t i = 0; i < s; ++i)
elem(i).~T();
data -= sizeof(size_t);
delete[] data;
}
}
};
}