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