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