// Copyright (c) 2009 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.

// This file contains some protocol structures for use with Flip.

#ifndef NET_FLIP_FLIP_PROTOCOL_H_
#define NET_FLIP_FLIP_PROTOCOL_H_

#ifdef WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif

#include "base/basictypes.h"
#include "base/logging.h"
#include "flip_bitmasks.h"  // cross-google3 directory naming.

//  Data Frame Format
//  +----------------------------------+
//  |0|       Stream-ID (31bits)       |
//  +----------------------------------+
//  | flags (8)  |  Length (24 bits)   |
//  +----------------------------------+
//  |               Data               |
//  +----------------------------------+
//
//  Control Frame Format
//  +----------------------------------+
//  |1| Version(15bits) | Type(16bits) |
//  +----------------------------------+
//  | flags (8)  |  Length (24 bits)   |
//  +----------------------------------+
//  |               Data               |
//  +----------------------------------+
//
//  Control Frame: SYN_STREAM
//  +----------------------------------+
//  |1|000000000000001|0000000000000001|
//  +----------------------------------+
//  | flags (8)  |  Length (24 bits)   |  >= 8
//  +----------------------------------+
//  |X|       Stream-ID(31bits)        |
//  +----------------------------------+
//  |Pri| unused      | Length (16bits)|
//  +----------------------------------+
//
//  Control Frame: SYN_REPLY
//  +----------------------------------+
//  |1|000000000000001|0000000000000010|
//  +----------------------------------+
//  | flags (8)  |  Length (24 bits)   |  >= 8
//  +----------------------------------+
//  |X|       Stream-ID(31bits)        |
//  +----------------------------------+
//  | unused (16 bits)| Length (16bits)|
//  +----------------------------------+
//
//  Control Frame: FIN_STREAM
//  +----------------------------------+
//  |1|000000000000001|0000000000000011|
//  +----------------------------------+
//  | flags (8)  |  Length (24 bits)   |  >= 4
//  +----------------------------------+
//  |X|       Stream-ID(31bits)        |
//  +----------------------------------+
//  |             Status (32 bits)     |
//  +----------------------------------+
//
//  Control Frame: SetMaxStreams
//  +----------------------------------+
//  |1|000000000000001|0000000000000100|
//  +----------------------------------+
//  | flags (8)  |  Length (24 bits)   |  >= 4
//  +----------------------------------+
//  |X|       Stream-ID(31bits)        |
//  +----------------------------------+

// TODO(fenix): add ChangePriority support.

namespace flip {

// This implementation of Flip is version 1.
const int kFlipProtocolVersion = 1;

// Note: all protocol data structures are on-the-wire format.  That means that
//       data is stored in network-normalized order.  Readers must use the
//       accessors provided or call ntohX() functions.

// Types of Flip Control Frames.
enum FlipControlType {
  SYN_STREAM = 1,
  SYN_REPLY,
  FIN_STREAM,
  NOOP
};

// Flags on data packets
enum FlipDataFlags {
  DATA_FLAG_NONE = 0,
  DATA_FLAG_FIN = 1,
  DATA_FLAG_COMPRESSED = 2  // TODO(mbelshe): remove me.
};

// Flags on control packets
enum FlipControlFlags {
  CONTROL_FLAG_NONE = 0,
  CONTROL_FLAG_FIN = 1
};

// A FLIP stream id is a 31 bit entity.
typedef uint32 FlipStreamId;

// FLIP Priorities. (there are only 2 bits)
#define FLIP_PRIORITY_LOWEST 3
#define FLIP_PRIORITY_HIGHEST 0

// -------------------------------------------------------------------------
// These structures mirror the protocol structure definitions.

// For the control data structures, we pack so that sizes match the
// protocol over-the-wire sizes.
#pragma pack(push)
#pragma pack(1)

// A special structure for the 8 bit flags and 24 bit length fields.
union FlagsAndLength {
  uint8 flags_[4];  // 8 bits
  uint32 length_;   // 24 bits
};

// The basic FLIP Frame structure.
struct FlipFrameBlock {
  union {
    struct {
      uint16 version_;
      uint16 type_;
    } control_;
    struct {
      FlipStreamId stream_id_;
    } data_;
  };
  FlagsAndLength flags_length_;
};

// A Control Frame structure.
struct FlipControlFrameBlock : FlipFrameBlock {
  FlipStreamId stream_id_;
};

// A SYN_STREAM Control Frame structure.
struct FlipSynStreamControlFrameBlock : FlipControlFrameBlock {
  uint8 priority_;
  uint8 unused_;
};

// A SYN_REPLY Control Frame structure.
struct FlipSynReplyControlFrameBlock : FlipControlFrameBlock {
  uint16 unused_;
};

// A FNI_STREAM Control Frame structure.
struct FlipFinStreamControlFrameBlock : FlipControlFrameBlock {
  uint32 status_;
};

#pragma pack(pop)

// -------------------------------------------------------------------------
// Wrapper classes for various FLIP frames.

// All Flip Frame types derive from this FlipFrame class.
class FlipFrame {
 public:
  // Create a FlipFrame for a given sized buffer.
  explicit FlipFrame(size_t size) : frame_(NULL), owns_buffer_(true) {
    DCHECK_GE(size, sizeof(struct FlipFrameBlock));
    char* buffer = new char[size];
    memset(buffer, 0, size);
    frame_ = reinterpret_cast<struct FlipFrameBlock*>(buffer);
  }

