• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &lt;= 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