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