blob: 0623d02518369d58a3511d980643f946c12a5594 [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 <algorithm>
#include <bit>
#include <concepts>
#include <functional>
#include <type_traits>
#include <wtf/GetPtr.h>
#include <wtf/StdLibExtras.h>
#include <wtf/Variant.h>
#include <wtf/VariantExtras.h>
namespace WTF {
template<typename T> struct CompactVariantTraits {
static constexpr bool hasAlternativeRepresentation = false;
/*
If `hasAlternativeRepresentation` is set to `true`, you must also implement the following functions.
static constexpr uint64_t encodeFromArguments(...) { ... }
static constexpr uint64_t encode(const T&) { ... }
static constexpr uint64_t encode(T&&) { ... }
static constexpr T decode(uint64_t) { ... }
*/
};
template<typename T> concept CompactVariantAlternativeSmallEnough = sizeof(T) <= 4;
template<typename T> concept CompactVariantAlternativePointer =
std::is_pointer_v<T>
|| IsSmartPtr<T>::value;
template<typename T> concept CompactVariantAlternative =
CompactVariantAlternativePointer<T>
|| CompactVariantAlternativeSmallEnough<T>
|| CompactVariantTraits<T>::hasAlternativeRepresentation;
// A CompactVariant stores data by bit-packing the variant index
// and data into a storage of type Storage (uint64_t):
// * Bit 63-56: if pointer/smart pointer, bit 63-56 of the pointer. Useful
// if the architecture supports top-byte ignore, and information
// is stored there. Otherwise zeroes.
// * Bit 55-48: variant index (indicates the data type of the variant)
// * Bit 47-0 : if pointer/smart pointer, bit 48-0 of the pointer. If other
// types of data, the encoded data.
// This struct provides operations on the packed storage in a CompactVariant.
template<CompactVariantAlternative... Ts> struct CompactVariantOperations {
using StdVariant = Variant<Ts...>;
using Index = uint8_t;
using Storage = uint64_t;
static constexpr Storage movedFromDataValue = std::numeric_limits<Storage>::max();
static constexpr Storage indexShift = 48;
static constexpr Storage indexMask = 0xFFULL << indexShift;
static constexpr Storage payloadMask = ~indexMask;
static constexpr Storage topByteShift = 64 - 8;
static constexpr Storage topByteMask = 0xFFULL << topByteShift;
static constexpr Storage encodedIndex(Index index)
{
return static_cast<Storage>(index) << indexShift;
}
static constexpr Index decodedIndex(Storage value)
{
return static_cast<Index>(static_cast<uint8_t>((value & indexMask) >> indexShift));
}
template<typename T, typename U> static constexpr Storage encodedPayload(U&& payload)
{
Storage data = 0;
if constexpr (CompactVariantTraits<T>::hasAlternativeRepresentation)
data = CompactVariantTraits<T>::encode(std::forward<U>(payload));
else
new (NotNull, &data) T(std::forward<U>(payload));
// For data other than pointers, ensure the data doesn't overwrite the top byte.
// (i.e top byte should be zero)
// (pointers may store arbitrary data in the top byte, and that's okay due to TBI)
if constexpr (!CompactVariantAlternativePointer<T>)
RELEASE_ASSERT(!(data & topByteMask));
// Ensure the bits in the index area are zeroes.
// Sanity check to make sure the data doesn't overwrite the index.
RELEASE_ASSERT(!(data & indexMask));
data &= payloadMask;
return data;
}
template<typename T, typename... Args> static constexpr Storage encodedPayloadFromArguments(Args&&... arguments)
{
Storage data = 0;
if constexpr (CompactVariantTraits<T>::hasAlternativeRepresentation)
data = CompactVariantTraits<T>::encodeFromArguments(std::forward<Args>(arguments)...);
else
new (NotNull, &data) T(std::forward<Args>(arguments)...);
// For data other than pointers, ensure the data doesn't overwrite the top byte.
// (i.e top byte should be zero)
// (pointers may store arbitrary data in the top byte, and that's okay due to TBI)
if constexpr (!CompactVariantAlternativePointer<T>)
RELEASE_ASSERT(!(data & topByteMask));
// Ensure the bits in the index area are zeroes.
// Sanity check to make sure the data doesn't overwrite the index.
RELEASE_ASSERT(!(data & indexMask));
data &= payloadMask;
return data;
}
template<typename T, typename F> static constexpr decltype(auto) decodedPayload(Storage value, NOESCAPE F&& f)
{
Storage maskedData = value & payloadMask;
if constexpr (CompactVariantTraits<T>::hasAlternativeRepresentation) {
T decodedData = CompactVariantTraits<T>::decode(maskedData);
return f(decodedData);
} else {
T& decodedData = *std::launder(reinterpret_cast<T*>(&maskedData));
return f(decodedData);
}
}
template<typename T, typename F> static constexpr decltype(auto) decodedConstPayload(Storage value, NOESCAPE F&& f)
{
Storage maskedData = value & payloadMask;
if constexpr (CompactVariantTraits<T>::hasAlternativeRepresentation) {
T decodedData = CompactVariantTraits<T>::decode(maskedData);
return f(std::as_const(decodedData));
} else {
T& decodedData = *std::launder(reinterpret_cast<T*>(&maskedData));
return f(std::as_const(decodedData));
}
}
template<typename T, typename U> static Storage encode(U&& argument)
{
return encodedPayload<T>(std::forward<U>(argument)) | encodedIndex(alternativeIndexV<T, StdVariant>);
}
template<typename T, typename... Args> static Storage encodeFromArguments(Args&&... arguments)
{
return encodedPayloadFromArguments<T>(std::forward<Args>(arguments)...) | encodedIndex(alternativeIndexV<T, StdVariant>);
}
template<typename... F> static decltype(auto) payloadForData(Storage data, F&&... f)
{
auto visitor = makeVisitor(std::forward<F>(f)...);
return typeForIndex<StdVariant>(decodedIndex(data), [&]<typename T>() {
return decodedPayload<T>(data, visitor);
});
}
template<typename... F> static decltype(auto) constPayloadForData(Storage data, F&&... f)
{
auto visitor = makeVisitor(std::forward<F>(f)...);
return typeForIndex<StdVariant>(decodedIndex(data), [&]<typename T>() {
return decodedConstPayload<T>(data, visitor);
});
}
static void destruct(Storage data)
{
if (data == movedFromDataValue)
return;
payloadForData(data, [&]<typename T>(T& value) {
if constexpr (!std::is_trivially_destructible_v<T>)
value.~T();
});
}
static void copy(Storage& to, Storage from)
{
if (from == movedFromDataValue) {
to = from;
return;
}
payloadForData(from, [&]<typename T>(T& value) {
to = encodedPayload<T>(value) | encodedIndex(alternativeIndexV<T, StdVariant>);
});
}
static void move(Storage& to, Storage from)
{
if (from == movedFromDataValue) {
to = from;
return;
}
payloadForData(from, [&]<typename T>(T& value) {
to = encodedPayload<T>(WTF::move(value)) | encodedIndex(alternativeIndexV<T, StdVariant>);
});
}
template<typename T> static bool equal(Storage a, Storage b)
{
Storage maskedA = a & payloadMask;
Storage maskedB = b & payloadMask;
if constexpr (CompactVariantTraits<T>::hasAlternativeRepresentation)
return CompactVariantTraits<T>::decode(maskedA) == CompactVariantTraits<T>::decode(maskedB);
else
return *std::launder(reinterpret_cast<T*>(&maskedA)) == *std::launder(reinterpret_cast<T*>(&maskedB));
}
};
} // namespace WTF