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