| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #pragma once | |
| #include <cstddef> | |
| #include <memory> | |
| #include <string> | |
| #include <vector> | |
| #include <cm/type_traits> | |
| #include "cmSbomSerializer.h" | |
| class cmSbomObject; | |
| template <typename T> | |
| inline void SerializeDispatch(T const& t, cmSbomSerializer& serializer) | |
| { | |
| serializer.BeginObject(); | |
| t.Serialize(serializer); | |
| serializer.EndObject(); | |
| } | |
| template <typename T> | |
| inline void SerializeDispatch(T* p, cmSbomSerializer& serializer) | |
| { | |
| if (p->SpdxId) { | |
| serializer.AddReference(*p->SpdxId); | |
| } | |
| } | |
| inline void SerializeDispatch(std::nullptr_t, cmSbomSerializer&) | |
| { | |
| } | |
| struct ObjectInterface | |
| { | |
| virtual ObjectInterface* Copy(void* storage) const = 0; | |
| virtual ObjectInterface* Move(void* storage) = 0; | |
| virtual void* Addr() noexcept = 0; | |
| virtual void const* Addr() const noexcept = 0; | |
| virtual void SerializeImpl(cmSbomSerializer& os) const = 0; | |
| virtual ~ObjectInterface() = default; | |
| ObjectInterface() = default; | |
| ObjectInterface(ObjectInterface const&) = default; | |
| ObjectInterface& operator=(ObjectInterface const&) = default; | |
| ObjectInterface(ObjectInterface&&) noexcept = default; | |
| ObjectInterface& operator=(ObjectInterface&&) noexcept = default; | |
| }; | |
| namespace impl { | |
| #if defined(__GLIBCXX__) && __GLIBCXX__ <= 20150623 | |
| using max_align_t = ::max_align_t; | |
| #else | |
| using max_align_t = std::max_align_t; | |
| #endif | |
| enum class StorageKind | |
| { | |
| Stack, | |
| Heap | |
| }; | |
| template <typename, StorageKind> | |
| struct Storage | |
| { | |
| }; | |
| template <typename T> | |
| struct Storage<T, StorageKind::Stack> : ObjectInterface | |
| { | |
| using ValueType = cm::decay_t<T>; | |
| explicit Storage(T x) | |
| : Value(std::move(x)) | |
| { | |
| } | |
| Storage(Storage&&) noexcept( | |
| std::is_nothrow_move_constructible<ValueType>::value) = default; | |
| ~Storage() override = default; | |
| void* Addr() noexcept override { return std::addressof(Value); } | |
| void const* Addr() const noexcept override { return std::addressof(Value); } | |
| ValueType& get() { return Value; } | |
| ValueType const& get() const { return Value; } | |
| private: | |
| ValueType Value; | |
| }; | |
| template <typename T> | |
| struct Storage<T, StorageKind::Heap> : ObjectInterface | |
| { | |
| using ValueType = cm::decay_t<T>; | |
| explicit Storage(T x) | |
| : Ptr(new T(std::move(x))) | |
| { | |
| } | |
| Storage(Storage&&) noexcept = default; | |
| ~Storage() override = default; | |
| void* Addr() noexcept override { return Ptr.get(); } | |
| void const* Addr() const noexcept override { return Ptr.get(); } | |
| ValueType& get() { return *Ptr; } | |
| ValueType const& get() const { return *Ptr; } | |
| private: | |
| std::unique_ptr<ValueType> Ptr; | |
| }; | |
| template <> | |
| struct Storage<std::nullptr_t, StorageKind::Stack> : ObjectInterface | |
| { | |
| explicit Storage(std::nullptr_t) {} | |
| Storage(Storage&&) noexcept = default; | |
| ~Storage() override = default; | |
| void* Addr() noexcept override { return nullptr; } | |
| void const* Addr() const noexcept override { return nullptr; } | |
| void SerializeImpl(cmSbomSerializer&) const override {} | |
| std::nullptr_t get() { return nullptr; } | |
| std::nullptr_t get() const { return nullptr; } | |
| }; | |
| } | |
| struct ObjectStorage | |
| { | |
| template <typename T> | |
| using Stack = impl::Storage<T, impl::StorageKind::Stack>; | |
| template <typename T> | |
| using Heap = impl::Storage<T, impl::StorageKind::Heap>; | |
| static constexpr std::size_t BufferSize = 128u; | |
| static constexpr std::size_t Size = sizeof(Heap<std::nullptr_t>) > BufferSize | |
| ? sizeof(Heap<std::nullptr_t>) | |
| : BufferSize; | |
| static constexpr std::size_t Align = alignof(impl::max_align_t); | |
| struct Buffer | |
| { | |
| alignas(Align) unsigned char Data[Size]; | |
| }; | |
| template <typename Concrete> | |
| using Model = cm::conditional_t<sizeof(Stack<Concrete>) <= Size && | |
| alignof(Stack<Concrete>) <= Align, | |
| Stack<Concrete>, Heap<Concrete>>; | |
| }; | |
| class cmSbomObject | |
| { | |
| public: | |
| template <typename T> | |
| struct Instance : ObjectStorage::Model<T> | |
| { | |
| using Base = ObjectStorage::Model<T>; | |
| using Base::Base; | |
| Instance(Instance&&) noexcept = default; | |
| ObjectInterface* Copy(void* storage) const override | |
| { | |
| return ::new (storage) Instance(this->get()); | |
| } | |
| ObjectInterface* Move(void* storage) override | |
| { | |
| return ::new (storage) Instance(std::move(*this)); | |
| } | |
| void SerializeImpl(cmSbomSerializer& os) const override | |
| { | |
| SerializeDispatch(this->get(), os); | |
| } | |
| }; | |
| cmSbomObject() { ::new (Storage()) Instance<std::nullptr_t>(nullptr); } | |
| cmSbomObject(std::nullptr_t) | |
| : cmSbomObject() | |
| { | |
| } | |
| template < | |
| typename T, typename Decayed = cm::remove_cv_t<cm::remove_reference_t<T>>, | |
| typename = cm::enable_if_t<!std::is_same<Decayed, cmSbomObject>::value>> | |
| cmSbomObject(T&& x) | |
| { | |
| ::new (Storage()) Instance<Decayed>(std::forward<T>(x)); | |
| } | |
| cmSbomObject(cmSbomObject const& other) | |
| { | |
| other.Interface().Copy(Storage()); | |
| } | |
| cmSbomObject(cmSbomObject&& other) noexcept | |
| { | |
| other.Interface().Move(Storage()); | |
| } | |
| ~cmSbomObject() noexcept { Interface().~ObjectInterface(); } | |
| cmSbomObject& operator=(cmSbomObject rhs) | |
| { | |
| Interface().~ObjectInterface(); | |
| rhs.Interface().Move(Storage()); | |
| return *this; | |
| } | |
| bool IsNull() const noexcept { return Interface().Addr() == nullptr; } | |
| explicit operator bool() const noexcept { return !IsNull(); } | |
| void Serialize(cmSbomSerializer& os) const { Interface().SerializeImpl(os); } | |
| template <typename T> | |
| T& CastUnchecked() | |
| { | |
| return *static_cast<T*>(Interface().Addr()); | |
| } | |
| template <typename T> | |
| T const& CastUnchecked() const | |
| { | |
| return *static_cast<T const*>(Interface().Addr()); | |
| } | |
| ObjectInterface& Interface() | |
| { | |
| return *static_cast<ObjectInterface*>(Storage()); | |
| } | |
| ObjectInterface const& Interface() const | |
| { | |
| return *static_cast<ObjectInterface const*>(Storage()); | |
| } | |
| void* Storage() { return &Data.Data; } | |
| void const* Storage() const { return &Data.Data; } | |
| template <typename U> | |
| U* ptr() noexcept | |
| { | |
| return std::addressof(this->template CastUnchecked<U>()); | |
| } | |
| template <typename U> | |
| U const* ptr() const noexcept | |
| { | |
| return std::addressof(this->template CastUnchecked<U>()); | |
| } | |
| private: | |
| ObjectStorage::Buffer Data{}; | |
| }; | |
| template <typename T, typename U = cm::decay_t<T>> | |
| U* insert_back(std::vector<cmSbomObject>& vec, T&& obj) noexcept | |
| { | |
| vec.emplace_back(std::forward<U>(obj)); | |
| return std::addressof(vec.back().CastUnchecked<U>()); | |
| } |