| // 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 "quiche/quic/core/quic_interval_set.h" | |
| #include "quiche/quic/core/quic_packets.h" | |
| #include "quiche/quic/core/quic_types.h" | |
| #include "quiche/quic/platform/api/quic_export.h" | |
| #include "quiche/common/platform/api/quiche_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_; | |
| }; | |
| } // namespace quic | |
| #endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_ |