blob: e9ca0dcb10930eaa3e12532ac1e5d368fb268807 [file] [log] [blame]
Yi Jin974a9c22017-10-02 18:37:08 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#define LOG_TAG "libprotoutil"
17
18#include <android/util/protobuf.h>
19#include <android/util/ProtoOutputStream.h>
20#include <cutils/log.h>
21#include <cstring>
22
23namespace android {
24namespace util {
25
26/**
27 * Position of the field type in a (long long) fieldId.
28 */
29const uint64_t FIELD_TYPE_SHIFT = 32;
30
31/**
32 * Mask for the field types stored in a fieldId. Leaves a whole
33 * byte for future expansion, even though there are currently only 17 types.
34 */
35const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
36
37const uint64_t FIELD_TYPE_UNKNOWN = 0;
38const uint64_t TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire.
39const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire.
40const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers
41 // take 10 bytes. Use TYPE_SINT64 if negative
42 // values are likely.
43const uint64_t TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire.
44const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers
45 // take 10 bytes. Use TYPE_SINT32 if negative
46 // values are likely.
47const uint64_t TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire.
48const uint64_t TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire.
49const uint64_t TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire.
50const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text.
51const uint64_t TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated.
52const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message.
53
54const uint64_t TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array.
55const uint64_t TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire
56const uint64_t TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire
57const uint64_t TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire
58const uint64_t TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire
59const uint64_t TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire
60const uint64_t TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire
61
62//
63// FieldId flags for whether the field is single, repeated or packed.
64// TODO: packed is not supported yet.
65//
66const uint64_t FIELD_COUNT_SHIFT = 40;
67const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
68const uint64_t FIELD_COUNT_UNKNOWN = 0;
69const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
70const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
71const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
72
73ProtoOutputStream::ProtoOutputStream(int fd)
74 :mBuffer(),
75 mFd(fd),
76 mCopyBegin(0),
77 mCompact(false),
78 mDepth(0),
79 mObjectId(0),
80 mExpectedObjectToken(0LL)
81{
82}
83
84ProtoOutputStream::~ProtoOutputStream()
85{
86}
87
88bool
89ProtoOutputStream::write(uint64_t fieldId, double val)
90{
91 if (mCompact) return false;
92 const uint32_t id = (uint32_t)fieldId;
93 switch (fieldId & FIELD_TYPE_MASK) {
94 case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
95 case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
96 case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
97 case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
98 case TYPE_INT32: writeInt32Impl(id, (int)val); break;
99 case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
100 case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
101 case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
102 case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
103 case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
104 case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
105 case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
106 default:
107 ALOGW("Field type %d is not supported when writing double val.",
108 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
109 return false;
110 }
111 return true;
112}
113
114bool
115ProtoOutputStream::write(uint64_t fieldId, float val)
116{
117 if (mCompact) return false;
118 const uint32_t id = (uint32_t)fieldId;
119 switch (fieldId & FIELD_TYPE_MASK) {
120 case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
121 case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
122 case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
123 case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
124 case TYPE_INT32: writeInt32Impl(id, (int)val); break;
125 case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
126 case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
127 case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
128 case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
129 case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
130 case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
131 case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
132 default:
133 ALOGW("Field type %d is not supported when writing float val.",
134 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
135 return false;
136 }
137 return true;
138}
139
140bool
141ProtoOutputStream::write(uint64_t fieldId, int val)
142{
143 if (mCompact) return false;
144 const uint32_t id = (uint32_t)fieldId;
145 switch (fieldId & FIELD_TYPE_MASK) {
146 case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
147 case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
148 case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
149 case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
150 case TYPE_INT32: writeInt32Impl(id, (int)val); break;
151 case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
152 case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
153 case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
154 case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
155 case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
156 case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
157 case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
158 case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
159 case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
160 default:
161 ALOGW("Field type %d is not supported when writing int val.",
162 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
163 return false;
164 }
165 return true;
166}
167
168bool
169ProtoOutputStream::write(uint64_t fieldId, long long val)
170{
171 if (mCompact) return false;
172 const uint32_t id = (uint32_t)fieldId;
173 switch (fieldId & FIELD_TYPE_MASK) {
174 case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
175 case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
176 case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
177 case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
178 case TYPE_INT32: writeInt32Impl(id, (int)val); break;
179 case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
180 case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
181 case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
182 case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
183 case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
184 case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
185 case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
186 case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
187 case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
188 default:
189 ALOGW("Field type %d is not supported when writing long long val.",
190 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
191 return false;
192 }
193 return true;
194}
195
196bool
197ProtoOutputStream::write(uint64_t fieldId, bool val)
198{
199 if (mCompact) return false;
200 const uint32_t id = (uint32_t)fieldId;
201 switch (fieldId & FIELD_TYPE_MASK) {
202 case TYPE_BOOL:
203 writeBoolImpl(id, val);
204 return true;
205 default:
206 ALOGW("Field type %d is not supported when writing bool val.",
207 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
208 return false;
209 }
210}
211
212bool
213ProtoOutputStream::write(uint64_t fieldId, string val)
214{
215 if (mCompact) return false;
216 const uint32_t id = (uint32_t)fieldId;
217 switch (fieldId & FIELD_TYPE_MASK) {
218 case TYPE_STRING:
219 writeUtf8StringImpl(id, val.c_str(), val.size());
220 return true;
221 default:
222 ALOGW("Field type %d is not supported when writing string val.",
223 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
224 return false;
225 }
226}
227
228bool
229ProtoOutputStream::write(uint64_t fieldId, const char* val)
230{
231 if (mCompact) return false;
232 const uint32_t id = (uint32_t)fieldId;
233 int size = 0;
234 while (val[size] != '\0') size++;
235 switch (fieldId & FIELD_TYPE_MASK) {
236 case TYPE_STRING:
237 writeUtf8StringImpl(id, val, size);
238 return true;
239 default:
240 ALOGW("Field type %d is not supported when writing char[] val.",
241 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
242 return false;
243 }
244}
245
246/**
247 * Make a token.
248 * Bits 61-63 - tag size (So we can go backwards later if the object had not data)
249 * - 3 bits, max value 7, max value needed 5
250 * Bit 60 - true if the object is repeated
251 * Bits 59-51 - depth (For error checking)
252 * - 9 bits, max value 512, when checking, value is masked (if we really
253 * are more than 512 levels deep)
254 * Bits 32-50 - objectId (For error checking)
255 * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap
256 * because of the overflow, and only the tokens are compared.
257 * Bits 0-31 - offset of the first size field in the buffer.
258 */
259long long
260makeToken(int tagSize, bool repeated, int depth, int objectId, int sizePos) {
261 return ((0x07L & (long long)tagSize) << 61)
262 | (repeated ? (1LL << 60) : 0)
263 | (0x01ffL & (long long)depth) << 51
264 | (0x07ffffL & (long long)objectId) << 32
265 | (0x0ffffffffL & (long long)sizePos);
266}
267
268/**
269 * Get the encoded tag size from the token.
270 */
271static int getTagSizeFromToken(long long token) {
272 return (int)(0x7 & (token >> 61));
273}
274
275/**
276 * Get the nesting depth of startObject calls from the token.
277 */
278static int getDepthFromToken(long long token) {
279 return (int)(0x01ff & (token >> 51));
280}
281
282/**
283 * Get the location of the childRawSize (the first 32 bit size field) in this object.
284 */
285static int getSizePosFromToken(long long token) {
286 return (int)token;
287}
288
289long long
290ProtoOutputStream::start(uint64_t fieldId)
291{
292 if ((fieldId & FIELD_TYPE_MASK) != TYPE_MESSAGE) {
293 ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId);
294 return 0;
295 }
296
297 uint32_t id = (uint32_t)fieldId;
298 mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
299
300 size_t sizePos = mBuffer.wp()->pos();
301
302 mDepth++;
303 mObjectId++;
304 mBuffer.writeRawFixed64(mExpectedObjectToken); // push previous token into stack.
305
306 mExpectedObjectToken = makeToken(get_varint_size(id),
307 (bool)(fieldId & FIELD_COUNT_REPEATED), mDepth, mObjectId, sizePos);
308 return mExpectedObjectToken;
309}
310
311void
312ProtoOutputStream::end(long long token)
313{
314 if (token != mExpectedObjectToken) {
315 ALOGE("Unexpected token: 0x%llx, should be 0x%llx", token, mExpectedObjectToken);
316 return;
317 }
318
319 int depth = getDepthFromToken(token);
320 if (depth != (mDepth & 0x01ff)) {
321 ALOGE("Unexpected depth: %d, should be %d", depth, mDepth);
322 return;
323 }
324 mDepth--;
325
326 int sizePos = getSizePosFromToken(token);
327 // number of bytes written in this start-end session.
328 int childRawSize = mBuffer.wp()->pos() - sizePos - 8;
329
330 // retrieve the old token from stack.
331 mBuffer.ep()->rewind()->move(sizePos);
332 mExpectedObjectToken = mBuffer.readRawFixed64();
333
334 // If raw size is larger than 0, write the negative value here to indicate a compact is needed.
335 if (childRawSize > 0) {
336 mBuffer.editRawFixed32(sizePos, -childRawSize);
337 mBuffer.editRawFixed32(sizePos+4, -1);
338 } else {
339 // reset wp which erase the header tag of the message when its size is 0.
340 mBuffer.wp()->rewind()->move(sizePos - getTagSizeFromToken(token));
341 }
342}
343
344bool
345ProtoOutputStream::compact() {
346 if (mCompact) return true;
347 if (mDepth != 0) {
348 ALOGE("Can't compact when depth(%d) is not zero. Missing calls to end.", mDepth);
349 return false;
350 }
351 // record the size of the original buffer.
352 size_t rawBufferSize = mBuffer.size();
353 if (rawBufferSize == 0) return true; // nothing to do if the buffer is empty;
354
355 // reset edit pointer and recursively compute encoded size of messages.
356 mBuffer.ep()->rewind();
357 if (editEncodedSize(rawBufferSize) == 0) {
358 ALOGE("Failed to editEncodedSize.");
359 return false;
360 }
361
362 // reset both edit pointer and write pointer, and compact recursively.
363 mBuffer.ep()->rewind();
364 mBuffer.wp()->rewind();
365 if (!compactSize(rawBufferSize)) {
366 ALOGE("Failed to compactSize.");
367 return false;
368 }
369 // copy the reset to the buffer.
370 if (mCopyBegin < rawBufferSize) {
371 mBuffer.copy(mCopyBegin, rawBufferSize - mCopyBegin);
372 }
373
374 // mark true means it is not legal to write to this ProtoOutputStream anymore
375 mCompact = true;
376 return true;
377}
378
379/**
380 * First compaction pass. Iterate through the data, and fill in the
381 * nested object sizes so the next pass can compact them.
382 */
383size_t
384ProtoOutputStream::editEncodedSize(size_t rawSize)
385{
386 size_t objectStart = mBuffer.ep()->pos();
387 size_t objectEnd = objectStart + rawSize;
388 size_t encodedSize = 0;
389 int childRawSize, childEncodedSize;
390 size_t childEncodedSizePos;
391
392 while (mBuffer.ep()->pos() < objectEnd) {
393 uint32_t tag = (uint32_t)mBuffer.readRawVarint();
394 encodedSize += get_varint_size(tag);
395 switch (read_wire_type(tag)) {
396 case WIRE_TYPE_VARINT:
397 do {
398 encodedSize++;
399 } while ((mBuffer.readRawByte() & 0x80) != 0);
400 break;
401 case WIRE_TYPE_FIXED64:
402 encodedSize += 8;
403 mBuffer.ep()->move(8);
404 break;
405 case WIRE_TYPE_LENGTH_DELIMITED:
406 childRawSize = (int)mBuffer.readRawFixed32();
407 childEncodedSizePos = mBuffer.ep()->pos();
408 childEncodedSize = (int)mBuffer.readRawFixed32();
409 if (childRawSize >= 0 && childRawSize == childEncodedSize) {
410 mBuffer.ep()->move(childRawSize);
411 } else if (childRawSize < 0 && childEncodedSize == -1){
412 childEncodedSize = editEncodedSize(-childRawSize);
413 mBuffer.editRawFixed32(childEncodedSizePos, childEncodedSize);
414 } else {
415 ALOGE("Bad raw or encoded values: raw=%d, encoded=%d at %zu",
416 childRawSize, childEncodedSize, childEncodedSizePos);
417 return 0;
418 }
419 encodedSize += get_varint_size(childEncodedSize) + childEncodedSize;
420 break;
421 case WIRE_TYPE_FIXED32:
422 encodedSize += 4;
423 mBuffer.ep()->move(4);
424 break;
425 default:
426 ALOGE("Unexpected wire type %d in editEncodedSize at [%zu, %zu]",
427 read_wire_type(tag), objectStart, objectEnd);
428 return 0;
429 }
430 }
431 return encodedSize;
432}
433
434/**
435 * Second compaction pass. Iterate through the data, and copy the data
436 * forward in the buffer, converting the pairs of uint32s into a single
437 * unsigned varint of the size.
438 */
439bool
440ProtoOutputStream::compactSize(size_t rawSize)
441{
442 size_t objectStart = mBuffer.ep()->pos();
443 size_t objectEnd = objectStart + rawSize;
444 int childRawSize, childEncodedSize;
445
446 while (mBuffer.ep()->pos() < objectEnd) {
447 uint32_t tag = (uint32_t)mBuffer.readRawVarint();
448 switch (read_wire_type(tag)) {
449 case WIRE_TYPE_VARINT:
450 while ((mBuffer.readRawByte() & 0x80) != 0) {}
451 break;
452 case WIRE_TYPE_FIXED64:
453 mBuffer.ep()->move(8);
454 break;
455 case WIRE_TYPE_LENGTH_DELIMITED:
456 mBuffer.copy(mCopyBegin, mBuffer.ep()->pos() - mCopyBegin);
457
458 childRawSize = (int)mBuffer.readRawFixed32();
459 childEncodedSize = (int)mBuffer.readRawFixed32();
460 mCopyBegin = mBuffer.ep()->pos();
461
462 // write encoded size to buffer.
463 mBuffer.writeRawVarint32(childEncodedSize);
464 if (childRawSize >= 0 && childRawSize == childEncodedSize) {
465 mBuffer.ep()->move(childEncodedSize);
466 } else if (childRawSize < 0){
467 if (!compactSize(-childRawSize)) return false;
468 } else {
469 ALOGE("Bad raw or encoded values: raw=%d, encoded=%d",
470 childRawSize, childEncodedSize);
471 return false;
472 }
473 break;
474 case WIRE_TYPE_FIXED32:
475 mBuffer.ep()->move(4);
476 break;
477 default:
478 ALOGE("Unexpected wire type %d in compactSize at [%zu, %zu]",
479 read_wire_type(tag), objectStart, objectEnd);
480 return false;
481 }
482 }
483 return true;
484}
485
486static bool write_all(int fd, uint8_t const* buf, size_t size)
487{
488 while (size > 0) {
489 ssize_t amt = ::write(fd, buf, size);
490 if (amt < 0) {
491 return false;
492 }
493 size -= amt;
494 buf += amt;
495 }
496 return true;
497}
498
499bool
500ProtoOutputStream::flush()
501{
502 if (mFd < 0) return false;
503 if (!compact()) return false;
504
505 EncodedBuffer::iterator it = mBuffer.begin();
506 while (it.readBuffer() != NULL) {
507 if (!write_all(mFd, it.readBuffer(), it.currentToRead())) return false;
508 it.rp()->move(it.currentToRead());
509 }
510 return true;
511}
512
513
514// =========================================================================
515// Private functions
516
517/**
518 * bit_cast
519 */
520template <class From, class To>
521inline To bit_cast(From const &from) {
522 To to;
523 memcpy(&to, &from, sizeof(to));
524 return to;
525}
526
527inline void
528ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
529{
530 if (val == 0.0) return;
531 mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
532 mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val));
533}
534
535inline void
536ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
537{
538 if (val == 0.0) return;
539 mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
540 mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val));
541}
542
543inline void
544ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
545{
546 if (val == 0) return;
547 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
548 mBuffer.writeRawVarint64((uint64_t)val);
549}
550
551inline void
552ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
553{
554 if (val == 0) return;
555 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
556 mBuffer.writeRawVarint32((uint32_t)val);
557}
558
559inline void
560ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
561{
562 if (val == 0) return;
563 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
564 mBuffer.writeRawVarint64(val);
565}
566
567inline void
568ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
569{
570 if (val == 0) return;
571 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
572 mBuffer.writeRawVarint32(val);
573}
574
575inline void
576ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
577{
578 if (val == 0) return;
579 mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
580 mBuffer.writeRawFixed64(val);
581}
582
583inline void
584ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
585{
586 if (val == 0) return;
587 mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
588 mBuffer.writeRawFixed32(val);
589}
590
591inline void
592ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
593{
594 if (val == 0) return;
595 mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
596 mBuffer.writeRawFixed64((uint64_t)val);
597}
598
599inline void
600ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
601{
602 if (val == 0) return;
603 mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
604 mBuffer.writeRawFixed32((uint32_t)val);
605}
606
607inline void
608ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
609{
610 if (val == 0) return;
611 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
612 mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
613}
614
615inline void
616ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
617{
618 if (val == 0) return;
619 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
620 mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
621}
622
623inline void
624ProtoOutputStream::writeEnumImpl(uint32_t id, int val)
625{
626 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
627 mBuffer.writeRawVarint32((uint32_t) val);
628}
629
630inline void
631ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
632{
633 if (!val) return;
634 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
635 mBuffer.writeRawVarint32(val ? 1 : 0);
636}
637
638inline void
639ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size)
640{
641 if (val == NULL || size == 0) return;
642 mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
643 mBuffer.writeRawFixed32(size);
644 mBuffer.writeRawFixed32(size);
645 for (size_t i=0; i<size; i++) {
646 mBuffer.writeRawByte((uint8_t)val[i]);
647 }
648}
649
650} // util
651} // android
652