summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h
blob: 83ce01bd36688d9375878df8d3ec866bcac31b57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_
#define QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_

// QuicStreamSequencerBuffer is a circular stream buffer with random write and
// in-sequence read. It consists of a vector of pointers pointing
// to memory blocks created as needed and an interval set recording received
// data.
// - Data are written in with offset indicating where it should be in the
// stream, and the buffer grown as needed (up to the maximum buffer capacity),
// without expensive copying (extra blocks are allocated).
// - Data can be read from the buffer if there is no gap before it,
// and the buffer shrinks as the data are consumed.
// - An upper limit on the number of blocks in the buffer provides an upper
//   bound on memory use.
//
// This class is thread-unsafe.
//
// QuicStreamSequencerBuffer maintains a concept of the readable region, which
// contains all written data that has not been read.
// It promises stability of the underlying memory addresses in the readable
// region, so pointers into it can be maintained, and the offset of a pointer
// from the start of the read region can be calculated.
//
// Expected Use:
//  QuicStreamSequencerBuffer buffer(2.5 * 8 * 1024);
//  std::string source(1024, 'a');
//  absl::string_view string_piece(source.data(), source.size());
//  size_t written = 0;
//  buffer.OnStreamData(800, string_piece, GetEpollClockNow(), &written);
//  source = std::string{800, 'b'};
//  absl::string_view string_piece1(source.data(), 800);
//  // Try to write to [1, 801), but should fail due to overlapping,
//  // res should be QUIC_INVALID_STREAM_DATA
//  auto res = buffer.OnStreamData(1, string_piece1, &written));
//  // write to [0, 800), res should be QUIC_NO_ERROR
//  auto res = buffer.OnStreamData(0, string_piece1, GetEpollClockNow(),
//                                  &written);
//
//  // Read into a iovec array with total capacity of 120 bytes.
//  char dest[120];
//  iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40},
//                  iovec{dest + 80, 40}};
//  size_t read = buffer.Readv(iovecs, 3);
//
//  // Get single readable region.
//  iovec iov;
//  buffer.GetReadableRegion(iov);
//
//  // Get readable regions from [256, 1024) and consume some of it.
//  iovec iovs[2];
//  int iov_count = buffer.GetReadableRegions(iovs, 2);
//  // Consume some bytes in iovs, returning number of bytes having been
//  consumed.
//  size_t consumed = consume_iovs(iovs, iov_count);
//  buffer.MarkConsumed(consumed);

#include <cstddef>
#include <functional>
#include <list>
#include <memory>
#include <string>

#include "absl/strings/string_view.h"
#include "quic/core/quic_interval_set.h"
#include "quic/core/quic_packets.h"
#include "quic/core/quic_types.h"
#include "quic/platform/api/quic_export.h"
#include "quic/platform/api/quic_iovec.h"

namespace quic {

namespace test {
class QuicStreamSequencerBufferPeer;
}  // namespace test

class QUIC_EXPORT_PRIVATE QuicStreamSequencerBuffer {
 public:
  // Size of blocks used by this buffer.
  // Choose 8K to make block large enough to hold multiple frames, each of
  // which could be up to 1.5 KB.
  static const size_t kBlockSizeBytes = 8 * 1024;  // 8KB

  // The basic storage block used by this buffer.
  struct QUIC_EXPORT_PRIVATE BufferBlock {
    char buffer[kBlockSizeBytes];
  };

  explicit QuicStreamSequencerBuffer(size_t max_capacity_bytes);
  QuicStreamSequencerBuffer(const QuicStreamSequencerBuffer&) = delete;
  QuicStreamSequencerBuffer(QuicStreamSequencerBuffer&&) = default;
  QuicStreamSequencerBuffer& operator=(const QuicStreamSequencerBuffer&) =
      delete;
  QuicStreamSequencerBuffer& operator=(QuicStreamSequencerBuffer&&) = default;
  ~QuicStreamSequencerBuffer();

  // Free the space used to buffer data.
  void Clear();

  // Returns true if there is nothing to read in this buffer.
  bool Empty() const;

  // Called to buffer new data received for this stream.  If the data was
  // successfully buffered, returns QUIC_NO_ERROR and stores the number of
  // bytes buffered in |bytes_buffered|. Returns an error otherwise.
  QuicErrorCode OnStreamData(QuicStreamOffset offset,
                             absl::string_view data,
                             size_t* bytes_buffered,
                             std::string* error_details);

  // Reads from this buffer into given iovec array, up to number of iov_len
  // iovec objects and returns the number of bytes read.
  QuicErrorCode Readv(const struct iovec* dest_iov,
                      size_t dest_count,
                      size_t* bytes_read,
                      std::string* error_details);

