blob: 11edcf5176bdaa4be8bb6b38e224fd8acd07f307 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PLATFORM_BASE_SPAN_H_
#define PLATFORM_BASE_SPAN_H_
#include <stddef.h>
#include <stdint.h>
#include <cassert>
#include <type_traits>
#include <vector>
namespace openscreen {
template <typename T>
class Span;
// In Open Screen code, use these aliases for the most common types of Spans.
// These can be converted to use std::span once the library supports C++20.
using ByteView = Span<const uint8_t>;
using ByteBuffer = Span<uint8_t>;
namespace internal {
template <typename From, typename To>
using EnableIfConvertible = std::enable_if_t<
std::is_convertible<From (*)[], To (*)[]>::value>; // NOLINT
} // namespace internal
// Contains a pointer and length to a span of contiguous data.
//
// The API is a slimmed-down version of a C++20 std::span<T> and is intended to
// be forwards-compatible with very slight modifications. We don't intend to
// add support for static extents.
//
// NOTES:
// - Although other span implementations allow passing zero to last(), we do
// not, as the behavior is undefined. Callers should explicitly create an
// empty Span instead.
//
// - operator== is not implemented to align with std::span. For more
// discussion, this blog post has considerations when implementing operators
// on types that don't own the data they depend upon:
// https://abseil.io/blog/20180531-regular-types
//
// - Unit tests that want to compare the bytes behind two ByteViews can use
// ExpectByteViewsHaveSameBytes().
template <typename T>
class Span {
public:
constexpr Span() noexcept = default;
constexpr Span(const Span&) noexcept = default;
Span(Span&& other) noexcept = default;
constexpr Span& operator=(const Span&) noexcept = default;
Span& operator=(Span&& other) noexcept = default;
constexpr Span(T* data, size_t count) : data_(data), count_(count) {}
template <typename U, typename = internal::EnableIfConvertible<U, T>>
Span(std::vector<U>& v) : data_(v.data()), count_(v.size()) {} // NOLINT
template <typename U, typename = internal::EnableIfConvertible<U, T>>
constexpr Span(const Span<U>& other) noexcept
: data_(other.data()), count_(other.size()) {} // NOLINT
template <typename U, typename = internal::EnableIfConvertible<U, T>>
constexpr Span& operator=(const Span<U>& other) noexcept {
data_ = other.data();
count_ = other.size();
return *this;
}
~Span() = default;
constexpr T* data() const noexcept { return data_; }
constexpr T& operator[](size_t idx) const {
assert(idx < count_);
return *(data_ + idx);
}
constexpr size_t size() const { return count_; }
[[nodiscard]] constexpr bool empty() const { return count_ == 0; }
constexpr Span first(size_t count) const {
assert(count <= count_);
return Span(data_, count);
}
constexpr Span last(size_t count) const {
assert(count <= count_);
assert(count != 0);
return Span(data_ + (count_ - count), count);
}
constexpr T* begin() const noexcept { return data_; }
constexpr T* end() const noexcept { return data_ + count_; }
constexpr const T* cbegin() const noexcept { return data_; }
constexpr const T* cend() const noexcept { return data_ + count_; }
void remove_prefix(size_t count) noexcept {
assert(count_ >= count);
data_ += count;
count_ -= count;
}
void remove_suffix(size_t count) noexcept {
assert(count_ >= count);
count_ -= count;
}
constexpr Span subspan(size_t offset, size_t count) const {
assert(offset + count < count_);
return Span(data_ + offset, count);
}
private:
T* data_{nullptr};
size_t count_{0};
};
} // namespace openscreen
#endif // PLATFORM_BASE_SPAN_H_