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