1 /*
2 * (c) Schrodinger, Inc.
3 */
4
5 #pragma once
6
7 #include "MemoryDebug.h"
8 #include "pymol/memory.h"
9 #include <algorithm>
10 #include <cassert>
11 #include <cstddef>
12 #if 0
13 #include <vector>
14 #endif
15
16 namespace pymol
17 {
18
19 /*
20 * NOTE: Use this only for existing VLAs
21 * otherwise, Use std::vector<T> instead!
22 *
23 * NOTE: safe RAII version of our PyMOL VLAs
24 *
25 * NOTE: allocation exceptions handled by VLA API
26 */
27
28 /**
29 * Variable sized array.
30 *
31 * Compatible with legacy VLA API (MemoryDebug.h) through implicit casts.
32 *
33 * Conceptually (mostly) equivalent to std::vector<T>. Differences are:
34 * - no capacity(), but size() will grow in a similar fashion when calling
35 * check()
36 */
37 template <typename T> class vla
38 {
39 T* m_vla = nullptr;
40
swap(vla<T> & other)41 void swap(vla<T>& other) noexcept { std::swap(m_vla, other.m_vla); }
42
43 public:
44 // implicit NULL constructor
45 vla() = default;
46
47 // NULL assignment
48 vla<T>& operator=(std::nullptr_t)
49 {
50 freeP();
51 return *this;
52 }
53
54 /**
55 * Takes ownership of a legacy VLA pointer
56 * @param ptr Pointer to a legacy VLA which was constructed with VLAlloc (or a related function)
57 */
58 template <typename U> friend vla<U> vla_take_ownership(U* ptr);
59
60 /**
61 * Construct a new zero-initialized container
62 * @param size the size of the container
63 */
vla(std::size_t size)64 explicit vla(std::size_t size) { m_vla = VLACalloc(T, size); }
65
66 /**
67 * Constructs the container with @a size copies of elements with value @a
68 * value.
69 * @param size the size of the container
70 * @param value the value to initialize elements of the container with
71 */
vla(std::size_t size,T value)72 vla(std::size_t size, T value)
73 {
74 m_vla = VLAlloc(T, size);
75 for (size_t i = 0; i < size; ++i) {
76 new (m_vla + i) T(value);
77 }
78 }
79
80 // constructor with initializer list
81 // Empty list constructs a NULL VLA to be consistent with default constructor
vla(std::initializer_list<T> init)82 vla(std::initializer_list<T> init)
83 {
84 if (init.size() == 0)
85 return;
86 resize(init.size());
87 std::copy(init.begin(), init.end(), this->begin());
88 }
89
90 #if 0
91 // constructor from std::vector
92 explicit vla(const std::vector<T>& vec) : vla(vec.size())
93 {
94 std::copy(vec.begin(), vec.end(), this->begin());
95 }
96 #endif
97
98 // copy constructor
vla(const vla<T> & other)99 vla(const vla<T>& other) { m_vla = VLACopy2<T>(other.m_vla); }
100
101 // copy assignment
102 vla<T>& operator=(const vla<T>& other)
103 {
104 vla<T> tmp(other);
105 swap(tmp);
106 return *this;
107 }
108
109 // move constructor
vla(vla<T> && other)110 vla(vla<T>&& other) noexcept { swap(other); }
111
112 // move assignment
113 vla<T>& operator=(vla<T>&& other) noexcept
114 {
115 swap(other);
116 return *this;
117 }
118 // destructor
~vla()119 ~vla() { freeP(); }
120
121 /**
122 * Returns pointer to the underlying array serving as element storage.
123 */
data()124 const T* data() const { return m_vla; }
data()125 T* data() { return m_vla; }
126
127 /// legacy VLA cast
128 operator const T*() const { return m_vla; }
129
130 /// legacy address-of VLA cast
131 T** operator&() { return &m_vla; }
132
133 // note: VS2015 fails in various situations if this is not a template
134 /// legacy pointer arithmetic
135 template <typename S> const T* operator+(S i) const { return m_vla + i; }
136 /// legacy pointer arithmetic
137 template <typename S> T* operator+(S i) { return m_vla + i; }
138
139 // note: VS2015 32bit fails with "overloads have similar conversions" if this is not a template
140 /**
141 * Returns a reference to the element at specified location @a i. No bounds
142 * checking is performed.
143 * @param i position of the element to return
144 */
145 template <typename S> T& operator[](S i) { return m_vla[i]; }
146 template <typename S> const T& operator[](S i) const { return m_vla[i]; }
147
148 /// legacy VLA member access
149 T* operator->() { return m_vla; }
150 /// legacy VLA member access
151 const T* operator->() const { return m_vla; }
152
153 /**
154 * checks whether the owned pointer is not nullptr
155 */
156 explicit operator bool() const { return m_vla; }
157
158 /**
159 * Resizes the container to contain @a newSize elements.
160 * If this didn't manage any data yet, a new zero-initialized buffer will be
161 * allocated. Otherwise new elements will only be zero-initialized if the
162 * first allocation was done with VLACalloc.
163 * @param newSize new size of the container
164 */
resize(std::size_t newSize)165 void resize(std::size_t newSize)
166 {
167 if (m_vla == nullptr) {
168 m_vla = VLACalloc(T, newSize);
169 } else {
170 VLASize(m_vla, T, newSize);
171 }
172 }
173
174 /**
175 * Returns the number of elements in the container
176 */
size()177 std::size_t size() const
178 {
179 if (m_vla == nullptr) {
180 return 0;
181 }
182 return VLAGetSize(m_vla);
183 }
184
185 /**
186 * Grow the container size if index @a i is out-of-bounds.
187 * @post size() > i
188 * @return pointer to element @a i
189 * @pre operator bool() != false
190 */
check(std::size_t i)191 T* check(std::size_t i)
192 {
193 assert(m_vla != nullptr);
194 VLACheck(m_vla, T, i);
195 return m_vla + i;
196 }
197
198 /**
199 * Erases all elements from the container and frees the underlying buffer.
200 * @post size() == 0
201 * @post operator bool() == false
202 */
freeP()203 void freeP()
204 {
205 if (m_vla != nullptr) {
206 pymol::destroy(begin(), end());
207 VLAFreeP(m_vla);
208 }
209 }
210
211 #if 0
212 // Util functions
213 std::vector<T> toStdVector() const
214 {
215 return std::vector<T>(m_vla, m_vla + size());
216 }
217 #endif
218
begin()219 T* begin() { return m_vla; }
end()220 T* end() { return m_vla + size(); }
begin()221 const T* begin() const { return m_vla; }
end()222 const T* end() const { return m_vla + size(); }
223
224 /**
225 * Unlike resize(), won't shrink the size, and unlike check(), can be
226 * called on empty container.
227 * @post size() >= count
228 * @post operator bool() != false
229 */
reserve(std::size_t count)230 void reserve(std::size_t count)
231 {
232 if (m_vla == nullptr) {
233 m_vla = VLACalloc(T, count);
234 } else if (count != 0) {
235 VLACheck(m_vla, T, count - 1);
236 }
237 }
238
239 /**
240 * Inserts 'count' number of elements to the vla at a given index
241 * @param index position of the vla where elements are added
242 * @param count number of elements added
243 */
insert(int index,int count)244 void insert(int index, int count)
245 {
246 m_vla = static_cast<T*>(VLAInsertRaw(m_vla, index, count));
247 }
248
249 /**
250 * Removes 'count' number of elements to the vla at a given index
251 * @param index position of the vla where elements are removed
252 * @param count number of elements removed
253 */
erase(int index,int count)254 void erase(int index, int count)
255 {
256 m_vla = static_cast<T*>(VLADeleteRaw(m_vla, index, count));
257 }
258 };
259
vla_take_ownership(U * ptr)260 template <typename U> vla<U> vla_take_ownership(U* ptr)
261 {
262 vla<U> instance;
263 instance.m_vla = ptr;
264 return instance;
265 }
266 } // namespace pymol
267
VLACopy2(const pymol::vla<T> & v)268 template <typename T> pymol::vla<T> VLACopy2(const pymol::vla<T>& v)
269 {
270 return v; // calls copy constructor
271 }
272
VLACheck2(pymol::vla<T> & v,size_t pos)273 template <typename T> void VLACheck2(pymol::vla<T>& v, size_t pos)
274 {
275 v.check(pos);
276 }
277
VLASize2(pymol::vla<T> & v,size_t size)278 template <typename T> void VLASize2(pymol::vla<T>& v, size_t size)
279 {
280 v.resize(size);
281 }
282
VLAFreeP(pymol::vla<T> & v)283 template <typename T> void VLAFreeP(pymol::vla<T>& v)
284 {
285 v.freeP();
286 }
287
288 // vi:sw=2:expandtab
289