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