aboutsummaryrefslogtreecommitdiffstats
path: root/common/flexarray.h
diff options
context:
space:
mode:
Diffstat (limited to 'common/flexarray.h')
-rw-r--r--common/flexarray.h125
1 files changed, 125 insertions, 0 deletions
diff --git a/common/flexarray.h b/common/flexarray.h
new file mode 100644
index 00000000..b8077988
--- /dev/null
+++ b/common/flexarray.h
@@ -0,0 +1,125 @@
+#ifndef AL_FLEXARRAY_H
+#define AL_FLEXARRAY_H
+
+#include <algorithm>
+#include <cstddef>
+#include <stdexcept>
+#include <type_traits>
+
+#include "almalloc.h"
+#include "alspan.h"
+
+namespace al {
+
+/* Storage for flexible array data. This is trivially destructible if type T is
+ * trivially destructible.
+ */
+template<typename T, size_t alignment, bool = std::is_trivially_destructible<T>::value>
+struct alignas(std::max(alignment, alignof(al::span<T>))) FlexArrayStorage : al::span<T> {
+ static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept
+ { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; }
+
+ FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v<T>)
+ : al::span<T>{::new(static_cast<void*>(this+1)) T[size], size}
+ { }
+ ~FlexArrayStorage() = default;
+
+ FlexArrayStorage(const FlexArrayStorage&) = delete;
+ FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
+};
+
+template<typename T, size_t alignment>
+struct alignas(std::max(alignment, alignof(al::span<T>))) FlexArrayStorage<T,alignment,false> : al::span<T> {
+ static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept
+ { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; }
+
+ FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v<T>)
+ : al::span<T>{::new(static_cast<void*>(this+1)) T[size], size}
+ { }
+ ~FlexArrayStorage() { std::destroy(this->begin(), this->end()); }
+
+ FlexArrayStorage(const FlexArrayStorage&) = delete;
+ FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
+};
+
+/* A flexible array type. Used either standalone or at the end of a parent
+ * struct, to have a run-time-sized array that's embedded with its size. Should
+ * be used delicately, ensuring there's no additional data after the FlexArray
+ * member.
+ */
+template<typename T, size_t Align=alignof(T)>
+struct FlexArray {
+ using element_type = T;
+ using value_type = std::remove_cv_t<T>;
+ using index_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ static constexpr size_t alignment{std::max(alignof(T), Align)};
+ using Storage_t_ = FlexArrayStorage<element_type,alignment>;
+
+ const Storage_t_ mStore;
+
+ static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept
+ { return Storage_t_::Sizeof(count, base); }
+ static std::unique_ptr<FlexArray> Create(index_type count)
+ { return std::unique_ptr<FlexArray>{new(FamCount{count}) FlexArray{count}}; }
+
+ FlexArray(index_type size) noexcept(std::is_nothrow_constructible_v<Storage_t_,index_type>)
+ : mStore{size}
+ { }
+ ~FlexArray() = default;
+
+ [[nodiscard]] auto size() const noexcept -> index_type { return mStore.size(); }
+ [[nodiscard]] auto empty() const noexcept -> bool { return mStore.empty(); }
+
+ [[nodiscard]] auto data() noexcept -> pointer { return mStore.data(); }
+ [[nodiscard]] auto data() const noexcept -> const_pointer { return mStore.data(); }
+
+ [[nodiscard]] auto operator[](index_type i) noexcept -> reference { return mStore[i]; }
+ [[nodiscard]] auto operator[](index_type i) const noexcept -> const_reference { return mStore[i]; }
+
+ [[nodiscard]] auto front() noexcept -> reference { return mStore.front(); }
+ [[nodiscard]] auto front() const noexcept -> const_reference { return mStore.front(); }
+
+ [[nodiscard]] auto back() noexcept -> reference { return mStore.back(); }
+ [[nodiscard]] auto back() const noexcept -> const_reference { return mStore.back(); }
+
+ [[nodiscard]] auto begin() noexcept -> iterator { return mStore.begin(); }
+ [[nodiscard]] auto begin() const noexcept -> const_iterator { return mStore.cbegin(); }
+ [[nodiscard]] auto cbegin() const noexcept -> const_iterator { return mStore.cbegin(); }
+ [[nodiscard]] auto end() noexcept -> iterator { return mStore.end(); }
+ [[nodiscard]] auto end() const noexcept -> const_iterator { return mStore.cend(); }
+ [[nodiscard]] auto cend() const noexcept -> const_iterator { return mStore.cend(); }
+
+ [[nodiscard]] auto rbegin() noexcept -> reverse_iterator { return end(); }
+ [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator { return cend(); }
+ [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator { return cend(); }
+ [[nodiscard]] auto rend() noexcept -> reverse_iterator { return begin(); }
+ [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator { return cbegin(); }
+ [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator { return cbegin(); }
+
+ gsl::owner<void*> operator new(size_t, FamCount count)
+ { return ::operator new[](Sizeof(count), std::align_val_t{alignof(FlexArray)}); }
+ void operator delete(gsl::owner<void*> block, FamCount) noexcept
+ { ::operator delete[](block, std::align_val_t{alignof(FlexArray)}); }
+ void operator delete(gsl::owner<void*> block) noexcept
+ { ::operator delete[](block, std::align_val_t{alignof(FlexArray)}); }
+
+ void *operator new(size_t size) = delete;
+ void *operator new[](size_t size) = delete;
+ void operator delete[](void *block) = delete;
+};
+
+} // namespace al
+
+#endif /* AL_FLEXARRAY_H */