blob: 89afcb10520a5470fc70cf82a5c81e689c7a99d2 [file] [log] [blame] [edit]
// Copyright 2023 The Dawn & Tint Authors
//
// 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR 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.
#ifndef SRC_TINT_UTILS_CONTAINERS_SLICE_H_
#define SRC_TINT_UTILS_CONTAINERS_SLICE_H_
#include <array>
#include <cstdint>
#include <iterator>
#include "src/tint/utils/ice/ice.h"
#include "src/tint/utils/memory/bitcast.h"
#include "src/tint/utils/rtti/castable.h"
#include "src/tint/utils/traits/traits.h"
namespace tint {
/// A type used to indicate an empty array.
struct EmptyType {};
/// An instance of the EmptyType.
static constexpr EmptyType Empty;
/// Mode enumerator for ReinterpretSlice
enum class ReinterpretMode {
/// Only upcasts of pointers are permitted
kSafe,
/// Potentially unsafe downcasts of pointers are also permitted
kUnsafe,
};
namespace detail {
template <typename TO, typename FROM>
static constexpr bool ConstRemoved = std::is_const_v<FROM> && !std::is_const_v<TO>;
/// Private implementation of tint::CanReinterpretSlice.
/// Specialized for the case of TO equal to FROM, which is the common case, and avoids inspection of
/// the base classes, which can be troublesome if the slice is of an incomplete type.
template <ReinterpretMode MODE, typename TO, typename FROM>
struct CanReinterpretSlice {
private:
using TO_EL = std::remove_pointer_t<std::decay_t<TO>>;
using FROM_EL = std::remove_pointer_t<std::decay_t<FROM>>;
public:
/// @see tint::CanReinterpretSlice
static constexpr bool value =
// const can only be applied, not removed
!ConstRemoved<TO, FROM> &&
// Both TO and FROM are the same type (ignoring const)
(std::is_same_v<std::remove_const_t<TO>, std::remove_const_t<FROM>> ||
// Both TO and FROM are pointers...
((std::is_pointer_v<TO> && std::is_pointer_v<FROM>)&&
// const can only be applied to element type, not removed
!ConstRemoved<TO_EL, FROM_EL> &&
// Either:
// * Both the pointer elements are of the same type (ignoring const)
// * Both the pointer elements are both Castable, and MODE is kUnsafe, or FROM is of,
// or
// derives from TO
(std::is_same_v<std::remove_const_t<FROM_EL>, std::remove_const_t<TO_EL>> ||
(IsCastable<FROM_EL, TO_EL> &&
(MODE == ReinterpretMode::kUnsafe || tint::traits::IsTypeOrDerived<FROM_EL, TO_EL>)))));
};
/// Specialization of 'CanReinterpretSlice' for when TO and FROM are equal types.
template <typename T, ReinterpretMode MODE>
struct CanReinterpretSlice<MODE, T, T> {
/// Always `true` as TO and FROM are the same type.
static constexpr bool value = true;
};
} // namespace detail
/// Evaluates whether a `Slice<FROM>` and be reinterpreted as a `Slice<TO>`.
/// Slices can be reinterpreted if:
/// * TO has the same or more 'constness' than FROM.
/// * And either:
/// * `FROM` and `TO` are pointers to the same type
/// * `FROM` and `TO` are pointers to CastableBase (or derived), and the pointee type of `TO` is of
/// the same type as, or is an ancestor of the pointee type of `FROM`.
template <ReinterpretMode MODE, typename TO, typename FROM>
static constexpr bool CanReinterpretSlice =
tint::detail::CanReinterpretSlice<MODE, TO, FROM>::value;
/// A slice represents a contigious array of elements of type T.
template <typename T>
struct Slice {
/// Type of `T`.
using value_type = T;
/// The pointer to the first element in the slice
T* data = nullptr;
/// The total number of elements in the slice
size_t len = 0;
/// The total capacity of the backing store for the slice
size_t cap = 0;
/// Constructor
constexpr Slice() = default;
/// Constructor
constexpr Slice(EmptyType) {} // NOLINT
/// Copy constructor with covariance / const conversion
/// @param other the vector to copy
/// @see CanReinterpretSlice for rules about conversion
template <typename U,
typename = std::enable_if_t<CanReinterpretSlice<ReinterpretMode::kSafe, T, U>>>
Slice(const Slice<U>& other) { // NOLINT(runtime/explicit)
*this = other.template Reinterpret<T, ReinterpretMode::kSafe>();
}
/// Constructor
/// @param d pointer to the first element in the slice
/// @param l total number of elements in the slice
/// @param c total capacity of the backing store for the slice
constexpr Slice(T* d, size_t l, size_t c) : data(d), len(l), cap(c) {}
/// Constructor
/// @param d pointer to the first element in the slice
/// @param l total number of elements in the slice
constexpr Slice(T* d, size_t l) : data(d), len(l), cap(l) {}
/// Constructor
/// @param elements c-array of elements
template <size_t N>
constexpr Slice(T (&elements)[N]) // NOLINT
: data(elements), len(N), cap(N) {}
/// Constructor
/// @param array std::array of elements
template <size_t N>
constexpr Slice(std::array<T, N>& array) // NOLINT
: data(array.data()), len(N), cap(N) {}
/// Reinterprets this slice as `const Slice<TO>&`
/// @returns the reinterpreted slice
/// @see CanReinterpretSlice
template <typename TO, ReinterpretMode MODE = ReinterpretMode::kSafe>
const Slice<TO>& Reinterpret() const {
static_assert(CanReinterpretSlice<MODE, TO, T>);
return *Bitcast<const Slice<TO>*>(this);
}
/// Reinterprets this slice as `Slice<TO>&`
/// @returns the reinterpreted slice
/// @see CanReinterpretSlice
template <typename TO, ReinterpretMode MODE = ReinterpretMode::kSafe>
Slice<TO>& Reinterpret() {
static_assert(CanReinterpretSlice<MODE, TO, T>);
return *Bitcast<Slice<TO>*>(this);
}
/// @return true if the slice length is zero
bool IsEmpty() const { return len == 0; }
/// @return the length of the slice
size_t Length() const { return len; }
/// Create a new slice that represents an offset into this slice
/// @param offset the number of elements to offset
/// @return the new slice
Slice<T> Offset(size_t offset) const {
if (offset > len) {
offset = len;
}
return Slice(data + offset, len - offset, cap - offset);
}
/// Create a new slice that represents a truncated version of this slice
/// @param length the new length
/// @return a new slice that is truncated to `length` elements
Slice<T> Truncate(size_t length) const {
if (length > len) {
length = len;
}
return Slice(data, length, length);
}
/// Index operator
/// @param i the element index. Must be less than `len`.
/// @returns a reference to the i'th element.
T& operator[](size_t i) {
TINT_ASSERT(i < Length());
return data[i];
}
/// Index operator
/// @param i the element index. Must be less than `len`.
/// @returns a reference to the i'th element.
const T& operator[](size_t i) const {
TINT_ASSERT(i < Length());
return data[i];
}
/// @returns a reference to the first element in the vector
T& Front() {
TINT_ASSERT(!IsEmpty());
return data[0];
}
/// @returns a reference to the first element in the vector
const T& Front() const {
TINT_ASSERT(!IsEmpty());
return data[0];
}
/// @returns a reference to the last element in the vector
T& Back() {
TINT_ASSERT(!IsEmpty());
return data[len - 1];
}
/// @returns a reference to the last element in the vector
const T& Back() const {
TINT_ASSERT(!IsEmpty());
return data[len - 1];
}
/// @returns a pointer to the first element in the vector
T* begin() { return data; }
/// @returns a pointer to the first element in the vector
const T* begin() const { return data; }
/// @returns a pointer to one past the last element in the vector
T* end() { return data + len; }
/// @returns a pointer to one past the last element in the vector
const T* end() const { return data + len; }
/// @returns a reverse iterator starting with the last element in the vector
auto rbegin() { return std::reverse_iterator<T*>(end()); }
/// @returns a reverse iterator starting with the last element in the vector
auto rbegin() const { return std::reverse_iterator<const T*>(end()); }
/// @returns the end for a reverse iterator
auto rend() { return std::reverse_iterator<T*>(begin()); }
/// @returns the end for a reverse iterator
auto rend() const { return std::reverse_iterator<const T*>(begin()); }
/// Equality operator.
/// @param other the other slice to compare against
/// @returns true if all fields of this slice are equal to the fields of @p other
bool operator==(const Slice& other) const {
return data == other.data && len == other.len && cap == other.cap;
}
/// Inequality operator.
/// @param other the other slice to compare against
/// @returns false if any fields of this slice are not equal to the fields of @p other
bool operator!=(const Slice& other) const { return !(*this == other); }
};
/// Deduction guide for Slice from c-array
/// @param elements the input elements
template <typename T, size_t N>
Slice(T (&elements)[N]) -> Slice<T>;
} // namespace tint
#endif // SRC_TINT_UTILS_CONTAINERS_SLICE_H_