/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file LICENSE.rst or https://cmake.org/licensing for details. */ #pragma once #include #include #include #include #include #include "cmSbomSerializer.h" class cmSbomObject; template inline void SerializeDispatch(T const& t, cmSbomSerializer& serializer) { serializer.BeginObject(); t.Serialize(serializer); serializer.EndObject(); } template 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 struct Storage { }; template struct Storage : ObjectInterface { using ValueType = cm::decay_t; explicit Storage(T x) : Value(std::move(x)) { } Storage(Storage&&) noexcept( std::is_nothrow_move_constructible::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 struct Storage : ObjectInterface { using ValueType = cm::decay_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 Ptr; }; template <> struct Storage : 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 using Stack = impl::Storage; template using Heap = impl::Storage; static constexpr std::size_t BufferSize = 128u; static constexpr std::size_t Size = sizeof(Heap) > BufferSize ? sizeof(Heap) : BufferSize; static constexpr std::size_t Align = alignof(impl::max_align_t); struct Buffer { alignas(Align) unsigned char Data[Size]; }; template using Model = cm::conditional_t) <= Size && alignof(Stack) <= Align, Stack, Heap>; }; class cmSbomObject { public: template struct Instance : ObjectStorage::Model { using Base = ObjectStorage::Model; 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(nullptr); } cmSbomObject(std::nullptr_t) : cmSbomObject() { } template < typename T, typename Decayed = cm::remove_cv_t>, typename = cm::enable_if_t::value>> cmSbomObject(T&& x) { ::new (Storage()) Instance(std::forward(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 T& CastUnchecked() { return *static_cast(Interface().Addr()); } template T const& CastUnchecked() const { return *static_cast(Interface().Addr()); } ObjectInterface& Interface() { return *static_cast(Storage()); } ObjectInterface const& Interface() const { return *static_cast(Storage()); } void* Storage() { return &Data.Data; } void const* Storage() const { return &Data.Data; } template U* ptr() noexcept { return std::addressof(this->template CastUnchecked()); } template U const* ptr() const noexcept { return std::addressof(this->template CastUnchecked()); } private: ObjectStorage::Buffer Data{}; }; template > U* insert_back(std::vector& vec, T&& obj) noexcept { vec.emplace_back(std::forward(obj)); return std::addressof(vec.back().CastUnchecked()); }