diff --git a/Makefile.am b/Makefile.am index 2133a7a76..6a3eb6075 100644 --- a/Makefile.am +++ b/Makefile.am @@ -396,6 +396,7 @@ libutil_a_SOURCES = \ src/util/Clamp.hxx \ src/util/DeleteDisposer.hxx \ src/util/Alloc.cxx src/util/Alloc.hxx \ + src/util/AllocatedArray.hxx \ src/util/VarSize.hxx \ src/util/ScopeExit.hxx \ src/util/Error.cxx src/util/Error.hxx \ diff --git a/src/util/AllocatedArray.hxx b/src/util/AllocatedArray.hxx new file mode 100644 index 000000000..b682cbeb0 --- /dev/null +++ b/src/util/AllocatedArray.hxx @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010-2016 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALLOCATED_ARRAY_HXX +#define ALLOCATED_ARRAY_HXX + +#include "WritableBuffer.hxx" +#include "Compiler.h" + +#include + +#include + +/** + * An array allocated on the heap with a length determined at runtime. + */ +template +class AllocatedArray { + typedef WritableBuffer Buffer; + +public: + typedef typename Buffer::size_type size_type; + typedef typename Buffer::reference_type reference_type; + typedef typename Buffer::const_reference_type const_reference_type; + typedef typename Buffer::iterator iterator; + typedef typename Buffer::const_iterator const_iterator; + +protected: + Buffer buffer{nullptr}; + +public: + constexpr AllocatedArray() = default; + + explicit AllocatedArray(size_type _size) + :buffer{new T[_size], _size} { + assert(size() == 0 || buffer.data != nullptr); + } + + explicit AllocatedArray(const AllocatedArray &other) + :buffer{new T[other.buffer.size], other.buffer.size} { + assert(size() == 0 || buffer.data != nullptr); + assert(other.size() == 0 || other.buffer.data != nullptr); + + std::copy_n(other.buffer.data, buffer.size, buffer.data); + } + + AllocatedArray(AllocatedArray &&other) + :buffer(other.buffer) { + other.buffer = Buffer::Null(); + } + + ~AllocatedArray() { + delete[] buffer.data; + } + + AllocatedArray &operator=(const AllocatedArray &other) { + assert(size() == 0 || buffer.data != nullptr); + assert(other.size() == 0 || other.buffer.data != nullptr); + + if (&other == this) + return *this; + + ResizeDiscard(other.size()); + std::copy_n(other.buffer.data, other.buffer.size, buffer.data); + return *this; + } + + AllocatedArray &operator=(AllocatedArray &&other) { + std::swap(buffer, other.buffer); + return *this; + } + + constexpr bool IsNull() const { + return buffer.IsNull(); + } + + /** + * Returns true if no memory was allocated so far. + */ + constexpr bool empty() const { + return buffer.IsEmpty(); + } + + /** + * Returns the number of allocated elements. + */ + constexpr size_type size() const { + return buffer.size; + } + + reference_type front() { + return buffer.front(); + } + + const_reference_type front() const { + return buffer.front(); + } + + reference_type back() { + return buffer.back(); + } + + const_reference_type back() const { + return buffer.back(); + } + + /** + * Returns one element. No bounds checking. + */ + reference_type operator[](size_type i) { + assert(i < size()); + + return buffer.data[i]; + } + + /** + * Returns one constant element. No bounds checking. + */ + const_reference_type operator[](size_type i) const { + assert(i < size()); + + return buffer.data[i]; + } + + iterator begin() { + return buffer.begin(); + } + + constexpr const_iterator begin() const { + return buffer.cbegin(); + } + + iterator end() { + return buffer.end(); + } + + constexpr const_iterator end() const { + return buffer.cend(); + } + + /** + * Resizes the array, discarding old data. + */ + void ResizeDiscard(size_type _size) { + if (_size == buffer.size) + return; + + delete[] buffer.data; + buffer.size = _size; + buffer.data = new T[buffer.size]; + + assert(size() == 0 || buffer.data != nullptr); + } + + /** + * Grows the array to the specified size, discarding old data. + * Similar to ResizeDiscard(), but will never shrink the array to + * avoid expensive heap operations. + */ + void GrowDiscard(size_type _size) { + if (_size > buffer.size) + ResizeDiscard(_size); + } + + /** + * Grows the array to the specified size, preserving the value of a + * range of elements, starting from the beginning. + */ + void GrowPreserve(size_type _size, size_type preserve) { + if (_size <= buffer.size) + return; + + T *new_data = new T[_size]; + assert(_size == 0 || new_data != nullptr); + + std::move(buffer.data, buffer.data + preserve, new_data); + + delete[] buffer.data; + buffer.data = new_data; + buffer.size = _size; + } + + /** + * Declare that the buffer has the specified size. Must not be + * larger than the current size. Excess elements are not used (but + * they are still allocated). + */ + void SetSize(size_type _size) { + assert(_size <= buffer.size); + + buffer.size = _size; + } +}; + +#endif