  // Create a FlipFrame using a pre-created buffer.
  // If |owns_buffer| is true, this class takes ownership of the buffer
  // and will delete it on cleanup.  The buffer must have been created using
  // new char[].
  // If |owns_buffer| is false, the caller retains ownership of the buffer and
  // is responsible for making sure the buffer outlives this frame.  In other
  // words, this class does NOT create a copy of the buffer.
  FlipFrame(char* data, bool owns_buffer)
      : frame_(reinterpret_cast<struct FlipFrameBlock*>(data)),
        owns_buffer_(owns_buffer) {
    DCHECK(frame_);
  }

  virtual ~FlipFrame() {
    if (owns_buffer_) {
      char* buffer = reinterpret_cast<char*>(frame_);
      delete [] buffer;
    }
    frame_ = NULL;
  }

  // Provides access to the frame bytes, which is a buffer containing
  // the frame packed as expected for sending over the wire.
  char* data() const { return reinterpret_cast<char*>(frame_); }

  uint8 flags() const { return frame_->flags_length_.flags_[0]; }
  void set_flags(uint8 flags) { frame_->flags_length_.flags_[0] = flags; }

  uint32 length() const {
    return ntohl(frame_->flags_length_.length_) & kLengthMask;
  }

  void set_length(uint32 length) {
    DCHECK_EQ(0u, (length & ~kLengthMask));
    length = htonl(length & kLengthMask);
    frame_->flags_length_.length_ = flags() | length;
  }

  bool is_control_frame() const {
    return (ntohs(frame_->control_.version_) & kControlFlagMask) ==
        kControlFlagMask;
  }

  // Returns the size of the FlipFrameBlock structure.
  // Note: this is not the size of the FlipFrame class.
  // Every FlipFrame* class has a static size() method for accessing
  // the size of the data structure which will be sent over the wire.
  // Note:  this is not the same as sizeof(FlipFrame).
  static size_t size() { return sizeof(struct FlipFrameBlock); }

 protected:
  FlipFrameBlock* frame_;

 private:
  bool owns_buffer_;
  DISALLOW_COPY_AND_ASSIGN(FlipFrame);
};

// A Data Frame.
class FlipDataFrame : public FlipFrame {
 public:
  FlipDataFrame() : FlipFrame(size()) {}
  FlipDataFrame(char* data, bool owns_buffer)
      : FlipFrame(data, owns_buffer) {}
  virtual ~FlipDataFrame() {}

  FlipStreamId stream_id() const {
    return ntohl(frame_->data_.stream_id_) & kStreamIdMask;
  }

  // Note that setting the stream id sets the control bit to false.
  // As stream id should always be set, this means the control bit
  // should always be set correctly.
  void set_stream_id(FlipStreamId id) {
    DCHECK_EQ(0u, (id & ~kStreamIdMask));
    frame_->data_.stream_id_ = htonl(id & kStreamIdMask);
  }

  // Returns the size of the FlipFrameBlock structure.
  // Note: this is not the size of the FlipDataFrame class.
  static size_t size() { return FlipFrame::size(); }

 private:
  DISALLOW_COPY_AND_ASSIGN(FlipDataFrame);
};

// A Control Frame.
class FlipControlFrame : public FlipFrame {
 public:
  explicit FlipControlFrame(size_t size) : FlipFrame(size) {}
  FlipControlFrame(char* data, bool owns_buffer)
      : FlipFrame(data, owns_buffer) {}
  virtual ~FlipControlFrame() {}

