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