1 /* 2 * Copyright (C) 2012 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 17 package android.util.configinfrastructure.proto; 18 19 import android.util.Log; 20 21 import java.util.ArrayList; 22 23 /** 24 * A stream of bytes containing a read pointer and a write pointer, 25 * backed by a set of fixed-size buffers. There are write functions for the 26 * primitive types stored by protocol buffers, but none of the logic 27 * for tags, inner objects, or any of that. 28 * 29 * Terminology: 30 * *Pos: Position in the whole data set (as if it were a single buffer). 31 * *Index: Position within a buffer. 32 * *BufIndex: Index of a buffer within the mBuffers list 33 * 34 * This is copied from frameworks/base/core/java/android/util/proto/EncodedBuffer.java 35 * so ConfigInfra can use ProtoInputStream. Any major bugfixes in the original 36 * EncodedBuffer should be copied here. 37 * 38 * @hide 39 */ 40 @android.ravenwood.annotation.RavenwoodKeepWholeClass 41 public final class EncodedBuffer { 42 private static final String TAG = "EncodedBuffer"; 43 44 private final ArrayList<byte[]> mBuffers = new ArrayList<byte[]>(); 45 46 private final int mChunkSize; 47 48 /** 49 * The number of buffers in mBuffers. Stored separately to avoid the extra 50 * function call to size() everywhere for bounds checking. 51 */ 52 private int mBufferCount; 53 54 /** 55 * The buffer we are currently writing to. 56 */ 57 private byte[] mWriteBuffer; 58 59 /** 60 * The index into mWriteBuffer that we will write to next. 61 * It may point to the end of the buffer, in which case, 62 * the NEXT write will allocate a new buffer. 63 */ 64 private int mWriteIndex; 65 66 /** 67 * The index of mWriteBuffer in mBuffers. 68 */ 69 private int mWriteBufIndex; 70 71 /** 72 * The buffer we are currently reading from. 73 */ 74 private byte[] mReadBuffer; 75 76 /** 77 * The index of mReadBuffer in mBuffers. 78 */ 79 private int mReadBufIndex; 80 81 /** 82 * The index into mReadBuffer that we will read from next. 83 * It may point to the end of the buffer, in which case, 84 * the NEXT read will advance to the next buffer. 85 */ 86 private int mReadIndex; 87 88 /** 89 * The amount of data in the last buffer. 90 */ 91 private int mReadLimit = -1; 92 93 /** 94 * How much data there is total. 95 */ 96 private int mReadableSize = -1; 97 EncodedBuffer()98 public EncodedBuffer() { 99 this(0); 100 } 101 102 /** 103 * Construct an EncodedBuffer object. 104 * 105 * @param chunkSize The size of the buffers to use. If chunkSize <= 0, a default 106 * size will be used instead. 107 */ EncodedBuffer(int chunkSize)108 public EncodedBuffer(int chunkSize) { 109 if (chunkSize <= 0) { 110 chunkSize = 8 * 1024; 111 } 112 mChunkSize = chunkSize; 113 mWriteBuffer = new byte[mChunkSize]; 114 mBuffers.add(mWriteBuffer); 115 mBufferCount = 1; 116 } 117 118 // 119 // Buffer management. 120 // 121 122 /** 123 * Rewind the read and write pointers, and record how much data was last written. 124 */ startEditing()125 public void startEditing() { 126 mReadableSize = ((mWriteBufIndex) * mChunkSize) + mWriteIndex; 127 mReadLimit = mWriteIndex; 128 129 mWriteBuffer = mBuffers.get(0); 130 mWriteIndex = 0; 131 mWriteBufIndex = 0; 132 133 mReadBuffer = mWriteBuffer; 134 mReadBufIndex = 0; 135 mReadIndex = 0; 136 } 137 138 /** 139 * Rewind the read pointer. Don't touch the write pointer. 140 */ rewindRead()141 public void rewindRead() { 142 mReadBuffer = mBuffers.get(0); 143 mReadBufIndex = 0; 144 mReadIndex = 0; 145 } 146 147 /** 148 * Only valid after startEditing. Returns -1 before that. 149 */ getReadableSize()150 public int getReadableSize() { 151 return mReadableSize; 152 } 153 154 /** 155 * Returns the buffer size 156 * @return the buffer size 157 */ getSize()158 public int getSize() { 159 return ((mBufferCount - 1) * mChunkSize) + mWriteIndex; 160 } 161 162 // 163 // Reading from the read position. 164 // 165 166 /** 167 * Only valid after startEditing. 168 */ getReadPos()169 public int getReadPos() { 170 return ((mReadBufIndex) * mChunkSize) + mReadIndex; 171 } 172 173 /** 174 * Skip over _amount_ bytes. 175 */ skipRead(int amount)176 public void skipRead(int amount) { 177 if (amount < 0) { 178 throw new RuntimeException("skipRead with negative amount=" + amount); 179 } 180 if (amount == 0) { 181 return; 182 } 183 if (amount <= mChunkSize - mReadIndex) { 184 mReadIndex += amount; 185 } else { 186 amount -= mChunkSize - mReadIndex; 187 mReadIndex = amount % mChunkSize; 188 if (mReadIndex == 0) { 189 mReadIndex = mChunkSize; 190 mReadBufIndex += (amount / mChunkSize); 191 } else { 192 mReadBufIndex += 1 + (amount / mChunkSize); 193 } 194 mReadBuffer = mBuffers.get(mReadBufIndex); 195 } 196 } 197 198 /** 199 * Read one byte from the stream and advance the read pointer. 200 * 201 * @throws IndexOutOfBoundsException if the read point is past the end of 202 * the buffer or past the read limit previously set by startEditing(). 203 */ readRawByte()204 public byte readRawByte() { 205 if (mReadBufIndex > mBufferCount 206 || (mReadBufIndex == mBufferCount - 1 && mReadIndex >= mReadLimit)) { 207 throw new IndexOutOfBoundsException("Trying to read too much data" 208 + " mReadBufIndex=" + mReadBufIndex + " mBufferCount=" + mBufferCount 209 + " mReadIndex=" + mReadIndex + " mReadLimit=" + mReadLimit); 210 } 211 if (mReadIndex >= mChunkSize) { 212 mReadBufIndex++; 213 mReadBuffer = mBuffers.get(mReadBufIndex); 214 mReadIndex = 0; 215 } 216 return mReadBuffer[mReadIndex++]; 217 } 218 219 /** 220 * Read an unsigned varint. The value will be returend in a java signed long. 221 */ readRawUnsigned()222 public long readRawUnsigned() { 223 int bits = 0; 224 long result = 0; 225 while (true) { 226 final byte b = readRawByte(); 227 result |= ((long)(b & 0x7F)) << bits; 228 if ((b & 0x80) == 0) { 229 return result; 230 } 231 bits += 7; 232 if (bits > 64) { 233 throw new ProtoParseException("Varint too long -- " + getDebugString()); 234 } 235 } 236 } 237 238 /** 239 * Read 32 little endian bits from the stream. 240 */ readRawFixed32()241 public int readRawFixed32() { 242 return (readRawByte() & 0x0ff) 243 | ((readRawByte() & 0x0ff) << 8) 244 | ((readRawByte() & 0x0ff) << 16) 245 | ((readRawByte() & 0x0ff) << 24); 246 } 247 248 // 249 // Writing at a the end of the stream. 250 // 251 252 /** 253 * Advance to the next write buffer, allocating it if necessary. 254 * 255 * Must be called immediately <b>before</b> the next write, not after a write, 256 * so that a dangling empty buffer is not created. Doing so will interfere 257 * with the expectation that mWriteIndex will point past the end of the buffer 258 * until the next read happens. 259 */ nextWriteBuffer()260 private void nextWriteBuffer() { 261 mWriteBufIndex++; 262 if (mWriteBufIndex >= mBufferCount) { 263 mWriteBuffer = new byte[mChunkSize]; 264 mBuffers.add(mWriteBuffer); 265 mBufferCount++; 266 } else { 267 mWriteBuffer = mBuffers.get(mWriteBufIndex); 268 } 269 mWriteIndex = 0; 270 } 271 272 /** 273 * Write a single byte to the stream. 274 */ writeRawByte(byte val)275 public void writeRawByte(byte val) { 276 if (mWriteIndex >= mChunkSize) { 277 nextWriteBuffer(); 278 } 279 mWriteBuffer[mWriteIndex++] = val; 280 } 281 282 /** 283 * Return how many bytes a 32 bit unsigned varint will take when written to the stream. 284 */ getRawVarint32Size(int val)285 public static int getRawVarint32Size(int val) { 286 if ((val & (0xffffffff << 7)) == 0) return 1; 287 if ((val & (0xffffffff << 14)) == 0) return 2; 288 if ((val & (0xffffffff << 21)) == 0) return 3; 289 if ((val & (0xffffffff << 28)) == 0) return 4; 290 return 5; 291 } 292 293 /** 294 * Write an unsigned varint to the stream. A signed value would need to take 10 bytes. 295 * 296 * @param val treated as unsigned. 297 */ writeRawVarint32(int val)298 public void writeRawVarint32(int val) { 299 while (true) { 300 if ((val & ~0x7F) == 0) { 301 writeRawByte((byte)val); 302 return; 303 } else { 304 writeRawByte((byte)((val & 0x7F) | 0x80)); 305 val >>>= 7; 306 } 307 } 308 } 309 310 /** 311 * Return how many bytes a 32 bit signed zig zag value will take when written to the stream. 312 */ getRawZigZag32Size(int val)313 public static int getRawZigZag32Size(int val) { 314 return getRawVarint32Size(zigZag32(val)); 315 } 316 317 /** 318 * Write a zig-zag encoded value. 319 * 320 * @param val treated as signed 321 */ writeRawZigZag32(int val)322 public void writeRawZigZag32(int val) { 323 writeRawVarint32(zigZag32(val)); 324 } 325 326 /** 327 * Return how many bytes a 64 bit varint will take when written to the stream. 328 */ getRawVarint64Size(long val)329 public static int getRawVarint64Size(long val) { 330 if ((val & (0xffffffffffffffffL << 7)) == 0) return 1; 331 if ((val & (0xffffffffffffffffL << 14)) == 0) return 2; 332 if ((val & (0xffffffffffffffffL << 21)) == 0) return 3; 333 if ((val & (0xffffffffffffffffL << 28)) == 0) return 4; 334 if ((val & (0xffffffffffffffffL << 35)) == 0) return 5; 335 if ((val & (0xffffffffffffffffL << 42)) == 0) return 6; 336 if ((val & (0xffffffffffffffffL << 49)) == 0) return 7; 337 if ((val & (0xffffffffffffffffL << 56)) == 0) return 8; 338 if ((val & (0xffffffffffffffffL << 63)) == 0) return 9; 339 return 10; 340 } 341 342 /** 343 * Write a 64 bit varint to the stream. 344 */ writeRawVarint64(long val)345 public void writeRawVarint64(long val) { 346 while (true) { 347 if ((val & ~0x7FL) == 0) { 348 writeRawByte((byte)val); 349 return; 350 } else { 351 writeRawByte((byte)((val & 0x7F) | 0x80)); 352 val >>>= 7; 353 } 354 } 355 } 356 357 /** 358 * Return how many bytes a signed 64 bit zig zag value will take when written to the stream. 359 */ getRawZigZag64Size(long val)360 public static int getRawZigZag64Size(long val) { 361 return getRawVarint64Size(zigZag64(val)); 362 } 363 364 /** 365 * Write a 64 bit signed zig zag value to the stream. 366 */ writeRawZigZag64(long val)367 public void writeRawZigZag64(long val) { 368 writeRawVarint64(zigZag64(val)); 369 } 370 371 /** 372 * Write 4 little endian bytes to the stream. 373 */ writeRawFixed32(int val)374 public void writeRawFixed32(int val) { 375 writeRawByte((byte)(val)); 376 writeRawByte((byte)(val >> 8)); 377 writeRawByte((byte)(val >> 16)); 378 writeRawByte((byte)(val >> 24)); 379 } 380 381 /** 382 * Write 8 little endian bytes to the stream. 383 */ writeRawFixed64(long val)384 public void writeRawFixed64(long val) { 385 writeRawByte((byte)(val)); 386 writeRawByte((byte)(val >> 8)); 387 writeRawByte((byte)(val >> 16)); 388 writeRawByte((byte)(val >> 24)); 389 writeRawByte((byte)(val >> 32)); 390 writeRawByte((byte)(val >> 40)); 391 writeRawByte((byte)(val >> 48)); 392 writeRawByte((byte)(val >> 56)); 393 } 394 395 /** 396 * Write a buffer to the stream. Writes nothing if val is null or zero-length. 397 */ writeRawBuffer(byte[] val)398 public void writeRawBuffer(byte[] val) { 399 if (val != null && val.length > 0) { 400 writeRawBuffer(val, 0, val.length); 401 } 402 } 403 404 /** 405 * Write part of an array of bytes. 406 */ writeRawBuffer(byte[] val, int offset, int length)407 public void writeRawBuffer(byte[] val, int offset, int length) { 408 if (val == null) { 409 return; 410 } 411 // Write up to the amount left in the first chunk to write. 412 int amt = length < (mChunkSize - mWriteIndex) ? length : (mChunkSize - mWriteIndex); 413 if (amt > 0) { 414 System.arraycopy(val, offset, mWriteBuffer, mWriteIndex, amt); 415 mWriteIndex += amt; 416 length -= amt; 417 offset += amt; 418 } 419 while (length > 0) { 420 // We know we're now at the beginning of a chunk 421 nextWriteBuffer(); 422 amt = length < mChunkSize ? length : mChunkSize; 423 System.arraycopy(val, offset, mWriteBuffer, mWriteIndex, amt); 424 mWriteIndex += amt; 425 length -= amt; 426 offset += amt; 427 } 428 } 429 430 /** 431 * Copies data _size_ bytes of data within this buffer from _srcOffset_ 432 * to the current write position. Like memmov but handles the chunked buffer. 433 */ 434 public void writeFromThisBuffer(int srcOffset, int size) { 435 if (mReadLimit < 0) { 436 throw new IllegalStateException("writeFromThisBuffer before startEditing"); 437 } 438 if (srcOffset < getWritePos()) { 439 throw new IllegalArgumentException("Can only move forward in the buffer --" 440 + " srcOffset=" + srcOffset + " size=" + size + " " + getDebugString()); 441 } 442 if (srcOffset + size > mReadableSize) { 443 throw new IllegalArgumentException("Trying to move more data than there is --" 444 + " srcOffset=" + srcOffset + " size=" + size + " " + getDebugString()); 445 } 446 if (size == 0) { 447 return; 448 } 449 if (srcOffset == ((mWriteBufIndex) * mChunkSize) + mWriteIndex /* write pos */) { 450 // Writing to the same location. Just advance the write pointer. We already 451 // checked that size is in bounds, so we don't need to do any more range 452 // checking. 453 if (size <= mChunkSize - mWriteIndex) { 454 mWriteIndex += size; 455 } else { 456 size -= mChunkSize - mWriteIndex; 457 mWriteIndex = size % mChunkSize; 458 if (mWriteIndex == 0) { 459 // Roll it back so nextWriteBuffer can do its job 460 // on the next call (also makes mBuffers.get() not 461 // fail if we're at the end). 462 mWriteIndex = mChunkSize; 463 mWriteBufIndex += (size / mChunkSize); 464 } else { 465 mWriteBufIndex += 1 + (size / mChunkSize); 466 } 467 mWriteBuffer = mBuffers.get(mWriteBufIndex); 468 } 469 } else { 470 // Loop through the buffer, copying as much as we can each time. 471 // We already bounds checked so we don't need to do it again here, 472 // and nextWriteBuffer will never allocate. 473 int readBufIndex = srcOffset / mChunkSize; 474 byte[] readBuffer = mBuffers.get(readBufIndex); 475 int readIndex = srcOffset % mChunkSize; 476 while (size > 0) { 477 if (mWriteIndex >= mChunkSize) { 478 nextWriteBuffer(); 479 } 480 if (readIndex >= mChunkSize) { 481 readBufIndex++; 482 readBuffer = mBuffers.get(readBufIndex); 483 readIndex = 0; 484 } 485 final int spaceInWriteBuffer = mChunkSize - mWriteIndex; 486 final int availableInReadBuffer = mChunkSize - readIndex; 487 final int amt = Math.min(size, Math.min(spaceInWriteBuffer, availableInReadBuffer)); 488 System.arraycopy(readBuffer, readIndex, mWriteBuffer, mWriteIndex, amt); 489 mWriteIndex += amt; 490 readIndex += amt; 491 size -= amt; 492 } 493 } 494 } 495 496 // 497 // Writing at a particular location. 498 // 499 500 /** 501 * Returns the index into the virtual array of the write pointer. 502 */ 503 public int getWritePos() { 504 return ((mWriteBufIndex) * mChunkSize) + mWriteIndex; 505 } 506 507 /** 508 * Resets the write pointer to a virtual location as returned by getWritePos. 509 */ 510 public void rewindWriteTo(int writePos) { 511 if (writePos > getWritePos()) { 512 throw new RuntimeException("rewindWriteTo only can go backwards" + writePos); 513 } 514 mWriteBufIndex = writePos / mChunkSize; 515 mWriteIndex = writePos % mChunkSize; 516 if (mWriteIndex == 0 && mWriteBufIndex != 0) { 517 // Roll back so nextWriteBuffer can do its job on the next call 518 // but at the first write we're at 0. 519 mWriteIndex = mChunkSize; 520 mWriteBufIndex--; 521 } 522 mWriteBuffer = mBuffers.get(mWriteBufIndex); 523 } 524 525 /** 526 * Read a 32 bit value from the stream. 527 * 528 * Doesn't touch or affect mWritePos. 529 */ 530 public int getRawFixed32At(int pos) { 531 return (0x00ff & (int)mBuffers.get(pos / mChunkSize)[pos % mChunkSize]) 532 | ((0x0ff & (int)mBuffers.get((pos+1) / mChunkSize)[(pos+1) % mChunkSize]) << 8) 533 | ((0x0ff & (int)mBuffers.get((pos+2) / mChunkSize)[(pos+2) % mChunkSize]) << 16) 534 | ((0x0ff & (int)mBuffers.get((pos+3) / mChunkSize)[(pos+3) % mChunkSize]) << 24); 535 } 536 537 /** 538 * Overwrite a 32 bit value in the stream. 539 * 540 * Doesn't touch or affect mWritePos. 541 */ 542 public void editRawFixed32(int pos, int val) { 543 mBuffers.get(pos / mChunkSize)[pos % mChunkSize] = (byte)(val); 544 mBuffers.get((pos+1) / mChunkSize)[(pos+1) % mChunkSize] = (byte)(val >> 8); 545 mBuffers.get((pos+2) / mChunkSize)[(pos+2) % mChunkSize] = (byte)(val >> 16); 546 mBuffers.get((pos+3) / mChunkSize)[(pos+3) % mChunkSize] = (byte)(val >> 24); 547 } 548 549 // 550 // Zigging and zagging 551 // 552 553 /** 554 * Zig-zag encode a 32 bit value. 555 */ 556 private static int zigZag32(int val) { 557 return (val << 1) ^ (val >> 31); 558 } 559 560 /** 561 * Zig-zag encode a 64 bit value. 562 */ 563 private static long zigZag64(long val) { 564 return (val << 1) ^ (val >> 63); 565 } 566 567 // 568 // Debugging / testing 569 // 570 // VisibleForTesting 571 572 /** 573 * Get a copy of the first _size_ bytes of data. This is not range 574 * checked, and if the bounds are outside what has been written you will 575 * get garbage and if it is outside the buffers that have been allocated, 576 * you will get an exception. 577 */ 578 public byte[] getBytes(int size) { 579 final byte[] result = new byte[size]; 580 581 final int bufCount = size / mChunkSize; 582 int bufIndex; 583 int writeIndex = 0; 584 585 for (bufIndex=0; bufIndex<bufCount; bufIndex++) { 586 System.arraycopy(mBuffers.get(bufIndex), 0, result, writeIndex, mChunkSize); 587 writeIndex += mChunkSize; 588 } 589 590 final int lastSize = size - (bufCount * mChunkSize); 591 if (lastSize > 0) { 592 System.arraycopy(mBuffers.get(bufIndex), 0, result, writeIndex, lastSize); 593 } 594 595 return result; 596 } 597 598 /** 599 * Get the number of chunks allocated. 600 */ 601 // VisibleForTesting 602 public int getChunkCount() { 603 return mBuffers.size(); 604 } 605 606 /** 607 * Get the write position inside the current write chunk. 608 */ 609 // VisibleForTesting 610 public int getWriteIndex() { 611 return mWriteIndex; 612 } 613 614 /** 615 * Get the index of the current write chunk in the list of chunks. 616 */ 617 // VisibleForTesting 618 public int getWriteBufIndex() { 619 return mWriteBufIndex; 620 } 621 622 /** 623 * Return debugging information about this EncodedBuffer object. 624 */ 625 public String getDebugString() { 626 return "EncodedBuffer( mChunkSize=" + mChunkSize + " mBuffers.size=" + mBuffers.size() 627 + " mBufferCount=" + mBufferCount + " mWriteIndex=" + mWriteIndex 628 + " mWriteBufIndex=" + mWriteBufIndex + " mReadBufIndex=" + mReadBufIndex 629 + " mReadIndex=" + mReadIndex + " mReadableSize=" + mReadableSize 630 + " mReadLimit=" + mReadLimit + " )"; 631 } 632 633 /** 634 * Print the internal buffer chunks. 635 */ 636 public void dumpBuffers(String tag) { 637 final int N = mBuffers.size(); 638 int start = 0; 639 for (int i=0; i<N; i++) { 640 start += dumpByteString(tag, "{" + i + "} ", start, mBuffers.get(i)); 641 } 642 } 643 644 /** 645 * Print the internal buffer chunks. 646 */ 647 public static void dumpByteString(String tag, String prefix, byte[] buf) { 648 dumpByteString(tag, prefix, 0, buf); 649 } 650 651 /** 652 * Print the internal buffer chunks. 653 */ 654 private static int dumpByteString(String tag, String prefix, int start, byte[] buf) { 655 StringBuilder sb = new StringBuilder(); 656 final int length = buf.length; 657 final int lineLen = 16; 658 int i; 659 for (i=0; i<length; i++) { 660 if (i % lineLen == 0) { 661 if (i != 0) { 662 Log.d(tag, sb.toString()); 663 sb = new StringBuilder(); 664 } 665 sb.append(prefix); 666 sb.append('['); 667 sb.append(start + i); 668 sb.append(']'); 669 sb.append(' '); 670 } else { 671 sb.append(' '); 672 } 673 byte b = buf[i]; 674 byte c = (byte)((b >> 4) & 0x0f); 675 if (c < 10) { 676 sb.append((char)('0' + c)); 677 } else { 678 sb.append((char)('a' - 10 + c)); 679 } 680 byte d = (byte)(b & 0x0f); 681 if (d < 10) { 682 sb.append((char)('0' + d)); 683 } else { 684 sb.append((char)('a' - 10 + d)); 685 } 686 } 687 Log.d(tag, sb.toString()); 688 return length; 689 } 690 } 691