xref: /aosp_15_r20/external/pigweed/seed/0109.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker.. _seed-0109:
2*61c4878aSAndroid Build Coastguard Worker
3*61c4878aSAndroid Build Coastguard Worker===========================
4*61c4878aSAndroid Build Coastguard Worker0109: Communication Buffers
5*61c4878aSAndroid Build Coastguard Worker===========================
6*61c4878aSAndroid Build Coastguard Worker.. seed::
7*61c4878aSAndroid Build Coastguard Worker   :number: 109
8*61c4878aSAndroid Build Coastguard Worker   :name: Communication Buffers
9*61c4878aSAndroid Build Coastguard Worker   :status: Accepted
10*61c4878aSAndroid Build Coastguard Worker   :proposal_date: 2023-08-28
11*61c4878aSAndroid Build Coastguard Worker   :cl: 168357
12*61c4878aSAndroid Build Coastguard Worker   :authors: Taylor Cramer
13*61c4878aSAndroid Build Coastguard Worker   :facilitator: Erik Gilling
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker-------
16*61c4878aSAndroid Build Coastguard WorkerSummary
17*61c4878aSAndroid Build Coastguard Worker-------
18*61c4878aSAndroid Build Coastguard WorkerThis SEED proposes that Pigweed adopt a standard buffer type for network-style
19*61c4878aSAndroid Build Coastguard Workercommunications. This buffer type will be used in the new sockets API
20*61c4878aSAndroid Build Coastguard Worker(see the recently-accepted `Communications SEED 107
21*61c4878aSAndroid Build Coastguard Worker<https://pigweed.dev/seed/0107-communications.html>`_ for more details), and
22*61c4878aSAndroid Build Coastguard Workerwill be carried throughout the communications stack as-appropriate.
23*61c4878aSAndroid Build Coastguard Worker
24*61c4878aSAndroid Build Coastguard Worker------------------------
25*61c4878aSAndroid Build Coastguard WorkerTop-Level Usage Examples
26*61c4878aSAndroid Build Coastguard Worker------------------------
27*61c4878aSAndroid Build Coastguard Worker
28*61c4878aSAndroid Build Coastguard WorkerSending a Proto Into a Socket
29*61c4878aSAndroid Build Coastguard Worker=============================
30*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
31*61c4878aSAndroid Build Coastguard Worker
32*61c4878aSAndroid Build Coastguard Worker   Allocator& msg_allocator = socket.Allocator();
33*61c4878aSAndroid Build Coastguard Worker   size_t size = EncodedProtoSize(some_proto);
34*61c4878aSAndroid Build Coastguard Worker   std::optional<pw::MultiBuf> buffer = msg_allocator.Allocate(size);
35*61c4878aSAndroid Build Coastguard Worker   if (!buffer) { return; }
36*61c4878aSAndroid Build Coastguard Worker   EncodeProto(some_proto, *buffer);
37*61c4878aSAndroid Build Coastguard Worker   socket.Send(std::move(*buffer));
38*61c4878aSAndroid Build Coastguard Worker
39*61c4878aSAndroid Build Coastguard Worker``Socket`` s provide an allocator which should be used to create a
40*61c4878aSAndroid Build Coastguard Worker``pw::MultiBuf``. The data to be sent is then filled into this buffer before
41*61c4878aSAndroid Build Coastguard Workerbeing sent. ``Socket`` must accept ``pw::MultiBuf`` s which were not created
42*61c4878aSAndroid Build Coastguard Workerby their own ``Allocator``, but this may incur a performance penalty.
43*61c4878aSAndroid Build Coastguard Worker
44*61c4878aSAndroid Build Coastguard WorkerZero-Copy Fragmentation
45*61c4878aSAndroid Build Coastguard Worker=======================
46*61c4878aSAndroid Build Coastguard Worker
47*61c4878aSAndroid Build Coastguard WorkerThe example above has a hidden feature: zero-copy fragmentation! The socket
48*61c4878aSAndroid Build Coastguard Workercan provide a ``pw::MultiBuf`` which is divided up into separate MTU-sized
49*61c4878aSAndroid Build Coastguard Workerchunks, each of which has reserved space for headers and/or footers:
50*61c4878aSAndroid Build Coastguard Worker
51*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
52*61c4878aSAndroid Build Coastguard Worker
53*61c4878aSAndroid Build Coastguard Worker   std::optional<pw::MultiBuf> Allocate(size_t size) {
54*61c4878aSAndroid Build Coastguard Worker     if (size == 0) { return pw::MultiBuf(); }
55*61c4878aSAndroid Build Coastguard Worker     size_t data_per_chunk = mtu_size_ - header_size_;
56*61c4878aSAndroid Build Coastguard Worker     size_t num_chunks = 1 + ((size - 1) / data_per_chunk);
57*61c4878aSAndroid Build Coastguard Worker     std::optional<pw::MultiBuf> buffer = pw::MultiBuf::WithCapacity(num_chunks);
58*61c4878aSAndroid Build Coastguard Worker     if (!buffer) { return std::nullopt; }
59*61c4878aSAndroid Build Coastguard Worker     for (size_t i = 0; i < num_chunks; i++) {
60*61c4878aSAndroid Build Coastguard Worker       // Note: we could allocate smaller than `mtu_size_` for the final
61*61c4878aSAndroid Build Coastguard Worker       // chunk. This is ommitted here for brevity.
62*61c4878aSAndroid Build Coastguard Worker       std::optional<pw::MultiBuf::Chunk> chunk = internal_alloc_.Allocate(mtu_size_);
63*61c4878aSAndroid Build Coastguard Worker       if (!chunk) { return std::nullopt; }
64*61c4878aSAndroid Build Coastguard Worker       // Reserve the header size by discarding the front of each buffer.
65*61c4878aSAndroid Build Coastguard Worker       chunk->DiscardFront(header_size_);
66*61c4878aSAndroid Build Coastguard Worker       buffer->Chunks()[i] = std::move(chunk);
67*61c4878aSAndroid Build Coastguard Worker     }
68*61c4878aSAndroid Build Coastguard Worker     return *buffer;
69*61c4878aSAndroid Build Coastguard Worker   }
70*61c4878aSAndroid Build Coastguard Worker
71*61c4878aSAndroid Build Coastguard WorkerThis code reserves ``header_size_`` bytes at the beginning of each ``Chunk``.
72*61c4878aSAndroid Build Coastguard WorkerWhen the caller writes into this memory and then passes it back to the socket,
73*61c4878aSAndroid Build Coastguard Workerthese bytes can be claimed for the header using the ``ClaimPrefix`` function.
74*61c4878aSAndroid Build Coastguard Worker
75*61c4878aSAndroid Build Coastguard WorkerOne usecase that seems to demand the ability to fragment like this is breaking
76*61c4878aSAndroid Build Coastguard Workerup ``SOCK_SEQPACKET`` messages which (at least on Unix / Linux) may be much larger
77*61c4878aSAndroid Build Coastguard Workerthan the MTU size: up to ``SO_SNDBUF`` (see `this man page
78*61c4878aSAndroid Build Coastguard Worker<https://man7.org/linux/man-pages/man7/socket.7.html>`_).
79*61c4878aSAndroid Build Coastguard Worker
80*61c4878aSAndroid Build Coastguard WorkerMultiplexing Incoming Data
81*61c4878aSAndroid Build Coastguard Worker==========================
82*61c4878aSAndroid Build Coastguard Worker
83*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
84*61c4878aSAndroid Build Coastguard Worker
85*61c4878aSAndroid Build Coastguard Worker   [[nodiscard]] bool SplitAndSend(pw::MultiBuf buffer) {
86*61c4878aSAndroid Build Coastguard Worker     std::optional<std::array<pw::MultiBuf, 2>> buffers =
87*61c4878aSAndroid Build Coastguard Worker       std::move(buffer).Split(split_index);
88*61c4878aSAndroid Build Coastguard Worker     if (!buffers) { return false; }
89*61c4878aSAndroid Build Coastguard Worker     socket_1_.Send(std::move(buffers->at(0)));
90*61c4878aSAndroid Build Coastguard Worker     socket_2_.Send(std::move(buffers->at(1)));
91*61c4878aSAndroid Build Coastguard Worker     return true;
92*61c4878aSAndroid Build Coastguard Worker   }
93*61c4878aSAndroid Build Coastguard Worker
94*61c4878aSAndroid Build Coastguard WorkerIncoming buffers can be split without copying, and the results can be forwarded
95*61c4878aSAndroid Build Coastguard Workerto multiple different ``Socket`` s, RPC services or clients.
96*61c4878aSAndroid Build Coastguard Worker
97*61c4878aSAndroid Build Coastguard Worker----------
98*61c4878aSAndroid Build Coastguard WorkerMotivation
99*61c4878aSAndroid Build Coastguard Worker----------
100*61c4878aSAndroid Build Coastguard WorkerToday, a Pigweed communications stack typically involves a number of different
101*61c4878aSAndroid Build Coastguard Workerbuffers.
102*61c4878aSAndroid Build Coastguard Worker
103*61c4878aSAndroid Build Coastguard Worker``pw_rpc`` users, for example, frequently use direct-memory access (DMA) or
104*61c4878aSAndroid Build Coastguard Workerother system primitives to read data into a buffer, apply some link-layer
105*61c4878aSAndroid Build Coastguard Workerprotocol such as HDLC which copies data into a second buffer, pass the resulting
106*61c4878aSAndroid Build Coastguard Workerdata into pw_rpc which parses it into its own buffer. Multiple sets of buffers
107*61c4878aSAndroid Build Coastguard Workerare present on the output side as well. Between DMA in and DMA out, it's easy
108*61c4878aSAndroid Build Coastguard Workerfor data to pass through six or more different buffers.
109*61c4878aSAndroid Build Coastguard Worker
110*61c4878aSAndroid Build Coastguard WorkerThese independent buffer systems introduce both time and space overhead. Aside
111*61c4878aSAndroid Build Coastguard Workerfrom the additional CPU time required to move the data around, users need to
112*61c4878aSAndroid Build Coastguard Workerensure that all of the different buffer pools involved along the way have enough
113*61c4878aSAndroid Build Coastguard Workerspace reserved to contain the entire message. Where caching is present, moving
114*61c4878aSAndroid Build Coastguard Workerthe memory between locations may create an additional delay by thrashing
115*61c4878aSAndroid Build Coastguard Workerbetween memory regions.
116*61c4878aSAndroid Build Coastguard Worker
117*61c4878aSAndroid Build Coastguard Worker--------
118*61c4878aSAndroid Build Coastguard WorkerProposal
119*61c4878aSAndroid Build Coastguard Worker--------
120*61c4878aSAndroid Build Coastguard Worker``pw::buffers::MultiBuf`` is a handle to a buffer optimized for use within
121*61c4878aSAndroid Build Coastguard WorkerPigweed's communications stack. It provides efficient, low-overhead buffer
122*61c4878aSAndroid Build Coastguard Workermanagement, and serves as a standard type for passing data between drivers,
123*61c4878aSAndroid Build Coastguard WorkerTCP/IP implementations, RPC 2.0, and transfer 2.0.
124*61c4878aSAndroid Build Coastguard Worker
125*61c4878aSAndroid Build Coastguard WorkerA single ``MultiBuf`` is a list of ``Chunk`` s of data. Each ``Chunk``
126*61c4878aSAndroid Build Coastguard Workerpoints to an exclusively-owned portion of a reference-counted allocation.
127*61c4878aSAndroid Build Coastguard Worker``MultiBuf`` s can be easily split, joined, prefixed, postfixed, or infixed
128*61c4878aSAndroid Build Coastguard Workerwithout needing to copy the underlying data.
129*61c4878aSAndroid Build Coastguard Worker
130*61c4878aSAndroid Build Coastguard WorkerThe memory pointed to by ``Chunk`` s is typically allocated from a pool
131*61c4878aSAndroid Build Coastguard Workerprovided by a ``Socket``. This allows the ``Socket`` to provide backpressure to
132*61c4878aSAndroid Build Coastguard Workercallers, and to ensure that memory is placed in DMA or shared memory regions
133*61c4878aSAndroid Build Coastguard Workeras-necessary.
134*61c4878aSAndroid Build Coastguard Worker
135*61c4878aSAndroid Build Coastguard WorkerIn-Memory Layout
136*61c4878aSAndroid Build Coastguard Worker================
137*61c4878aSAndroid Build Coastguard Worker
138*61c4878aSAndroid Build Coastguard WorkerThis diagram shows an example in-memory relationship between two buffers:
139*61c4878aSAndroid Build Coastguard Worker- ``Buffer1`` references one chunks from region A.
140*61c4878aSAndroid Build Coastguard Worker- ``Buffer2`` references two chunk from regions A and B.
141*61c4878aSAndroid Build Coastguard Worker
142*61c4878aSAndroid Build Coastguard Worker.. mermaid::
143*61c4878aSAndroid Build Coastguard Worker
144*61c4878aSAndroid Build Coastguard Worker   graph TB;
145*61c4878aSAndroid Build Coastguard Worker   Buffer1 --> Chunk1A
146*61c4878aSAndroid Build Coastguard Worker   Chunk1A -- "[0..64]" --> MemoryRegionA(Memory Region A)
147*61c4878aSAndroid Build Coastguard Worker   Chunk1A --> ChunkRegionTrackerA
148*61c4878aSAndroid Build Coastguard Worker   Buffer2 --> Chunk2A & Chunk2B
149*61c4878aSAndroid Build Coastguard Worker   Chunk2A --> ChunkRegionTrackerA
150*61c4878aSAndroid Build Coastguard Worker   Chunk2A -- "[64..128]" --> MemoryRegionA(Memory Region A)
151*61c4878aSAndroid Build Coastguard Worker   Chunk2B -- "[0..128]" --> MemoryRegionB
152*61c4878aSAndroid Build Coastguard Worker   Chunk2B --> ChunkRegionTrackerB
153*61c4878aSAndroid Build Coastguard Worker
154*61c4878aSAndroid Build Coastguard WorkerAPI
155*61c4878aSAndroid Build Coastguard Worker===
156*61c4878aSAndroid Build Coastguard Worker
157*61c4878aSAndroid Build Coastguard WorkerThe primary API is as follows:
158*61c4878aSAndroid Build Coastguard Worker
159*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp
160*61c4878aSAndroid Build Coastguard Worker
161*61c4878aSAndroid Build Coastguard Worker   /// An object that manages a single allocated region which is referenced
162*61c4878aSAndroid Build Coastguard Worker   /// by one or more chunks.
163*61c4878aSAndroid Build Coastguard Worker   class ChunkRegionTracker {
164*61c4878aSAndroid Build Coastguard Worker    public:
165*61c4878aSAndroid Build Coastguard Worker     ChunkRegionTracker(ByteSpan);
166*61c4878aSAndroid Build Coastguard Worker
167*61c4878aSAndroid Build Coastguard Worker     /// Creates the first ``Chunk`` referencing a whole region of memory.
168*61c4878aSAndroid Build Coastguard Worker     /// This must only be called once per ``ChunkRegionTracker``.
169*61c4878aSAndroid Build Coastguard Worker     Chunk ChunkForRegion();
170*61c4878aSAndroid Build Coastguard Worker
171*61c4878aSAndroid Build Coastguard Worker    protected:
172*61c4878aSAndroid Build Coastguard Worker     pw::Mutex lock();
173*61c4878aSAndroid Build Coastguard Worker
174*61c4878aSAndroid Build Coastguard Worker     /// Destroys the `ChunkRegionTracker`.
175*61c4878aSAndroid Build Coastguard Worker     ///
176*61c4878aSAndroid Build Coastguard Worker     /// Typical implementations will call `std::destroy_at(this)` and then
177*61c4878aSAndroid Build Coastguard Worker     /// free the memory associated with the tracker.
178*61c4878aSAndroid Build Coastguard Worker     virtual void Destroy();
179*61c4878aSAndroid Build Coastguard Worker
180*61c4878aSAndroid Build Coastguard Worker     /// Defines the entire span of the region being managed. ``Chunk`` s
181*61c4878aSAndroid Build Coastguard Worker     /// referencing this tracker may not expand beyond this region
182*61c4878aSAndroid Build Coastguard Worker     /// (or into one another).
183*61c4878aSAndroid Build Coastguard Worker     ///
184*61c4878aSAndroid Build Coastguard Worker     /// This region must not change for the lifetime of this
185*61c4878aSAndroid Build Coastguard Worker     /// ``ChunkRegionTracker``.
186*61c4878aSAndroid Build Coastguard Worker     virtual ByteSpan region();
187*61c4878aSAndroid Build Coastguard Worker
188*61c4878aSAndroid Build Coastguard Worker    private:
189*61c4878aSAndroid Build Coastguard Worker     /// Used to manage the internal linked-list of ``Chunk`` s that allows
190*61c4878aSAndroid Build Coastguard Worker     /// chunks to know whether or not they can expand to fill neighboring
191*61c4878aSAndroid Build Coastguard Worker     /// regions of memory.
192*61c4878aSAndroid Build Coastguard Worker     pw::Mutex lock_;
193*61c4878aSAndroid Build Coastguard Worker   };
194*61c4878aSAndroid Build Coastguard Worker
195*61c4878aSAndroid Build Coastguard Worker   /// A handle to a contiguous refcounted slice of data.
196*61c4878aSAndroid Build Coastguard Worker   ///
197*61c4878aSAndroid Build Coastguard Worker   /// Note: this Chunk may acquire a ``pw::sync::Mutex`` during destruction, and
198*61c4878aSAndroid Build Coastguard Worker   /// so must not be destroyed within ISR context.
199*61c4878aSAndroid Build Coastguard Worker   class Chunk {
200*61c4878aSAndroid Build Coastguard Worker    public:
201*61c4878aSAndroid Build Coastguard Worker     Chunk();
202*61c4878aSAndroid Build Coastguard Worker     Chunk(ChunkRegionTracker&);
203*61c4878aSAndroid Build Coastguard Worker     Chunk(Chunk&& other) noexcept;
204*61c4878aSAndroid Build Coastguard Worker     Chunk& operator=(Chunk&& other);
205*61c4878aSAndroid Build Coastguard Worker     ~Chunk();
206*61c4878aSAndroid Build Coastguard Worker     std::byte* data();
207*61c4878aSAndroid Build Coastguard Worker     const std::byte* data() const;
208*61c4878aSAndroid Build Coastguard Worker     ByteSpan span();
209*61c4878aSAndroid Build Coastguard Worker     ConstByteSpan span() const;
210*61c4878aSAndroid Build Coastguard Worker     size_t size() const;
211*61c4878aSAndroid Build Coastguard Worker
212*61c4878aSAndroid Build Coastguard Worker     std::byte& operator[](size_t index);
213*61c4878aSAndroid Build Coastguard Worker     std::byte operator[](size_t index) const;
214*61c4878aSAndroid Build Coastguard Worker
215*61c4878aSAndroid Build Coastguard Worker     /// Decrements the reference count on the underlying chunk of data and empties
216*61c4878aSAndroid Build Coastguard Worker     /// this handle so that `span()` now returns an empty (zero-sized) span.
217*61c4878aSAndroid Build Coastguard Worker     ///
218*61c4878aSAndroid Build Coastguard Worker     /// Does not modify the underlying data, but may cause it to be
219*61c4878aSAndroid Build Coastguard Worker     /// released if this was the only remaining ``Chunk`` referring to it.
220*61c4878aSAndroid Build Coastguard Worker     /// This is equivalent to ``{ Chunk _unused = std::move(chunk_ref); }``
221*61c4878aSAndroid Build Coastguard Worker     void Release();
222*61c4878aSAndroid Build Coastguard Worker
223*61c4878aSAndroid Build Coastguard Worker     /// Attempts to add `prefix_bytes` bytes to the front of this buffer by
224*61c4878aSAndroid Build Coastguard Worker     /// advancing its range backwards in memory. Returns `true` if the
225*61c4878aSAndroid Build Coastguard Worker     /// operation succeeded.
226*61c4878aSAndroid Build Coastguard Worker     ///
227*61c4878aSAndroid Build Coastguard Worker     /// This will only succeed if this `Chunk` points to a section of a chunk
228*61c4878aSAndroid Build Coastguard Worker     /// that has unreferenced bytes preceeding it. For example, a `Chunk`
229*61c4878aSAndroid Build Coastguard Worker     /// which has been shrunk using `DiscardFront` can then be re-expanded using
230*61c4878aSAndroid Build Coastguard Worker     /// `ClaimPrefix`.
231*61c4878aSAndroid Build Coastguard Worker     ///
232*61c4878aSAndroid Build Coastguard Worker     /// Note that this will fail if a preceding `Chunk` has claimed this
233*61c4878aSAndroid Build Coastguard Worker     /// memory using `ClaimSuffix`.
234*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool ClaimPrefix(size_t prefix_bytes);
235*61c4878aSAndroid Build Coastguard Worker
236*61c4878aSAndroid Build Coastguard Worker     /// Attempts to add `suffix_bytes` bytes to the back of this buffer by
237*61c4878aSAndroid Build Coastguard Worker     /// advancing its range forwards in memory. Returns `true` if the
238*61c4878aSAndroid Build Coastguard Worker     /// operation succeeded.
239*61c4878aSAndroid Build Coastguard Worker     ///
240*61c4878aSAndroid Build Coastguard Worker     /// This will only succeed if this `Chunk` points to a section of a chunk
241*61c4878aSAndroid Build Coastguard Worker     /// that has unreferenced bytes following it. For example, a `Chunk`
242*61c4878aSAndroid Build Coastguard Worker     /// which has been shrunk using `Truncate` can then be re-expanded using
243*61c4878aSAndroid Build Coastguard Worker     /// `ClaimSuffix`.
244*61c4878aSAndroid Build Coastguard Worker     ///
245*61c4878aSAndroid Build Coastguard Worker     /// Note that this will fail if a following `Chunk` has claimed this
246*61c4878aSAndroid Build Coastguard Worker     /// memory using `ClaimPrefix`.
247*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool ClaimSuffix(size_t suffix_bytes);
248*61c4878aSAndroid Build Coastguard Worker
249*61c4878aSAndroid Build Coastguard Worker     /// Shrinks this handle to refer to the data beginning at offset ``new_start``.
250*61c4878aSAndroid Build Coastguard Worker     ///
251*61c4878aSAndroid Build Coastguard Worker     /// Does not modify the underlying data.
252*61c4878aSAndroid Build Coastguard Worker     void DiscardFront(size_t new_start);
253*61c4878aSAndroid Build Coastguard Worker
254*61c4878aSAndroid Build Coastguard Worker     /// Shrinks this handle to refer to data in the range ``begin..<end``.
255*61c4878aSAndroid Build Coastguard Worker     ///
256*61c4878aSAndroid Build Coastguard Worker     /// Does not modify the underlying data.
257*61c4878aSAndroid Build Coastguard Worker     void Slice(size_t begin, size_t end);
258*61c4878aSAndroid Build Coastguard Worker
259*61c4878aSAndroid Build Coastguard Worker     /// Shrinks this handle to refer to only the first ``len`` bytes.
260*61c4878aSAndroid Build Coastguard Worker     ///
261*61c4878aSAndroid Build Coastguard Worker     /// Does not modify the underlying data.
262*61c4878aSAndroid Build Coastguard Worker     void Truncate(size_t len);
263*61c4878aSAndroid Build Coastguard Worker
264*61c4878aSAndroid Build Coastguard Worker     /// Splits this chunk in two, with the second chunk starting at `split_point`.
265*61c4878aSAndroid Build Coastguard Worker     std::array<Chunk, 2> Split(size_t split_point) &&;
266*61c4878aSAndroid Build Coastguard Worker   };
267*61c4878aSAndroid Build Coastguard Worker
268*61c4878aSAndroid Build Coastguard Worker   /// A handle to a sequence of potentially non-contiguous refcounted slices of
269*61c4878aSAndroid Build Coastguard Worker   /// data.
270*61c4878aSAndroid Build Coastguard Worker   class MultiBuf {
271*61c4878aSAndroid Build Coastguard Worker    public:
272*61c4878aSAndroid Build Coastguard Worker     struct Index {
273*61c4878aSAndroid Build Coastguard Worker       size_t chunk_index;
274*61c4878aSAndroid Build Coastguard Worker       size_t byte_index;
275*61c4878aSAndroid Build Coastguard Worker     };
276*61c4878aSAndroid Build Coastguard Worker
277*61c4878aSAndroid Build Coastguard Worker     MultiBuf();
278*61c4878aSAndroid Build Coastguard Worker
279*61c4878aSAndroid Build Coastguard Worker     /// Creates a ``MultiBuf`` pointing to a single, contiguous chunk of data.
280*61c4878aSAndroid Build Coastguard Worker     ///
281*61c4878aSAndroid Build Coastguard Worker     /// Returns ``std::nullopt`` if the ``ChunkList`` allocator is out of memory.
282*61c4878aSAndroid Build Coastguard Worker     static std::optional<MultiBuf> FromChunk(Chunk chunk);
283*61c4878aSAndroid Build Coastguard Worker
284*61c4878aSAndroid Build Coastguard Worker     /// Splits the ``MultiBuf`` into two separate buffers at ``split_point``.
285*61c4878aSAndroid Build Coastguard Worker     ///
286*61c4878aSAndroid Build Coastguard Worker     /// Returns ``std::nullopt`` if the ``ChunkList`` allocator is out of memory.
287*61c4878aSAndroid Build Coastguard Worker     std::optional<std::array<MultiBuf, 2>> Split(Index split_point) &&;
288*61c4878aSAndroid Build Coastguard Worker     std::optional<std::array<MultiBuf, 2>> Split(size_t split_point) &&;
289*61c4878aSAndroid Build Coastguard Worker
290*61c4878aSAndroid Build Coastguard Worker     /// Appends the contents of ``suffix`` to this ``MultiBuf`` without copying data.
291*61c4878aSAndroid Build Coastguard Worker     /// Returns ``false`` if the ``ChunkList`` allocator is out of memory.
292*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool Append(MultiBuf suffix);
293*61c4878aSAndroid Build Coastguard Worker
294*61c4878aSAndroid Build Coastguard Worker     /// Discards the first elements of ``MultiBuf`` up to (but not including)
295*61c4878aSAndroid Build Coastguard Worker     /// ``new_start``.
296*61c4878aSAndroid Build Coastguard Worker     ///
297*61c4878aSAndroid Build Coastguard Worker     /// Returns ``false`` if the ``ChunkList`` allocator is out of memory.
298*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool DiscardFront(Index new_start);
299*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool DiscardFront(size_t new_start);
300*61c4878aSAndroid Build Coastguard Worker
301*61c4878aSAndroid Build Coastguard Worker     /// Shifts and truncates this handle to refer to data in the range
302*61c4878aSAndroid Build Coastguard Worker     /// ``begin..<stop``.
303*61c4878aSAndroid Build Coastguard Worker     ///
304*61c4878aSAndroid Build Coastguard Worker     /// Does not modify the underlying data.
305*61c4878aSAndroid Build Coastguard Worker     ///
306*61c4878aSAndroid Build Coastguard Worker     /// Returns ``false`` if the ``ChunkList`` allocator is out of memory.
307*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool Slice(size_t begin, size_t end);
308*61c4878aSAndroid Build Coastguard Worker
309*61c4878aSAndroid Build Coastguard Worker     /// Discards the tail of this ``MultiBuf`` starting with ``first_index_to_drop``.
310*61c4878aSAndroid Build Coastguard Worker     /// Returns ``false`` if the ``ChunkList`` allocator is out of memory.
311*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool Truncate(Index first_index_to_drop);
312*61c4878aSAndroid Build Coastguard Worker     /// Discards the tail of this ``MultiBuf`` so that only ``len`` elements remain.
313*61c4878aSAndroid Build Coastguard Worker     /// Returns ``false`` if the ``ChunkList`` allocator is out of memory.
314*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool Truncate(size_t len);
315*61c4878aSAndroid Build Coastguard Worker
316*61c4878aSAndroid Build Coastguard Worker     /// Discards the elements beginning with ``cut_start`` and resuming at
317*61c4878aSAndroid Build Coastguard Worker     /// ``resume_point``.
318*61c4878aSAndroid Build Coastguard Worker     ///
319*61c4878aSAndroid Build Coastguard Worker     /// Returns ``false`` if the ``ChunkList`` allocator is out of memory.
320*61c4878aSAndroid Build Coastguard Worker     [[nodiscard]] bool DiscardSegment(Index cut_start, Index resume_point);
321*61c4878aSAndroid Build Coastguard Worker
322*61c4878aSAndroid Build Coastguard Worker     /// Returns an iterable over the ``Chunk``s of memory within this ``MultiBuf``.
323*61c4878aSAndroid Build Coastguard Worker     auto Chunks();
324*61c4878aSAndroid Build Coastguard Worker     auto Chunks() const;
325*61c4878aSAndroid Build Coastguard Worker
326*61c4878aSAndroid Build Coastguard Worker     /// Returns a ``BidirectionalIterator`` over the bytes in this ``MultiBuf``.
327*61c4878aSAndroid Build Coastguard Worker     ///
328*61c4878aSAndroid Build Coastguard Worker     /// Note that use of this iterator type may be less efficient than
329*61c4878aSAndroid Build Coastguard Worker     /// performing chunk-wise operations due to the noncontiguous nature of
330*61c4878aSAndroid Build Coastguard Worker     /// the iterator elements.
331*61c4878aSAndroid Build Coastguard Worker     auto begin();
332*61c4878aSAndroid Build Coastguard Worker     auto end();
333*61c4878aSAndroid Build Coastguard Worker
334*61c4878aSAndroid Build Coastguard Worker     /// Counts the total number of ``Chunk`` s.
335*61c4878aSAndroid Build Coastguard Worker     ///
336*61c4878aSAndroid Build Coastguard Worker     /// This function is ``O(n)`` in the number of ``Chunk`` s.
337*61c4878aSAndroid Build Coastguard Worker     size_t CalculateNumChunks() const;
338*61c4878aSAndroid Build Coastguard Worker
339*61c4878aSAndroid Build Coastguard Worker     /// Counts the total size in bytes of all ``Chunk`` s combined.
340*61c4878aSAndroid Build Coastguard Worker     ///
341*61c4878aSAndroid Build Coastguard Worker     /// This function is ``O(n)`` in the number of ``Chunk`` s.
342*61c4878aSAndroid Build Coastguard Worker     size_t CalculateTotalSize() const;
343*61c4878aSAndroid Build Coastguard Worker
344*61c4878aSAndroid Build Coastguard Worker     /// Returns an ``Index`` which can be used to provide constant-time access to
345*61c4878aSAndroid Build Coastguard Worker     /// the element at ``position``.
346*61c4878aSAndroid Build Coastguard Worker     ///
347*61c4878aSAndroid Build Coastguard Worker     /// Any mutation of this ``MultiBuf`` (e.g. ``Append``, ``DiscardFront``,
348*61c4878aSAndroid Build Coastguard Worker     /// ``Slice``, or ``Truncate``) may invalidate this ``Index``.
349*61c4878aSAndroid Build Coastguard Worker     Index IndexAt(size_t position) const;
350*61c4878aSAndroid Build Coastguard Worker   };
351*61c4878aSAndroid Build Coastguard Worker
352*61c4878aSAndroid Build Coastguard Worker
353*61c4878aSAndroid Build Coastguard Worker   class MultiBufAllocationFuture {
354*61c4878aSAndroid Build Coastguard Worker    public:
355*61c4878aSAndroid Build Coastguard Worker     Poll<std::optional<Buffer>> Poll(Context&);
356*61c4878aSAndroid Build Coastguard Worker   };
357*61c4878aSAndroid Build Coastguard Worker
358*61c4878aSAndroid Build Coastguard Worker   class MultiBufAllocationFuture {
359*61c4878aSAndroid Build Coastguard Worker    public:
360*61c4878aSAndroid Build Coastguard Worker     Poll<std::optional<MultiBuf::Chunk>> Poll(Context&);
361*61c4878aSAndroid Build Coastguard Worker   };
362*61c4878aSAndroid Build Coastguard Worker
363*61c4878aSAndroid Build Coastguard Worker   class MultiBufAllocator {
364*61c4878aSAndroid Build Coastguard Worker    public:
365*61c4878aSAndroid Build Coastguard Worker     std::optional<MultiBuf> Allocate(size_t size);
366*61c4878aSAndroid Build Coastguard Worker     std::optional<MultiBuf> Allocate(size_t min_size, size_t desired_size);
367*61c4878aSAndroid Build Coastguard Worker     std::optional<MultiBuf::Chunk> AllocateContiguous(size_t size);
368*61c4878aSAndroid Build Coastguard Worker     std::optional<MultiBuf::Chunk> AllocateContiguous(size_t min_size, size_t desired_size);
369*61c4878aSAndroid Build Coastguard Worker
370*61c4878aSAndroid Build Coastguard Worker     MultiBufAllocationFuture AllocateAsync(size_t size);
371*61c4878aSAndroid Build Coastguard Worker     MultiBufAllocationFuture AllocateAsync(size_t min_size, size_t desired_size);
372*61c4878aSAndroid Build Coastguard Worker     MultiBufChunkAllocationFuture AllocateContiguousAsync(size_t size);
373*61c4878aSAndroid Build Coastguard Worker     MultiBufChunkAllocationFuture AllocateContiguousAsync(size_t min_size, size_t desired_size);
374*61c4878aSAndroid Build Coastguard Worker
375*61c4878aSAndroid Build Coastguard Worker    protected:
376*61c4878aSAndroid Build Coastguard Worker     virtual std::optional<MultiBuf> DoAllocate(size_t min_size, size_t desired_size);
377*61c4878aSAndroid Build Coastguard Worker     virtual std::optional<MultiBuf::Chunk> DoAllocateContiguous(size_t min_size, size_t desired_size);
378*61c4878aSAndroid Build Coastguard Worker
379*61c4878aSAndroid Build Coastguard Worker     /// Invoked by the ``MultiBufAllocator`` to signal waiting futures that buffers of
380*61c4878aSAndroid Build Coastguard Worker     /// the provided sizes may be available for allocation.
381*61c4878aSAndroid Build Coastguard Worker     void AllocationAvailable(size_t size_available, size_t contiguous_size_available);
382*61c4878aSAndroid Build Coastguard Worker   };
383*61c4878aSAndroid Build Coastguard Worker
384*61c4878aSAndroid Build Coastguard Worker
385*61c4878aSAndroid Build Coastguard WorkerThe ``Chunk`` s in the prototype are stored in fallible dynamically-allocated
386*61c4878aSAndroid Build Coastguard Workerarrays, but they could be stored in vectors of a fixed maximum size. The ``Chunk`` s
387*61c4878aSAndroid Build Coastguard Workercannot be stored as an intrusively-linked list because this would require per-``Chunk``
388*61c4878aSAndroid Build Coastguard Workeroverhead in the underlying buffer, and there may be any number of ``Chunk`` s within
389*61c4878aSAndroid Build Coastguard Workerthe same buffer.
390*61c4878aSAndroid Build Coastguard Worker
391*61c4878aSAndroid Build Coastguard WorkerMultiple ``Chunk`` s may not refer to the same memory, but they may refer to
392*61c4878aSAndroid Build Coastguard Workernon-overlapping sections of memory within the same region. When one ``Chunk``
393*61c4878aSAndroid Build Coastguard Workerwithin a region is deallocated, a neighboring chunk may expand to use its space.
394*61c4878aSAndroid Build Coastguard Worker
395*61c4878aSAndroid Build Coastguard Worker--------------------
396*61c4878aSAndroid Build Coastguard WorkerVectorized Structure
397*61c4878aSAndroid Build Coastguard Worker--------------------
398*61c4878aSAndroid Build Coastguard WorkerThe most significant design choices made in this API is supporting vectorized
399*61c4878aSAndroid Build Coastguard WorkerIO via a list of ``Chunk`` s. While this does carry an additional overhead, it
400*61c4878aSAndroid Build Coastguard Workeris strongly motivated by the desire to carry data throughout the communications
401*61c4878aSAndroid Build Coastguard Workerstack without needing a copy. By carrying a list of ``Chunk`` s, ``MultiBuf``
402*61c4878aSAndroid Build Coastguard Workerallows data to be prefixed, suffixed, infixed, or split without incurring the
403*61c4878aSAndroid Build Coastguard Workeroverhead of a separate allocation and copy.
404*61c4878aSAndroid Build Coastguard Worker
405*61c4878aSAndroid Build Coastguard Worker--------------------------------------------------------------------------
406*61c4878aSAndroid Build Coastguard WorkerManaging Allocations with ``MultiBufAllocator`` and ``ChunkRegionTracker``
407*61c4878aSAndroid Build Coastguard Worker--------------------------------------------------------------------------
408*61c4878aSAndroid Build Coastguard Worker``pw::MultiBuf`` is not associated with a concrete allocator implementation.
409*61c4878aSAndroid Build Coastguard WorkerInstead, it delegates the creation of buffers to implementations of
410*61c4878aSAndroid Build Coastguard Workerthe ``MultiBufAllocator`` base class. This allows different allocator
411*61c4878aSAndroid Build Coastguard Workerimplementations to vend out ``pw::MultiBuf`` s that are optimized for use with a
412*61c4878aSAndroid Build Coastguard Workerparticular communications stack.
413*61c4878aSAndroid Build Coastguard Worker
414*61c4878aSAndroid Build Coastguard WorkerFor example, a communications stack which runs off of shared memory or specific
415*61c4878aSAndroid Build Coastguard WorkerDMA'able regions might choose to allocate memory out of those regions to allow
416*61c4878aSAndroid Build Coastguard Workerfor zero-copy writes.
417*61c4878aSAndroid Build Coastguard Worker
418*61c4878aSAndroid Build Coastguard WorkerAdditionally, custom allocator implementations can reserve headers, footers, or
419*61c4878aSAndroid Build Coastguard Workereven split a ``pw::MultiBuf`` into multiple chunks in order to provide zero-copy
420*61c4878aSAndroid Build Coastguard Workerfragmentation.
421*61c4878aSAndroid Build Coastguard Worker
422*61c4878aSAndroid Build Coastguard WorkerDeallocation of these regions is managed through the ``ChunkRegionTracker``.
423*61c4878aSAndroid Build Coastguard WorkerWhen no more ``Chunk`` s associated with a ``ChunkRegionTracker`` exist,
424*61c4878aSAndroid Build Coastguard Workerthe ``ChunkRegionTracker`` receives a ``Destroy`` call to release both the
425*61c4878aSAndroid Build Coastguard Workerregion and the ``ChunkRegionTracker``.
426*61c4878aSAndroid Build Coastguard Worker
427*61c4878aSAndroid Build Coastguard WorkerThe ``MultiBufAllocator`` can place the ``ChunkRegionTracker`` wherever it
428*61c4878aSAndroid Build Coastguard Workerwishes, including as a header or footer for the underlying region allocation.
429*61c4878aSAndroid Build Coastguard WorkerThis is not required, however, as memory in regions like shared or DMA'able
430*61c4878aSAndroid Build Coastguard Workermemory might be limited, in which case the ``ChunkRegionTracker`` can be
431*61c4878aSAndroid Build Coastguard Workerstored elsewhere.
432*61c4878aSAndroid Build Coastguard Worker
433*61c4878aSAndroid Build Coastguard Worker-----------------------------------------------------
434*61c4878aSAndroid Build Coastguard WorkerCompatibility With Existing Communications Interfaces
435*61c4878aSAndroid Build Coastguard Worker-----------------------------------------------------
436*61c4878aSAndroid Build Coastguard Worker
437*61c4878aSAndroid Build Coastguard WorkerlwIP
438*61c4878aSAndroid Build Coastguard Worker====
439*61c4878aSAndroid Build Coastguard Worker`Lightweight IP stack (lwIP)
440*61c4878aSAndroid Build Coastguard Worker<https://www.nongnu.org/lwip>`_
441*61c4878aSAndroid Build Coastguard Workeris a TCP/IP implementation designed for use on small embedded systems. Some
442*61c4878aSAndroid Build Coastguard WorkerPigweed platforms may choose to use lwIP as the backend for their ``Socket``
443*61c4878aSAndroid Build Coastguard Workerimplementations, so it's important that ``pw::MultiBuf`` interoperate efficiently
444*61c4878aSAndroid Build Coastguard Workerwith their native buffer type.
445*61c4878aSAndroid Build Coastguard Worker
446*61c4878aSAndroid Build Coastguard WorkerlwIP has its own buffer type, `pbuf
447*61c4878aSAndroid Build Coastguard Worker<https://www.nongnu.org/lwip/2_1_x/structpbuf.html>`_ optimized for use with
448*61c4878aSAndroid Build Coastguard Worker`zero-copy applications
449*61c4878aSAndroid Build Coastguard Worker<https://www.nongnu.org/lwip/2_1_x/zerocopyrx.html>`_.
450*61c4878aSAndroid Build Coastguard Worker``pbuf`` represents buffers as linked lists of reference-counted ``pbuf`` s
451*61c4878aSAndroid Build Coastguard Workerwhich each have a pointer to their payload, a length, and some metadata. While
452*61c4878aSAndroid Build Coastguard Workerthis does not precisely match the representation of ``pw::MultiBuf``, it is
453*61c4878aSAndroid Build Coastguard Workerpossible to construct a ``pbuf`` list which points at the various chunks of a
454*61c4878aSAndroid Build Coastguard Worker``pw::MultiBuf`` without needing to perform a copy of the data.
455*61c4878aSAndroid Build Coastguard Worker
456*61c4878aSAndroid Build Coastguard WorkerSimilarly, a ``pw::MultiBuf`` can refer to a ``pbuf`` by using each ``pbuf`` as
457*61c4878aSAndroid Build Coastguard Workera "``Chunk`` region", holding a reference count on the region's ``pbuf`` so
458*61c4878aSAndroid Build Coastguard Workerlong as any ``Chunk`` continues to reference the data referenced by that
459*61c4878aSAndroid Build Coastguard Workerbuffer.
460*61c4878aSAndroid Build Coastguard Worker
461*61c4878aSAndroid Build Coastguard Worker------------------
462*61c4878aSAndroid Build Coastguard WorkerExisting Solutions
463*61c4878aSAndroid Build Coastguard Worker------------------
464*61c4878aSAndroid Build Coastguard Worker
465*61c4878aSAndroid Build Coastguard WorkerLinux's ``sk_buff``
466*61c4878aSAndroid Build Coastguard Worker===================
467*61c4878aSAndroid Build Coastguard WorkerLinux has a similar buffer structure called `sk_buff
468*61c4878aSAndroid Build Coastguard Worker<https://docs.kernel.org/networking/skbuff.html#struct-sk-buff>`_.
469*61c4878aSAndroid Build Coastguard WorkerIt differs in a few significant ways:
470*61c4878aSAndroid Build Coastguard Worker
471*61c4878aSAndroid Build Coastguard WorkerIt provides separate ``head``, ``data``, ``tail``, and ``end`` pointers.
472*61c4878aSAndroid Build Coastguard WorkerOther scatter-gather fragments are supplied using the ``frags[]`` structure.
473*61c4878aSAndroid Build Coastguard Worker
474*61c4878aSAndroid Build Coastguard WorkerSeparately, it has a ``frags_list`` of IP fragments which is created via calls to
475*61c4878aSAndroid Build Coastguard Worker``ip_push_pending_frames``. Fragments are supplied by the ``frags[]`` payload in
476*61c4878aSAndroid Build Coastguard Workeraddition to the ``skb_shared_info.frag_list`` pointing to the tail.
477*61c4878aSAndroid Build Coastguard Worker
478*61c4878aSAndroid Build Coastguard Worker``sk_buff`` reference-counts not only the underlying chunks of memory, but also the
479*61c4878aSAndroid Build Coastguard Worker``sk_buff`` structure itself. This allows for clones of ``sk_buff`` without
480*61c4878aSAndroid Build Coastguard Workermanipulating the reference counts of the individual chunks. We anticipate that
481*61c4878aSAndroid Build Coastguard Workercloning a whole ``pw::buffers::MultiBuf`` will be uncommon enough that it is
482*61c4878aSAndroid Build Coastguard Workerbetter to keep these structures single-owner (and mutable) rather than sharing
483*61c4878aSAndroid Build Coastguard Workerand reference-counting them.
484*61c4878aSAndroid Build Coastguard Worker
485*61c4878aSAndroid Build Coastguard WorkerFreeBSD's ``mbuf``
486*61c4878aSAndroid Build Coastguard Worker==================
487*61c4878aSAndroid Build Coastguard WorkerFreeBSD uses a design called `mbuf
488*61c4878aSAndroid Build Coastguard Worker<https://man.freebsd.org/cgi/man.cgi?query=mbuf>`_
489*61c4878aSAndroid Build Coastguard Workerwhich interestingly allows data within the middle of a buffer to be given a
490*61c4878aSAndroid Build Coastguard Workerspecified alignment, allowing data to be prepended within the same buffer.
491*61c4878aSAndroid Build Coastguard WorkerThis is similar to the structure of ``Chunk``, which may reference data in the
492*61c4878aSAndroid Build Coastguard Workermiddle of an allocated buffer, allowing prepending without a copy.
493