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