Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/pickle.h"
      6 
      7 #include <stdlib.h>
      8 
      9 #include <algorithm>  // for max()
     10 
     11 //------------------------------------------------------------------------------
     12 
     13 using base::char16;
     14 using base::string16;
     15 
     16 // static
     17 const int Pickle::kPayloadUnit = 64;
     18 
     19 static const size_t kCapacityReadOnly = static_cast<size_t>(-1);
     20 
     21 PickleIterator::PickleIterator(const Pickle& pickle)
     22     : read_ptr_(pickle.payload()),
     23       read_end_ptr_(pickle.end_of_payload()) {
     24 }
     25 
     26 template <typename Type>
     27 inline bool PickleIterator::ReadBuiltinType(Type* result) {
     28   const char* read_from = GetReadPointerAndAdvance<Type>();
     29   if (!read_from)
     30     return false;
     31   if (sizeof(Type) > sizeof(uint32))
     32     memcpy(result, read_from, sizeof(*result));
     33   else
     34     *result = *reinterpret_cast<const Type*>(read_from);
     35   return true;
     36 }
     37 
     38 template<typename Type>
     39 inline const char* PickleIterator::GetReadPointerAndAdvance() {
     40   const char* current_read_ptr = read_ptr_;
     41   if (read_ptr_ + sizeof(Type) > read_end_ptr_)
     42     return NULL;
     43   if (sizeof(Type) < sizeof(uint32))
     44     read_ptr_ += AlignInt(sizeof(Type), sizeof(uint32));
     45   else
     46     read_ptr_ += sizeof(Type);
     47   return current_read_ptr;
     48 }
     49 
     50 const char* PickleIterator::GetReadPointerAndAdvance(int num_bytes) {
     51   if (num_bytes < 0 || read_end_ptr_ - read_ptr_ < num_bytes)
     52     return NULL;
     53   const char* current_read_ptr = read_ptr_;
     54   read_ptr_ += AlignInt(num_bytes, sizeof(uint32));
     55   return current_read_ptr;
     56 }
     57 
     58 inline const char* PickleIterator::GetReadPointerAndAdvance(int num_elements,
     59                                                           size_t size_element) {
     60   // Check for int32 overflow.
     61   int64 num_bytes = static_cast<int64>(num_elements) * size_element;
     62   int num_bytes32 = static_cast<int>(num_bytes);
     63   if (num_bytes != static_cast<int64>(num_bytes32))
     64     return NULL;
     65   return GetReadPointerAndAdvance(num_bytes32);
     66 }
     67 
     68 bool PickleIterator::ReadBool(bool* result) {
     69   return ReadBuiltinType(result);
     70 }
     71 
     72 bool PickleIterator::ReadInt(int* result) {
     73   return ReadBuiltinType(result);
     74 }
     75 
     76 bool PickleIterator::ReadLong(long* result) {
     77   return ReadBuiltinType(result);
     78 }
     79 
     80 bool PickleIterator::ReadUInt16(uint16* result) {
     81   return ReadBuiltinType(result);
     82 }
     83 
     84 bool PickleIterator::ReadUInt32(uint32* result) {
     85   return ReadBuiltinType(result);
     86 }
     87 
     88 bool PickleIterator::ReadInt64(int64* result) {
     89   return ReadBuiltinType(result);
     90 }
     91 
     92 bool PickleIterator::ReadUInt64(uint64* result) {
     93   return ReadBuiltinType(result);
     94 }
     95 
     96 bool PickleIterator::ReadFloat(float* result) {
     97   // crbug.com/315213
     98   // The source data may not be properly aligned, and unaligned float reads
     99   // cause SIGBUS on some ARM platforms, so force using memcpy to copy the data
    100   // into the result.
    101   const char* read_from = GetReadPointerAndAdvance<float>();
    102   if (!read_from)
    103     return false;
    104   memcpy(result, read_from, sizeof(*result));
    105   return true;
    106 }
    107 
    108 bool PickleIterator::ReadString(std::string* result) {
    109   int len;
    110   if (!ReadInt(&len))
    111     return false;
    112   const char* read_from = GetReadPointerAndAdvance(len);
    113   if (!read_from)
    114     return false;
    115 
    116   result->assign(read_from, len);
    117   return true;
    118 }
    119 
    120 bool PickleIterator::ReadWString(std::wstring* result) {
    121   int len;
    122   if (!ReadInt(&len))
    123     return false;
    124   const char* read_from = GetReadPointerAndAdvance(len, sizeof(wchar_t));
    125   if (!read_from)
    126     return false;
    127 
    128   result->assign(reinterpret_cast<const wchar_t*>(read_from), len);
    129   return true;
    130 }
    131 
    132 bool PickleIterator::ReadString16(string16* result) {
    133   int len;
    134   if (!ReadInt(&len))
    135     return false;
    136   const char* read_from = GetReadPointerAndAdvance(len, sizeof(char16));
    137   if (!read_from)
    138     return false;
    139 
    140   result->assign(reinterpret_cast<const char16*>(read_from), len);
    141   return true;
    142 }
    143 
    144 bool PickleIterator::ReadData(const char** data, int* length) {
    145   *length = 0;
    146   *data = 0;
    147 
    148   if (!ReadInt(length))
    149     return false;
    150 
    151   return ReadBytes(data, *length);
    152 }
    153 
    154 bool PickleIterator::ReadBytes(const char** data, int length) {
    155   const char* read_from = GetReadPointerAndAdvance(length);
    156   if (!read_from)
    157     return false;
    158   *data = read_from;
    159   return true;
    160 }
    161 
    162 // Payload is uint32 aligned.
    163 
    164 Pickle::Pickle()
    165     : header_(NULL),
    166       header_size_(sizeof(Header)),
    167       capacity_after_header_(0),
    168       write_offset_(0) {
    169   Resize(kPayloadUnit);
    170   header_->payload_size = 0;
    171 }
    172 
    173 Pickle::Pickle(int header_size)
    174     : header_(NULL),
    175       header_size_(AlignInt(header_size, sizeof(uint32))),
    176       capacity_after_header_(0),
    177       write_offset_(0) {
    178   DCHECK_GE(static_cast<size_t>(header_size), sizeof(Header));
    179   DCHECK_LE(header_size, kPayloadUnit);
    180   Resize(kPayloadUnit);
    181   header_->payload_size = 0;
    182 }
    183 
    184 Pickle::Pickle(const char* data, int data_len)
    185     : header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
    186       header_size_(0),
    187       capacity_after_header_(kCapacityReadOnly),
    188       write_offset_(0) {
    189   if (data_len >= static_cast<int>(sizeof(Header)))
    190     header_size_ = data_len - header_->payload_size;
    191 
    192   if (header_size_ > static_cast<unsigned int>(data_len))
    193     header_size_ = 0;
    194 
    195   if (header_size_ != AlignInt(header_size_, sizeof(uint32)))
    196     header_size_ = 0;
    197 
    198   // If there is anything wrong with the data, we're not going to use it.
    199   if (!header_size_)
    200     header_ = NULL;
    201 }
    202 
    203 Pickle::Pickle(const Pickle& other)
    204     : header_(NULL),
    205       header_size_(other.header_size_),
    206       capacity_after_header_(0),
    207       write_offset_(other.write_offset_) {
    208   size_t payload_size = header_size_ + other.header_->payload_size;
    209   Resize(payload_size);
    210   memcpy(header_, other.header_, payload_size);
    211 }
    212 
    213 Pickle::~Pickle() {
    214   if (capacity_after_header_ != kCapacityReadOnly)
    215     free(header_);
    216 }
    217 
    218 Pickle& Pickle::operator=(const Pickle& other) {
    219   if (this == &other) {
    220     NOTREACHED();
    221     return *this;
    222   }
    223   if (capacity_after_header_ == kCapacityReadOnly) {
    224     header_ = NULL;
    225     capacity_after_header_ = 0;
    226   }
    227   if (header_size_ != other.header_size_) {
    228     free(header_);
    229     header_ = NULL;
    230     header_size_ = other.header_size_;
    231   }
    232   Resize(other.header_->payload_size);
    233   memcpy(header_, other.header_,
    234          other.header_size_ + other.header_->payload_size);
    235   write_offset_ = other.write_offset_;
    236   return *this;
    237 }
    238 
    239 bool Pickle::WriteString(const std::string& value) {
    240   if (!WriteInt(static_cast<int>(value.size())))
    241     return false;
    242 
    243   return WriteBytes(value.data(), static_cast<int>(value.size()));
    244 }
    245 
    246 bool Pickle::WriteWString(const std::wstring& value) {
    247   if (!WriteInt(static_cast<int>(value.size())))
    248     return false;
    249 
    250   return WriteBytes(value.data(),
    251                     static_cast<int>(value.size() * sizeof(wchar_t)));
    252 }
    253 
    254 bool Pickle::WriteString16(const string16& value) {
    255   if (!WriteInt(static_cast<int>(value.size())))
    256     return false;
    257 
    258   return WriteBytes(value.data(),
    259                     static_cast<int>(value.size()) * sizeof(char16));
    260 }
    261 
    262 bool Pickle::WriteData(const char* data, int length) {
    263   return length >= 0 && WriteInt(length) && WriteBytes(data, length);
    264 }
    265 
    266 bool Pickle::WriteBytes(const void* data, int length) {
    267   WriteBytesCommon(data, length);
    268   return true;
    269 }
    270 
    271 void Pickle::Reserve(size_t length) {
    272   size_t data_len = AlignInt(length, sizeof(uint32));
    273   DCHECK_GE(data_len, length);
    274 #ifdef ARCH_CPU_64_BITS
    275   DCHECK_LE(data_len, kuint32max);
    276 #endif
    277   DCHECK_LE(write_offset_, kuint32max - data_len);
    278   size_t new_size = write_offset_ + data_len;
    279   if (new_size > capacity_after_header_)
    280     Resize(capacity_after_header_ * 2 + new_size);
    281 }
    282 
    283 void Pickle::Resize(size_t new_capacity) {
    284   new_capacity = AlignInt(new_capacity, kPayloadUnit);
    285 
    286   CHECK_NE(capacity_after_header_, kCapacityReadOnly);
    287   void* p = realloc(header_, header_size_ + new_capacity);
    288   CHECK(p);
    289   header_ = reinterpret_cast<Header*>(p);
    290   capacity_after_header_ = new_capacity;
    291 }
    292 
    293 // static
    294 const char* Pickle::FindNext(size_t header_size,
    295                              const char* start,
    296                              const char* end) {
    297   DCHECK_EQ(header_size, AlignInt(header_size, sizeof(uint32)));
    298   DCHECK_LE(header_size, static_cast<size_t>(kPayloadUnit));
    299 
    300   size_t length = static_cast<size_t>(end - start);
    301   if (length < sizeof(Header))
    302     return NULL;
    303 
    304   const Header* hdr = reinterpret_cast<const Header*>(start);
    305   if (length < header_size || length - header_size < hdr->payload_size)
    306     return NULL;
    307   return start + header_size + hdr->payload_size;
    308 }
    309 
    310 template <size_t length> void Pickle::WriteBytesStatic(const void* data) {
    311   WriteBytesCommon(data, length);
    312 }
    313 
    314 template void Pickle::WriteBytesStatic<2>(const void* data);
    315 template void Pickle::WriteBytesStatic<4>(const void* data);
    316 template void Pickle::WriteBytesStatic<8>(const void* data);
    317 
    318 inline void Pickle::WriteBytesCommon(const void* data, size_t length) {
    319   DCHECK_NE(kCapacityReadOnly, capacity_after_header_)
    320       << "oops: pickle is readonly";
    321   size_t data_len = AlignInt(length, sizeof(uint32));
    322   DCHECK_GE(data_len, length);
    323 #ifdef ARCH_CPU_64_BITS
    324   DCHECK_LE(data_len, kuint32max);
    325 #endif
    326   DCHECK_LE(write_offset_, kuint32max - data_len);
    327   size_t new_size = write_offset_ + data_len;
    328   if (new_size > capacity_after_header_) {
    329     Resize(std::max(capacity_after_header_ * 2, new_size));
    330   }
    331 
    332   char* write = mutable_payload() + write_offset_;
    333   memcpy(write, data, length);
    334   memset(write + length, 0, data_len - length);
    335   header_->payload_size = static_cast<uint32>(write_offset_ + length);
    336   write_offset_ = new_size;
    337 }
    338