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