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