218 lines
6.7 KiB
C++
218 lines
6.7 KiB
C++
// Copyright 2017 The Chromium Authors. All rights reserved.
|
|
// 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 <algorithm>
|
|
#include <type_traits>
|
|
|
|
#include "base/check_op.h"
|
|
#include "components/zucchini/algorithm.h"
|
|
|
|
namespace zucchini {
|
|
|
|
// Describes a region within a buffer, with starting offset and size.
|
|
struct BufferRegion {
|
|
// 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 T>
|
|
class BufferViewBase {
|
|
public:
|
|
using value_type = T;
|
|
using reference = T&;
|
|
using pointer = T*;
|
|
using iterator = T*;
|
|
using const_iterator = typename std::add_const<T>::type*;
|
|
using size_type = std::size_t;
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
static BufferViewBase FromRange(iterator first, iterator last) {
|
|
DCHECK_GE(last, first);
|
|
BufferViewBase ret;
|
|
ret.first_ = first;
|
|
ret.last_ = last;
|
|
return ret;
|
|
}
|
|
|
|
BufferViewBase() = default;
|
|
|
|
BufferViewBase(iterator first, size_type size)
|
|
: first_(first), last_(first_ + size) {
|
|
DCHECK_GE(last_, first_);
|
|
}
|
|
|
|
template <class U>
|
|
BufferViewBase(const BufferViewBase<U>& that)
|
|
: first_(that.begin()), last_(that.end()) {}
|
|
|
|
template <class U>
|
|
BufferViewBase(BufferViewBase<U>&& that)
|
|
: first_(that.begin()), last_(that.end()) {}
|
|
|
|
BufferViewBase(const BufferViewBase&) = default;
|
|
BufferViewBase& operator=(const BufferViewBase&) = default;
|
|
|
|
// Iterators
|
|
|
|
iterator begin() const { return first_; }
|
|
iterator end() const { return last_; }
|
|
const_iterator cbegin() const { return begin(); }
|
|
const_iterator cend() const { return end(); }
|
|
|
|
// Capacity
|
|
|
|
bool empty() const { return first_ == last_; }
|
|
size_type size() const { return last_ - first_; }
|
|
|
|
// 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_t offset, size_t num, size_t elt_size) {
|
|
DCHECK_GT(elt_size, 0U);
|
|
// Use subtraction and division to avoid overflow.
|
|
return offset <= size() && (size() - offset) / elt_size >= num;
|
|
}
|
|
|
|
// Element access
|
|
|
|
// Returns the raw value at specified location |pos|.
|
|
// If |pos| is not within the range of the buffer, the process is terminated.
|
|
reference operator[](size_type pos) const {
|
|
CHECK_LT(pos, size());
|
|
return first_[pos];
|
|
}
|
|
|
|
// Returns a sub-buffer described by |region|.
|
|
BufferViewBase operator[](BufferRegion region) const {
|
|
DCHECK_LE(region.offset, size());
|
|
DCHECK_LE(region.size, size() - region.offset);
|
|
return {begin() + region.offset, region.size};
|
|
}
|
|
|
|
template <class U>
|
|
const U& read(size_type pos) const {
|
|
// TODO(huangs): Use can_access<U>(pos) after fixing can_access().
|
|
CHECK_LE(sizeof(U), size());
|
|
CHECK_LE(pos, size() - sizeof(U));
|
|
return *reinterpret_cast<const U*>(begin() + pos);
|
|
}
|
|
|
|
template <class U>
|
|
void write(size_type pos, const U& value) {
|
|
// TODO(huangs): Use can_access<U>(pos) after fixing can_access().
|
|
CHECK_LE(sizeof(U), size());
|
|
CHECK_LE(pos, size() - sizeof(U));
|
|
*reinterpret_cast<U*>(begin() + pos) = value;
|
|
}
|
|
|
|
// Returns a mutable reference to an object type U whose raw storage starts
|
|
// at location |pos|.
|
|
template <class U>
|
|
U& modify(size_type pos) {
|
|
// TODO(huangs): Use can_access<U>(pos) after fixing can_access().
|
|
CHECK_LE(sizeof(U), size());
|
|
CHECK_LE(pos, size() - sizeof(U));
|
|
return *reinterpret_cast<U*>(begin() + pos);
|
|
}
|
|
|
|
template <class U>
|
|
bool can_access(size_type pos) const {
|
|
return pos < size() && size() - pos >= sizeof(U);
|
|
}
|
|
|
|
// 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(BufferViewBase other) const {
|
|
return size() == other.size() && std::equal(begin(), end(), other.begin());
|
|
}
|
|
|
|
// Modifiers
|
|
|
|
void shrink(size_type new_size) {
|
|
DCHECK_LE(first_ + new_size, last_);
|
|
last_ = first_ + new_size;
|
|
}
|
|
|
|
// Moves the start of the view forward by n bytes.
|
|
void remove_prefix(size_type n) {
|
|
DCHECK_LE(n, size());
|
|
first_ += n;
|
|
}
|
|
|
|
// Moves the start of the view to |it|, which is in range [begin(), end()).
|
|
void seek(iterator it) {
|
|
DCHECK_GE(it, begin());
|
|
DCHECK_LE(it, end());
|
|
first_ = it;
|
|
}
|
|
|
|
// 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(BufferViewBase origin, size_type alignment) {
|
|
DCHECK_GT(alignment, 0U);
|
|
DCHECK_LE(origin.first_, first_);
|
|
DCHECK_GE(origin.last_, last_);
|
|
size_type aligned_size =
|
|
AlignCeil(static_cast<size_type>(first_ - origin.first_), alignment);
|
|
if (aligned_size > static_cast<size_type>(last_ - origin.first_))
|
|
return false;
|
|
first_ = origin.first_ + aligned_size;
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
iterator first_ = nullptr;
|
|
iterator last_ = nullptr;
|
|
};
|
|
|
|
} // 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_
|