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