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