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