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