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