// 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_SPDY_FRAME_BUILDER_H_ #define NET_SPDY_FRAME_BUILDER_H_ #ifdef WIN32 #include // for htonl() functions #else #include #endif #include #include "base/logging.h" #include "net/spdy/spdy_protocol.h" namespace spdy { // This class provides facilities for basic binary value packing and unpacking // into Spdy frames. // // The SpdyFrameBuilder supports appending primitive values (int, string, etc) // to a frame instance. The SpdyFrameBuilder grows its internal memory buffer // dynamically to hold the sequence of primitive values. The internal memory // buffer is exposed as the "data" of the SpdyFrameBuilder. // // When reading from a SpdyFrameBuilder the consumer must know what value types // to read and in what order to read them as the SpdyFrameBuilder does not keep // track of the type of data written to it. class SpdyFrameBuilder { public: SpdyFrameBuilder(); ~SpdyFrameBuilder(); // Initializes a SpdyFrameBuilder from a const block of data. The data is // not copied; instead the data is merely referenced by this // SpdyFrameBuilder. Only const methods should be used when initialized // this way. SpdyFrameBuilder(const char* data, int data_len); // Returns the size of the SpdyFrameBuilder's data. int length() const { return length_; } // Takes the buffer from the SpdyFrameBuilder. SpdyFrame* take() { SpdyFrame* rv = new SpdyFrame(buffer_, true); buffer_ = NULL; capacity_ = 0; length_ = 0; return rv; } // Methods for reading the payload of the SpdyFrameBuilder. To read from the // start of the SpdyFrameBuilder, 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 SpdyFrameBuilder 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 SpdyFrameBuilder. // 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 SpdyFrameBuilder. 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(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(*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 spdy #endif // NET_SPDY_FRAME_BUILDER_H_