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 // static
     14 const int Pickle::kPayloadUnit = 64;
     15 
     16 static const size_t kCapacityReadOnly = static_cast<size_t>(-1);
     17 
     18 PickleIterator::PickleIterator(const Pickle& pickle)
     19     : read_ptr_(pickle.payload()),
     20       read_end_ptr_(pickle.end_of_payload()) {
     21 }
     22 
     23 template <typename Type>
     24 inline bool PickleIterator::ReadBuiltinType(Type* result) {
     25   const char* read_from = GetReadPointerAndAdvance<Type>();
     26   if (!read_from)
     27     return false;
     28   if (sizeof(Type) > sizeof(uint32))
     29     memcpy(result, read_from, sizeof(*result));
     30   else
     31     *result = *reinterpret_cast<const Type*>(read_from);
     32   return true;
     33 }
     34 
     35 template<typename Type>
     36 inline const char* PickleIterator::GetReadPointerAndAdvance() {
     37   const char* current_read_ptr = read_ptr_;
     38   if (read_ptr_ + sizeof(Type) > read_end_ptr_)
     39     return NULL;
     40   if (sizeof(Type) < sizeof(uint32))
     41     read_ptr_ += AlignInt(sizeof(Type), sizeof(uint32));
     42   else
     43     read_ptr_ += sizeof(Type);
     44   return current_read_ptr;
     45 }
     46 
     47 const char* PickleIterator::GetReadPointerAndAdvance(int num_bytes) {
     48   if (num_bytes < 0 || read_end_ptr_ - read_ptr_ < num_bytes)
     49     return NULL;
     50   const char* current_read_ptr = read_ptr_;
     51   read_ptr_ += AlignInt(num_bytes, sizeof(uint32));
     52   return current_read_ptr;
     53 }
     54 
     55 inline const char* PickleIterator::GetReadPointerAndAdvance(int num_elements,
     56                                                           size_t size_element) {
     57   // Check for int32 overflow.
     58   int64 num_bytes = static_cast<int64>(num_elements) * size_element;
     59   int num_bytes32 = static_cast<int>(num_bytes);
     60   if (num_bytes != static_cast<int64>(num_bytes32))
     61     return NULL;
     62   return GetReadPointerAndAdvance(num_bytes32);
     63 }
     64 
     65 bool PickleIterator::ReadBool(bool* result) {
     66   return ReadBuiltinType(result);
     67 }
     68 
     69 bool PickleIterator::ReadInt(int* result) {
     70   return ReadBuiltinType(result);
     71 }
     72 
     73 bool PickleIterator::ReadLong(long* result) {
     74   return ReadBuiltinType(result);
     75 }
     76 
     77 bool PickleIterator::ReadUInt16(uint16* result) {
     78   return ReadBuiltinType(result);
     79 }
     80 
     81 bool PickleIterator::ReadUInt32(uint32* result) {
     82   return ReadBuiltinType(result);
     83 }
     84 
     85 bool PickleIterator::ReadInt64(int64* result) {
     86   return ReadBuiltinType(result);
     87 }
     88 
     89 bool PickleIterator::ReadUInt64(uint64* result) {
     90   return ReadBuiltinType(result);
     91 }
     92 
     93 bool PickleIterator::ReadFloat(float* result) {
     94   return ReadBuiltinType(result);
     95 }
     96 
     97 bool PickleIterator::ReadString(std::string* result) {
     98   int len;
     99   if (!ReadInt(&len))
    100     return false;
    101   const char* read_from = GetReadPointerAndAdvance(len);
    102   if (!read_from)
    103     return false;
    104 
    105   result->assign(read_from, len);
    106   return true;
    107 }
    108 
    109 bool PickleIterator::ReadWString(std::wstring* result) {
    110   int len;
    111   if (!ReadInt(&len))
    112     return false;
    113   const char* read_from = GetReadPointerAndAdvance(len, sizeof(wchar_t));
    114   if (!read_from)
    115     return false;
    116 
    117   result->assign(reinterpret_cast<const wchar_t*>(read_from), len);
    118   return true;
    119 }
    120 
    121 bool PickleIterator::ReadString16(string16* result) {
    122   int len;
    123   if (!ReadInt(&len))
    124     return false;
    125   const char* read_from = GetReadPointerAndAdvance(len, sizeof(char16));
    126   if (!read_from)
    127     return false;
    128 
    129   result->assign(reinterpret_cast<const char16*>(read_from), len);
    130   return true;
    131 }
    132 
    133 bool PickleIterator::ReadData(const char** data, int* length) {
    134   *length = 0;
    135   *data = 0;
    136 
    137   if (!ReadInt(length))
    138     return false;
    139 
    140   return ReadBytes(data, *length);
    141 }
    142 
    143 bool PickleIterator::ReadBytes(const char** data, int length) {
    144   const char* read_from = GetReadPointerAndAdvance(length);
    145   if (!read_from)
    146     return false;
    147   *data = read_from;
    148   return true;
    149 }
    150 
    151 // Payload is uint32 aligned.
    152 
    153 Pickle::Pickle()
    154     : header_(NULL),
    155       header_size_(sizeof(Header)),
    156       capacity_(0),
    157       variable_buffer_offset_(0) {
    158   Resize(kPayloadUnit);
    159   header_->payload_size = 0;
    160 }
    161 
    162 Pickle::Pickle(int header_size)
    163     : header_(NULL),
    164       header_size_(AlignInt(header_size, sizeof(uint32))),
    165       capacity_(0),
    166       variable_buffer_offset_(0) {
    167   DCHECK_GE(static_cast<size_t>(header_size), sizeof(Header));
    168   DCHECK_LE(header_size, kPayloadUnit);
    169   Resize(kPayloadUnit);
    170   header_->payload_size = 0;
    171 }
    172 
    173 Pickle::Pickle(const char* data, int data_len)
    174     : header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
    175       header_size_(0),
    176       capacity_(kCapacityReadOnly),
    177       variable_buffer_offset_(0) {
    178   if (data_len >= static_cast<int>(sizeof(Header)))
    179     header_size_ = data_len - header_->payload_size;
    180 
    181   if (header_size_ > static_cast<unsigned int>(data_len))
    182     header_size_ = 0;
    183 
    184   if (header_size_ != AlignInt(header_size_, sizeof(uint32)))
    185     header_size_ = 0;
    186 
    187   // If there is anything wrong with the data, we're not going to use it.
    188   if (!header_size_)
    189     header_ = NULL;
    190 }
    191 
    192 Pickle::Pickle(const Pickle& other)
    193     : header_(NULL),
    194       header_size_(other.header_size_),
    195       capacity_(0),
    196       variable_buffer_offset_(other.variable_buffer_offset_) {
    197   size_t payload_size = header_size_ + other.header_->payload_size;
    198   bool resized = Resize(payload_size);
    199   CHECK(resized);  // Realloc failed.
    200   memcpy(header_, other.header_, payload_size);
    201 }
    202 
    203 Pickle::~Pickle() {
    204   if (capacity_ != kCapacityReadOnly)
    205     free(header_);
    206 }
    207 
    208 Pickle& Pickle::operator=(const Pickle& other) {
    209   if (this == &other) {
    210     NOTREACHED();
    211     return *this;
    212   }
    213   if (capacity_ == kCapacityReadOnly) {
    214     header_ = NULL;
    215     capacity_ = 0;
    216   }
    217   if (header_size_ != other.header_size_) {
    218     free(header_);
    219     header_ = NULL;
    220     header_size_ = other.header_size_;
    221   }
    222   bool resized = Resize(other.header_size_ + other.header_->payload_size);
    223   CHECK(resized);  // Realloc failed.
    224   memcpy(header_, other.header_,
    225          other.header_size_ + other.header_->payload_size);
    226   variable_buffer_offset_ = other.variable_buffer_offset_;
    227   return *this;
    228 }
    229 
    230 bool Pickle::WriteString(const std::string& value) {
    231   if (!WriteInt(static_cast<int>(value.size())))
    232     return false;
    233 
    234   return WriteBytes(value.data(), static_cast<int>(value.size()));
    235 }
    236 
    237 bool Pickle::WriteWString(const std::wstring& value) {
    238   if (!WriteInt(static_cast<int>(value.size())))
    239     return false;
    240 
    241   return WriteBytes(value.data(),
    242                     static_cast<int>(value.size() * sizeof(wchar_t)));
    243 }
    244 
    245 bool Pickle::WriteString16(const string16& value) {
    246   if (!WriteInt(static_cast<int>(value.size())))
    247     return false;
    248 
    249   return WriteBytes(value.data(),
    250                     static_cast<int>(value.size()) * sizeof(char16));
    251 }
    252 
    253 bool Pickle::WriteData(const char* data, int length) {
    254   return length >= 0 && WriteInt(length) && WriteBytes(data, length);
    255 }
    256 
    257 bool Pickle::WriteBytes(const void* data, int data_len) {
    258   DCHECK_NE(kCapacityReadOnly, capacity_) << "oops: pickle is readonly";
    259 
    260   char* dest = BeginWrite(data_len);
    261   if (!dest)
    262     return false;
    263 
    264   memcpy(dest, data, data_len);
    265 
    266   EndWrite(dest, data_len);
    267   return true;
    268 }
    269 
    270 char* Pickle::BeginWriteData(int length) {
    271   DCHECK_EQ(variable_buffer_offset_, 0U) <<
    272     "There can only be one variable buffer in a Pickle";
    273 
    274   if (length < 0 || !WriteInt(length))
    275     return NULL;
    276 
    277   char *data_ptr = BeginWrite(length);
    278   if (!data_ptr)
    279     return NULL;
    280 
    281   variable_buffer_offset_ =
    282       data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
    283 
    284   // EndWrite doesn't necessarily have to be called after the write operation,
    285   // so we call it here to pad out what the caller will eventually write.
    286   EndWrite(data_ptr, length);
    287   return data_ptr;
    288 }
    289 
    290 void Pickle::TrimWriteData(int new_length) {
    291   DCHECK_NE(variable_buffer_offset_, 0U);
    292 
    293   // Fetch the the variable buffer size
    294   int* cur_length = reinterpret_cast<int*>(
    295       reinterpret_cast<char*>(header_) + variable_buffer_offset_);
    296 
    297   if (new_length < 0 || new_length > *cur_length) {
    298     NOTREACHED() << "Invalid length in TrimWriteData.";
    299     return;
    300   }
    301 
    302   // Update the payload size and variable buffer size
    303   header_->payload_size -= (*cur_length - new_length);
    304   *cur_length = new_length;
    305 }
    306 
    307 char* Pickle::BeginWrite(size_t length) {
    308   // write at a uint32-aligned offset from the beginning of the header
    309   size_t offset = AlignInt(header_->payload_size, sizeof(uint32));
    310 
    311   size_t new_size = offset + length;
    312   size_t needed_size = header_size_ + new_size;
    313   if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size)))
    314     return NULL;
    315 
    316 #ifdef ARCH_CPU_64_BITS
    317   DCHECK_LE(length, kuint32max);
    318 #endif
    319 
    320   header_->payload_size = static_cast<uint32>(new_size);
    321   return mutable_payload() + offset;
    322 }
    323 
    324 void Pickle::EndWrite(char* dest, int length) {
    325   // Zero-pad to keep tools like valgrind from complaining about uninitialized
    326   // memory.
    327   if (length % sizeof(uint32))
    328     memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32)));
    329 }
    330 
    331 bool Pickle::Resize(size_t new_capacity) {
    332   new_capacity = AlignInt(new_capacity, kPayloadUnit);
    333 
    334   CHECK_NE(capacity_, kCapacityReadOnly);
    335   void* p = realloc(header_, new_capacity);
    336   if (!p)
    337     return false;
    338 
    339   header_ = reinterpret_cast<Header*>(p);
    340   capacity_ = new_capacity;
    341   return true;
    342 }
    343 
    344 // static
    345 const char* Pickle::FindNext(size_t header_size,
    346                              const char* start,
    347                              const char* end) {
    348   DCHECK_EQ(header_size, AlignInt(header_size, sizeof(uint32)));
    349   DCHECK_LE(header_size, static_cast<size_t>(kPayloadUnit));
    350 
    351   if (static_cast<size_t>(end - start) < sizeof(Header))
    352     return NULL;
    353 
    354   const Header* hdr = reinterpret_cast<const Header*>(start);
    355   const char* payload_base = start + header_size;
    356   const char* payload_end = payload_base + hdr->payload_size;
    357   if (payload_end < payload_base)
    358     return NULL;
    359 
    360   return (payload_end > end) ? NULL : payload_end;
    361 }
    362