  uint16 version() const {
    const int kVersionMask = 0x7fff;
    return ntohs(block()->control_.version_) & kVersionMask;
  }
  FlipControlType type() const {
    uint16 type = ntohs(block()->control_.type_);
    DCHECK(type >= SYN_STREAM && type <= NOOP);
    return static_cast<FlipControlType>(type);
  }
  FlipStreamId stream_id() const {
    return ntohl(block()->stream_id_) & kStreamIdMask;
  }

  void set_stream_id(FlipStreamId id) {
    block()->stream_id_ = htonl(id & kStreamIdMask);
  }

  // Returns the size of the FlipControlFrameBlock structure.
  // Note: this is not the size of the FlipControlFrame class.
  static size_t size() { return sizeof(FlipControlFrameBlock); }

 private:
  struct FlipControlFrameBlock* block() const {
    return static_cast<FlipControlFrameBlock*>(frame_);
  }
  DISALLOW_COPY_AND_ASSIGN(FlipControlFrame);
};

// A SYN_STREAM frame.
class FlipSynStreamControlFrame : public FlipControlFrame {
 public:
  FlipSynStreamControlFrame() : FlipControlFrame(size()) {}
  FlipSynStreamControlFrame(char* data, bool owns_buffer)
      : FlipControlFrame(data, owns_buffer) {}
  virtual ~FlipSynStreamControlFrame() {}

  uint8 priority() const { return (block()->priority_ & kPriorityMask) >> 6; }

  // The number of bytes in the header block beyond the frame header length.
  int header_block_len() const {
    return length() - (size() - FlipFrame::size());
  }

  const char* header_block() const {
    return reinterpret_cast<const char*>(block()) + size();
  }

  // Returns the size of the FlipSynStreamControlFrameBlock structure.
  // Note: this is not the size of the FlipSynStreamControlFrame class.
  static size_t size() { return sizeof(FlipSynStreamControlFrameBlock); }

 private:
  struct FlipSynStreamControlFrameBlock* block() const {
    return static_cast<FlipSynStreamControlFrameBlock*>(frame_);
  }
  DISALLOW_COPY_AND_ASSIGN(FlipSynStreamControlFrame);
};

// A SYN_REPLY frame.
class FlipSynReplyControlFrame : public FlipControlFrame {
 public:
  FlipSynReplyControlFrame() : FlipControlFrame(size()) {}
  FlipSynReplyControlFrame(char* data, bool owns_buffer)
      : FlipControlFrame(data, owns_buffer) {}
  virtual ~FlipSynReplyControlFrame() {}

  int header_block_len() const {
    return length() - (size() - FlipFrame::size());
  }

  const char* header_block() const {
    return reinterpret_cast<const char*>(block()) + size();
  }

  // Returns the size of the FlipSynReplyControlFrameBlock structure.
  // Note: this is not the size of the FlipSynReplyControlFrame class.
  static size_t size() { return sizeof(FlipSynReplyControlFrameBlock); }

 private:
  struct FlipSynReplyControlFrameBlock* block() const {
    return static_cast<FlipSynReplyControlFrameBlock*>(frame_);
  }
  DISALLOW_COPY_AND_ASSIGN(FlipSynReplyControlFrame);
};

// A FIN_STREAM frame.
class FlipFinStreamControlFrame : public FlipControlFrame {
 public:
  FlipFinStreamControlFrame() : FlipControlFrame(size()) {}
  FlipFinStreamControlFrame(char* data, bool owns_buffer)
      : FlipControlFrame(data, owns_buffer) {}
  virtual ~FlipFinStreamControlFrame() {}

  uint32 status() const { return ntohl(block()->status_); }
  void set_status(uint32 status) { block()->status_ = htonl(status); }

  // Returns the size of the FlipFinStreamControlFrameBlock structure.
  // Note: this is not the size of the FlipFinStreamControlFrame class.
  static size_t size() { return sizeof(FlipFinStreamControlFrameBlock); }

 private:
  struct FlipFinStreamControlFrameBlock* block() const {
    return static_cast<FlipFinStreamControlFrameBlock*>(frame_);
  }
  DISALLOW_COPY_AND_ASSIGN(FlipFinStreamControlFrame);
};

}  // namespace flip

#endif  // NET_FLIP_FLIP_PROTOCOL_H_
