Files
PlantAI-dev/tools/pymol-open-source/layer0/vla.h
T
2026-08-03 18:42:47 +08:00

261 lines
6.1 KiB
C++

/*
* (c) Schrodinger, Inc.
*/
#pragma once
#include "MemoryDebug.h"
#include "pymol/memory.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <type_traits>
namespace pymol
{
/*
* NOTE: Use this only for existing VLAs
* otherwise, Use std::vector<T> instead!
*
* NOTE: safe RAII version of our PyMOL VLAs
*
* NOTE: allocation exceptions handled by VLA API
*/
/**
* Variable sized array.
*
* Compatible with legacy VLA API (MemoryDebug.h) through implicit casts.
*
* Conceptually (mostly) equivalent to std::vector<T>. Differences are:
* - no capacity(), but size() will grow in a similar fashion when calling
* check()
*/
template <typename T> class vla
{
#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 5
// Not available in GCC 4.8
static_assert(std::is_trivially_copyable<T>::value, "");
#endif
T* m_vla = nullptr;
void swap(vla<T>& other) noexcept { std::swap(m_vla, other.m_vla); }
public:
// implicit nullptr constructor
vla() = default;
// nullptr assignment
vla<T>& operator=(std::nullptr_t)
{
freeP();
return *this;
}
/**
* Takes ownership of a legacy VLA pointer
* @param ptr Pointer to a legacy VLA which was constructed with VLAlloc (or a related function)
*/
template <typename U> friend vla<U> vla_take_ownership(U* ptr);
/**
* Construct a new zero-initialized container
* @param size the size of the container
*/
explicit vla(std::size_t size) { m_vla = VLACalloc(T, size); }
// constructor with initializer list
// Empty list constructs a nullptr VLA to be consistent with default constructor
vla(std::initializer_list<T> init)
{
if (init.size() == 0)
return;
resize(init.size());
std::copy(init.begin(), init.end(), this->begin());
}
// copy constructor
vla(const vla<T>& other) { m_vla = VLACopy2<T>(other.m_vla); }
// copy assignment
vla<T>& operator=(const vla<T>& other)
{
vla<T> tmp(other);
swap(tmp);
return *this;
}
// move constructor
vla(vla<T>&& other) noexcept { swap(other); }
// move assignment
vla<T>& operator=(vla<T>&& other) noexcept
{
swap(other);
return *this;
}
// destructor
~vla() { freeP(); }
/**
* Returns pointer to the underlying array serving as element storage.
*/
const T* data() const { return m_vla; }
T* data() { return m_vla; }
/// legacy VLA cast
operator const T*() const { return m_vla; }
/// legacy address-of VLA cast
T** operator&() { return &m_vla; }
// note: VS2015 fails in various situations if this is not a template
/// legacy pointer arithmetic
template <typename S> const T* operator+(S i) const { return m_vla + i; }
/// legacy pointer arithmetic
template <typename S> T* operator+(S i) { return m_vla + i; }
// note: VS2015 32bit fails with "overloads have similar conversions" if this is not a template
/**
* Returns a reference to the element at specified location @a i. No bounds
* checking is performed.
* @param i position of the element to return
*/
template <typename S> T& operator[](S i) { return m_vla[i]; }
template <typename S> const T& operator[](S i) const { return m_vla[i]; }
/// legacy VLA member access
T* operator->() { return m_vla; }
/// legacy VLA member access
const T* operator->() const { return m_vla; }
/**
* checks whether the owned pointer is not nullptr
*/
explicit operator bool() const { return m_vla; }
/**
* Resizes the container to contain @a newSize elements.
* If this didn't manage any data yet, a new zero-initialized buffer will be
* allocated. Otherwise new elements will only be zero-initialized if the
* first allocation was done with VLACalloc.
* @param newSize new size of the container
*/
void resize(std::size_t newSize)
{
if (m_vla == nullptr) {
m_vla = VLACalloc(T, newSize);
} else {
VLASize(m_vla, T, newSize);
}
}
/**
* Returns the number of elements in the container
*/
std::size_t size() const
{
if (m_vla == nullptr) {
return 0;
}
return VLAGetSize(m_vla);
}
/**
* Grow the container size if index @a i is out-of-bounds.
* @post size() > i
* @return pointer to element @a i
* @pre operator bool() != false
*/
T* check(std::size_t i)
{
assert(m_vla != nullptr);
VLACheck(m_vla, T, i);
return m_vla + i;
}
/**
* Erases all elements from the container and frees the underlying buffer.
* @post size() == 0
* @post operator bool() == false
*/
void freeP()
{
if (m_vla != nullptr) {
VLAFreeP(m_vla);
}
}
T* begin() { return m_vla; }
T* end() { return m_vla + size(); }
const T* begin() const { return m_vla; }
const T* end() const { return m_vla + size(); }
/**
* Unlike resize(), won't shrink the size, and unlike check(), can be
* called on empty container.
* @post size() >= count
* @post operator bool() != false
*/
void reserve(std::size_t count)
{
if (m_vla == nullptr) {
m_vla = VLACalloc(T, count);
} else if (count != 0) {
VLACheck(m_vla, T, count - 1);
}
}
/**
* Inserts 'count' number of elements to the vla at a given index
* @param index position of the vla where elements are added
* @param count number of elements added
*/
void insert(int index, int count)
{
m_vla = static_cast<T*>(VLAInsertRaw(m_vla, index, count));
}
/**
* Removes 'count' number of elements to the vla at a given index
* @param index position of the vla where elements are removed
* @param count number of elements removed
*/
void erase(int index, int count)
{
m_vla = static_cast<T*>(VLADeleteRaw(m_vla, index, count));
}
};
template <typename U> vla<U> vla_take_ownership(U* ptr)
{
vla<U> instance;
instance.m_vla = ptr;
return instance;
}
} // namespace pymol
template <typename T> pymol::vla<T> VLACopy2(const pymol::vla<T>& v)
{
return v; // calls copy constructor
}
template <typename T> void VLACheck2(pymol::vla<T>& v, size_t pos)
{
v.check(pos);
}
template <typename T> void VLASize2(pymol::vla<T>& v, size_t size)
{
v.resize(size);
}
template <typename T> void VLAFreeP(pymol::vla<T>& v)
{
v.freeP();
}
// vi:sw=2:expandtab