#pragma once #include #include namespace DB { /** Массив (почти) неизменяемого размера: * размер задаётся в конструкторе; * метод resize приводит к удалению старых данных и нужен лишь для того, * чтобы можно было сначала создать пустой объект, используя конструктор по-умолчанию, * а потом уже определиться с размером. * * Есть возможность не инициализировать элементы по-умолчанию, а создавать их inplace. * Деструкторы элементов вызываются автоматически. * * sizeof равен размеру одного указателя. * * Не exception-safe. * Копирование и присваивание разрушающее: исходный объект становится пустым. * То есть, использовать этот массив во многих случаях неудобно. * * Предназначен для ситуаций, в которых создаётся много массивов одинакового небольшого размера, * но при этом размер не известен во время компиляции. * Также даёт существенное преимущество в случаях, когда важно, чтобы sizeof был минимальным. * Например, если массивы кладутся в open-addressing хэш-таблицу с inplace хранением значений (как DB::HashMap) * * В этом случае, по сравнению с std::vector: * - для массивов размером в 1 элемент - преимущество примерно в 2 раза; * - для массивов размером в 5 элементов - преимущество примерно в 1.5 раза * (в качестве T использовались DB::Field, содержащие UInt64 и String); */ const size_t empty_auto_array_helper = 0; struct DontInitElemsTag {}; template 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(const AutoArray & src) { //std::cerr << this << " AutoArray(const AutoArray & src)" << std::endl; setEmpty(); data = src.data; const_cast &>(src).setEmpty(); } AutoArray & operator= (const AutoArray & src) { //std::cerr << this << " operator=(const AutoArray & src)" << std::endl; uninit(); data = src.data; const_cast &>(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 & 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 & rhs) const { return !(*this == rhs); } bool operator< (const AutoArray & 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(data)[-1]; } size_t m_size() const { return reinterpret_cast(data)[-1]; } T & elem(size_t i) { return reinterpret_cast(data)[i]; } const T & elem(size_t i) const { return reinterpret_cast(data)[i]; } void setEmpty() { data = const_cast(reinterpret_cast(&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; } } }; }