QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1 | // Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_ |
| 6 | #define QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_ |
| 7 | |
| 8 | // QuicStreamSequencerBuffer is a circular stream buffer with random write and |
| 9 | // in-sequence read. It consists of a vector of pointers pointing |
| 10 | // to memory blocks created as needed and an interval set recording received |
| 11 | // data. |
| 12 | // - Data are written in with offset indicating where it should be in the |
| 13 | // stream, and the buffer grown as needed (up to the maximum buffer capacity), |
| 14 | // without expensive copying (extra blocks are allocated). |
| 15 | // - Data can be read from the buffer if there is no gap before it, |
| 16 | // and the buffer shrinks as the data are consumed. |
| 17 | // - An upper limit on the number of blocks in the buffer provides an upper |
| 18 | // bound on memory use. |
| 19 | // |
| 20 | // This class is thread-unsafe. |
| 21 | // |
| 22 | // QuicStreamSequencerBuffer maintains a concept of the readable region, which |
| 23 | // contains all written data that has not been read. |
| 24 | // It promises stability of the underlying memory addresses in the readable |
| 25 | // region, so pointers into it can be maintained, and the offset of a pointer |
| 26 | // from the start of the read region can be calculated. |
| 27 | // |
| 28 | // Expected Use: |
| 29 | // QuicStreamSequencerBuffer buffer(2.5 * 8 * 1024); |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 30 | // std::string source(1024, 'a'); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 31 | // QuicStringPiece string_piece(source.data(), source.size()); |
| 32 | // size_t written = 0; |
| 33 | // buffer.OnStreamData(800, string_piece, GetEpollClockNow(), &written); |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 34 | // source = std::string{800, 'b'}; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 35 | // QuicStringPiece string_piece1(source.data(), 800); |
| 36 | // // Try to write to [1, 801), but should fail due to overlapping, |
| 37 | // // res should be QUIC_INVALID_STREAM_DATA |
| 38 | // auto res = buffer.OnStreamData(1, string_piece1, &written)); |
| 39 | // // write to [0, 800), res should be QUIC_NO_ERROR |
| 40 | // auto res = buffer.OnStreamData(0, string_piece1, GetEpollClockNow(), |
| 41 | // &written); |
| 42 | // |
| 43 | // // Read into a iovec array with total capacity of 120 bytes. |
| 44 | // char dest[120]; |
| 45 | // iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40}, |
| 46 | // iovec{dest + 80, 40}}; |
| 47 | // size_t read = buffer.Readv(iovecs, 3); |
| 48 | // |
| 49 | // // Get single readable region. |
| 50 | // iovec iov; |
| 51 | // buffer.GetReadableRegion(iov); |
| 52 | // |
| 53 | // // Get readable regions from [256, 1024) and consume some of it. |
| 54 | // iovec iovs[2]; |
| 55 | // int iov_count = buffer.GetReadableRegions(iovs, 2); |
| 56 | // // Consume some bytes in iovs, returning number of bytes having been |
| 57 | // consumed. |
| 58 | // size_t consumed = consume_iovs(iovs, iov_count); |
| 59 | // buffer.MarkConsumed(consumed); |
| 60 | |
| 61 | #include <cstddef> |
| 62 | #include <functional> |
| 63 | #include <list> |
| 64 | #include <memory> |
vasilvv | 872e7a3 | 2019-03-12 16:42:44 -0700 | [diff] [blame] | 65 | #include <string> |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 66 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 67 | #include "net/third_party/quiche/src/quic/core/quic_interval_set.h" |
| 68 | #include "net/third_party/quiche/src/quic/core/quic_packets.h" |
| 69 | #include "net/third_party/quiche/src/quic/core/quic_types.h" |
| 70 | #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" |
| 71 | #include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 72 | #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" |
| 73 | |
| 74 | namespace quic { |
| 75 | |
| 76 | namespace test { |
| 77 | class QuicStreamSequencerBufferPeer; |
| 78 | } // namespace test |
| 79 | |
| 80 | class QUIC_EXPORT_PRIVATE QuicStreamSequencerBuffer { |
| 81 | public: |
| 82 | // Size of blocks used by this buffer. |
| 83 | // Choose 8K to make block large enough to hold multiple frames, each of |
| 84 | // which could be up to 1.5 KB. |
| 85 | static const size_t kBlockSizeBytes = 8 * 1024; // 8KB |
| 86 | |
| 87 | // The basic storage block used by this buffer. |
dschinazi | f25169a | 2019-10-23 08:12:18 -0700 | [diff] [blame] | 88 | struct QUIC_EXPORT_PRIVATE BufferBlock { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 89 | char buffer[kBlockSizeBytes]; |
| 90 | }; |
| 91 | |
| 92 | explicit QuicStreamSequencerBuffer(size_t max_capacity_bytes); |
| 93 | QuicStreamSequencerBuffer(const QuicStreamSequencerBuffer&) = delete; |
| 94 | QuicStreamSequencerBuffer(QuicStreamSequencerBuffer&&) = default; |
| 95 | QuicStreamSequencerBuffer& operator=(const QuicStreamSequencerBuffer&) = |
| 96 | delete; |
| 97 | ~QuicStreamSequencerBuffer(); |
| 98 | |
| 99 | // Free the space used to buffer data. |
| 100 | void Clear(); |
| 101 | |
| 102 | // Returns true if there is nothing to read in this buffer. |
| 103 | bool Empty() const; |
| 104 | |
| 105 | // Called to buffer new data received for this stream. If the data was |
| 106 | // successfully buffered, returns QUIC_NO_ERROR and stores the number of |
| 107 | // bytes buffered in |bytes_buffered|. Returns an error otherwise. |
| 108 | QuicErrorCode OnStreamData(QuicStreamOffset offset, |
| 109 | QuicStringPiece data, |
| 110 | size_t* bytes_buffered, |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 111 | std::string* error_details); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 112 | |
| 113 | // Reads from this buffer into given iovec array, up to number of iov_len |
| 114 | // iovec objects and returns the number of bytes read. |
| 115 | QuicErrorCode Readv(const struct iovec* dest_iov, |
| 116 | size_t dest_count, |
| 117 | size_t* bytes_read, |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 118 | std::string* error_details); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 119 | |
| 120 | // Returns the readable region of valid data in iovec format. The readable |
| 121 | // region is the buffer region where there is valid data not yet read by |
| 122 | // client. |
| 123 | // Returns the number of iovec entries in |iov| which were populated. |
| 124 | // If the region is empty, one iovec entry with 0 length |
| 125 | // is returned, and the function returns 0. If there are more readable |
| 126 | // regions than |iov_size|, the function only processes the first |
| 127 | // |iov_size| of them. |
| 128 | int GetReadableRegions(struct iovec* iov, int iov_len) const; |
| 129 | |
| 130 | // Fills in one iovec with data from the next readable region. |
| 131 | // Returns false if there is no readable region available. |
| 132 | bool GetReadableRegion(iovec* iov) const; |
| 133 | |
bnc | 7b3e0a9 | 2019-06-24 16:06:45 -0700 | [diff] [blame] | 134 | // Returns true and sets |*iov| to point to a region starting at |offset|. |
| 135 | // Returns false if no data can be read at |offset|, which can be because data |
| 136 | // has not been received yet or it is already consumed. |
| 137 | // Does not consume data. |
| 138 | bool PeekRegion(QuicStreamOffset offset, iovec* iov) const; |
| 139 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 140 | // Called after GetReadableRegions() to free up |bytes_used| space if these |
| 141 | // bytes are processed. |
| 142 | // Pre-requisite: bytes_used <= available bytes to read. |
| 143 | bool MarkConsumed(size_t bytes_buffered); |
| 144 | |
| 145 | // Deletes and records as consumed any buffered data and clear the buffer. |
| 146 | // (To be called only after sequencer's StopReading has been called.) |
| 147 | size_t FlushBufferedFrames(); |
| 148 | |
| 149 | // Free the memory of buffered data. |
| 150 | void ReleaseWholeBuffer(); |
| 151 | |
| 152 | // Whether there are bytes can be read out. |
| 153 | bool HasBytesToRead() const; |
| 154 | |
| 155 | // Count how many bytes have been consumed (read out of buffer). |
| 156 | QuicStreamOffset BytesConsumed() const; |
| 157 | |
| 158 | // Count how many bytes are in buffer at this moment. |
| 159 | size_t BytesBuffered() const; |
| 160 | |
| 161 | // Returns number of bytes available to be read out. |
| 162 | size_t ReadableBytes() const; |
| 163 | |
| 164 | private: |
| 165 | friend class test::QuicStreamSequencerBufferPeer; |
| 166 | |
| 167 | // Copies |data| to blocks_, sets |bytes_copy|. Returns true if the copy is |
| 168 | // successful. Otherwise, sets |error_details| and returns false. |
| 169 | bool CopyStreamData(QuicStreamOffset offset, |
| 170 | QuicStringPiece data, |
| 171 | size_t* bytes_copy, |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 172 | std::string* error_details); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 173 | |
| 174 | // Dispose the given buffer block. |
| 175 | // After calling this method, blocks_[index] is set to nullptr |
| 176 | // in order to indicate that no memory set is allocated for that block. |
| 177 | // Returns true on success, false otherwise. |
| 178 | bool RetireBlock(size_t index); |
| 179 | |
| 180 | // Should only be called after the indexed block is read till the end of the |
| 181 | // block or missing data has been reached. |
| 182 | // If the block at |block_index| contains no buffered data, the block |
| 183 | // should be retired. |
| 184 | // Returns true on success, or false otherwise. |
| 185 | bool RetireBlockIfEmpty(size_t block_index); |
| 186 | |
| 187 | // Calculate the capacity of block at specified index. |
| 188 | // Return value should be either kBlockSizeBytes for non-trailing blocks and |
| 189 | // max_buffer_capacity % kBlockSizeBytes for trailing block. |
| 190 | size_t GetBlockCapacity(size_t index) const; |
| 191 | |
| 192 | // Does not check if offset is within reasonable range. |
| 193 | size_t GetBlockIndex(QuicStreamOffset offset) const; |
| 194 | |
| 195 | // Given an offset in the stream, return the offset from the beginning of the |
| 196 | // block which contains this data. |
| 197 | size_t GetInBlockOffset(QuicStreamOffset offset) const; |
| 198 | |
| 199 | // Get offset relative to index 0 in logical 1st block to start next read. |
| 200 | size_t ReadOffset() const; |
| 201 | |
| 202 | // Get the index of the logical 1st block to start next read. |
| 203 | size_t NextBlockToRead() const; |
| 204 | |
| 205 | // Returns offset of first missing byte. |
| 206 | QuicStreamOffset FirstMissingByte() const; |
| 207 | |
| 208 | // Returns offset of highest received byte + 1. |
| 209 | QuicStreamOffset NextExpectedByte() const; |
| 210 | |
| 211 | // Return |gaps_| as a string: [1024, 1500) [1800, 2048)... for debugging. |
bnc | 198aa99 | 2019-06-25 10:37:43 -0700 | [diff] [blame] | 212 | std::string GapsDebugString() const; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 213 | |
| 214 | // Return all received frames as a string in same format as GapsDebugString(); |
bnc | 198aa99 | 2019-06-25 10:37:43 -0700 | [diff] [blame] | 215 | std::string ReceivedFramesDebugString() const; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 216 | |
| 217 | // The maximum total capacity of this buffer in byte, as constructed. |
| 218 | const size_t max_buffer_capacity_bytes_; |
| 219 | |
| 220 | // How many blocks this buffer would need when it reaches full capacity. |
| 221 | const size_t blocks_count_; |
| 222 | |
| 223 | // Number of bytes read out of buffer. |
| 224 | QuicStreamOffset total_bytes_read_; |
| 225 | |
| 226 | // An ordered, variable-length list of blocks, with the length limited |
| 227 | // such that the number of blocks never exceeds blocks_count_. |
| 228 | // Each list entry can hold up to kBlockSizeBytes bytes. |
| 229 | std::unique_ptr<BufferBlock*[]> blocks_; |
| 230 | |
| 231 | // Number of bytes in buffer. |
| 232 | size_t num_bytes_buffered_; |
| 233 | |
| 234 | // Currently received data. |
| 235 | QuicIntervalSet<QuicStreamOffset> bytes_received_; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 236 | }; |
bnc | c16be36 | 2019-08-28 08:07:29 -0700 | [diff] [blame] | 237 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 238 | } // namespace quic |
| 239 | |
| 240 | #endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_ |