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