blob: e5f5329662ebd78ceddec64f6dafb2dd38ec613c [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/fit/function.h>
#include <zircon/assert.h>
#include <zircon/types.h>
#include <variant>
#include <storage/buffer/block_buffer.h>
#include "src/storage/minfs/block_utils.h"
namespace minfs {
// Wraps either a regular pointer or a BlockBuffer. This exists because the mapped address for a
// storage::BlockBuffer isn't stable. In particular, a BlockBuffer that happens to be a resizeable
// VMO, can have its mapping change when it grows. When that happens, we don't want a BufferView to
// be invalidated, so we wrap a BlockBuffer and always call through to get the current mapped
// address.
class BufferPtr {
BufferPtr() : ptr_(std::in_place_index<0>, nullptr) {}
BufferPtr(const BufferPtr&) = default;
BufferPtr& operator=(const BufferPtr&) = default;
static BufferPtr FromMemory(void* buffer) {
return BufferPtr(Ptr(std::in_place_index<0>, buffer));
static BufferPtr FromBlockBuffer(storage::BlockBuffer* buffer) {
return BufferPtr(Ptr(std::in_place_index<1>, buffer));
void* get() const {
if (std::holds_alternative<void*>(ptr_)) {
return std::get<void*>(ptr_);
} else {
return std::get<storage::BlockBuffer*>(ptr_)->Data(0);
using Ptr = std::variant<void*, storage::BlockBuffer*>;
explicit BufferPtr(Ptr ptr) : ptr_(ptr) {}
std::variant<void*, storage::BlockBuffer*> ptr_;
// BaseBufferView and BufferView are views of a buffer, a contiguous range in memory. It can be
// mutable or immutable. It keeps track of the use of mutable methods to record whether or not it is
// dirty. A flusher object is provided for flushing the buffer and is called via the Flush method if
// the buffer is deemed dirty. If no flusher is provided, the view is considered immutable. The
// underlying buffer can be memory, or it can be a BlockBuffer which we specialise for, in case
// BlockBuffer is resized, in which case its mapped address can change.
class BaseBufferView {
using Flusher = fit::function<zx::result<>(BaseBufferView* view)>;
BaseBufferView() = default;
explicit BaseBufferView(BufferPtr buffer, size_t offset, size_t length)
: buffer_(buffer), offset_(offset), length_(length) {}
explicit BaseBufferView(BufferPtr buffer, size_t offset, size_t length, Flusher flusher)
: buffer_(buffer), offset_(offset), length_(length), flusher_(std::move(flusher)) {}
// Movable, but not copyable.
BaseBufferView(BaseBufferView&& other) noexcept { *this = std::move(other); }
BaseBufferView& operator=(BaseBufferView&& other) noexcept;
bool IsValid() const { return data() != nullptr; }
size_t length() const { return length_; }
size_t offset() const { return offset_; }
ByteRange GetByteRange() const { return ByteRange(offset_, offset_ + length_); }
bool dirty() const { return dirty_; }
void set_dirty(bool v) {
ZX_ASSERT(data() != nullptr);
dirty_ = v;
// Does nothing if the buffer is not dirty. The buffer is always marked clean after calling flush;
// it is up to the caller to handle errors appropriately.
[[nodiscard]] zx::result<> Flush();
// N.B. Take care with the 'as' methods and alignment. On some architectures, unaligned access is
// a problem, so if you're trying to access, say, a uint32_t at offset 5, you'll have an issue.
// Returns const T&.
template <typename T>
const T& as() const {
ZX_ASSERT(data() != nullptr);
ZX_ASSERT(sizeof(T) <= length_);
return *reinterpret_cast<T*>(data());
// Returns T&.
template <typename T>
T& as_mut() {
ZX_ASSERT(data() != nullptr);
ZX_ASSERT(sizeof(T) <= length_);
dirty_ = true;
return *reinterpret_cast<T*>(data());
void* data() const { return static_cast<uint8_t*>(buffer_.get()) + offset_; }
BufferPtr buffer_;
size_t offset_ = 0;
size_t length_ = 0;
bool dirty_ = false;
Flusher flusher_;
// BufferView is a typed version of BaseBufferView which will make it appear to be an array of
// objects of type T.
template <typename T>
class BufferView : public BaseBufferView {
BufferView() = default;
// |buffer| needs to be aligned sufficiently for T.
BufferView(BufferPtr buffer, size_t index, size_t count)
: BaseBufferView(buffer, sizeof(T) * index, sizeof(T) * count) {}
BufferView(BufferPtr buffer, size_t index, size_t count, Flusher flusher)
: BaseBufferView(buffer, sizeof(T) * index, sizeof(T) * count, std::move(flusher)) {}
// Movable, but not copyable.
BufferView(BufferView&& other) = default;
BufferView& operator=(BufferView&& other) = default;
const T* data() const { return &as<T>(); }
size_t count() const { return length() / sizeof(T); }
// Non mutating accessors.
const T& operator*() const { return as<T>(); }
const T& operator[](size_t index) const {
ZX_ASSERT(index < count());
return (&as<T>())[index];
// Mutating accessors.
T& mut_ref() { return as_mut<T>(); }
T& mut_ref(size_t index) {
ZX_ASSERT(index < count());
return (&as_mut<T>())[index];
} // namespace minfs