blob: 32556cc9013dbdec0c263e1b96d0184f6a9c8ee2 [file] [edit]
/*
* Copyright (C) 2024 Samuel Weinig <sam@webkit.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <wtf/CompactVariantOperations.h>
namespace WTF {
// A `CompactVariant` acts like a `Variant` with the following differences:
// - All alternatives must be pointers, smart pointers, have size of 48 bits or fewer, or be specialized for `CompactVariantTraits`.
// - Can only contain 254 or fewer alternatives.
// - Has a more limited API, only offering `holds_alternative()` for type checking and `switchOn()` for value access.
template<CompactVariantAlternative... Ts> class CompactVariant {
static_assert(sizeof...(Ts) < 255);
using StdVariant = Variant<Ts...>;
using Index = uint8_t;
using Storage = uint64_t;
using Operations = CompactVariantOperations<Ts...>;
public:
template<typename U> constexpr CompactVariant(U&& value)
requires
std::constructible_from<StdVariant, U>
&& (!std::same_as<std::remove_cvref_t<U>, CompactVariant>)
&& (!IsStdInPlaceTypeV<std::remove_cvref_t<U>>)
&& (!IsStdInPlaceIndexV<std::remove_cvref_t<U>>)
{
m_data = Operations::template encode<typename VariantBestMatch<StdVariant, U>::type>(std::forward<U>(value));
}
template<typename T, typename... Args> explicit constexpr CompactVariant(WTF::InPlaceTypeT<T>, Args&&... args)
requires std::constructible_from<StdVariant, T>
{
m_data = Operations::template encodeFromArguments<T>(std::forward<Args>(args)...);
}
template<size_t I, typename... Args> explicit constexpr CompactVariant(WTF::InPlaceIndexT<I>, Args&&... args)
requires std::constructible_from<StdVariant, WTF::VariantAlternativeT<I, StdVariant>>
{
m_data = Operations::template encodeFromArguments<WTF::VariantAlternativeT<I, StdVariant>>(std::forward<Args>(args)...);
}
CompactVariant(const CompactVariant& other)
{
Operations::copy(m_data, other.m_data);
}
CompactVariant& operator=(const CompactVariant& other)
{
if (m_data == other.m_data)
return *this;
Operations::destruct(m_data);
Operations::copy(m_data, other.m_data);
return *this;
}
CompactVariant(CompactVariant&& other)
{
Operations::move(m_data, other.m_data);
// Set `other` to the "moved from" state.
other.m_data = Operations::movedFromDataValue;
}
CompactVariant& operator=(CompactVariant&& other)
{
if (m_data == other.m_data)
return *this;
Operations::destruct(m_data);
Operations::move(m_data, other.m_data);
// Set `other` to the "moved from" state.
other.m_data = Operations::movedFromDataValue;
return *this;
}
template<typename U> CompactVariant& operator=(U&& value)
requires
std::constructible_from<StdVariant, U>
&& (!std::same_as<std::remove_cvref_t<U>, CompactVariant>)
&& (!IsStdInPlaceTypeV<std::remove_cvref_t<U>>)
&& (!IsStdInPlaceIndexV<std::remove_cvref_t<U>>)
{
Operations::destruct(m_data);
m_data = Operations::template encode<typename VariantBestMatch<StdVariant, U>::type>(std::forward<U>(value));
return *this;
}
~CompactVariant()
{
Operations::destruct(m_data);
}
void swap(CompactVariant& other)
{
std::swap(m_data, other.m_data);
}
template<typename T, typename... Args> void emplace(Args&&... args)
{
Operations::destruct(m_data);
m_data = Operations::template encodeFromArguments<T>(std::forward<Args>(args)...);
}
template<size_t I, typename... Args> void emplace(Args&&... args)
{
Operations::destruct(m_data);
m_data = Operations::template encodeFromArguments<WTF::VariantAlternativeT<I, StdVariant>>(std::forward<Args>(args)...);
}
constexpr Index index() const
{
return Operations::decodedIndex(m_data);
}
constexpr bool valueless_by_move() const
{
return m_data == Operations::movedFromDataValue;
}
template<typename T> constexpr bool holdsAlternative() const
{
static_assert(alternativeIndexV<T, StdVariant> <= WTF::VariantSizeV<StdVariant>);
return index() == alternativeIndexV<T, StdVariant>;
}
template<size_t I> constexpr bool holdsAlternative() const
{
static_assert(I <= WTF::VariantSizeV<StdVariant>);
return index() == I;
}
template<typename... F> decltype(auto) switchOn(F&&... f) const
{
return Operations::constPayloadForData(m_data, std::forward<F>(f)...);
}
bool operator==(const CompactVariant& other) const
{
if (index() != other.index())
return false;
return typeForIndex<StdVariant>(index(), [&]<typename T>() {
return Operations::template equal<T>(m_data, other.m_data);
});
}
private:
// FIXME: Use a smaller data type if values are small enough / empty.
Storage m_data;
};
// Utility for making a CompactVariant directly from a parameter pack of types.
template<typename... Ts> using CompactVariantWrapper = CompactVariant<Ts...>;
} // namespace WTF
using WTF::CompactVariant;
using WTF::CompactVariantWrapper;