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