  // Returns the readable region of valid data in iovec format. The readable
  // region is the buffer region where there is valid data not yet read by
  // client.
  // Returns the number of iovec entries in |iov| which were populated.
  // If the region is empty, one iovec entry with 0 length
  // is returned, and the function returns 0. If there are more readable
  // regions than |iov_size|, the function only processes the first
  // |iov_size| of them.
  int GetReadableRegions(struct iovec* iov, int iov_len) const;

  // Fills in one iovec with data from the next readable region.
  // Returns false if there is no readable region available.
  bool GetReadableRegion(iovec* iov) const;

  // Returns true and sets |*iov| to point to a region starting at |offset|.
  // Returns false if no data can be read at |offset|, which can be because data
  // has not been received yet or it is already consumed.
  // Does not consume data.
  bool PeekRegion(QuicStreamOffset offset, iovec* iov) const;

  // Called after GetReadableRegions() to free up |bytes_consumed| space if
  // these bytes are processed.
  // Pre-requisite: bytes_consumed <= available bytes to read.
  bool MarkConsumed(size_t bytes_consumed);

  // Deletes and records as consumed any buffered data and clear the buffer.
  // (To be called only after sequencer's StopReading has been called.)
  size_t FlushBufferedFrames();

  // Free the memory of buffered data.
  void ReleaseWholeBuffer();

  // Whether there are bytes can be read out.
  bool HasBytesToRead() const;

  // Count how many bytes have been consumed (read out of buffer).
  QuicStreamOffset BytesConsumed() const;

  // Count how many bytes are in buffer at this moment.
  size_t BytesBuffered() const;

  // Returns number of bytes available to be read out.
  size_t ReadableBytes() const;

 private:
  friend class test::QuicStreamSequencerBufferPeer;

  // Copies |data| to blocks_, sets |bytes_copy|. Returns true if the copy is
  // successful. Otherwise, sets |error_details| and returns false.
  bool CopyStreamData(QuicStreamOffset offset,
                      absl::string_view data,
                      size_t* bytes_copy,
                      std::string* error_details);

  // Dispose the given buffer block.
  // After calling this method, blocks_[index] is set to nullptr
  // in order to indicate that no memory set is allocated for that block.
  // Returns true on success, false otherwise.
  bool RetireBlock(size_t index);

  // Should only be called after the indexed block is read till the end of the
  // block or missing data has been reached.
  // If the block at |block_index| contains no buffered data, the block
  // should be retired.
  // Returns true on success, or false otherwise.
  bool RetireBlockIfEmpty(size_t block_index);

  // Calculate the capacity of block at specified index.
  // Return value should be either kBlockSizeBytes for non-trailing blocks and
  // max_buffer_capacity % kBlockSizeBytes for trailing block.
  size_t GetBlockCapacity(size_t index) const;

  // Does not check if offset is within reasonable range.
  size_t GetBlockIndex(QuicStreamOffset offset) const;

  // Given an offset in the stream, return the offset from the beginning of the
  // block which contains this data.
  size_t GetInBlockOffset(QuicStreamOffset offset) const;

  // Get offset relative to index 0 in logical 1st block to start next read.
  size_t ReadOffset() const;

  // Get the index of the logical 1st block to start next read.
  size_t NextBlockToRead() const;

  // Returns offset of first missing byte.
  QuicStreamOffset FirstMissingByte() const;

  // Returns offset of highest received byte + 1.
  QuicStreamOffset NextExpectedByte() const;

  // Return all received frames as a string.
  std::string ReceivedFramesDebugString() const;

  // Resize blocks_ if more blocks are needed to accomodate bytes before
  // next_expected_byte.
  void MaybeAddMoreBlocks(QuicStreamOffset next_expected_byte);

  // The maximum total capacity of this buffer in byte, as constructed.
  size_t max_buffer_capacity_bytes_;

  // Number of blocks this buffer would have when it reaches full capacity,
  // i.e., maximal number of blocks in blocks_.
  size_t max_blocks_count_;

  // Number of blocks this buffer currently has.
  size_t current_blocks_count_;

  // Number of bytes read out of buffer.
  QuicStreamOffset total_bytes_read_;

  // An ordered, variable-length list of blocks, with the length limited
  // such that the number of blocks never exceeds max_blocks_count_.
  // Each list entry can hold up to kBlockSizeBytes bytes.
  std::unique_ptr<BufferBlock*[]> blocks_;

  // Number of bytes in buffer.
  size_t num_bytes_buffered_;

  // Currently received data.
  QuicIntervalSet<QuicStreamOffset> bytes_received_;

  bool delay_allocation_until_new_data_ = GetQuicReloadableFlag(
      quic_delay_sequencer_buffer_allocation_until_new_data);
};

}  // namespace quic

#endif  // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_