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