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: #include "base/pickle.h" michael@0: michael@0: #include "mozilla/Alignment.h" michael@0: #include "mozilla/Endian.h" michael@0: #include "mozilla/TypeTraits.h" michael@0: michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "nsDebug.h" michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t), michael@0: "Insufficient alignment"); michael@0: michael@0: // static michael@0: const int Pickle::kPayloadUnit = 64; michael@0: michael@0: // We mark a read only pickle with a special capacity_. michael@0: static const uint32_t kCapacityReadOnly = (uint32_t) -1; michael@0: michael@0: static const char kBytePaddingMarker = char(0xbf); michael@0: michael@0: namespace { michael@0: michael@0: // We want to copy data to our payload as efficiently as possible. michael@0: // memcpy fits the bill for copying, but not all compilers or michael@0: // architectures support inlining memcpy from void*, which has unknown michael@0: // static alignment. However, we know that all the members of our michael@0: // payload will be aligned on memberAlignmentType boundaries. We michael@0: // therefore use that knowledge to construct a copier that will copy michael@0: // efficiently (via standard C++ assignment mechanisms) if the datatype michael@0: // needs that alignment or less, and memcpy otherwise. (The compiler michael@0: // may still inline memcpy, of course.) michael@0: michael@0: template michael@0: struct Copier michael@0: { michael@0: static void Copy(T* dest, void** iter) { michael@0: memcpy(dest, *iter, sizeof(T)); michael@0: } michael@0: }; michael@0: michael@0: // Copying 64-bit quantities happens often enough and can easily be made michael@0: // worthwhile on 32-bit platforms, so handle it specially. Only do it michael@0: // if 64-bit types aren't sufficiently aligned; the alignment michael@0: // requirements for them vary between 32-bit platforms. michael@0: #ifndef HAVE_64BIT_OS michael@0: template michael@0: struct Copier michael@0: { michael@0: static void Copy(T* dest, void** iter) { michael@0: #if MOZ_LITTLE_ENDIAN michael@0: static const int loIndex = 0, hiIndex = 1; michael@0: #else michael@0: static const int loIndex = 1, hiIndex = 0; michael@0: #endif michael@0: static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*), michael@0: "Pointers have different alignments"); michael@0: uint32_t* src = *reinterpret_cast(iter); michael@0: uint32_t* uint32dest = reinterpret_cast(dest); michael@0: uint32dest[loIndex] = src[loIndex]; michael@0: uint32dest[hiIndex] = src[hiIndex]; michael@0: } michael@0: }; michael@0: #endif michael@0: michael@0: template michael@0: struct Copier michael@0: { michael@0: static void Copy(T* dest, void** iter) { michael@0: // The reinterpret_cast is only safe if two conditions hold: michael@0: // (1) If the alignment of T* is the same as void*; michael@0: // (2) The alignment of the data in *iter is at least as michael@0: // big as MOZ_ALIGNOF(T). michael@0: // Check the first condition, as the second condition is already michael@0: // known to be true, or we wouldn't be here. michael@0: static_assert(MOZ_ALIGNOF(T*) == MOZ_ALIGNOF(void*), michael@0: "Pointers have different alignments"); michael@0: *dest = *(*reinterpret_cast(iter)); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: void CopyFromIter(T* dest, void** iter) { michael@0: static_assert(mozilla::IsPod::value, "Copied type must be a POD type"); michael@0: Copier::Copy(dest, iter); michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: // Payload is sizeof(Pickle::memberAlignmentType) aligned. michael@0: michael@0: Pickle::Pickle() michael@0: : header_(NULL), michael@0: header_size_(sizeof(Header)), michael@0: capacity_(0), michael@0: variable_buffer_offset_(0) { michael@0: Resize(kPayloadUnit); michael@0: header_->payload_size = 0; michael@0: } michael@0: michael@0: Pickle::Pickle(int header_size) michael@0: : header_(NULL), michael@0: header_size_(AlignInt(header_size)), michael@0: capacity_(0), michael@0: variable_buffer_offset_(0) { michael@0: DCHECK(static_cast(header_size) >= sizeof(Header)); michael@0: DCHECK(header_size <= kPayloadUnit); michael@0: Resize(kPayloadUnit); michael@0: if (!header_) { michael@0: NS_ABORT_OOM(kPayloadUnit); michael@0: } michael@0: header_->payload_size = 0; michael@0: } michael@0: michael@0: Pickle::Pickle(const char* data, int data_len) michael@0: : header_(reinterpret_cast(const_cast(data))), michael@0: header_size_(data_len - header_->payload_size), michael@0: capacity_(kCapacityReadOnly), michael@0: variable_buffer_offset_(0) { michael@0: DCHECK(header_size_ >= sizeof(Header)); michael@0: DCHECK(header_size_ == AlignInt(header_size_)); michael@0: } michael@0: michael@0: Pickle::Pickle(const Pickle& other) michael@0: : header_(NULL), michael@0: header_size_(other.header_size_), michael@0: capacity_(0), michael@0: variable_buffer_offset_(other.variable_buffer_offset_) { michael@0: uint32_t payload_size = header_size_ + other.header_->payload_size; michael@0: bool resized = Resize(payload_size); michael@0: if (!resized) { michael@0: NS_ABORT_OOM(payload_size); michael@0: } michael@0: memcpy(header_, other.header_, payload_size); michael@0: } michael@0: michael@0: Pickle::~Pickle() { michael@0: if (capacity_ != kCapacityReadOnly) michael@0: free(header_); michael@0: } michael@0: michael@0: Pickle& Pickle::operator=(const Pickle& other) { michael@0: if (header_size_ != other.header_size_ && capacity_ != kCapacityReadOnly) { michael@0: free(header_); michael@0: header_ = NULL; michael@0: header_size_ = other.header_size_; michael@0: } michael@0: bool resized = Resize(other.header_size_ + other.header_->payload_size); michael@0: if (!resized) { michael@0: NS_ABORT_OOM(other.header_size_ + other.header_->payload_size); michael@0: } michael@0: memcpy(header_, other.header_, header_size_ + other.header_->payload_size); michael@0: variable_buffer_offset_ = other.variable_buffer_offset_; michael@0: return *this; michael@0: } michael@0: michael@0: bool Pickle::ReadBool(void** iter, bool* result) const { michael@0: DCHECK(iter); michael@0: michael@0: int tmp; michael@0: if (!ReadInt(iter, &tmp)) michael@0: return false; michael@0: DCHECK(0 == tmp || 1 == tmp); michael@0: *result = tmp ? true : false; michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadInt16(void** iter, int16_t* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadUInt16(void** iter, uint16_t* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadInt(void** iter, int* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: // Always written as a 64-bit value since the size for this type can michael@0: // differ between architectures. michael@0: bool Pickle::ReadLong(void** iter, long* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: int64_t bigResult = 0; michael@0: if (!IteratorHasRoomFor(*iter, sizeof(bigResult))) michael@0: return false; michael@0: michael@0: CopyFromIter(&bigResult, iter); michael@0: DCHECK(bigResult <= LONG_MAX && bigResult >= LONG_MIN); michael@0: *result = static_cast(bigResult); michael@0: michael@0: UpdateIter(iter, sizeof(bigResult)); michael@0: return true; michael@0: } michael@0: michael@0: // Always written as a 64-bit value since the size for this type can michael@0: // differ between architectures. michael@0: bool Pickle::ReadULong(void** iter, unsigned long* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: uint64_t bigResult = 0; michael@0: if (!IteratorHasRoomFor(*iter, sizeof(bigResult))) michael@0: return false; michael@0: michael@0: CopyFromIter(&bigResult, iter); michael@0: DCHECK(bigResult <= ULONG_MAX); michael@0: *result = static_cast(bigResult); michael@0: michael@0: UpdateIter(iter, sizeof(bigResult)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadLength(void** iter, int* result) const { michael@0: if (!ReadInt(iter, result)) michael@0: return false; michael@0: return ((*result) >= 0); michael@0: } michael@0: michael@0: // Always written as a 64-bit value since the size for this type can michael@0: // differ between architectures. michael@0: bool Pickle::ReadSize(void** iter, size_t* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: uint64_t bigResult = 0; michael@0: if (!IteratorHasRoomFor(*iter, sizeof(bigResult))) michael@0: return false; michael@0: michael@0: CopyFromIter(&bigResult, iter); michael@0: DCHECK(bigResult <= std::numeric_limits::max()); michael@0: *result = static_cast(bigResult); michael@0: michael@0: UpdateIter(iter, sizeof(bigResult)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadInt32(void** iter, int32_t* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadUInt32(void** iter, uint32_t* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadInt64(void** iter, int64_t* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadUInt64(void** iter, uint64_t* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadDouble(void** iter, double* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: // Always written as a 64-bit value since the size for this type can michael@0: // differ between architectures. michael@0: bool Pickle::ReadIntPtr(void** iter, intptr_t* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: int64_t bigResult = 0; michael@0: if (!IteratorHasRoomFor(*iter, sizeof(bigResult))) michael@0: return false; michael@0: michael@0: CopyFromIter(&bigResult, iter); michael@0: DCHECK(bigResult <= std::numeric_limits::max() && bigResult >= std::numeric_limits::min()); michael@0: *result = static_cast(bigResult); michael@0: michael@0: UpdateIter(iter, sizeof(bigResult)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadUnsignedChar(void** iter, unsigned char* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!IteratorHasRoomFor(*iter, sizeof(*result))) michael@0: return false; michael@0: michael@0: CopyFromIter(result, iter); michael@0: michael@0: UpdateIter(iter, sizeof(*result)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadString(void** iter, std::string* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: int len; michael@0: if (!ReadLength(iter, &len)) michael@0: return false; michael@0: if (!IteratorHasRoomFor(*iter, len)) michael@0: return false; michael@0: michael@0: char* chars = reinterpret_cast(*iter); michael@0: result->assign(chars, len); michael@0: michael@0: UpdateIter(iter, len); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadWString(void** iter, std::wstring* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: int len; michael@0: if (!ReadLength(iter, &len)) michael@0: return false; michael@0: if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t))) michael@0: return false; michael@0: michael@0: wchar_t* chars = reinterpret_cast(*iter); michael@0: result->assign(chars, len); michael@0: michael@0: UpdateIter(iter, len * sizeof(wchar_t)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadString16(void** iter, string16* result) const { michael@0: DCHECK(iter); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: int len; michael@0: if (!ReadLength(iter, &len)) michael@0: return false; michael@0: if (!IteratorHasRoomFor(*iter, len)) michael@0: return false; michael@0: michael@0: char16* chars = reinterpret_cast(*iter); michael@0: result->assign(chars, len); michael@0: michael@0: UpdateIter(iter, len * sizeof(char16)); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadBytes(void** iter, const char** data, int length, michael@0: uint32_t alignment) const { michael@0: DCHECK(iter); michael@0: DCHECK(data); michael@0: DCHECK(alignment == 4 || alignment == 8); michael@0: DCHECK(intptr_t(header_) % alignment == 0); michael@0: michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: uint32_t paddingLen = intptr_t(*iter) % alignment; michael@0: if (paddingLen) { michael@0: #ifdef DEBUG michael@0: { michael@0: const char* padding = static_cast(*iter); michael@0: for (uint32_t i = 0; i < paddingLen; i++) { michael@0: DCHECK(*(padding + i) == kBytePaddingMarker); michael@0: } michael@0: } michael@0: #endif michael@0: length += paddingLen; michael@0: } michael@0: michael@0: if (!IteratorHasRoomFor(*iter, length)) michael@0: return false; michael@0: michael@0: *data = static_cast(*iter) + paddingLen; michael@0: DCHECK(intptr_t(*data) % alignment == 0); michael@0: michael@0: UpdateIter(iter, length); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::ReadData(void** iter, const char** data, int* length) const { michael@0: DCHECK(iter); michael@0: DCHECK(data); michael@0: DCHECK(length); michael@0: if (!*iter) michael@0: *iter = const_cast(payload()); michael@0: michael@0: if (!ReadLength(iter, length)) michael@0: return false; michael@0: michael@0: return ReadBytes(iter, data, *length); michael@0: } michael@0: michael@0: char* Pickle::BeginWrite(uint32_t length, uint32_t alignment) { michael@0: DCHECK(alignment % 4 == 0) << "Must be at least 32-bit aligned!"; michael@0: michael@0: // write at an alignment-aligned offset from the beginning of the header michael@0: uint32_t offset = AlignInt(header_->payload_size); michael@0: uint32_t padding = (header_size_ + offset) % alignment; michael@0: uint32_t new_size = offset + padding + AlignInt(length); michael@0: uint32_t needed_size = header_size_ + new_size; michael@0: michael@0: if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size))) michael@0: return NULL; michael@0: michael@0: DCHECK(intptr_t(header_) % alignment == 0); michael@0: michael@0: #ifdef ARCH_CPU_64_BITS michael@0: DCHECK_LE(length, std::numeric_limits::max()); michael@0: #endif michael@0: michael@0: char* buffer = payload() + offset; michael@0: michael@0: if (padding) { michael@0: memset(buffer, kBytePaddingMarker, padding); michael@0: buffer += padding; michael@0: } michael@0: michael@0: DCHECK(intptr_t(buffer) % alignment == 0); michael@0: michael@0: header_->payload_size = new_size; michael@0: michael@0: #ifdef MOZ_VALGRIND michael@0: // pad the trailing end as well, so that valgrind michael@0: // doesn't complain when we write the buffer michael@0: padding = AlignInt(length) - length; michael@0: if (padding) { michael@0: memset(buffer + length, kBytePaddingMarker, padding); michael@0: } michael@0: #endif michael@0: michael@0: return buffer; michael@0: } michael@0: michael@0: void Pickle::EndWrite(char* dest, int length) { michael@0: // Zero-pad to keep tools like purify from complaining about uninitialized michael@0: // memory. michael@0: if (length % sizeof(memberAlignmentType)) michael@0: memset(dest + length, 0, michael@0: sizeof(memberAlignmentType) - (length % sizeof(memberAlignmentType))); michael@0: } michael@0: michael@0: bool Pickle::WriteBytes(const void* data, int data_len, uint32_t alignment) { michael@0: DCHECK(capacity_ != kCapacityReadOnly) << "oops: pickle is readonly"; michael@0: DCHECK(alignment == 4 || alignment == 8); michael@0: DCHECK(intptr_t(header_) % alignment == 0); michael@0: michael@0: char* dest = BeginWrite(data_len, alignment); michael@0: if (!dest) michael@0: return false; michael@0: michael@0: memcpy(dest, data, data_len); michael@0: michael@0: EndWrite(dest, data_len); michael@0: return true; michael@0: } michael@0: michael@0: bool Pickle::WriteString(const std::string& value) { michael@0: if (!WriteInt(static_cast(value.size()))) michael@0: return false; michael@0: michael@0: return WriteBytes(value.data(), static_cast(value.size())); michael@0: } michael@0: michael@0: bool Pickle::WriteWString(const std::wstring& value) { michael@0: if (!WriteInt(static_cast(value.size()))) michael@0: return false; michael@0: michael@0: return WriteBytes(value.data(), michael@0: static_cast(value.size() * sizeof(wchar_t))); michael@0: } michael@0: michael@0: bool Pickle::WriteString16(const string16& value) { michael@0: if (!WriteInt(static_cast(value.size()))) michael@0: return false; michael@0: michael@0: return WriteBytes(value.data(), michael@0: static_cast(value.size()) * sizeof(char16)); michael@0: } michael@0: michael@0: bool Pickle::WriteData(const char* data, int length) { michael@0: return WriteInt(length) && WriteBytes(data, length); michael@0: } michael@0: michael@0: char* Pickle::BeginWriteData(int length) { michael@0: DCHECK_EQ(variable_buffer_offset_, 0U) << michael@0: "There can only be one variable buffer in a Pickle"; michael@0: michael@0: if (!WriteInt(length)) michael@0: return NULL; michael@0: michael@0: char *data_ptr = BeginWrite(length, sizeof(memberAlignmentType)); michael@0: if (!data_ptr) michael@0: return NULL; michael@0: michael@0: variable_buffer_offset_ = michael@0: data_ptr - reinterpret_cast(header_) - sizeof(int); michael@0: michael@0: // EndWrite doesn't necessarily have to be called after the write operation, michael@0: // so we call it here to pad out what the caller will eventually write. michael@0: EndWrite(data_ptr, length); michael@0: return data_ptr; michael@0: } michael@0: michael@0: void Pickle::TrimWriteData(int new_length) { michael@0: DCHECK(variable_buffer_offset_ != 0); michael@0: michael@0: // Fetch the the variable buffer size michael@0: int* cur_length = reinterpret_cast( michael@0: reinterpret_cast(header_) + variable_buffer_offset_); michael@0: michael@0: if (new_length < 0 || new_length > *cur_length) { michael@0: NOTREACHED() << "Invalid length in TrimWriteData."; michael@0: return; michael@0: } michael@0: michael@0: // Update the payload size and variable buffer size michael@0: header_->payload_size -= (*cur_length - new_length); michael@0: *cur_length = new_length; michael@0: } michael@0: michael@0: bool Pickle::Resize(uint32_t new_capacity) { michael@0: new_capacity = ConstantAligner::align(new_capacity); michael@0: michael@0: void* p = realloc(header_, new_capacity); michael@0: if (!p) michael@0: return false; michael@0: michael@0: header_ = reinterpret_cast(p); michael@0: capacity_ = new_capacity; michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: const char* Pickle::FindNext(uint32_t header_size, michael@0: const char* start, michael@0: const char* end) { michael@0: DCHECK(header_size == AlignInt(header_size)); michael@0: DCHECK(header_size <= static_cast(kPayloadUnit)); michael@0: michael@0: const Header* hdr = reinterpret_cast(start); michael@0: const char* payload_base = start + header_size; michael@0: const char* payload_end = payload_base + hdr->payload_size; michael@0: if (payload_end < payload_base) michael@0: return NULL; michael@0: michael@0: return (payload_end > end) ? NULL : payload_end; michael@0: }