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