blob: bc3981fb960638872a7fa9985f44bef5d5c1c439 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_CORE_IPCZ_DRIVER_RING_BUFFER_H_
#define MOJO_CORE_IPCZ_DRIVER_RING_BUFFER_H_
#include <cstddef>
#include <cstdint>
#include <utility>
#include "base/check.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/memory/scoped_refptr.h"
#include "mojo/core/ipcz_driver/shared_buffer.h"
#include "mojo/core/ipcz_driver/shared_buffer_mapping.h"
#include "mojo/core/system_impl_export.h"
namespace mojo::core::ipcz_driver {
// RingBuffer implements a simple circular data buffer over a shared memory
// mapping. This class is not thread-safe and it makes no effort to synchronize
// access to the underlying buffer.
class MOJO_SYSTEM_IMPL_EXPORT RingBuffer {
public:
// A DirectWriter exposes the first contiguous span of available capacity
// within a RingBuffer for direct writing.
class DirectWriter {
public:
using Bytes = base::span<uint8_t>;
// Constructs a new DirectWriter to write into `buffer`.
explicit DirectWriter(RingBuffer& buffer)
: buffer_(&buffer), bytes_(buffer_->GetAvailableCapacityView()) {}
DirectWriter(const DirectWriter&) = delete;
DirectWriter& operator=(const DirectWriter&) = delete;
DirectWriter(DirectWriter&& other)
: buffer_(std::exchange(other.buffer_, nullptr)),
bytes_(other.bytes_) {}
// The span of bytes available for writing. Any writes here are incomplete
// until Commit() is called.
Bytes bytes() const { return bytes_; }
// Commits the first `n` bytes of `bytes()` into the buffer and invalidates
// the DirectWriter. Returns true on success or false if `n` exceeds the
// size of `bytes()`.
bool Commit(size_t n) && {
return n <= bytes_.size() && buffer_->ExtendDataRange(n);
}
private:
// This field is not a raw_ptr<> because it was filtered by the rewriter
// for: #union
RAW_PTR_EXCLUSION RingBuffer* buffer_;
const Bytes bytes_;
};
// A DirectReader exposes the first contiguous span of data within a
// RingBuffer for direct reading.
class DirectReader {
public:
using Bytes = base::span<const uint8_t>;
explicit DirectReader(RingBuffer& buffer)
: buffer_(&buffer), bytes_(buffer_->GetReadableDataView()) {}
DirectReader(const DirectReader&) = delete;
DirectReader& operator=(const DirectReader&) = delete;
DirectReader(DirectReader&& other)
: buffer_(std::exchange(other.buffer_, nullptr)),
bytes_(other.bytes_) {}
// The span of bytes available for reading.
Bytes bytes() const { return bytes_; }
// Consumes `n` bytes from the front of `bytes()`, making that space
// available for subsequent writes and invalidating the DirectReader.
// Returns true on success or false if `n` exceeds the size of `bytes()`.
bool Consume(size_t n) && {
DCHECK(buffer_);
return n <= bytes_.size() && buffer_->DiscardAll(n);
}
private:
// This field is not a raw_ptr<> because it was filtered by the rewriter
// for: #union
RAW_PTR_EXCLUSION RingBuffer* buffer_;
const Bytes bytes_;
};
// Constructs a new empty RingBuffer backed by the entire region of `mapping`.
explicit RingBuffer(scoped_refptr<SharedBufferMapping> mapping);
RingBuffer(const RingBuffer&) = delete;
RingBuffer& operator=(const RingBuffer&) = delete;
~RingBuffer();
SharedBufferMapping& mapping() const { return *mapping_; }
// The total capacity of this RingBuffer in bytes.
size_t capacity() const { return mapping_->size(); }
// The number of bytes of data currently in the RingBuffer.
size_t data_size() const { return data_range_.size; }
// The number of bytes of available capacity in the RingBuffer.
size_t available_capacity() const { return capacity() - data_size(); }
// Attempts to append `source` to the buffer. Returns the number of bytes
// written, which may be less than the length of `source` if there was
// insufficient capacity available.
size_t Write(base::span<const uint8_t> source);
// Like Write() but this only writes data if there's enough room for all of
// `source`. Returns true if the write happened and false otherwise.
bool WriteAll(base::span<const uint8_t> source);
// Attempts to copy bytes from the front of the buffer and into `target`,
// discarding them from the buffer. May consume less than the length of
// `target` if the buffer doesn't have enough data to read. Returns the number
// of bytes consumed.
size_t Read(base::span<uint8_t> target);
// Like Read() but this only reads data if there's enough to fill `target`.
// Returns true if the read happened and false otherwise.
bool ReadAll(base::span<uint8_t> target);
// Same semantics as Read() but no data is discarded from the buffer.
size_t Peek(base::span<uint8_t> target);
// Like Peek() but this only reads data if there's enough to fill `target`.
// Returns true if the read happened and false otherwise.
bool PeekAll(base::span<uint8_t> target);
// Attempts to discard `n` bytes from the front of the buffer. Returns the
// number of bytes discarded, which may be smaller than `n` if there weren't
// enough bytes in the buffer.
size_t Discard(size_t n);
// Like Discard() but this only discards data if there are `n` bytes of data
// to discard. Returns true on success or false if there wasn't enough data.
bool DiscardAll(size_t n);
// Attempts to extend the range of readable data in this RingBuffer by `n`
// bytes, implying that the data has already been populated within the buffer
// immediately following any currently readable data. Returns true if
// successful or false if `n` exceeds the available capacity of the buffer.
bool ExtendDataRange(size_t n);
// Serialize and deserialize the state of this RingBuffer.
struct SerializedState {
uint32_t offset = 0;
uint32_t size = 0;
};
static_assert(sizeof(SerializedState) == 8, "Invalid SerializedState size");
void Serialize(SerializedState& state);
bool Deserialize(const SerializedState& state);
private:
// Range describes a range of bytes within the underlying physical buffer.
// Ranges are circular. For a 16-byte buffer, a Range of 8 bytes at offset 13
// refers to bytes 13-15 followed immediately by bytes 0-4:
//
// Range(13, 8)
// end start
// v v
// Buffer (16 bytes) [xxxxx........xxx]
// ^^^^^ ^^^
// bytes 0-4 bytes 13-15
struct Range {
// The buffer offset of the first byte in the range.
size_t offset = 0;
// The size of the range in bytes. Must be no larger than the buffer size.
size_t size = 0;
};
// Returns one or two non-empty spans of data which correspond precisely to
// the range of bytes within this buffer described by `range`.
using SplitBytes = std::pair<base::span<uint8_t>, base::span<uint8_t>>;
SplitBytes MapRange(const Range& range) const;
// Returns the complement of `range` within the underlying buffer: that is
// the range which includes only all bytes NOT in `range`.
Range ComplementRange(const Range& range) const;
// Returns the longest contiguous span of available capacity within the
// buffer, starting from the first byte of available capacity.
base::span<uint8_t> GetAvailableCapacityView() const;
// Returns the longest contiguous span of readable data within the buffer,
// starting from the first byte of readable data.
base::span<const uint8_t> GetReadableDataView() const;
// The memory mapping which backs this RingBuffer.
const scoped_refptr<SharedBufferMapping> mapping_;
// Tracks the range of bytes currently occupied by readable data.
Range data_range_{.offset = 0, .size = 0};
};
} // namespace mojo::core::ipcz_driver
#endif // MOJO_CORE_IPCZ_DRIVER_RING_BUFFER_H_