AllocatedArray.hxx 5.93 KB
Newer Older
1
/*
2
 * Copyright 2010-2019 Max Kellermann <max.kellermann@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * 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

33
#include "ConstBuffer.hxx"
34 35 36 37
#include "WritableBuffer.hxx"
#include "Compiler.h"

#include <algorithm>
38
#include <cassert>
39 40 41 42 43 44 45 46 47

/**
 * An array allocated on the heap with a length determined at runtime.
 */
template<class T>
class AllocatedArray {
	typedef WritableBuffer<T> Buffer;

public:
48 49 50 51 52
	using size_type = typename Buffer::size_type;
	using reference = typename Buffer::reference;
	using const_reference = typename Buffer::const_reference;
	using iterator = typename Buffer::iterator;
	using const_iterator = typename Buffer::const_iterator;
53 54 55 56 57 58 59

protected:
	Buffer buffer{nullptr};

public:
	constexpr AllocatedArray() = default;

60
	explicit AllocatedArray(size_type _size) noexcept
61
		:buffer{new T[_size], _size} {}
62

63 64
	explicit AllocatedArray(ConstBuffer<T> src) noexcept {
		if (src == nullptr)
65 66
			return;

67 68
		buffer = {new T[src.size], src.size};
		std::copy_n(src.data, src.size, buffer.data);
69 70
	}

71 72 73
	explicit AllocatedArray(const AllocatedArray &other) noexcept
		:AllocatedArray(other.buffer) {}

74
	AllocatedArray(AllocatedArray &&other) noexcept
75
		:buffer(std::exchange(other.buffer, nullptr)) {}
76

77
	~AllocatedArray() noexcept {
78 79 80
		delete[] buffer.data;
	}

81 82 83 84 85 86 87 88 89
	AllocatedArray &operator=(ConstBuffer<T> src) noexcept {
		assert(size() == 0 || buffer.data != nullptr);
		assert(src.size == 0 || src.data != nullptr);

		ResizeDiscard(src.size);
		std::copy_n(src.data, src.size, buffer.data);
		return *this;
	}

90
	AllocatedArray &operator=(const AllocatedArray &other) noexcept {
91 92 93 94 95 96 97 98 99 100 101
		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;
	}

102
	AllocatedArray &operator=(AllocatedArray &&other) noexcept {
103 104
		using std::swap;
		swap(buffer, other.buffer);
105 106 107
		return *this;
	}

108 109 110 111 112 113 114 115
	operator ConstBuffer<T>() const noexcept {
		return buffer;
	}

	operator WritableBuffer<T>() noexcept {
		return buffer;
	}

116
	constexpr bool IsNull() const noexcept {
117 118 119
		return buffer.IsNull();
	}

120
	constexpr bool operator==(std::nullptr_t) const noexcept {
121 122 123
		return buffer == nullptr;
	}

124
	constexpr bool operator!=(std::nullptr_t) const noexcept {
125 126 127
		return buffer != nullptr;
	}

128 129 130
	/**
	 * Returns true if no memory was allocated so far.
	 */
131
	constexpr bool empty() const noexcept {
132
		return buffer.empty();
133 134 135 136 137
	}

	/**
	 * Returns the number of allocated elements.
	 */
138
	constexpr size_type size() const noexcept {
139 140 141
		return buffer.size;
	}

142 143 144 145 146 147 148
	/**
	 * Returns the number of allocated elements.
	 */
	constexpr size_type capacity() const noexcept {
		return buffer.size;
	}

149
	reference front() noexcept {
150 151 152
		return buffer.front();
	}

153
	const_reference front() const noexcept {
154 155 156
		return buffer.front();
	}

157
	reference back() noexcept {
158 159 160
		return buffer.back();
	}

161
	const_reference back() const noexcept {
162 163 164 165 166 167
		return buffer.back();
	}

	/**
	 * Returns one element.  No bounds checking.
	 */
168
	reference operator[](size_type i) noexcept {
169
		return buffer[i];
170 171 172 173 174
	}

	/**
	 * Returns one constant element.  No bounds checking.
	 */
175
	const_reference operator[](size_type i) const noexcept {
176
		return buffer[i];
177 178
	}

179
	iterator begin() noexcept {
180 181 182
		return buffer.begin();
	}

183
	constexpr const_iterator begin() const noexcept {
184 185 186
		return buffer.cbegin();
	}

187
	iterator end() noexcept {
188 189 190
		return buffer.end();
	}

191
	constexpr const_iterator end() const noexcept {
192 193 194 195 196 197
		return buffer.cend();
	}

	/**
	 * Resizes the array, discarding old data.
	 */
198
	void ResizeDiscard(size_type _size) noexcept {
199 200 201 202 203 204 205 206 207 208 209 210 211
		if (_size == buffer.size)
			return;

		delete[] buffer.data;
		buffer.size = _size;
		buffer.data = new T[buffer.size];
	}

	/**
	 * Grows the array to the specified size, discarding old data.
	 * Similar to ResizeDiscard(), but will never shrink the array to
	 * avoid expensive heap operations.
	 */
212
	void GrowDiscard(size_type _size) noexcept {
213 214 215 216 217 218 219 220
		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.
	 */
221
	void GrowPreserve(size_type _size, size_type preserve) noexcept {
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
		if (_size <= buffer.size)
			return;

		T *new_data = new T[_size];

		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).
	 */
239
	void SetSize(size_type _size) noexcept {
240 241 242 243 244 245 246
		assert(_size <= buffer.size);

		buffer.size = _size;
	}
};

#endif