• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // http://code.google.com/p/protobuf/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf.micro;
32 
33 import java.io.IOException;
34 import java.io.InputStream;
35 
36 /**
37  * Reads and decodes protocol message fields.
38  *
39  * This class contains two kinds of methods:  methods that read specific
40  * protocol message constructs and field types (e.g. {@link #readTag()} and
41  * {@link #readInt32()}) and methods that read low-level values (e.g.
42  * {@link #readRawVarint32()} and {@link #readRawBytes}).  If you are reading
43  * encoded protocol messages, you should use the former methods, but if you are
44  * reading some other format of your own design, use the latter.
45  *
46  * @author kenton@google.com Kenton Varda
47  */
48 public final class CodedInputStreamMicro {
49   /**
50    * Create a new CodedInputStream wrapping the given InputStream.
51    */
newInstance(final InputStream input)52   public static CodedInputStreamMicro newInstance(final InputStream input) {
53     return new CodedInputStreamMicro(input);
54   }
55 
56   /**
57    * Create a new CodedInputStream wrapping the given byte array.
58    */
newInstance(final byte[] buf)59   public static CodedInputStreamMicro newInstance(final byte[] buf) {
60     return newInstance(buf, 0, buf.length);
61   }
62 
63   /**
64    * Create a new CodedInputStream wrapping the given byte array slice.
65    */
newInstance(final byte[] buf, final int off, final int len)66   public static CodedInputStreamMicro newInstance(final byte[] buf, final int off,
67                                              final int len) {
68     return new CodedInputStreamMicro(buf, off, len);
69   }
70 
71   // -----------------------------------------------------------------
72 
73   /**
74    * Attempt to read a field tag, returning zero if we have reached EOF.
75    * Protocol message parsers use this to read tags, since a protocol message
76    * may legally end wherever a tag occurs, and zero is not a valid tag number.
77    */
readTag()78   public int readTag() throws IOException {
79     if (isAtEnd()) {
80       lastTag = 0;
81       return 0;
82     }
83 
84     lastTag = readRawVarint32();
85     if (lastTag == 0) {
86       // If we actually read zero, that's not a valid tag.
87       throw InvalidProtocolBufferMicroException.invalidTag();
88     }
89     return lastTag;
90   }
91 
92   /**
93    * Verifies that the last call to readTag() returned the given tag value.
94    * This is used to verify that a nested group ended with the correct
95    * end tag.
96    *
97    * @throws InvalidProtocolBufferMicroException {@code value} does not match the
98    *                                        last tag.
99    */
checkLastTagWas(final int value)100   public void checkLastTagWas(final int value)
101                               throws InvalidProtocolBufferMicroException {
102     if (lastTag != value) {
103       throw InvalidProtocolBufferMicroException.invalidEndTag();
104     }
105   }
106 
107   /**
108    * Reads and discards a single field, given its tag value.
109    *
110    * @return {@code false} if the tag is an endgroup tag, in which case
111    *         nothing is skipped.  Otherwise, returns {@code true}.
112    */
skipField(final int tag)113   public boolean skipField(final int tag) throws IOException {
114     switch (WireFormatMicro.getTagWireType(tag)) {
115       case WireFormatMicro.WIRETYPE_VARINT:
116         readInt32();
117         return true;
118       case WireFormatMicro.WIRETYPE_FIXED64:
119         readRawLittleEndian64();
120         return true;
121       case WireFormatMicro.WIRETYPE_LENGTH_DELIMITED:
122         skipRawBytes(readRawVarint32());
123         return true;
124       case WireFormatMicro.WIRETYPE_START_GROUP:
125         skipMessage();
126         checkLastTagWas(
127           WireFormatMicro.makeTag(WireFormatMicro.getTagFieldNumber(tag),
128                              WireFormatMicro.WIRETYPE_END_GROUP));
129         return true;
130       case WireFormatMicro.WIRETYPE_END_GROUP:
131         return false;
132       case WireFormatMicro.WIRETYPE_FIXED32:
133         readRawLittleEndian32();
134         return true;
135       default:
136         throw InvalidProtocolBufferMicroException.invalidWireType();
137     }
138   }
139 
140   /**
141    * Reads and discards an entire message.  This will read either until EOF
142    * or until an endgroup tag, whichever comes first.
143    */
skipMessage()144   public void skipMessage() throws IOException {
145     while (true) {
146       final int tag = readTag();
147       if (tag == 0 || !skipField(tag)) {
148         return;
149       }
150     }
151   }
152 
153   // -----------------------------------------------------------------
154 
155   /** Read a {@code double} field value from the stream. */
readDouble()156   public double readDouble() throws IOException {
157     return Double.longBitsToDouble(readRawLittleEndian64());
158   }
159 
160   /** Read a {@code float} field value from the stream. */
readFloat()161   public float readFloat() throws IOException {
162     return Float.intBitsToFloat(readRawLittleEndian32());
163   }
164 
165   /** Read a {@code uint64} field value from the stream. */
readUInt64()166   public long readUInt64() throws IOException {
167     return readRawVarint64();
168   }
169 
170   /** Read an {@code int64} field value from the stream. */
readInt64()171   public long readInt64() throws IOException {
172     return readRawVarint64();
173   }
174 
175   /** Read an {@code int32} field value from the stream. */
readInt32()176   public int readInt32() throws IOException {
177     return readRawVarint32();
178   }
179 
180   /** Read a {@code fixed64} field value from the stream. */
readFixed64()181   public long readFixed64() throws IOException {
182     return readRawLittleEndian64();
183   }
184 
185   /** Read a {@code fixed32} field value from the stream. */
readFixed32()186   public int readFixed32() throws IOException {
187     return readRawLittleEndian32();
188   }
189 
190   /** Read a {@code bool} field value from the stream. */
readBool()191   public boolean readBool() throws IOException {
192     return readRawVarint32() != 0;
193   }
194 
195   /** Read a {@code string} field value from the stream. */
readString()196   public String readString() throws IOException {
197     final int size = readRawVarint32();
198     if (size <= (bufferSize - bufferPos) && size > 0) {
199       // Fast path:  We already have the bytes in a contiguous buffer, so
200       //   just copy directly from it.
201       final String result = new String(buffer, bufferPos, size, "UTF-8");
202       bufferPos += size;
203       return result;
204     } else {
205       // Slow path:  Build a byte array first then copy it.
206       return new String(readRawBytes(size), "UTF-8");
207     }
208   }
209 
210   /** Read a {@code group} field value from the stream. */
readGroup(final MessageMicro msg, final int fieldNumber)211   public void readGroup(final MessageMicro msg, final int fieldNumber)
212       throws IOException {
213     if (recursionDepth >= recursionLimit) {
214       throw InvalidProtocolBufferMicroException.recursionLimitExceeded();
215     }
216     ++recursionDepth;
217     msg.mergeFrom(this);
218     checkLastTagWas(
219       WireFormatMicro.makeTag(fieldNumber, WireFormatMicro.WIRETYPE_END_GROUP));
220     --recursionDepth;
221   }
222 
readMessage(final MessageMicro msg)223   public void readMessage(final MessageMicro msg)
224       throws IOException {
225     final int length = readRawVarint32();
226     if (recursionDepth >= recursionLimit) {
227       throw InvalidProtocolBufferMicroException.recursionLimitExceeded();
228     }
229     final int oldLimit = pushLimit(length);
230     ++recursionDepth;
231     msg.mergeFrom(this);
232     checkLastTagWas(0);
233     --recursionDepth;
234     popLimit(oldLimit);
235   }
236 
237   /** Read a {@code bytes} field value from the stream. */
readBytes()238   public ByteStringMicro readBytes() throws IOException {
239     final int size = readRawVarint32();
240     if (size <= (bufferSize - bufferPos) && size > 0) {
241       // Fast path:  We already have the bytes in a contiguous buffer, so
242       //   just copy directly from it.
243       final ByteStringMicro result = ByteStringMicro.copyFrom(buffer, bufferPos, size);
244       bufferPos += size;
245       return result;
246     } else if (size == 0) {
247       return ByteStringMicro.EMPTY;
248     } else {
249       // Slow path:  Build a byte array first then copy it.
250       return ByteStringMicro.copyFrom(readRawBytes(size));
251     }
252   }
253 
254   /** Read a {@code uint32} field value from the stream. */
readUInt32()255   public int readUInt32() throws IOException {
256     return readRawVarint32();
257   }
258 
259   /**
260    * Read an enum field value from the stream.  Caller is responsible
261    * for converting the numeric value to an actual enum.
262    */
readEnum()263   public int readEnum() throws IOException {
264     return readRawVarint32();
265   }
266 
267   /** Read an {@code sfixed32} field value from the stream. */
readSFixed32()268   public int readSFixed32() throws IOException {
269     return readRawLittleEndian32();
270   }
271 
272   /** Read an {@code sfixed64} field value from the stream. */
readSFixed64()273   public long readSFixed64() throws IOException {
274     return readRawLittleEndian64();
275   }
276 
277   /** Read an {@code sint32} field value from the stream. */
readSInt32()278   public int readSInt32() throws IOException {
279     return decodeZigZag32(readRawVarint32());
280   }
281 
282   /** Read an {@code sint64} field value from the stream. */
readSInt64()283   public long readSInt64() throws IOException {
284     return decodeZigZag64(readRawVarint64());
285   }
286 
287   // =================================================================
288 
289   /**
290    * Read a raw Varint from the stream.  If larger than 32 bits, discard the
291    * upper bits.
292    */
readRawVarint32()293   public int readRawVarint32() throws IOException {
294     byte tmp = readRawByte();
295     if (tmp >= 0) {
296       return tmp;
297     }
298     int result = tmp & 0x7f;
299     if ((tmp = readRawByte()) >= 0) {
300       result |= tmp << 7;
301     } else {
302       result |= (tmp & 0x7f) << 7;
303       if ((tmp = readRawByte()) >= 0) {
304         result |= tmp << 14;
305       } else {
306         result |= (tmp & 0x7f) << 14;
307         if ((tmp = readRawByte()) >= 0) {
308           result |= tmp << 21;
309         } else {
310           result |= (tmp & 0x7f) << 21;
311           result |= (tmp = readRawByte()) << 28;
312           if (tmp < 0) {
313             // Discard upper 32 bits.
314             for (int i = 0; i < 5; i++) {
315               if (readRawByte() >= 0) {
316                 return result;
317               }
318             }
319             throw InvalidProtocolBufferMicroException.malformedVarint();
320           }
321         }
322       }
323     }
324     return result;
325   }
326 
327   /**
328    * Reads a varint from the input one byte at a time, so that it does not
329    * read any bytes after the end of the varint.  If you simply wrapped the
330    * stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
331    * then you would probably end up reading past the end of the varint since
332    * CodedInputStream buffers its input.
333    */
readRawVarint32(final InputStream input)334   static int readRawVarint32(final InputStream input) throws IOException {
335     int result = 0;
336     int offset = 0;
337     for (; offset < 32; offset += 7) {
338       final int b = input.read();
339       if (b == -1) {
340         throw InvalidProtocolBufferMicroException.truncatedMessage();
341       }
342       result |= (b & 0x7f) << offset;
343       if ((b & 0x80) == 0) {
344         return result;
345       }
346     }
347     // Keep reading up to 64 bits.
348     for (; offset < 64; offset += 7) {
349       final int b = input.read();
350       if (b == -1) {
351         throw InvalidProtocolBufferMicroException.truncatedMessage();
352       }
353       if ((b & 0x80) == 0) {
354         return result;
355       }
356     }
357     throw InvalidProtocolBufferMicroException.malformedVarint();
358   }
359 
360   /** Read a raw Varint from the stream. */
readRawVarint64()361   public long readRawVarint64() throws IOException {
362     int shift = 0;
363     long result = 0;
364     while (shift < 64) {
365       final byte b = readRawByte();
366       result |= (long)(b & 0x7F) << shift;
367       if ((b & 0x80) == 0) {
368         return result;
369       }
370       shift += 7;
371     }
372     throw InvalidProtocolBufferMicroException.malformedVarint();
373   }
374 
375   /** Read a 32-bit little-endian integer from the stream. */
readRawLittleEndian32()376   public int readRawLittleEndian32() throws IOException {
377     final byte b1 = readRawByte();
378     final byte b2 = readRawByte();
379     final byte b3 = readRawByte();
380     final byte b4 = readRawByte();
381     return ((b1 & 0xff)      ) |
382            ((b2 & 0xff) <<  8) |
383            ((b3 & 0xff) << 16) |
384            ((b4 & 0xff) << 24);
385   }
386 
387   /** Read a 64-bit little-endian integer from the stream. */
readRawLittleEndian64()388   public long readRawLittleEndian64() throws IOException {
389     final byte b1 = readRawByte();
390     final byte b2 = readRawByte();
391     final byte b3 = readRawByte();
392     final byte b4 = readRawByte();
393     final byte b5 = readRawByte();
394     final byte b6 = readRawByte();
395     final byte b7 = readRawByte();
396     final byte b8 = readRawByte();
397     return (((long)b1 & 0xff)      ) |
398            (((long)b2 & 0xff) <<  8) |
399            (((long)b3 & 0xff) << 16) |
400            (((long)b4 & 0xff) << 24) |
401            (((long)b5 & 0xff) << 32) |
402            (((long)b6 & 0xff) << 40) |
403            (((long)b7 & 0xff) << 48) |
404            (((long)b8 & 0xff) << 56);
405   }
406 
407   /**
408    * Decode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
409    * into values that can be efficiently encoded with varint.  (Otherwise,
410    * negative values must be sign-extended to 64 bits to be varint encoded,
411    * thus always taking 10 bytes on the wire.)
412    *
413    * @param n An unsigned 32-bit integer, stored in a signed int because
414    *          Java has no explicit unsigned support.
415    * @return A signed 32-bit integer.
416    */
decodeZigZag32(final int n)417   public static int decodeZigZag32(final int n) {
418     return (n >>> 1) ^ -(n & 1);
419   }
420 
421   /**
422    * Decode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
423    * into values that can be efficiently encoded with varint.  (Otherwise,
424    * negative values must be sign-extended to 64 bits to be varint encoded,
425    * thus always taking 10 bytes on the wire.)
426    *
427    * @param n An unsigned 64-bit integer, stored in a signed int because
428    *          Java has no explicit unsigned support.
429    * @return A signed 64-bit integer.
430    */
decodeZigZag64(final long n)431   public static long decodeZigZag64(final long n) {
432     return (n >>> 1) ^ -(n & 1);
433   }
434 
435   // -----------------------------------------------------------------
436 
437   private final byte[] buffer;
438   private int bufferSize;
439   private int bufferSizeAfterLimit;
440   private int bufferPos;
441   private final InputStream input;
442   private int lastTag;
443 
444   /**
445    * The total number of bytes read before the current buffer.  The total
446    * bytes read up to the current position can be computed as
447    * {@code totalBytesRetired + bufferPos}.
448    */
449   private int totalBytesRetired;
450 
451   /** The absolute position of the end of the current message. */
452   private int currentLimit = Integer.MAX_VALUE;
453 
454   /** See setRecursionLimit() */
455   private int recursionDepth;
456   private int recursionLimit = DEFAULT_RECURSION_LIMIT;
457 
458   /** See setSizeLimit() */
459   private int sizeLimit = DEFAULT_SIZE_LIMIT;
460 
461   private static final int DEFAULT_RECURSION_LIMIT = 64;
462   private static final int DEFAULT_SIZE_LIMIT = 64 << 20;  // 64MB
463   private static final int BUFFER_SIZE = 4096;
464 
CodedInputStreamMicro(final byte[] buffer, final int off, final int len)465   private CodedInputStreamMicro(final byte[] buffer, final int off, final int len) {
466     this.buffer = buffer;
467     bufferSize = off + len;
468     bufferPos = off;
469     input = null;
470   }
471 
CodedInputStreamMicro(final InputStream input)472   private CodedInputStreamMicro(final InputStream input) {
473     buffer = new byte[BUFFER_SIZE];
474     bufferSize = 0;
475     bufferPos = 0;
476     this.input = input;
477   }
478 
479   /**
480    * Set the maximum message recursion depth.  In order to prevent malicious
481    * messages from causing stack overflows, {@code CodedInputStream} limits
482    * how deeply messages may be nested.  The default limit is 64.
483    *
484    * @return the old limit.
485    */
setRecursionLimit(final int limit)486   public int setRecursionLimit(final int limit) {
487     if (limit < 0) {
488       throw new IllegalArgumentException(
489         "Recursion limit cannot be negative: " + limit);
490     }
491     final int oldLimit = recursionLimit;
492     recursionLimit = limit;
493     return oldLimit;
494   }
495 
496   /**
497    * Set the maximum message size.  In order to prevent malicious
498    * messages from exhausting memory or causing integer overflows,
499    * {@code CodedInputStream} limits how large a message may be.
500    * The default limit is 64MB.  You should set this limit as small
501    * as you can without harming your app's functionality.  Note that
502    * size limits only apply when reading from an {@code InputStream}, not
503    * when constructed around a raw byte array (nor with
504    * {@link ByteStringMicro#newCodedInput}).
505    * <p>
506    * If you want to read several messages from a single CodedInputStream, you
507    * could call {@link #resetSizeCounter()} after each one to avoid hitting the
508    * size limit.
509    *
510    * @return the old limit.
511    */
setSizeLimit(final int limit)512   public int setSizeLimit(final int limit) {
513     if (limit < 0) {
514       throw new IllegalArgumentException(
515         "Size limit cannot be negative: " + limit);
516     }
517     final int oldLimit = sizeLimit;
518     sizeLimit = limit;
519     return oldLimit;
520   }
521 
522   /**
523    * Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
524    */
resetSizeCounter()525   public void resetSizeCounter() {
526     totalBytesRetired = 0;
527   }
528 
529   /**
530    * Sets {@code currentLimit} to (current position) + {@code byteLimit}.  This
531    * is called when descending into a length-delimited embedded message.
532    *
533    * @return the old limit.
534    */
pushLimit(int byteLimit)535   public int pushLimit(int byteLimit) throws InvalidProtocolBufferMicroException {
536     if (byteLimit < 0) {
537       throw InvalidProtocolBufferMicroException.negativeSize();
538     }
539     byteLimit += totalBytesRetired + bufferPos;
540     final int oldLimit = currentLimit;
541     if (byteLimit > oldLimit) {
542       throw InvalidProtocolBufferMicroException.truncatedMessage();
543     }
544     currentLimit = byteLimit;
545 
546     recomputeBufferSizeAfterLimit();
547 
548     return oldLimit;
549   }
550 
recomputeBufferSizeAfterLimit()551   private void recomputeBufferSizeAfterLimit() {
552     bufferSize += bufferSizeAfterLimit;
553     final int bufferEnd = totalBytesRetired + bufferSize;
554     if (bufferEnd > currentLimit) {
555       // Limit is in current buffer.
556       bufferSizeAfterLimit = bufferEnd - currentLimit;
557       bufferSize -= bufferSizeAfterLimit;
558     } else {
559       bufferSizeAfterLimit = 0;
560     }
561   }
562 
563   /**
564    * Discards the current limit, returning to the previous limit.
565    *
566    * @param oldLimit The old limit, as returned by {@code pushLimit}.
567    */
popLimit(final int oldLimit)568   public void popLimit(final int oldLimit) {
569     currentLimit = oldLimit;
570     recomputeBufferSizeAfterLimit();
571   }
572 
573   /**
574    * Returns the number of bytes to be read before the current limit.
575    * If no limit is set, returns -1.
576    */
getBytesUntilLimit()577   public int getBytesUntilLimit() {
578     if (currentLimit == Integer.MAX_VALUE) {
579       return -1;
580     }
581 
582     final int currentAbsolutePosition = totalBytesRetired + bufferPos;
583     return currentLimit - currentAbsolutePosition;
584   }
585 
586   /**
587    * Returns true if the stream has reached the end of the input.  This is the
588    * case if either the end of the underlying input source has been reached or
589    * if the stream has reached a limit created using {@link #pushLimit(int)}.
590    */
isAtEnd()591   public boolean isAtEnd() throws IOException {
592     return bufferPos == bufferSize && !refillBuffer(false);
593   }
594 
595   /**
596    * Called with {@code this.buffer} is empty to read more bytes from the
597    * input.  If {@code mustSucceed} is true, refillBuffer() gurantees that
598    * either there will be at least one byte in the buffer when it returns
599    * or it will throw an exception.  If {@code mustSucceed} is false,
600    * refillBuffer() returns false if no more bytes were available.
601    */
refillBuffer(final boolean mustSucceed)602   private boolean refillBuffer(final boolean mustSucceed) throws IOException {
603     if (bufferPos < bufferSize) {
604       throw new IllegalStateException(
605         "refillBuffer() called when buffer wasn't empty.");
606     }
607 
608     if (totalBytesRetired + bufferSize == currentLimit) {
609       // Oops, we hit a limit.
610       if (mustSucceed) {
611         throw InvalidProtocolBufferMicroException.truncatedMessage();
612       } else {
613         return false;
614       }
615     }
616 
617     totalBytesRetired += bufferSize;
618 
619     bufferPos = 0;
620     bufferSize = (input == null) ? -1 : input.read(buffer);
621     if (bufferSize == 0 || bufferSize < -1) {
622       throw new IllegalStateException(
623           "InputStream#read(byte[]) returned invalid result: " + bufferSize +
624           "\nThe InputStream implementation is buggy.");
625     }
626     if (bufferSize == -1) {
627       bufferSize = 0;
628       if (mustSucceed) {
629         throw InvalidProtocolBufferMicroException.truncatedMessage();
630       } else {
631         return false;
632       }
633     } else {
634       recomputeBufferSizeAfterLimit();
635       final int totalBytesRead =
636         totalBytesRetired + bufferSize + bufferSizeAfterLimit;
637       if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
638         throw InvalidProtocolBufferMicroException.sizeLimitExceeded();
639       }
640       return true;
641     }
642   }
643 
644   /**
645    * Read one byte from the input.
646    *
647    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
648    *                                        limit was reached.
649    */
readRawByte()650   public byte readRawByte() throws IOException {
651     if (bufferPos == bufferSize) {
652       refillBuffer(true);
653     }
654     return buffer[bufferPos++];
655   }
656 
657   /**
658    * Read a fixed size of bytes from the input.
659    *
660    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
661    *                                        limit was reached.
662    */
readRawBytes(final int size)663   public byte[] readRawBytes(final int size) throws IOException {
664     if (size < 0) {
665       throw InvalidProtocolBufferMicroException.negativeSize();
666     }
667 
668     if (totalBytesRetired + bufferPos + size > currentLimit) {
669       // Read to the end of the stream anyway.
670       skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
671       // Then fail.
672       throw InvalidProtocolBufferMicroException.truncatedMessage();
673     }
674 
675     if (size <= bufferSize - bufferPos) {
676       // We have all the bytes we need already.
677       final byte[] bytes = new byte[size];
678       System.arraycopy(buffer, bufferPos, bytes, 0, size);
679       bufferPos += size;
680       return bytes;
681     } else if (size < BUFFER_SIZE) {
682       // Reading more bytes than are in the buffer, but not an excessive number
683       // of bytes.  We can safely allocate the resulting array ahead of time.
684 
685       // First copy what we have.
686       final byte[] bytes = new byte[size];
687       int pos = bufferSize - bufferPos;
688       System.arraycopy(buffer, bufferPos, bytes, 0, pos);
689       bufferPos = bufferSize;
690 
691       // We want to use refillBuffer() and then copy from the buffer into our
692       // byte array rather than reading directly into our byte array because
693       // the input may be unbuffered.
694       refillBuffer(true);
695 
696       while (size - pos > bufferSize) {
697         System.arraycopy(buffer, 0, bytes, pos, bufferSize);
698         pos += bufferSize;
699         bufferPos = bufferSize;
700         refillBuffer(true);
701       }
702 
703       System.arraycopy(buffer, 0, bytes, pos, size - pos);
704       bufferPos = size - pos;
705 
706       return bytes;
707     } else {
708       // The size is very large.  For security reasons, we can't allocate the
709       // entire byte array yet.  The size comes directly from the input, so a
710       // maliciously-crafted message could provide a bogus very large size in
711       // order to trick the app into allocating a lot of memory.  We avoid this
712       // by allocating and reading only a small chunk at a time, so that the
713       // malicious message must actually *be* extremely large to cause
714       // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
715 
716       // Remember the buffer markers since we'll have to copy the bytes out of
717       // it later.
718       final int originalBufferPos = bufferPos;
719       final int originalBufferSize = bufferSize;
720 
721       // Mark the current buffer consumed.
722       totalBytesRetired += bufferSize;
723       bufferPos = 0;
724       bufferSize = 0;
725 
726       // Read all the rest of the bytes we need.
727       int sizeLeft = size - (originalBufferSize - originalBufferPos);
728 
729       // For compatibility with Java 1.3 use Vector
730       final java.util.Vector chunks = new java.util.Vector();
731 
732       while (sizeLeft > 0) {
733         final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
734         int pos = 0;
735         while (pos < chunk.length) {
736           final int n = (input == null) ? -1 :
737             input.read(chunk, pos, chunk.length - pos);
738           if (n == -1) {
739             throw InvalidProtocolBufferMicroException.truncatedMessage();
740           }
741           totalBytesRetired += n;
742           pos += n;
743         }
744         sizeLeft -= chunk.length;
745         chunks.addElement(chunk);
746       }
747 
748       // OK, got everything.  Now concatenate it all into one buffer.
749       final byte[] bytes = new byte[size];
750 
751       // Start by copying the leftover bytes from this.buffer.
752       int pos = originalBufferSize - originalBufferPos;
753       System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
754 
755       // And now all the chunks.
756       for (int i = 0; i < chunks.size(); i++) {
757         byte [] chunk = (byte [])chunks.elementAt(i);
758         System.arraycopy(chunk, 0, bytes, pos, chunk.length);
759         pos += chunk.length;
760       }
761 
762       // Done.
763       return bytes;
764     }
765   }
766 
767   /**
768    * Reads and discards {@code size} bytes.
769    *
770    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
771    *                                        limit was reached.
772    */
skipRawBytes(final int size)773   public void skipRawBytes(final int size) throws IOException {
774     if (size < 0) {
775       throw InvalidProtocolBufferMicroException.negativeSize();
776     }
777 
778     if (totalBytesRetired + bufferPos + size > currentLimit) {
779       // Read to the end of the stream anyway.
780       skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
781       // Then fail.
782       throw InvalidProtocolBufferMicroException.truncatedMessage();
783     }
784 
785     if (size <= bufferSize - bufferPos) {
786       // We have all the bytes we need already.
787       bufferPos += size;
788     } else {
789       // Skipping more bytes than are in the buffer.  First skip what we have.
790       int pos = bufferSize - bufferPos;
791       totalBytesRetired += bufferSize;
792       bufferPos = 0;
793       bufferSize = 0;
794 
795       // Then skip directly from the InputStream for the rest.
796       while (pos < size) {
797         final int n = (input == null) ? -1 : (int) input.skip(size - pos);
798         if (n <= 0) {
799           throw InvalidProtocolBufferMicroException.truncatedMessage();
800         }
801         pos += n;
802         totalBytesRetired += n;
803       }
804     }
805   }
806 }
807