blob: 5532668d5ea1c313b0c455bfe26b43a021d1a5ee [file]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_ZUCCHINI_BUFFER_VIEW_H_
#define COMPONENTS_ZUCCHINI_BUFFER_VIEW_H_
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <algorithm>
#include <type_traits>
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/memory/raw_span.h"
#include "components/zucchini/algorithm.h"
namespace zucchini {
// Describes a region within a buffer, with starting offset and size.
struct BufferRegion {
bool operator==(const BufferRegion& other) const = default;
// The region data are stored as |offset| and |size|, but often it is useful
// to represent it as an interval [lo(), hi()) = [offset, offset + size).
size_t lo() const { return offset; }
size_t hi() const { return offset + size; }
// Returns whether the Region fits in |[0, container_size)|. Special case:
// a size-0 region starting at |container_size| fits.
bool FitsIn(size_t container_size) const {
return offset <= container_size && container_size - offset >= size;
}
// Returns |v| clipped to the inclusive range |[lo(), hi()]|.
size_t InclusiveClamp(size_t v) const {
return zucchini::InclusiveClamp(v, lo(), hi());
}
// Region data use size_t to match BufferViewBase::size_type, to make it
// convenient to index into buffer view.
size_t offset;
size_t size;
};
namespace internal {
// TODO(huangs): Rename to BasicBufferView.
// BufferViewBase should not be used directly; it is an implementation used for
// both BufferView and MutableBufferView.
template <class ElementType>
class GSL_POINTER BufferViewBase {
public:
using element_type = ElementType;
using span_type = base::raw_span<element_type, DanglingUntriaged>;
using pointer = span_type::pointer;
using reference = span_type::reference;
using const_reference = span_type::const_reference;
using size_type = span_type::size_type;
// TODO(crbug.com/439964610): `iterator` and `const_iterator` should be
// `span_type::iterator` and `span_type::const_iterator` which are backed by
// `base::CheckedContiguousIterator` hence safe, however for backwards
// compatibility the code using BufferViewBase assumes iterator is always a
// pointer.
using iterator = span_type::pointer;
using const_iterator = span_type::const_pointer;
static BufferViewBase FromRange(pointer first, pointer last) {
return BufferViewBase(UNSAFE_TODO(span_type(first, last)));
}
constexpr BufferViewBase() noexcept = default;
// Support a conversion from BufferViewBase<T> to BufferViewBase<const T>.
template <typename T>
requires(std::same_as<std::remove_const_t<element_type>, T> &&
!std::same_as<element_type, T>)
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr BufferViewBase(const BufferViewBase<T>& other)
: span_(other.span_) {}
// TODO(crbug.com/439964610): The second argument type should be
// `base::StrictNumeric<size_type>`.
constexpr BufferViewBase(pointer first, size_type count)
: UNSAFE_TODO(span_(first, count)) {}
constexpr BufferViewBase(const BufferViewBase& other) noexcept = default;
constexpr BufferViewBase(BufferViewBase&& other) noexcept = default;
constexpr BufferViewBase& operator=(const BufferViewBase& other) noexcept =
default;
constexpr BufferViewBase& operator=(BufferViewBase&& other) noexcept =
default;
// Iterators
iterator begin() const { return base::to_address(span_.begin()); }
iterator end() const { return base::to_address(span_.end()); }
const_iterator cbegin() const { return base::to_address(span_.cbegin()); }
const_iterator cend() const { return base::to_address(span_.cend()); }
// Capacity
constexpr size_type size() const noexcept { return span_.size(); }
[[nodiscard]] constexpr bool empty() const noexcept { return span_.empty(); }
// Returns whether the buffer is large enough to cover |region|.
bool covers(const BufferRegion& region) const {
return region.FitsIn(size());
}
// Returns whether the buffer is large enough to cover an array starting at
// |offset| with |num| elements, each taking |elt_size| bytes.
bool covers_array(size_type offset, size_type num, size_type elt_size) const {
DCHECK_GT(elt_size, 0U);
// Use subtraction and division to avoid overflow.
return offset <= size() && (size() - offset) / elt_size >= num;
}
// Element access
// Returns a reference to the raw value at specified location |pos|.
// If |pos| is not within the range of the buffer, the process is terminated.
constexpr const_reference operator[](size_type index) const {
return span_[index];
}
constexpr reference operator[](size_type index) { return span_[index]; }
// Returns a sub-buffer described by |region|.
BufferViewBase operator[](BufferRegion region) const {
return span_.subspan(region.offset, region.size);
}
// TODO(crbug.com/439964610): The argument type should be
// `base::StrictNumeric<size_type>`.
template <class T>
T read(size_type pos) const {
T value;
base::byte_span_from_ref(value).copy_from_nonoverlapping(
base::as_bytes(span_.subspan(pos)).first(sizeof value));
return value;
}
// TODO(crbug.com/439964610): The first argument type should be
// `base::StrictNumeric<size_type>`.
template <class T>
void write(size_type pos, const T& value) {
base::as_writable_bytes(span_.subspan(pos))
.copy_prefix_from(base::byte_span_from_ref(value));
}
// TODO(crbug.com/439964610): The argument type should be
// `base::StrictNumeric<size_type>`.
template <class T>
bool can_access(size_type pos) const {
return pos < size() && size() - pos >= sizeof(T);
}
// Returns a BufferRegion describing the full view, with offset = 0. If the
// BufferViewBase is derived from another, this does *not* return the
// original region used for its definition (hence "local").
BufferRegion local_region() const { return BufferRegion{0, size()}; }
bool equals(const BufferViewBase& other) const {
return span_ == other.span_;
}
// Modifiers
// TODO(crbug.com/439964610): The argument type should be
// `base::StrictNumeric<size_type>`.
void shrink(size_type new_size) { span_ = span_.first(new_size); }
// Moves the start of the view forward by n bytes.
// TODO(crbug.com/439964610): The argument type should be
// `base::StrictNumeric<size_type>`.
void remove_prefix(size_type n) { span_ = span_.subspan(n); }
// Moves the start of the view to |it|, which is in range [begin(), end()).
void seek(pointer it) {
CHECK_LE(span_.data(), it);
const size_type offset = it - span_.data();
span_ = span_.subspan(offset);
}
// Given |origin| that contains |*this|, minimally increase |first_| (possibly
// by 0) so that |first_ <= last_|, and |first_ - origin.first_| is a multiple
// of |alignment|. On success, updates |first_| and returns true. Otherwise
// returns false.
bool AlignOn(const BufferViewBase& origin,
base::StrictNumeric<size_type> alignment_arg) {
static_assert(sizeof(element_type) == 1u);
const size_type alignment = static_cast<size_type>(alignment_arg);
DCHECK_GT(alignment, 0u);
CHECK_LE(origin.begin(), begin());
CHECK_LE(end(), origin.end());
const size_type offset =
static_cast<size_type>(span_.data() - origin.span_.data());
const size_type aligned_size = AlignCeil(offset, alignment);
if (aligned_size - offset > span_.size_bytes()) {
return false;
}
span_ = span_.subspan(aligned_size - offset);
return true;
}
private:
template <typename T>
friend class BufferViewBase;
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr BufferViewBase(base::span<element_type> other) noexcept
: span_(other) {}
span_type span_;
};
} // namespace internal
// Classes to encapsulate a contiguous sequence of raw data, without owning the
// encapsulated memory regions. These are intended to be used as value types.
using ConstBufferView = internal::BufferViewBase<const uint8_t>;
using MutableBufferView = internal::BufferViewBase<uint8_t>;
} // namespace zucchini
#endif // COMPONENTS_ZUCCHINI_BUFFER_VIEW_H_