// 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.

#ifndef NET_FLIP_FRAME_BUILDER_H_
#define NET_FLIP_FRAME_BUILDER_H_

#ifdef WIN32
#include <winsock2.h>  // for htonl() functions
#else
#include <arpa/inet.h>
#endif

#include <string>

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

namespace flip {

// This class provides facilities for basic binary value packing and unpacking
// into Flip frames.
//
// The FlipFrameBuilder supports appending primitive values (int, string, etc)
// to a frame instance.  The FlipFrameBuilder grows its internal memory buffer
// dynamically to hold the sequence of primitive values.   The internal memory
// buffer is exposed as the "data" of the FlipFrameBuilder.
//
// When reading from a FlipFrameBuilder the consumer must know what value types
// to read and in what order to read them as the FlipFrameBuilder does not keep
// track of the type of data written to it.
class FlipFrameBuilder {
 public:
  FlipFrameBuilder();
  ~FlipFrameBuilder();

  // Initializes a FlipFrameBuilder from a const block of data.  The data is
  // not copied; instead the data is merely referenced by this
  // FlipFrameBuilder.  Only const methods should be used when initialized
  // this way.
  FlipFrameBuilder(const char* data, int data_len);

  // Returns the size of the FlipFrameBuilder's data.
  int length() const { return length_; }

  // Takes the buffer from the FlipFrameBuilder.
  FlipFrame* take() {
    FlipFrame* rv = new FlipFrame(buffer_, true);
    buffer_ = NULL;
    capacity_ = 0;
    length_ = 0;
    return rv;
  }

  // Methods for reading the payload of the FlipFrameBuilder.  To read from the
  // start of the FlipFrameBuilder, initialize *iter to NULL.  If successful,
  // these methods return true.  Otherwise, false is returned to indicate that
  // the result could not be extracted.
  bool ReadUInt16(void** iter, uint16* result) const;
  bool ReadUInt32(void** iter, uint32* result) const;
  bool ReadString(void** iter, std::string* result) const;
  bool ReadBytes(void** iter, const char** data, uint16 length) const;
  bool ReadData(void** iter, const char** data, uint16* length) const;

  // Methods for adding to the payload.  These values are appended to the end
  // of the FlipFrameBuilder payload.  When reading values, you must read them
  // in the order they were added.  Note - binary integers are converted from
  // host to network form.
  bool WriteUInt16(uint16 value) {
    value = htons(value);
    return WriteBytes(&value, sizeof(value));
  }
  bool WriteUInt32(uint32 value) {
    value = htonl(value);
    return WriteBytes(&value, sizeof(value));
  }
  bool WriteString(const std::string& value);
  bool WriteBytes(const void* data, uint16 data_len);

  // Write an integer to a particular offset in the data buffer.
  bool WriteUInt32ToOffset(int offset, uint32 value) {
    value = htonl(value);
    return WriteBytesToOffset(offset, &value, sizeof(value));
  }

  // Write to a particular offset in the data buffer.
  bool WriteBytesToOffset(int offset, const void* data, uint32 data_len) {
    if (offset + data_len > length_)
      return false;
    char *ptr = buffer_ + offset;
    memcpy(ptr, data, data_len);
    return true;
  }

  // Allows the caller to write data directly into the FlipFrameBuilder.
  // This saves a copy when the data is not already available in a buffer.
  // The caller must not write more than the length it declares it will.
  // Use ReadData to get the data.
  // Returns NULL on failure.
  //
  // The returned pointer will only be valid until the next write operation
  // on this FlipFrameBuilder.
  char* BeginWriteData(uint16 length);

  // Returns true if the given iterator could point to data with the given
  // length. If there is no room for the given data before the end of the
  // payload, returns false.
  bool IteratorHasRoomFor(const void* iter, int len) const {
    const char* end_of_region = reinterpret_cast<const char*>(iter) + len;
    if (len < 0 ||
        iter < buffer_ ||
        iter > end_of_payload() ||
        iter > end_of_region ||
        end_of_region > end_of_payload())
      return false;

    // Watch out for overflow in pointer calculation, which wraps.
    return (iter <= end_of_region) && (end_of_region <= end_of_payload());
  }

 protected:
  size_t capacity() const {
    return capacity_;
  }

  const char* end_of_payload() const { return buffer_ + length_; }

  // Resizes the buffer for use when writing the specified amount of data. The
  // location that the data should be written at is returned, or NULL if there
  // was an error. Call EndWrite with the returned offset and the given length
  // to pad out for the next write.
  char* BeginWrite(size_t length);

  // Completes the write operation by padding the data with NULL bytes until it
  // is padded. Should be paired with BeginWrite, but it does not necessarily
  // have to be called after the data is written.
  void EndWrite(char* dest, int length);

  // Resize the capacity, note that the input value should include the size of
  // the header: new_capacity = sizeof(Header) + desired_payload_capacity.
  // A new failure will cause a Resize failure... and caller should check
  // the return result for true (i.e., successful resizing).
  bool Resize(size_t new_capacity);

  // Moves the iterator by the given number of bytes.
  static void UpdateIter(void** iter, int bytes) {
    *iter = static_cast<char*>(*iter) + bytes;
  }

  // Initial size of the payload.
  static const int kInitialPayload = 1024;

 private:
  char* buffer_;
  size_t capacity_;  // Allocation size of payload (or -1 if buffer is const).
  size_t length_;    // current length of the buffer
  size_t variable_buffer_offset_;  // IF non-zero, then offset to a buffer.
};

}  // namespace flip

#endif  // NET_FLIP_FRAME_BUILDER_H_

