blob: 661e3c3bd407e90f744df775be7e5a248b50597c [file] [log] [blame]
// 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_