1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
mojo / core / ipcz_driver / ring_buffer.h [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.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:
raw_ptr<RingBuffer> buffer_;
// TODO(367764863) Rewrite to base::raw_span
RAW_PTR_EXCLUSION 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:
raw_ptr<RingBuffer> buffer_;
// TODO(367764863) Rewrite to base::raw_span
RAW_PTR_EXCLUSION 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_