blob: 28b6292290073c2759eaffc5c6a2dde7bf01493f [file] [log] [blame]
pkasting@chromium.orgd23fe1a2011-04-01 05:34:25 +09001// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botf003cfe2008-08-24 09:55:55 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit3f4a7322008-07-27 06:49:38 +09004
initial.commit3f4a7322008-07-27 06:49:38 +09005#include "base/pickle.h"
6
maruel@google.com825f8792008-08-07 05:35:17 +09007#include <stdlib.h>
8
thestig@chromium.orgeb9afb42009-10-28 13:21:01 +09009#include <algorithm> // for max()
maruel@google.com825f8792008-08-07 05:35:17 +090010
initial.commit3f4a7322008-07-27 06:49:38 +090011//------------------------------------------------------------------------------
12
13// static
14const int Pickle::kPayloadUnit = 64;
15
thestig@chromium.orgf0c8f442011-10-11 07:20:33 +090016static const size_t kCapacityReadOnly = static_cast<size_t>(-1);
deanm@google.com19650de2008-08-13 23:57:51 +090017
initial.commit3f4a7322008-07-27 06:49:38 +090018// Payload is uint32 aligned.
19
20Pickle::Pickle()
21 : header_(NULL),
22 header_size_(sizeof(Header)),
23 capacity_(0),
24 variable_buffer_offset_(0) {
25 Resize(kPayloadUnit);
26 header_->payload_size = 0;
27}
28
29Pickle::Pickle(int header_size)
30 : header_(NULL),
31 header_size_(AlignInt(header_size, sizeof(uint32))),
32 capacity_(0),
33 variable_buffer_offset_(0) {
pkasting@chromium.orgd23fe1a2011-04-01 05:34:25 +090034 DCHECK_GE(static_cast<size_t>(header_size), sizeof(Header));
kushi.p@gmail.come4869772011-04-22 22:13:07 +090035 DCHECK_LE(header_size, kPayloadUnit);
initial.commit3f4a7322008-07-27 06:49:38 +090036 Resize(kPayloadUnit);
37 header_->payload_size = 0;
38}
39
40Pickle::Pickle(const char* data, int data_len)
41 : header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
rvargas@google.com8d9b2b92010-11-16 04:31:23 +090042 header_size_(0),
deanm@google.com19650de2008-08-13 23:57:51 +090043 capacity_(kCapacityReadOnly),
initial.commit3f4a7322008-07-27 06:49:38 +090044 variable_buffer_offset_(0) {
rvargas@google.com8d9b2b92010-11-16 04:31:23 +090045 if (data_len >= static_cast<int>(sizeof(Header)))
46 header_size_ = data_len - header_->payload_size;
47
48 if (header_size_ > static_cast<unsigned int>(data_len))
49 header_size_ = 0;
50
51 if (header_size_ != AlignInt(header_size_, sizeof(uint32)))
52 header_size_ = 0;
53
54 // If there is anything wrong with the data, we're not going to use it.
55 if (!header_size_)
56 header_ = NULL;
initial.commit3f4a7322008-07-27 06:49:38 +090057}
58
59Pickle::Pickle(const Pickle& other)
60 : header_(NULL),
61 header_size_(other.header_size_),
62 capacity_(0),
63 variable_buffer_offset_(other.variable_buffer_offset_) {
64 size_t payload_size = header_size_ + other.header_->payload_size;
65 bool resized = Resize(payload_size);
66 CHECK(resized); // Realloc failed.
67 memcpy(header_, other.header_, payload_size);
68}
69
70Pickle::~Pickle() {
deanm@google.com19650de2008-08-13 23:57:51 +090071 if (capacity_ != kCapacityReadOnly)
initial.commit3f4a7322008-07-27 06:49:38 +090072 free(header_);
73}
74
75Pickle& Pickle::operator=(const Pickle& other) {
jar@chromium.org4e105f12009-08-08 08:13:35 +090076 if (this == &other) {
77 NOTREACHED();
78 return *this;
79 }
jar@chromium.org766c5a02009-08-03 16:01:47 +090080 if (capacity_ == kCapacityReadOnly) {
81 header_ = NULL;
82 capacity_ = 0;
83 }
84 if (header_size_ != other.header_size_) {
initial.commit3f4a7322008-07-27 06:49:38 +090085 free(header_);
86 header_ = NULL;
87 header_size_ = other.header_size_;
88 }
jar@chromium.org4e105f12009-08-08 08:13:35 +090089 bool resized = Resize(other.header_size_ + other.header_->payload_size);
initial.commit3f4a7322008-07-27 06:49:38 +090090 CHECK(resized); // Realloc failed.
jar@chromium.org4e105f12009-08-08 08:13:35 +090091 memcpy(header_, other.header_,
92 other.header_size_ + other.header_->payload_size);
initial.commit3f4a7322008-07-27 06:49:38 +090093 variable_buffer_offset_ = other.variable_buffer_offset_;
94 return *this;
95}
96
97bool Pickle::ReadBool(void** iter, bool* result) const {
98 DCHECK(iter);
99
100 int tmp;
101 if (!ReadInt(iter, &tmp))
102 return false;
103 DCHECK(0 == tmp || 1 == tmp);
104 *result = tmp ? true : false;
105 return true;
106}
107
108bool Pickle::ReadInt(void** iter, int* result) const {
109 DCHECK(iter);
110 if (!*iter)
111 *iter = const_cast<char*>(payload());
112
113 if (!IteratorHasRoomFor(*iter, sizeof(*result)))
114 return false;
115
maruel@chromium.orgb7f65c32009-06-02 05:26:42 +0900116 // TODO(jar): http://crbug.com/13108 Pickle should be cleaned up, and not
117 // dependent on alignment.
initial.commit3f4a7322008-07-27 06:49:38 +0900118 // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result));
119 *result = *reinterpret_cast<int*>(*iter);
120
121 UpdateIter(iter, sizeof(*result));
122 return true;
123}
124
klink@google.comd7f60032008-08-23 08:24:54 +0900125bool Pickle::ReadLong(void** iter, long* result) const {
126 DCHECK(iter);
127 if (!*iter)
128 *iter = const_cast<char*>(payload());
129
130 if (!IteratorHasRoomFor(*iter, sizeof(*result)))
131 return false;
132
maruel@chromium.orgb7f65c32009-06-02 05:26:42 +0900133 // TODO(jar): http://crbug.com/13108 Pickle should be cleaned up, and not
134 // dependent on alignment.
klink@google.comd7f60032008-08-23 08:24:54 +0900135 memcpy(result, *iter, sizeof(*result));
136
137 UpdateIter(iter, sizeof(*result));
138 return true;
139}
140
initial.commit3f4a7322008-07-27 06:49:38 +0900141bool Pickle::ReadSize(void** iter, size_t* result) const {
142 DCHECK(iter);
143 if (!*iter)
144 *iter = const_cast<char*>(payload());
145
146 if (!IteratorHasRoomFor(*iter, sizeof(*result)))
147 return false;
148
maruel@chromium.orgb7f65c32009-06-02 05:26:42 +0900149 // TODO(jar): http://crbug.com/13108 Pickle should be cleaned up, and not
150 // dependent on alignment.
initial.commit3f4a7322008-07-27 06:49:38 +0900151 // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result));
152 *result = *reinterpret_cast<size_t*>(*iter);
153
154 UpdateIter(iter, sizeof(*result));
155 return true;
156}
157
bryner@chromium.orgc85d0fd2011-02-23 04:47:19 +0900158bool Pickle::ReadUInt16(void** iter, uint16* result) const {
159 DCHECK(iter);
160 if (!*iter)
161 *iter = const_cast<char*>(payload());
162
163 if (!IteratorHasRoomFor(*iter, sizeof(*result)))
164 return false;
165
166 memcpy(result, *iter, sizeof(*result));
167
168 UpdateIter(iter, sizeof(*result));
169 return true;
170}
171
jeremy@chromium.org62ace902008-12-30 03:55:18 +0900172bool Pickle::ReadUInt32(void** iter, uint32* result) const {
173 DCHECK(iter);
174 if (!*iter)
175 *iter = const_cast<char*>(payload());
176
177 if (!IteratorHasRoomFor(*iter, sizeof(*result)))
178 return false;
179
180 memcpy(result, *iter, sizeof(*result));
181
182 UpdateIter(iter, sizeof(*result));
183 return true;
184}
185
initial.commit3f4a7322008-07-27 06:49:38 +0900186bool Pickle::ReadInt64(void** iter, int64* result) const {
187 DCHECK(iter);
188 if (!*iter)
189 *iter = const_cast<char*>(payload());
190
191 if (!IteratorHasRoomFor(*iter, sizeof(*result)))
192 return false;
193
194 memcpy(result, *iter, sizeof(*result));
195
196 UpdateIter(iter, sizeof(*result));
197 return true;
198}
199
thestig@chromium.orgeb9afb42009-10-28 13:21:01 +0900200bool Pickle::ReadUInt64(void** iter, uint64* result) const {
201 DCHECK(iter);
202 if (!*iter)
203 *iter = const_cast<char*>(payload());
204
205 if (!IteratorHasRoomFor(*iter, sizeof(*result)))
206 return false;
207
208 memcpy(result, *iter, sizeof(*result));
209
210 UpdateIter(iter, sizeof(*result));
211 return true;
212}
213
initial.commit3f4a7322008-07-27 06:49:38 +0900214bool Pickle::ReadString(void** iter, std::string* result) const {
215 DCHECK(iter);
216
217 int len;
218 if (!ReadLength(iter, &len))
219 return false;
220 if (!IteratorHasRoomFor(*iter, len))
221 return false;
222
223 char* chars = reinterpret_cast<char*>(*iter);
224 result->assign(chars, len);
225
226 UpdateIter(iter, len);
227 return true;
228}
229
230bool Pickle::ReadWString(void** iter, std::wstring* result) const {
231 DCHECK(iter);
232
233 int len;
234 if (!ReadLength(iter, &len))
235 return false;
cevans@chromium.orga834cdd2009-06-26 01:54:02 +0900236 // Avoid integer overflow.
237 if (len > INT_MAX / static_cast<int>(sizeof(wchar_t)))
238 return false;
initial.commit3f4a7322008-07-27 06:49:38 +0900239 if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t)))
240 return false;
241
242 wchar_t* chars = reinterpret_cast<wchar_t*>(*iter);
243 result->assign(chars, len);
244
245 UpdateIter(iter, len * sizeof(wchar_t));
246 return true;
247}
248
estade@chromium.org38a18bf2009-03-04 12:36:36 +0900249bool Pickle::ReadString16(void** iter, string16* result) const {
250 DCHECK(iter);
251
252 int len;
253 if (!ReadLength(iter, &len))
254 return false;
cevans@chromium.orga834cdd2009-06-26 01:54:02 +0900255 if (!IteratorHasRoomFor(*iter, len * sizeof(char16)))
estade@chromium.org38a18bf2009-03-04 12:36:36 +0900256 return false;
257
258 char16* chars = reinterpret_cast<char16*>(*iter);
259 result->assign(chars, len);
260
261 UpdateIter(iter, len * sizeof(char16));
262 return true;
263}
264
erg@google.com67a25432011-01-08 05:23:43 +0900265bool Pickle::ReadData(void** iter, const char** data, int* length) const {
266 DCHECK(iter);
267 DCHECK(data);
268 DCHECK(length);
269 *length = 0;
270 *data = 0;
271
272 if (!ReadLength(iter, length))
273 return false;
274
275 return ReadBytes(iter, data, *length);
276}
277
initial.commit3f4a7322008-07-27 06:49:38 +0900278bool Pickle::ReadBytes(void** iter, const char** data, int length) const {
279 DCHECK(iter);
280 DCHECK(data);
cevans@chromium.org7c65f022009-12-31 05:09:02 +0900281 *data = 0;
mpcomplete@chromium.org0409ecb2010-03-31 08:52:24 +0900282 if (!*iter)
283 *iter = const_cast<char*>(payload());
initial.commit3f4a7322008-07-27 06:49:38 +0900284
285 if (!IteratorHasRoomFor(*iter, length))
286 return false;
287
288 *data = reinterpret_cast<const char*>(*iter);
289
290 UpdateIter(iter, length);
291 return true;
292}
293
erg@google.com67a25432011-01-08 05:23:43 +0900294bool Pickle::ReadLength(void** iter, int* result) const {
295 if (!ReadInt(iter, result))
initial.commit3f4a7322008-07-27 06:49:38 +0900296 return false;
erg@google.com67a25432011-01-08 05:23:43 +0900297 return ((*result) >= 0);
initial.commit3f4a7322008-07-27 06:49:38 +0900298}
299
300bool Pickle::WriteString(const std::string& value) {
301 if (!WriteInt(static_cast<int>(value.size())))
302 return false;
303
304 return WriteBytes(value.data(), static_cast<int>(value.size()));
305}
306
307bool Pickle::WriteWString(const std::wstring& value) {
308 if (!WriteInt(static_cast<int>(value.size())))
309 return false;
310
311 return WriteBytes(value.data(),
estade@chromium.org38a18bf2009-03-04 12:36:36 +0900312 static_cast<int>(value.size() * sizeof(wchar_t)));
313}
314
315bool Pickle::WriteString16(const string16& value) {
316 if (!WriteInt(static_cast<int>(value.size())))
317 return false;
318
319 return WriteBytes(value.data(),
320 static_cast<int>(value.size()) * sizeof(char16));
initial.commit3f4a7322008-07-27 06:49:38 +0900321}
322
323bool Pickle::WriteData(const char* data, int length) {
wtc@chromium.orgc2f15c52009-07-29 06:00:03 +0900324 return length >= 0 && WriteInt(length) && WriteBytes(data, length);
initial.commit3f4a7322008-07-27 06:49:38 +0900325}
326
erg@google.com67a25432011-01-08 05:23:43 +0900327bool Pickle::WriteBytes(const void* data, int data_len) {
kushi.p@gmail.com381e9662011-05-04 10:29:38 +0900328 DCHECK_NE(kCapacityReadOnly, capacity_) << "oops: pickle is readonly";
erg@google.com67a25432011-01-08 05:23:43 +0900329
330 char* dest = BeginWrite(data_len);
331 if (!dest)
332 return false;
333
334 memcpy(dest, data, data_len);
335
336 EndWrite(dest, data_len);
337 return true;
338}
339
initial.commit3f4a7322008-07-27 06:49:38 +0900340char* Pickle::BeginWriteData(int length) {
darin@google.comcf7eff42008-08-15 10:05:11 +0900341 DCHECK_EQ(variable_buffer_offset_, 0U) <<
initial.commit3f4a7322008-07-27 06:49:38 +0900342 "There can only be one variable buffer in a Pickle";
343
wtc@chromium.orgc2f15c52009-07-29 06:00:03 +0900344 if (length < 0 || !WriteInt(length))
345 return NULL;
initial.commit3f4a7322008-07-27 06:49:38 +0900346
347 char *data_ptr = BeginWrite(length);
348 if (!data_ptr)
349 return NULL;
350
351 variable_buffer_offset_ =
352 data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
353
354 // EndWrite doesn't necessarily have to be called after the write operation,
355 // so we call it here to pad out what the caller will eventually write.
356 EndWrite(data_ptr, length);
357 return data_ptr;
358}
359
maruel@google.coma01bb102008-08-08 02:04:23 +0900360void Pickle::TrimWriteData(int new_length) {
ukai@chromium.orgd37bd342009-10-28 14:27:22 +0900361 DCHECK_NE(variable_buffer_offset_, 0U);
initial.commit3f4a7322008-07-27 06:49:38 +0900362
maruel@google.coma01bb102008-08-08 02:04:23 +0900363 // Fetch the the variable buffer size
364 int* cur_length = reinterpret_cast<int*>(
initial.commit3f4a7322008-07-27 06:49:38 +0900365 reinterpret_cast<char*>(header_) + variable_buffer_offset_);
366
maruel@google.coma01bb102008-08-08 02:04:23 +0900367 if (new_length < 0 || new_length > *cur_length) {
368 NOTREACHED() << "Invalid length in TrimWriteData.";
369 return;
deanm@google.com8ba9dfb2008-08-08 00:18:20 +0900370 }
maruel@google.coma01bb102008-08-08 02:04:23 +0900371
372 // Update the payload size and variable buffer size
373 header_->payload_size -= (*cur_length - new_length);
374 *cur_length = new_length;
initial.commit3f4a7322008-07-27 06:49:38 +0900375}
376
erg@google.com67a25432011-01-08 05:23:43 +0900377char* Pickle::BeginWrite(size_t length) {
378 // write at a uint32-aligned offset from the beginning of the header
379 size_t offset = AlignInt(header_->payload_size, sizeof(uint32));
380
381 size_t new_size = offset + length;
382 size_t needed_size = header_size_ + new_size;
383 if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size)))
384 return NULL;
385
386#ifdef ARCH_CPU_64_BITS
thestig@chromium.orgf0c8f442011-10-11 07:20:33 +0900387 DCHECK_LE(length, kuint32max);
erg@google.com67a25432011-01-08 05:23:43 +0900388#endif
389
390 header_->payload_size = static_cast<uint32>(new_size);
391 return payload() + offset;
392}
393
394void Pickle::EndWrite(char* dest, int length) {
jeanluc@chromium.org5fcf7872011-08-18 02:41:02 +0900395 // Zero-pad to keep tools like valgrind from complaining about uninitialized
erg@google.com67a25432011-01-08 05:23:43 +0900396 // memory.
397 if (length % sizeof(uint32))
398 memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32)));
399}
400
initial.commit3f4a7322008-07-27 06:49:38 +0900401bool Pickle::Resize(size_t new_capacity) {
402 new_capacity = AlignInt(new_capacity, kPayloadUnit);
403
willchan@chromium.org33e05ef2010-03-03 03:29:42 +0900404 CHECK_NE(capacity_, kCapacityReadOnly);
initial.commit3f4a7322008-07-27 06:49:38 +0900405 void* p = realloc(header_, new_capacity);
406 if (!p)
407 return false;
408
409 header_ = reinterpret_cast<Header*>(p);
410 capacity_ = new_capacity;
411 return true;
412}
413
414// static
415const char* Pickle::FindNext(size_t header_size,
416 const char* start,
417 const char* end) {
kushi.p@gmail.come4869772011-04-22 22:13:07 +0900418 DCHECK_EQ(header_size, AlignInt(header_size, sizeof(uint32)));
419 DCHECK_LE(header_size, static_cast<size_t>(kPayloadUnit));
initial.commit3f4a7322008-07-27 06:49:38 +0900420
glider@chromium.org8b725fa2011-01-26 22:02:27 +0900421 if (static_cast<size_t>(end - start) < sizeof(Header))
422 return NULL;
423
initial.commit3f4a7322008-07-27 06:49:38 +0900424 const Header* hdr = reinterpret_cast<const Header*>(start);
425 const char* payload_base = start + header_size;
426 const char* payload_end = payload_base + hdr->payload_size;
427 if (payload_end < payload_base)
428 return NULL;
429
430 return (payload_end > end) ? NULL : payload_end;
431}