// Copyright (c) 2012 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_H_
#define QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_H_

#include <cstddef>
#include <map>
#include <string>

#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_stream_sequencer_buffer.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_export.h"

namespace quic {

namespace test {
class QuicStreamSequencerPeer;
}  // namespace test

// Buffers frames until we have something which can be passed
// up to the next layer.
class QUICHE_EXPORT QuicStreamSequencer final {
 public:
  // Interface that thie Sequencer uses to communicate with the Stream.
  class QUICHE_EXPORT StreamInterface {
   public:
    virtual ~StreamInterface() = default;

    // Called when new data is available to be read from the sequencer.
    virtual void OnDataAvailable() = 0;
    // Called when the end of the stream has been read.
    virtual void OnFinRead() = 0;
    // Called when bytes have been consumed from the sequencer.
    virtual void AddBytesConsumed(QuicByteCount bytes) = 0;
    // Called when an error has occurred which should result in the stream
    // being reset.
    virtual void ResetWithError(QuicResetStreamError error) = 0;
    // Called when an error has occurred which should result in the connection
    // being closed.
    virtual void OnUnrecoverableError(QuicErrorCode error,
                                      const std::string& details) = 0;
    // Called when an error has occurred which should result in the connection
    // being closed, specifying the wire error code |ietf_error| explicitly.
    virtual void OnUnrecoverableError(QuicErrorCode error,
                                      QuicIetfTransportErrorCodes ietf_error,
                                      const std::string& details) = 0;
    // Returns the stream id of this stream.
    virtual QuicStreamId id() const = 0;

    // Returns the QUIC version being used by this stream.
    virtual ParsedQuicVersion version() const = 0;
  };

  explicit QuicStreamSequencer(StreamInterface* quic_stream);
  QuicStreamSequencer(const QuicStreamSequencer&) = delete;
  QuicStreamSequencer(QuicStreamSequencer&&) = default;
  QuicStreamSequencer& operator=(const QuicStreamSequencer&) = delete;
  QuicStreamSequencer& operator=(QuicStreamSequencer&&) = default;
  ~QuicStreamSequencer();

  // If the frame is the next one we need in order to process in-order data,
  // ProcessData will be immediately called on the stream until all buffered
  // data is processed or the stream fails to consume data.  Any unconsumed
  // data will be buffered. If the frame is not the next in line, it will be
  // buffered.
  void OnStreamFrame(const QuicStreamFrame& frame);

  // If the frame is the next one we need in order to process in-order data,
  // ProcessData will be immediately called on the crypto stream until all
  // buffered data is processed or the crypto stream fails to consume data. Any
  // unconsumed data will be buffered. If the frame is not the next in line, it
  // will be buffered.
  void OnCryptoFrame(const QuicCryptoFrame& frame);

  // Once data is buffered, it's up to the stream to read it when the stream
  // can handle more data.  The following three functions make that possible.

  // Fills in up to iov_len iovecs with the next readable regions.  Returns the
  // number of iovs used.  Non-destructive of the underlying data.
  int GetReadableRegions(iovec* iov, size_t iov_len) const;

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

  // Fills in one iovec with the region starting at |offset| and returns true.
  // Returns false if no readable region is available, either because data has
  // not been received yet or has already been consumed.
  bool PeekRegion(QuicStreamOffset offset, iovec* iov) const;

  // Copies the data into the iov_len buffers provided.  Returns the number of
  // bytes read.  Any buffered data no longer in use will be released.
  // TODO(rch): remove this method and instead implement it as a helper method
  // based on GetReadableRegions and MarkConsumed.
  size_t Readv(const struct iovec* iov, size_t iov_len);

  // Consumes |num_bytes| data.  Used in conjunction with |GetReadableRegions|
  // to do zero-copy reads.
  void MarkConsumed(size_t num_bytes);

  // Appends all of the readable data to |buffer| and marks all of the appended
  // data as consumed.
  void Read(std::string* buffer);

  // Returns true if the sequncer has bytes available for reading.
  bool HasBytesToRead() const;

  // Number of bytes available to read.
  size_t ReadableBytes() const;

  // Returns true if the sequencer has delivered the fin.
  bool IsClosed() const;

  // Calls |OnDataAvailable| on |stream_| if there is buffered data that can
  // be processed, and causes |OnDataAvailable| to be called as new data
  // arrives.
  void SetUnblocked();

  // Blocks processing of frames until |SetUnblocked| is called.
  void SetBlockedUntilFlush();

  // Sets the sequencer to discard all incoming data itself and not call
  // |stream_->OnDataAvailable()|.  |stream_->OnFinRead()| will be called
  // automatically when the FIN is consumed (which may be immediately).
  void StopReading();

  // Free the memory of underlying buffer.
  void ReleaseBuffer();

  // Free the memory of underlying buffer when no bytes remain in it.
  void ReleaseBufferIfEmpty();

  // Number of bytes in the buffer right now.
  size_t NumBytesBuffered() const;

  // Number of bytes has been consumed.
  QuicStreamOffset NumBytesConsumed() const;

  // Returns true if all of the data within the stream up until the FIN is
  // available.
  bool IsAllDataAvailable() const;

  QuicStreamOffset close_offset() const { return close_offset_; }

  int num_frames_received() const { return num_frames_received_; }

  int num_duplicate_frames_received() const {
    return num_duplicate_frames_received_;
  }

  bool ignore_read_data() const { return ignore_read_data_; }

  void set_level_triggered(bool level_triggered) {
    level_triggered_ = level_triggered;
  }

  bool level_triggered() const { return level_triggered_; }

  void set_stream(StreamInterface* stream) { stream_ = stream; }

  // Returns string describing internal state.
  std::string DebugString() const;

 private:
  friend class test::QuicStreamSequencerPeer;

  // Deletes and records as consumed any buffered data that is now in-sequence.
  // (To be called only after StopReading has been called.)
  void FlushBufferedFrames();

  // Wait until we've seen 'offset' bytes, and then terminate the stream.
  // Returns true if |stream_| is still available to receive data, and false if
  // |stream_| is reset.
  bool CloseStreamAtOffset(QuicStreamOffset offset);

  // If we've received a FIN and have processed all remaining data, then inform
  // the stream of FIN, and clear buffers.
  void MaybeCloseStream();

  // Shared implementation between OnStreamFrame and OnCryptoFrame.
  void OnFrameData(QuicStreamOffset byte_offset, size_t data_len,
                   const char* data_buffer);

  // The stream which owns this sequencer.
  StreamInterface* stream_;

  // Stores received data in offset order.
  QuicStreamSequencerBuffer buffered_frames_;

  // The highest offset that is received so far.
  QuicStreamOffset highest_offset_;

  // The offset, if any, we got a stream termination for.  When this many bytes
  // have been processed, the sequencer will be closed.
  QuicStreamOffset close_offset_;

  // If true, the sequencer is blocked from passing data to the stream and will
  // buffer all new incoming data until FlushBufferedFrames is called.
  bool blocked_;

  // Count of the number of frames received.
  int num_frames_received_;

  // Count of the number of duplicate frames received.
  int num_duplicate_frames_received_;

  // If true, all incoming data will be discarded.
  bool ignore_read_data_;

  // If false, only call OnDataAvailable() when it becomes newly unblocked.
  // Otherwise, call OnDataAvailable() when number of readable bytes changes.
  bool level_triggered_;
};

}  // namespace quic

#endif  // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_H_
