michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #ifndef BASE_PICKLE_H__ michael@0: #define BASE_PICKLE_H__ michael@0: michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "base/logging.h" michael@0: #include "base/string16.h" michael@0: michael@0: // This class provides facilities for basic binary value packing and unpacking. michael@0: // michael@0: // The Pickle class supports appending primitive values (ints, strings, etc.) michael@0: // to a pickle instance. The Pickle instance grows its internal memory buffer michael@0: // dynamically to hold the sequence of primitive values. The internal memory michael@0: // buffer is exposed as the "data" of the Pickle. This "data" can be passed michael@0: // to a Pickle object to initialize it for reading. michael@0: // michael@0: // When reading from a Pickle object, it is important for the consumer to know michael@0: // what value types to read and in what order to read them as the Pickle does michael@0: // not keep track of the type of data written to it. michael@0: // michael@0: // The Pickle's data has a header which contains the size of the Pickle's michael@0: // payload. It can optionally support additional space in the header. That michael@0: // space is controlled by the header_size parameter passed to the Pickle michael@0: // constructor. michael@0: // michael@0: class Pickle { michael@0: public: michael@0: ~Pickle(); michael@0: michael@0: // Initialize a Pickle object using the default header size. michael@0: Pickle(); michael@0: michael@0: // Initialize a Pickle object with the specified header size in bytes, which michael@0: // must be greater-than-or-equal-to sizeof(Pickle::Header). The header size michael@0: // will be rounded up to ensure that the header size is 32bit-aligned. michael@0: explicit Pickle(int header_size); michael@0: michael@0: // Initializes a Pickle from a const block of data. The data is not copied; michael@0: // instead the data is merely referenced by this Pickle. Only const methods michael@0: // should be used on the Pickle when initialized this way. The header michael@0: // padding size is deduced from the data length. michael@0: Pickle(const char* data, int data_len); michael@0: michael@0: // Initializes a Pickle as a deep copy of another Pickle. michael@0: Pickle(const Pickle& other); michael@0: michael@0: // Performs a deep copy. michael@0: Pickle& operator=(const Pickle& other); michael@0: michael@0: // Returns the size of the Pickle's data. michael@0: int size() const { return static_cast(header_size_ + michael@0: header_->payload_size); } michael@0: michael@0: // Returns the data for this Pickle. michael@0: const void* data() const { return header_; } michael@0: michael@0: // Methods for reading the payload of the Pickle. To read from the start of michael@0: // the Pickle, initialize *iter to NULL. If successful, these methods return michael@0: // true. Otherwise, false is returned to indicate that the result could not michael@0: // be extracted. michael@0: bool ReadBool(void** iter, bool* result) const; michael@0: bool ReadInt16(void** iter, int16_t* result) const; michael@0: bool ReadUInt16(void** iter, uint16_t* result) const; michael@0: bool ReadShort(void** iter, short* result) const; michael@0: bool ReadInt(void** iter, int* result) const; michael@0: bool ReadLong(void** iter, long* result) const; michael@0: bool ReadULong(void** iter, unsigned long* result) const; michael@0: bool ReadSize(void** iter, size_t* result) const; michael@0: bool ReadInt32(void** iter, int32_t* result) const; michael@0: bool ReadUInt32(void** iter, uint32_t* result) const; michael@0: bool ReadInt64(void** iter, int64_t* result) const; michael@0: bool ReadUInt64(void** iter, uint64_t* result) const; michael@0: bool ReadDouble(void** iter, double* result) const; michael@0: bool ReadIntPtr(void** iter, intptr_t* result) const; michael@0: bool ReadUnsignedChar(void** iter, unsigned char* result) const; michael@0: bool ReadString(void** iter, std::string* result) const; michael@0: bool ReadWString(void** iter, std::wstring* result) const; michael@0: bool ReadString16(void** iter, string16* result) const; michael@0: bool ReadData(void** iter, const char** data, int* length) const; michael@0: bool ReadBytes(void** iter, const char** data, int length, michael@0: uint32_t alignment = sizeof(memberAlignmentType)) const; michael@0: michael@0: // Safer version of ReadInt() checks for the result not being negative. michael@0: // Use it for reading the object sizes. michael@0: bool ReadLength(void** iter, int* result) const; michael@0: michael@0: // Methods for adding to the payload of the Pickle. These values are michael@0: // appended to the end of the Pickle's payload. When reading values from a michael@0: // Pickle, it is important to read them in the order in which they were added michael@0: // to the Pickle. michael@0: bool WriteBool(bool value) { michael@0: return WriteInt(value ? 1 : 0); michael@0: } michael@0: bool WriteInt16(int16_t value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteUInt16(uint16_t value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteInt(int value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteLong(long value) { michael@0: // Always written as a 64-bit value since the size for this type can michael@0: // differ between architectures. michael@0: return WriteInt64(int64_t(value)); michael@0: } michael@0: bool WriteULong(unsigned long value) { michael@0: // Always written as a 64-bit value since the size for this type can michael@0: // differ between architectures. michael@0: return WriteUInt64(uint64_t(value)); michael@0: } michael@0: bool WriteSize(size_t value) { michael@0: // Always written as a 64-bit value since the size for this type can michael@0: // differ between architectures. michael@0: return WriteUInt64(uint64_t(value)); michael@0: } michael@0: bool WriteInt32(int32_t value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteUInt32(uint32_t value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteInt64(int64_t value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteUInt64(uint64_t value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteDouble(double value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteIntPtr(intptr_t value) { michael@0: // Always written as a 64-bit value since the size for this type can michael@0: // differ between architectures. michael@0: return WriteInt64(int64_t(value)); michael@0: } michael@0: bool WriteUnsignedChar(unsigned char value) { michael@0: return WriteBytes(&value, sizeof(value)); michael@0: } michael@0: bool WriteString(const std::string& value); michael@0: bool WriteWString(const std::wstring& value); michael@0: bool WriteString16(const string16& value); michael@0: bool WriteData(const char* data, int length); michael@0: bool WriteBytes(const void* data, int data_len, michael@0: uint32_t alignment = sizeof(memberAlignmentType)); michael@0: michael@0: // Same as WriteData, but allows the caller to write directly into the michael@0: // Pickle. This saves a copy in cases where the data is not already michael@0: // available in a buffer. The caller should take care to not write more michael@0: // than the length it declares it will. Use ReadData to get the data. michael@0: // Returns NULL on failure. michael@0: // michael@0: // The returned pointer will only be valid until the next write operation michael@0: // on this Pickle. michael@0: char* BeginWriteData(int length); michael@0: michael@0: // For Pickles which contain variable length buffers (e.g. those created michael@0: // with BeginWriteData), the Pickle can michael@0: // be 'trimmed' if the amount of data required is less than originally michael@0: // requested. For example, you may have created a buffer with 10K of data, michael@0: // but decided to only fill 10 bytes of that data. Use this function michael@0: // to trim the buffer so that we don't send 9990 bytes of unused data. michael@0: // You cannot increase the size of the variable buffer; only shrink it. michael@0: // This function assumes that the length of the variable buffer has michael@0: // not been changed. michael@0: void TrimWriteData(int length); michael@0: michael@0: void EndRead(void* iter) const { michael@0: DCHECK(iter == end_of_payload()); michael@0: } michael@0: michael@0: // Payload follows after allocation of Header (header size is customizable). michael@0: struct Header { michael@0: uint32_t payload_size; // Specifies the size of the payload. michael@0: }; michael@0: michael@0: // Returns the header, cast to a user-specified type T. The type T must be a michael@0: // subclass of Header and its size must correspond to the header_size passed michael@0: // to the Pickle constructor. michael@0: template michael@0: T* headerT() { michael@0: DCHECK(sizeof(T) == header_size_); michael@0: return static_cast(header_); michael@0: } michael@0: template michael@0: const T* headerT() const { michael@0: DCHECK(sizeof(T) == header_size_); michael@0: return static_cast(header_); michael@0: } michael@0: michael@0: // Returns true if the given iterator could point to data with the given michael@0: // length. If there is no room for the given data before the end of the michael@0: // payload, returns false. michael@0: bool IteratorHasRoomFor(const void* iter, int len) const { michael@0: if ((len < 0) || (iter < header_) || iter > end_of_payload()) michael@0: return false; michael@0: const char* end_of_region = reinterpret_cast(iter) + len; michael@0: // Watch out for overflow in pointer calculation, which wraps. michael@0: return (iter <= end_of_region) && (end_of_region <= end_of_payload()); michael@0: } michael@0: michael@0: typedef uint32_t memberAlignmentType; michael@0: michael@0: protected: michael@0: uint32_t payload_size() const { return header_->payload_size; } michael@0: michael@0: char* payload() { michael@0: return reinterpret_cast(header_) + header_size_; michael@0: } michael@0: const char* payload() const { michael@0: return reinterpret_cast(header_) + header_size_; michael@0: } michael@0: michael@0: // Returns the address of the byte immediately following the currently valid michael@0: // header + payload. michael@0: char* end_of_payload() { michael@0: return payload() + payload_size(); michael@0: } michael@0: const char* end_of_payload() const { michael@0: return payload() + payload_size(); michael@0: } michael@0: michael@0: uint32_t capacity() const { michael@0: return capacity_; michael@0: } michael@0: michael@0: // Resizes the buffer for use when writing the specified amount of data. The michael@0: // location that the data should be written at is returned, or NULL if there michael@0: // was an error. Call EndWrite with the returned offset and the given length michael@0: // to pad out for the next write. michael@0: char* BeginWrite(uint32_t length, uint32_t alignment); michael@0: michael@0: // Completes the write operation by padding the data with NULL bytes until it michael@0: // is padded. Should be paired with BeginWrite, but it does not necessarily michael@0: // have to be called after the data is written. michael@0: void EndWrite(char* dest, int length); michael@0: michael@0: // Resize the capacity, note that the input value should include the size of michael@0: // the header: new_capacity = sizeof(Header) + desired_payload_capacity. michael@0: // A realloc() failure will cause a Resize failure... and caller should check michael@0: // the return result for true (i.e., successful resizing). michael@0: bool Resize(uint32_t new_capacity); michael@0: michael@0: // Round 'bytes' up to the next multiple of 'alignment'. 'alignment' must be michael@0: // a power of 2. michael@0: template struct ConstantAligner { michael@0: static uint32_t align(int bytes) { michael@0: static_assert((alignment & (alignment - 1)) == 0, michael@0: "alignment must be a power of two"); michael@0: return (bytes + (alignment - 1)) & ~static_cast(alignment - 1); michael@0: } michael@0: }; michael@0: michael@0: static uint32_t AlignInt(int bytes) { michael@0: return ConstantAligner::align(bytes); michael@0: } michael@0: michael@0: // Moves the iterator by the given number of bytes, making sure it is aligned. michael@0: // Pointer (iterator) is NOT aligned, but the change in the pointer michael@0: // is guaranteed to be a multiple of sizeof(memberAlignmentType). michael@0: static void UpdateIter(void** iter, int bytes) { michael@0: *iter = static_cast(*iter) + AlignInt(bytes); michael@0: } michael@0: michael@0: // Find the end of the pickled data that starts at range_start. Returns NULL michael@0: // if the entire Pickle is not found in the given data range. michael@0: static const char* FindNext(uint32_t header_size, michael@0: const char* range_start, michael@0: const char* range_end); michael@0: michael@0: // The allocation granularity of the payload. michael@0: static const int kPayloadUnit; michael@0: michael@0: private: michael@0: Header* header_; michael@0: uint32_t header_size_; michael@0: uint32_t capacity_; michael@0: uint32_t variable_buffer_offset_; michael@0: }; michael@0: michael@0: #endif // BASE_PICKLE_H__