blob: 3f42cfbd501495fe3211c7699e0efcf26bca1300 [file] [log] [blame] [edit]
/* 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>());
}