1 /*
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 <cinttypes>
19 #include <type_traits>
20
21 #include <android-base/file.h>
22 #include <android/util/protobuf.h>
23 #include <android/util/ProtoOutputStream.h>
24 #include <cutils/log.h>
25
26 namespace android {
27 namespace util {
28
ProtoOutputStream()29 ProtoOutputStream::ProtoOutputStream()
30 :mBuffer(new EncodedBuffer()),
31 mCopyBegin(0),
32 mCompact(false),
33 mDepth(0),
34 mObjectId(0),
35 mExpectedObjectToken(UINT64_C(-1))
36 {
37 }
38
~ProtoOutputStream()39 ProtoOutputStream::~ProtoOutputStream()
40 {
41 }
42
43
44 void
clear()45 ProtoOutputStream::clear()
46 {
47 mBuffer->clear();
48 mCopyBegin = 0;
49 mCompact = false;
50 mDepth = 0;
51 mObjectId = 0;
52 mExpectedObjectToken = UINT64_C(-1);
53 }
54
55 template<typename T>
56 bool
internalWrite(uint64_t fieldId,T val,const char * typeName)57 ProtoOutputStream::internalWrite(uint64_t fieldId, T val, const char* typeName)
58 {
59 if (mCompact) return false;
60 const uint32_t id = (uint32_t)fieldId;
61 switch (fieldId & FIELD_TYPE_MASK) {
62 case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
63 case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
64 case FIELD_TYPE_INT64: writeInt64Impl(id, (int64_t)val); break;
65 case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
66 case FIELD_TYPE_INT32: writeInt32Impl(id, (int32_t)val); break;
67 case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
68 case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
69 case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
70 case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int32_t)val); break;
71 case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (int64_t)val); break;
72 case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int32_t)val); break;
73 case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (int64_t)val); break;
74 case FIELD_TYPE_ENUM:
75 if (std::is_integral<T>::value) {
76 writeEnumImpl(id, (int)val);
77 } else {
78 goto unsupported;
79 }
80 break;
81 case FIELD_TYPE_BOOL:
82 if (std::is_integral<T>::value) {
83 writeBoolImpl(id, val != 0);
84 } else {
85 goto unsupported;
86 }
87 break;
88 default:
89 goto unsupported;
90 }
91 return true;
92
93 unsupported:
94 ALOGW("Field type %" PRIu64 " is not supported when writing %s val.",
95 (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT, typeName);
96 return false;
97 }
98
99 bool
write(uint64_t fieldId,double val)100 ProtoOutputStream::write(uint64_t fieldId, double val)
101 {
102 return internalWrite(fieldId, val, "double");
103 }
104
105
106 bool
write(uint64_t fieldId,float val)107 ProtoOutputStream::write(uint64_t fieldId, float val)
108 {
109 return internalWrite(fieldId, val, "float");
110 }
111
112 bool
write(uint64_t fieldId,int val)113 ProtoOutputStream::write(uint64_t fieldId, int val)
114 {
115 return internalWrite(fieldId, val, "int");
116 }
117
118 bool
write(uint64_t fieldId,long val)119 ProtoOutputStream::write(uint64_t fieldId, long val)
120 {
121 if (mCompact) return false;
122 const uint32_t id = (uint32_t)fieldId;
123 switch (fieldId & FIELD_TYPE_MASK) {
124 case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
125 case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
126 case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
127 case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
128 case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
129 case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
130 case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
131 case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
132 case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
133 case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
134 case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
135 case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
136 case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
137 case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
138 default:
139 ALOGW("Field type %d is not supported when writing long val.",
140 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
141 return false;
142 }
143 return true;
144 }
145
146 bool
write(uint64_t fieldId,long long val)147 ProtoOutputStream::write(uint64_t fieldId, long long val)
148 {
149 return internalWrite(fieldId, val, "long long");
150 }
151
152 bool
write(uint64_t fieldId,bool val)153 ProtoOutputStream::write(uint64_t fieldId, bool val)
154 {
155 if (mCompact) return false;
156 const uint32_t id = (uint32_t)fieldId;
157 switch (fieldId & FIELD_TYPE_MASK) {
158 case FIELD_TYPE_BOOL:
159 writeBoolImpl(id, val);
160 return true;
161 default:
162 ALOGW("Field type %" PRIu64 " is not supported when writing bool val.",
163 (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
164 return false;
165 }
166 }
167
168 bool
write(uint64_t fieldId,std::string val)169 ProtoOutputStream::write(uint64_t fieldId, std::string val)
170 {
171 if (mCompact) return false;
172 const uint32_t id = (uint32_t)fieldId;
173 switch (fieldId & FIELD_TYPE_MASK) {
174 case FIELD_TYPE_STRING:
175 writeUtf8StringImpl(id, val.c_str(), val.size());
176 return true;
177 default:
178 ALOGW("Field type %" PRIu64 " is not supported when writing string val.",
179 (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
180 return false;
181 }
182 }
183
184 bool
write(uint64_t fieldId,const char * val,size_t size)185 ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size)
186 {
187 if (mCompact) return false;
188 const uint32_t id = (uint32_t)fieldId;
189 switch (fieldId & FIELD_TYPE_MASK) {
190 case FIELD_TYPE_STRING:
191 case FIELD_TYPE_BYTES:
192 writeUtf8StringImpl(id, val, size);
193 return true;
194 case FIELD_TYPE_MESSAGE:
195 // can directly write valid format of message bytes into ProtoOutputStream without calling start/end
196 writeMessageBytesImpl(id, val, size);
197 return true;
198 default:
199 ALOGW("Field type %" PRIu64 " is not supported when writing char[] val.",
200 (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
201 return false;
202 }
203 }
204
205 /**
206 * Make a token.
207 * Bits 61-63 - tag size (So we can go backwards later if the object had not data)
208 * - 3 bits, max value 7, max value needed 5
209 * Bit 60 - true if the object is repeated
210 * Bits 59-51 - depth (For error checking)
211 * - 9 bits, max value 511, when checking, value is masked (if we really
212 * are more than 511 levels deep)
213 * Bits 32-50 - objectId (For error checking)
214 * - 19 bits, max value 524,287. that's a lot of objects. IDs will wrap
215 * because of the overflow, and only the tokens are compared.
216 * Bits 0-31 - offset of the first size field in the buffer.
217 */
218 static uint64_t
makeToken(uint32_t tagSize,bool repeated,uint32_t depth,uint32_t objectId,size_t sizePos)219 makeToken(uint32_t tagSize, bool repeated, uint32_t depth, uint32_t objectId, size_t sizePos) {
220 return ((UINT64_C(0x07) & (uint64_t)tagSize) << 61)
221 | (repeated ? (UINT64_C(1) << 60) : 0)
222 | (UINT64_C(0x01ff) & (uint64_t)depth) << 51
223 | (UINT64_C(0x07ffff) & (uint64_t)objectId) << 32
224 | (UINT64_C(0x0ffffffff) & (uint64_t)sizePos);
225 }
226
227 /**
228 * Get the encoded tag size from the token.
229 */
getTagSizeFromToken(uint64_t token)230 static uint32_t getTagSizeFromToken(uint64_t token) {
231 return 0x7 & (token >> 61);
232 }
233
234 /**
235 * Get the nesting depth of startObject calls from the token.
236 */
getDepthFromToken(uint64_t token)237 static uint32_t getDepthFromToken(uint64_t token) {
238 return 0x01ff & (token >> 51);
239 }
240
241 /**
242 * Get the location of the childRawSize (the first 32 bit size field) in this object.
243 */
getSizePosFromToken(uint64_t token)244 static uint32_t getSizePosFromToken(uint64_t token) {
245 return (uint32_t)token;
246 }
247
248 uint64_t
start(uint64_t fieldId)249 ProtoOutputStream::start(uint64_t fieldId)
250 {
251 if ((fieldId & FIELD_TYPE_MASK) != FIELD_TYPE_MESSAGE) {
252 ALOGE("Can't call start for non-message type field: 0x%" PRIx64, fieldId);
253 return 0;
254 }
255
256 uint32_t id = (uint32_t)fieldId;
257 size_t prevPos = mBuffer->wp()->pos();
258 mBuffer->writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
259 size_t sizePos = mBuffer->wp()->pos();
260
261 mDepth++;
262 mObjectId++;
263 mBuffer->writeRawFixed64(mExpectedObjectToken); // push previous token into stack.
264
265 mExpectedObjectToken = makeToken(sizePos - prevPos,
266 (bool)(fieldId & FIELD_COUNT_REPEATED), mDepth, mObjectId, sizePos);
267 return mExpectedObjectToken;
268 }
269
270 void
end(uint64_t token)271 ProtoOutputStream::end(uint64_t token)
272 {
273 if (token != mExpectedObjectToken) {
274 ALOGE("Unexpected token: 0x%" PRIx64 ", should be 0x%" PRIx64, token, mExpectedObjectToken);
275 mDepth = UINT32_C(-1); // make depth invalid
276 return;
277 }
278
279 uint32_t depth = getDepthFromToken(token);
280 if (depth != (mDepth & 0x01ff)) {
281 ALOGE("Unexpected depth: %" PRIu32 ", should be %" PRIu32, depth, mDepth);
282 mDepth = UINT32_C(-1); // make depth invalid
283 return;
284 }
285 mDepth--;
286
287 uint32_t sizePos = getSizePosFromToken(token);
288 // number of bytes written in this start-end session.
289 int childRawSize = mBuffer->wp()->pos() - sizePos - 8;
290
291 // retrieve the old token from stack.
292 mBuffer->ep()->rewind()->move(sizePos);
293 mExpectedObjectToken = mBuffer->readRawFixed64();
294
295 // If raw size is larger than 0, write the negative value here to indicate a compact is needed.
296 if (childRawSize > 0) {
297 mBuffer->editRawFixed32(sizePos, -childRawSize);
298 mBuffer->editRawFixed32(sizePos+4, -1);
299 } else {
300 // reset wp which erase the header tag of the message when its size is 0.
301 mBuffer->wp()->rewind()->move(sizePos - getTagSizeFromToken(token));
302 }
303 }
304
305 size_t
bytesWritten()306 ProtoOutputStream::bytesWritten()
307 {
308 return mBuffer->size();
309 }
310
311 bool
compact()312 ProtoOutputStream::compact() {
313 if (mCompact) return true;
314 if (mDepth != 0) {
315 ALOGE("Can't compact when depth(%" PRIu32 ") is not zero. Missing or extra calls to end.", mDepth);
316 return false;
317 }
318 // record the size of the original buffer.
319 size_t rawBufferSize = mBuffer->size();
320 if (rawBufferSize == 0) return true; // nothing to do if the buffer is empty;
321
322 // reset edit pointer and recursively compute encoded size of messages.
323 mBuffer->ep()->rewind();
324 if (editEncodedSize(rawBufferSize) == 0) {
325 ALOGE("Failed to editEncodedSize.");
326 return false;
327 }
328
329 // reset both edit pointer and write pointer, and compact recursively.
330 mBuffer->ep()->rewind();
331 mBuffer->wp()->rewind();
332 if (!compactSize(rawBufferSize)) {
333 ALOGE("Failed to compactSize.");
334 return false;
335 }
336 // copy the reset to the buffer.
337 if (mCopyBegin < rawBufferSize) {
338 mBuffer->copy(mCopyBegin, rawBufferSize - mCopyBegin);
339 }
340
341 // mark true means it is not legal to write to this ProtoOutputStream anymore
342 mCompact = true;
343 return true;
344 }
345
346 /**
347 * First compaction pass. Iterate through the data, and fill in the
348 * nested object sizes so the next pass can compact them.
349 */
350 size_t
editEncodedSize(size_t rawSize)351 ProtoOutputStream::editEncodedSize(size_t rawSize)
352 {
353 size_t objectStart = mBuffer->ep()->pos();
354 size_t objectEnd = objectStart + rawSize;
355 size_t encodedSize = 0;
356 int childRawSize, childEncodedSize;
357 size_t childEncodedSizePos;
358
359 while (mBuffer->ep()->pos() < objectEnd) {
360 uint32_t tag = (uint32_t)mBuffer->readRawVarint();
361 encodedSize += get_varint_size(tag);
362 switch (read_wire_type(tag)) {
363 case WIRE_TYPE_VARINT:
364 do {
365 encodedSize++;
366 } while ((mBuffer->readRawByte() & 0x80) != 0);
367 break;
368 case WIRE_TYPE_FIXED64:
369 encodedSize += 8;
370 mBuffer->ep()->move(8);
371 break;
372 case WIRE_TYPE_LENGTH_DELIMITED:
373 childRawSize = (int)mBuffer->readRawFixed32();
374 childEncodedSizePos = mBuffer->ep()->pos();
375 childEncodedSize = (int)mBuffer->readRawFixed32();
376 if (childRawSize >= 0 && childRawSize == childEncodedSize) {
377 mBuffer->ep()->move(childRawSize);
378 } else if (childRawSize < 0 && childEncodedSize == -1){
379 childEncodedSize = editEncodedSize(-childRawSize);
380 mBuffer->editRawFixed32(childEncodedSizePos, childEncodedSize);
381 } else {
382 ALOGE("Bad raw or encoded values: raw=%d, encoded=%d at %zu",
383 childRawSize, childEncodedSize, childEncodedSizePos);
384 return 0;
385 }
386 encodedSize += get_varint_size(childEncodedSize) + childEncodedSize;
387 break;
388 case WIRE_TYPE_FIXED32:
389 encodedSize += 4;
390 mBuffer->ep()->move(4);
391 break;
392 default:
393 ALOGE("Unexpected wire type %d in editEncodedSize at [%zu, %zu]",
394 read_wire_type(tag), objectStart, objectEnd);
395 return 0;
396 }
397 }
398 return encodedSize;
399 }
400
401 /**
402 * Second compaction pass. Iterate through the data, and copy the data
403 * forward in the buffer, converting the pairs of uint32s into a single
404 * unsigned varint of the size.
405 */
406 bool
compactSize(size_t rawSize)407 ProtoOutputStream::compactSize(size_t rawSize)
408 {
409 size_t objectStart = mBuffer->ep()->pos();
410 size_t objectEnd = objectStart + rawSize;
411 int childRawSize, childEncodedSize;
412
413 while (mBuffer->ep()->pos() < objectEnd) {
414 uint32_t tag = (uint32_t)mBuffer->readRawVarint();
415 switch (read_wire_type(tag)) {
416 case WIRE_TYPE_VARINT:
417 while ((mBuffer->readRawByte() & 0x80) != 0) {}
418 break;
419 case WIRE_TYPE_FIXED64:
420 mBuffer->ep()->move(8);
421 break;
422 case WIRE_TYPE_LENGTH_DELIMITED:
423 mBuffer->copy(mCopyBegin, mBuffer->ep()->pos() - mCopyBegin);
424
425 childRawSize = (int)mBuffer->readRawFixed32();
426 childEncodedSize = (int)mBuffer->readRawFixed32();
427 mCopyBegin = mBuffer->ep()->pos();
428
429 // write encoded size to buffer.
430 mBuffer->writeRawVarint32(childEncodedSize);
431 if (childRawSize >= 0 && childRawSize == childEncodedSize) {
432 mBuffer->ep()->move(childEncodedSize);
433 } else if (childRawSize < 0){
434 if (!compactSize(-childRawSize)) return false;
435 } else {
436 ALOGE("Bad raw or encoded values: raw=%d, encoded=%d",
437 childRawSize, childEncodedSize);
438 return false;
439 }
440 break;
441 case WIRE_TYPE_FIXED32:
442 mBuffer->ep()->move(4);
443 break;
444 default:
445 ALOGE("Unexpected wire type %d in compactSize at [%zu, %zu]",
446 read_wire_type(tag), objectStart, objectEnd);
447 return false;
448 }
449 }
450 return true;
451 }
452
453 size_t
size()454 ProtoOutputStream::size()
455 {
456 if (!compact()) {
457 ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
458 return 0;
459 }
460 return mBuffer->size();
461 }
462
463 bool
flush(int fd)464 ProtoOutputStream::flush(int fd)
465 {
466 if (fd < 0) return false;
467 if (!compact()) return false;
468
469 sp<ProtoReader> reader = mBuffer->read();
470 while (reader->readBuffer() != NULL) {
471 if (!android::base::WriteFully(fd, reader->readBuffer(), reader->currentToRead())) {
472 return false;
473 }
474 reader->move(reader->currentToRead());
475 }
476 return true;
477 }
478
479 bool
serializeToString(std::string * out)480 ProtoOutputStream::serializeToString(std::string* out)
481 {
482 if (out == nullptr) return false;
483 if (!compact()) return false;
484
485 sp<ProtoReader> reader = mBuffer->read();
486 out->reserve(reader->size());
487 while (reader->hasNext()) {
488 out->append(static_cast<const char*>(static_cast<const void*>(reader->readBuffer())),
489 reader->currentToRead());
490 reader->move(reader->currentToRead());
491 }
492 return true;
493 }
494
495 bool
serializeToVector(std::vector<uint8_t> * out)496 ProtoOutputStream::serializeToVector(std::vector<uint8_t>* out)
497 {
498 if (out == nullptr) return false;
499 if (!compact()) return false;
500
501 sp<ProtoReader> reader = mBuffer->read();
502 out->reserve(reader->size());
503 while (reader->hasNext()) {
504 const uint8_t* buf = reader->readBuffer();
505 size_t size = reader->currentToRead();
506 out->insert(out->end(), buf, buf + size);
507 reader->move(size);
508 }
509 return true;
510 }
511
512 sp<ProtoReader>
data()513 ProtoOutputStream::data()
514 {
515 if (!compact()) {
516 ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
517 mBuffer->clear();
518 }
519 return mBuffer->read();
520 }
521
522 void
writeRawVarint(uint64_t varint)523 ProtoOutputStream::writeRawVarint(uint64_t varint)
524 {
525 mBuffer->writeRawVarint64(varint);
526 }
527
528 void
writeLengthDelimitedHeader(uint32_t id,size_t size)529 ProtoOutputStream::writeLengthDelimitedHeader(uint32_t id, size_t size)
530 {
531 mBuffer->writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
532 // reserves 64 bits for length delimited fields, if first field is negative, compact it.
533 mBuffer->writeRawFixed32(size);
534 mBuffer->writeRawFixed32(size);
535 }
536
537 void
writeRawByte(uint8_t byte)538 ProtoOutputStream::writeRawByte(uint8_t byte)
539 {
540 mBuffer->writeRawByte(byte);
541 }
542
543
544 // =========================================================================
545 // Private functions
546
547 /**
548 * bit_cast
549 */
550 template <class From, class To>
bit_cast(From const & from)551 inline To bit_cast(From const &from) {
552 To to;
553 memcpy(&to, &from, sizeof(to));
554 return to;
555 }
556
557 inline void
writeDoubleImpl(uint32_t id,double val)558 ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
559 {
560 mBuffer->writeHeader(id, WIRE_TYPE_FIXED64);
561 mBuffer->writeRawFixed64(bit_cast<double, uint64_t>(val));
562 }
563
564 inline void
writeFloatImpl(uint32_t id,float val)565 ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
566 {
567 mBuffer->writeHeader(id, WIRE_TYPE_FIXED32);
568 mBuffer->writeRawFixed32(bit_cast<float, uint32_t>(val));
569 }
570
571 inline void
writeInt64Impl(uint32_t id,int64_t val)572 ProtoOutputStream::writeInt64Impl(uint32_t id, int64_t val)
573 {
574 mBuffer->writeHeader(id, WIRE_TYPE_VARINT);
575 mBuffer->writeRawVarint64(val);
576 }
577
578 inline void
writeInt32Impl(uint32_t id,int32_t val)579 ProtoOutputStream::writeInt32Impl(uint32_t id, int32_t val)
580 {
581 mBuffer->writeHeader(id, WIRE_TYPE_VARINT);
582 mBuffer->writeRawVarint32(val);
583 }
584
585 inline void
writeUint64Impl(uint32_t id,uint64_t val)586 ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
587 {
588 mBuffer->writeHeader(id, WIRE_TYPE_VARINT);
589 mBuffer->writeRawVarint64(val);
590 }
591
592 inline void
writeUint32Impl(uint32_t id,uint32_t val)593 ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
594 {
595 mBuffer->writeHeader(id, WIRE_TYPE_VARINT);
596 mBuffer->writeRawVarint32(val);
597 }
598
599 inline void
writeFixed64Impl(uint32_t id,uint64_t val)600 ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
601 {
602 mBuffer->writeHeader(id, WIRE_TYPE_FIXED64);
603 mBuffer->writeRawFixed64(val);
604 }
605
606 inline void
writeFixed32Impl(uint32_t id,uint32_t val)607 ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
608 {
609 mBuffer->writeHeader(id, WIRE_TYPE_FIXED32);
610 mBuffer->writeRawFixed32(val);
611 }
612
613 inline void
writeSFixed64Impl(uint32_t id,int64_t val)614 ProtoOutputStream::writeSFixed64Impl(uint32_t id, int64_t val)
615 {
616 mBuffer->writeHeader(id, WIRE_TYPE_FIXED64);
617 mBuffer->writeRawFixed64(val);
618 }
619
620 inline void
writeSFixed32Impl(uint32_t id,int32_t val)621 ProtoOutputStream::writeSFixed32Impl(uint32_t id, int32_t val)
622 {
623 mBuffer->writeHeader(id, WIRE_TYPE_FIXED32);
624 mBuffer->writeRawFixed32(val);
625 }
626
627 inline void
writeZigzagInt64Impl(uint32_t id,int64_t val)628 ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, int64_t val)
629 {
630 mBuffer->writeHeader(id, WIRE_TYPE_VARINT);
631 mBuffer->writeRawVarint64((val << 1) ^ (val >> 63));
632 }
633
634 inline void
writeZigzagInt32Impl(uint32_t id,int32_t val)635 ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int32_t val)
636 {
637 mBuffer->writeHeader(id, WIRE_TYPE_VARINT);
638 mBuffer->writeRawVarint32((val << 1) ^ (val >> 31));
639 }
640
641 inline void
writeEnumImpl(uint32_t id,int val)642 ProtoOutputStream::writeEnumImpl(uint32_t id, int val)
643 {
644 mBuffer->writeHeader(id, WIRE_TYPE_VARINT);
645 mBuffer->writeRawVarint32((uint32_t) val);
646 }
647
648 inline void
writeBoolImpl(uint32_t id,bool val)649 ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
650 {
651 mBuffer->writeHeader(id, WIRE_TYPE_VARINT);
652 mBuffer->writeRawVarint32(val ? 1 : 0);
653 }
654
655 inline void
writeUtf8StringImpl(uint32_t id,const char * val,size_t size)656 ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size)
657 {
658 if (val == NULL) return;
659 writeLengthDelimitedHeader(id, size);
660 for (size_t i=0; i<size; i++) {
661 mBuffer->writeRawByte((uint8_t)val[i]);
662 }
663 }
664
665 inline void
writeMessageBytesImpl(uint32_t id,const char * val,size_t size)666 ProtoOutputStream::writeMessageBytesImpl(uint32_t id, const char* val, size_t size)
667 {
668 if (val == NULL) return;
669 writeLengthDelimitedHeader(id, size);
670 for (size_t i=0; i<size; i++) {
671 mBuffer->writeRawByte(val[i]);
672 }
673 }
674
675 } // util
676 } // android
677
678