ProtoOutputStream.cpp revision 56051569abf447f1e0682072df58f4db046c6520
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
24namespace android {
25namespace util {
26
27ProtoOutputStream::ProtoOutputStream()
28        :mBuffer(),
29         mCopyBegin(0),
30         mCompact(false),
31         mDepth(0),
32         mObjectId(0),
33         mExpectedObjectToken(UINT64_C(-1))
34{
35}
36
37ProtoOutputStream::~ProtoOutputStream()
38{
39}
40
41
42void
43ProtoOutputStream::clear()
44{
45    mBuffer.clear();
46    mCopyBegin = 0;
47    mCompact = false;
48    mDepth = 0;
49    mObjectId = 0;
50    mExpectedObjectToken = UINT64_C(-1);
51}
52
53bool
54ProtoOutputStream::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
79bool
80ProtoOutputStream::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
105bool
106ProtoOutputStream::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
133bool
134ProtoOutputStream::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
161bool
162ProtoOutputStream::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
177bool
178ProtoOutputStream::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
193bool
194ProtoOutputStream::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 */
227static uint64_t
228makeToken(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 */
239static 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 */
246static 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 */
253static uint32_t getSizePosFromToken(uint64_t token) {
254    return (uint32_t)token;
255}
256
257uint64_t
258ProtoOutputStream::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
279void
280ProtoOutputStream::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
312size_t
313ProtoOutputStream::bytesWritten()
314{
315    return mBuffer.size();
316}
317
318bool
319ProtoOutputStream::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 */
357size_t
358ProtoOutputStream::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 */
413bool
414ProtoOutputStream::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
460size_t
461ProtoOutputStream::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
470static 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
483bool
484ProtoOutputStream::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
497EncodedBuffer::iterator
498ProtoOutputStream::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
507void
508ProtoOutputStream::writeRawVarint(uint64_t varint)
509{
510    mBuffer.writeRawVarint64(varint);
511}
512
513void
514ProtoOutputStream::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
522void
523ProtoOutputStream::writeRawByte(uint8_t byte)
524{
525    mBuffer.writeRawByte(byte);
526}
527
528
529// =========================================================================
530// Private functions
531
532/**
533 * bit_cast
534 */
535template <class From, class To>
536inline To bit_cast(From const &from) {
537    To to;
538    memcpy(&to, &from, sizeof(to));
539    return to;
540}
541
542inline void
543ProtoOutputStream::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
549inline void
550ProtoOutputStream::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
556inline void
557ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
558{
559    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
560    mBuffer.writeRawVarint64((uint64_t)val);
561}
562
563inline void
564ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
565{
566    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
567    mBuffer.writeRawVarint32((uint32_t)val);
568}
569
570inline void
571ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
572{
573    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
574    mBuffer.writeRawVarint64(val);
575}
576
577inline void
578ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
579{
580    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
581    mBuffer.writeRawVarint32(val);
582}
583
584inline void
585ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
586{
587    mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
588    mBuffer.writeRawFixed64(val);
589}
590
591inline void
592ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
593{
594    mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
595    mBuffer.writeRawFixed32(val);
596}
597
598inline void
599ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
600{
601    mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
602    mBuffer.writeRawFixed64((uint64_t)val);
603}
604
605inline void
606ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
607{
608    mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
609    mBuffer.writeRawFixed32((uint32_t)val);
610}
611
612inline void
613ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
614{
615    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
616    mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
617}
618
619inline void
620ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
621{
622    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
623    mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
624}
625
626inline void
627ProtoOutputStream::writeEnumImpl(uint32_t id, int val)
628{
629    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
630    mBuffer.writeRawVarint32((uint32_t) val);
631}
632
633inline void
634ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
635{
636    mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
637    mBuffer.writeRawVarint32(val ? 1 : 0);
638}
639
640inline void
641ProtoOutputStream::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
650inline void
651ProtoOutputStream::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