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