// Copyright 2013 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_PACKET_WRITER_H_
#define QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_

#include <cstddef>
#include <optional>
#include <utility>

#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/platform/api/quic_export.h"
#include "quiche/quic/platform/api/quic_ip_address.h"
#include "quiche/quic/platform/api/quic_socket_address.h"

namespace quic {

struct WriteResult;

// This class allows a platform to pass instructions to an associated child of
// QuicWriter without intervening QUIC code understanding anything about its
// contents.
class QUICHE_EXPORT PerPacketOptions {
 public:
  virtual ~PerPacketOptions() {}

  // Returns a heap-allocated copy of |this|.
  //
  // The subclass implementation of this method should look like this:
  //   return std::make_unique<MyAwesomePerPacketOptions>(*this);
  //
  // This method is declared pure virtual in order to ensure the subclasses
  // would not forget to override it.
  virtual std::unique_ptr<PerPacketOptions> Clone() const = 0;
};

// The owner of QuicPacketWriter can pass control information via this struct.
struct QUICHE_EXPORT QuicPacketWriterParams {
  // Specifies ideal release time delay for this packet.
  QuicTime::Delta release_time_delay = QuicTime::Delta::Zero();
  // Whether it is allowed to send this packet without |release_time_delay|.
  bool allow_burst = false;
  // ECN codepoint to use when sending this packet.
  QuicEcnCodepoint ecn_codepoint = ECN_NOT_ECT;
};

// An interface between writers and the entity managing the
// socket (in our case the QuicDispatcher).  This allows the Dispatcher to
// control writes, and manage any writers who end up write blocked.
// A concrete writer works in one of the two modes:
// - PassThrough mode. This is the default mode. Caller calls WritePacket with
//   caller-allocated packet buffer. Unless the writer is blocked, each call to
//   WritePacket triggers a write using the underlying socket API.
//
// - Batch mode. In this mode, a call to WritePacket may not cause a packet to
//   be sent using the underlying socket API. Instead, multiple packets are
//   saved in the writer's internal buffer until they are flushed. The flush can
//   be explicit, by calling Flush, or implicit, e.g. by calling
//   WritePacket when the internal buffer is near full.
//
// Buffer management:
// In Batch mode, a writer manages an internal buffer, which is large enough to
// hold multiple packets' data. If the caller calls WritePacket with a
// caller-allocated packet buffer, the writer will memcpy the buffer into the
// internal buffer. Caller can also avoid this memcpy by:
// 1. Call GetNextWriteLocation to get a pointer P into the internal buffer.
// 2. Serialize the packet directly to P.
// 3. Call WritePacket with P as the |buffer|.
class QUICHE_EXPORT QuicPacketWriter {
 public:
  virtual ~QuicPacketWriter() {}

  // PassThrough mode:
  // Sends the packet out to the peer, with some optional per-packet options.
  // If the write succeeded, the result's status is WRITE_STATUS_OK and
  // bytes_written is populated. If the write failed, the result's status is
  // WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and error_code is populated.
  //
  // Batch mode:
  // If the writer is blocked, return WRITE_STATUS_BLOCKED immediately.
  // If the packet can be batched with other buffered packets, save the packet
  // to the internal buffer.
  // If the packet can not be batched, or the internal buffer is near full after
  // it is buffered, the internal buffer is flushed to free up space.
  // Return WriteResult(WRITE_STATUS_OK, <bytes_flushed>) on success. When
  // <bytes_flushed> is zero, it means the packet is buffered and not flushed.
  // Return WRITE_STATUS_BLOCKED if the packet is not buffered and the socket is
  // blocked while flushing.
  // Otherwise return an error status.
  //
  // Options must be either null, or created for the particular QuicPacketWriter
  // implementation. Options may be ignored, depending on the implementation.
  //
  // Some comment about memory management if |buffer| was previously acquired
  // by a call to "GetNextWriteLocation()":
  //
  // a) When WRITE_STATUS_OK is returned, the caller expects the writer owns the
  // packet buffers and they will be released when the write finishes.
  //
  // b) When this function returns any status >= WRITE_STATUS_ERROR, the caller
  // expects the writer releases the buffer (if needed) before the function
  // returns.
  //
  // c) When WRITE_STATUS_BLOCKED is returned, the caller makes a copy of the
  // buffer and will retry after unblock, so if |payload| is allocated from
  // GetNextWriteLocation(), it
  //    1) needs to be released before return, and
  //    2) the content of |payload| should not change after return.
  //
  // d) When WRITE_STATUS_BLOCKED_DATA_BUFFERED is returned, the caller expects
  // 1) the writer owns the packet buffers, and 2) the writer will re-send the
  // packet when it unblocks.
  virtual WriteResult WritePacket(const char* buffer, size_t buf_len,
                                  const QuicIpAddress& self_address,
                                  const QuicSocketAddress& peer_address,
                                  PerPacketOptions* options,
                                  const QuicPacketWriterParams& params) = 0;

  // Returns true if the network socket is not writable.
  virtual bool IsWriteBlocked() const = 0;

  // Records that the socket has become writable, for example when an EPOLLOUT
  // is received or an asynchronous write completes.
  virtual void SetWritable() = 0;

  // The error code used by the writer to indicate that the write failed due to
  // supplied packet being too big.  This is equivalent to returning
  // WRITE_STATUS_MSG_TOO_BIG as a status.
  virtual std::optional<int> MessageTooBigErrorCode() const = 0;

  // Returns the maximum size of the packet which can be written using this
  // writer for the supplied peer address.  This size may actually exceed the
  // size of a valid QUIC packet.
  virtual QuicByteCount GetMaxPacketSize(
      const QuicSocketAddress& peer_address) const = 0;

  // Returns true if the socket supports release timestamp.
  virtual bool SupportsReleaseTime() const = 0;

  // True=Batch mode. False=PassThrough mode.
  virtual bool IsBatchMode() const = 0;

  // Returns true if the writer will mark ECN on packets it writes.
  virtual bool SupportsEcn() const = 0;

  // PassThrough mode: Return {nullptr, nullptr}
  //
  // Batch mode:
  // Return the QuicPacketBuffer for the next packet. A minimum of
  // kMaxOutgoingPacketSize is guaranteed to be available from the returned
  // address. If the internal buffer does not have enough space,
  // {nullptr, nullptr} is returned. All arguments should be identical to the
  // follow-up call to |WritePacket|, they are here to allow advanced packet
  // memory management in packet writers, e.g. one packet buffer pool per
  // |peer_address|.
  //
  // If QuicPacketBuffer.release_buffer is !nullptr, it should be called iff
  // the caller does not call WritePacket for the returned buffer.
  virtual QuicPacketBuffer GetNextWriteLocation(
      const QuicIpAddress& self_address,
      const QuicSocketAddress& peer_address) = 0;

  // PassThrough mode: Return WriteResult(WRITE_STATUS_OK, 0).
  //
  // Batch mode:
  // Try send all buffered packets.
  // - Return WriteResult(WRITE_STATUS_OK, <bytes_flushed>) if all buffered
  //   packets were sent successfully.
  // - Return WRITE_STATUS_BLOCKED if the underlying socket is blocked while
  //   sending. Some packets may have been sent, packets not sent will stay in
  //   the internal buffer.
  // - Return a status >= WRITE_STATUS_ERROR if an error was encuontered while
  //   sending. As this is not a re-tryable error, any batched packets which
  //   were on memory acquired via GetNextWriteLocation() should be released and
  //   the batch should be dropped.
  virtual WriteResult Flush() = 0;
};

}  // namespace quic

#endif  // QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_
