1 /*
2  * This file is part of liblcf. Copyright (c) 2021 liblcf authors.
3  * https://github.com/EasyRPG/liblcf - https://easyrpg.org
4  *
5  * liblcf is Free/Libre Open Source Software, released under the MIT License.
6  * For the full copyright and license information, please view the COPYING
7  * file that was distributed with this source code.
8  */
9 
10 #ifndef LCF_DBARRAY_H
11 #define LCF_DBARRAY_H
12 #include <utility>
13 #include <string>
14 #include <iterator>
15 #include <cstdint>
16 #include <limits>
17 #include <algorithm>
18 #include <type_traits>
19 
20 #include "lcf/dbarrayalloc.h"
21 #include "lcf/scope_guard.h"
22 
23 namespace lcf {
24 
25 // An array data structure optimized for database storage.
26 // Low memory footprint and not dynamically resizable.
27 template <typename T>
28 class DBArray {
29 	public:
30 		using value_type = T;
31 		using size_type = DBArrayAlloc::size_type;
32 		using ssize_type = typename std::make_signed<size_type>::type;
33 
34 		using iterator = T*;
35 		using const_iterator = const T*;
36 		using reverse_iterator = std::reverse_iterator<iterator>;
37 		using const_reverse_iterator = std::reverse_iterator<const_iterator>;
38 
39 		constexpr DBArray() = default;
DBArray(size_type count)40 		explicit DBArray(size_type count) {
41 			construct(count, [](void* p) { new (p) T(); });
42 		}
43 
DBArray(size_type count,const T & value)44 		explicit DBArray(size_type count, const T& value) {
45 			construct(count, [&](void* p) { new (p) T(value); });
46 		}
47 
48 		template <typename Iter,
49 				 typename std::enable_if<
50 					 std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<Iter>::iterator_category>::value, int>::type = 0
51 				 >
DBArray(Iter first,Iter last)52 		DBArray(Iter first, Iter last) {
53 			construct(static_cast<size_type>(std::distance(first, last)), [&](void* p) { new (p) T(*first); ++first; });
54 		}
55 
DBArray(std::initializer_list<T> ilist)56 		DBArray(std::initializer_list<T> ilist)
57 			: DBArray(ilist.begin(), ilist.end()) {}
58 
DBArray(const DBArray & o)59 		DBArray(const DBArray& o) : DBArray(o.begin(), o.end()) {}
DBArray(DBArray && o)60 		DBArray(DBArray&& o) noexcept { swap(o); }
61 
62 		DBArray& operator=(const DBArray&);
63 		DBArray& operator=(DBArray&& o) noexcept;
64 
swap(DBArray & o)65 		void swap(DBArray& o) noexcept {
66 			std::swap(_storage, o._storage);
67 		}
68 
~DBArray()69 		~DBArray() { destroy(); }
70 
71 		T& operator[](size_type i) { return data()[i]; }
72 		const T& operator[](size_type i) const { return data()[i]; }
73 
front()74 		T& front() { return (*this)[0]; }
front()75 		const T& front() const { return (*this)[0]; }
76 
back()77 		T& back() { return (*this)[size()-1]; }
back()78 		const T& back() const { return (*this)[size()-1]; }
79 
data()80 		T* data() { return reinterpret_cast<T*>(_storage); }
data()81 		const T* data() const { return reinterpret_cast<const T*>(_storage); }
82 
begin()83 		iterator begin() { return data(); }
end()84 		iterator end() { return data() + size(); }
85 
begin()86 		const_iterator begin() const { return data(); }
end()87 		const_iterator end() const { return data() + size(); }
88 
cbegin()89 		const_iterator cbegin() const { return begin(); }
cend()90 		const_iterator cend() const { return end(); }
91 
rbegin()92 		reverse_iterator rbegin() { return reverse_iterator(end()); }
rend()93 		reverse_iterator rend() { return reverse_iterator(begin()); }
94 
rbegin()95 		const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
rend()96 		const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
97 
crbegin()98 		const_reverse_iterator crbegin() const { return rbegin(); }
crend()99 		const_reverse_iterator crend() const { return rend(); }
100 
empty()101 		bool empty() const { return size() == 0; }
size()102 		size_type size() const { return *DBArrayAlloc::get_size_ptr(_storage); }
103 
104 	private:
alloc(size_t count)105 		T* alloc(size_t count) {
106 			return reinterpret_cast<T*>(DBArrayAlloc::alloc(static_cast<size_type>(count * sizeof(T)), static_cast<size_type>(count), static_cast<size_type>(alignof(T))));
107 		}
108 
free(void * p)109 		void free(void* p) {
110 			DBArrayAlloc::free(p, alignof(T));
111 		}
112 
113 		template <typename F>
114 		void construct(size_type count, const F& make);
115 
116 		void destroy() noexcept;
117 		void destroy_impl(size_type n) noexcept;
118 	private:
119 		void* _storage = DBArrayAlloc::empty_buf();
120 };
121 
122 template <typename T>
123 inline bool operator==(const DBArray<T>& l, const DBArray<T>& r) { return std::equal(l.begin(), l.end(), r.begin(), r.end()); }
124 template <typename T>
125 inline bool operator!=(const DBArray<T>& l, const DBArray<T>& r) { return !(l == r); }
126 template <typename T>
127 inline bool operator<(const DBArray<T>& l, const DBArray<T>& r) { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); }
128 template <typename T>
129 inline bool operator>(const DBArray<T>& l, const DBArray<T>& r) { return r < l; }
130 template <typename T>
131 inline bool operator<=(const DBArray<T>& l, const DBArray<T>& r) { return !(l > r); }
132 template <typename T>
133 inline bool operator>=(const DBArray<T>& l, const DBArray<T>& r) { return !(l < r); }
134 
135 } // namespace lcf
136 
137 namespace lcf {
138 
139 template <typename T>
140 inline DBArray<T>& DBArray<T>::operator=(const DBArray<T>& o) {
141 	if (this != &o) {
142 		destroy();
143 		if (!o.empty()) {
144 			auto iter = o.begin();
145 			construct(o.size(), [&](void* p) { new (p) T(*(iter++)); });
146 		}
147 	}
148 	return *this;
149 }
150 
151 template <typename T>
152 inline DBArray<T>& DBArray<T>::operator=(DBArray<T>&& o) noexcept {
153 	if (this != &o) {
154 		destroy();
155 		swap(o);
156 	}
157 	return *this;
158 }
159 
160 template <typename T>
161 template <typename F>
construct(size_type count,const F & make)162 void DBArray<T>::construct(size_type count, const F& make) {
163 	auto* p = alloc(count);
164 
165 	size_type i = 0;
166 	auto rollback = makeScopeGuard([&]() { destroy_impl(i); });
167 	for (size_type i = 0; i < count; ++i) {
168 		make(p + i);
169 	}
170 	rollback.Dismiss();
171 	_storage = p;
172 }
173 
174 template <typename T>
destroy()175 void DBArray<T>::destroy() noexcept {
176 	if (_storage != DBArrayAlloc::empty_buf()) {
177 		destroy_impl(size());
178 		_storage = DBArrayAlloc::empty_buf();
179 	}
180 }
181 
182 template <typename T>
destroy_impl(size_type n)183 void DBArray<T>::destroy_impl(size_type n) noexcept {
184 	while (n > 0) {
185 		data()[--n].~T();
186 	}
187 	free(_storage);
188 }
189 
190 } // namespace lcf
191 
192 #endif
193