1 // Copyright 2017 The Fuchsia Authors 2 // 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file or at 5 // https://opensource.org/licenses/MIT 6 7 #pragma once 8 9 #include <stdint.h> 10 11 #include <lib/user_copy/user_ptr.h> 12 #include <zircon/types.h> 13 #include <fbl/intrusive_single_list.h> 14 15 // MBufChain is a container for storing a stream of bytes or a sequence of datagrams. 16 // 17 // It's designed to back sockets and channels. Don't simultaneously store stream data and datagrams 18 // in a single instance. 19 class MBufChain { 20 public: 21 MBufChain() = default; 22 ~MBufChain(); 23 24 // Writes |len| bytes of stream data from |src| and sets |written| to number of bytes written. 25 // 26 // Returns an error on failure. 27 zx_status_t WriteStream(user_in_ptr<const void> src, size_t len, size_t* written); 28 29 // Writes a datagram of |len| bytes from |src| and sets |written| to number of bytes written. 30 // 31 // This operation is atomic in that either the entire datagram is written successfully or the 32 // chain is unmodified. 33 // 34 // Writing a zero-length datagram is an error. 35 // 36 // Returns an error on failure. 37 zx_status_t WriteDatagram(user_in_ptr<const void> src, size_t len, size_t* written); 38 39 // Reads upto |len| bytes from chain into |dst|. 40 // 41 // When |datagram| is false, the data in the chain is treated as a stream (no boundaries). 42 // 43 // When |datagram| is true, the data in the chain is treated as a sequence of datagrams and the 44 // call will read at most one datagram. If |len| is too small to read a complete datagram, a 45 // partial datagram is returned and its remaining bytes are discarded. 46 // 47 // Returns number of bytes read. 48 size_t Read(user_out_ptr<void> dst, size_t len, bool datagram); 49 50 bool is_full() const; 51 bool is_empty() const; 52 53 // Returns number of bytes stored in the chain. 54 // When |datagram| is true, return only the number of bytes in the first 55 // datagram, or 0 if in ZX_SOCKET_STREAM mode. 56 size_t size(bool datagram = false) const { 57 if (datagram && size_) { 58 return tail_.front().pkt_len_; 59 } 60 return size_; 61 } 62 63 // Returns the maximum number of bytes that can be stored in the chain. max_size()64 size_t max_size() const { return kSizeMax; } 65 66 private: 67 // An MBuf is a small fixed-size chainable memory buffer. 68 struct MBuf : public fbl::SinglyLinkedListable<MBuf*> { 69 // 8 for the linked list and 4 for the explicit uint32_t fields. 70 static constexpr size_t kHeaderSize = 8 + (4 * 4); 71 // 16 is for the malloc header. 72 static constexpr size_t kMallocSize = 2048 - 16; 73 static constexpr size_t kPayloadSize = kMallocSize - kHeaderSize; 74 75 // Returns number of bytes of free space in this MBuf. 76 size_t rem() const; 77 78 uint32_t off_ = 0u; 79 uint32_t len_ = 0u; 80 // pkt_len_ is set to the total number of bytes in a packet 81 // when a socket is in ZX_SOCKET_DATAGRAM mode. A pkt_len_ of 82 // 0 means this mbuf is part of the body of a packet. 83 // 84 // Always 0 in ZX_SOCKET_STREAM mode. 85 uint32_t pkt_len_ = 0u; 86 uint32_t unused_; 87 char data_[kPayloadSize] = {0}; 88 // TODO: maybe union data_ with char* blocks for large messages 89 }; 90 static_assert(sizeof(MBuf) == MBuf::kMallocSize, ""); 91 92 static constexpr size_t kSizeMax = 128 * MBuf::kPayloadSize; 93 94 MBuf* AllocMBuf(); 95 void FreeMBuf(MBuf* buf); 96 97 fbl::SinglyLinkedList<MBuf*> freelist_; 98 fbl::SinglyLinkedList<MBuf*> tail_; 99 MBuf* head_ = nullptr; 100 size_t size_ = 0u; 101 }; 102