1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2013 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
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.nano;
32 
33 import java.io.IOException;
34 
35 /**
36  * Reads and decodes protocol message fields.
37  *
38  * This class contains two kinds of methods:  methods that read specific
39  * protocol message constructs and field types (e.g. {@link #readTag()} and
40  * {@link #readInt32()}) and methods that read low-level values (e.g.
41  * {@link #readRawVarint32()} and {@link #readRawBytes}).  If you are reading
42  * encoded protocol messages, you should use the former methods, but if you are
43  * reading some other format of your own design, use the latter.
44  *
45  * @author kenton@google.com Kenton Varda
46  */
47 public final class CodedInputByteBufferNano {
48   /**
49    * Create a new CodedInputStream wrapping the given byte array.
50    */
newInstance(final byte[] buf)51   public static CodedInputByteBufferNano newInstance(final byte[] buf) {
52     return newInstance(buf, 0, buf.length);
53   }
54 
55   /**
56    * Create a new CodedInputStream wrapping the given byte array slice.
57    */
newInstance(final byte[] buf, final int off, final int len)58   public static CodedInputByteBufferNano newInstance(final byte[] buf, final int off,
59                                              final int len) {
60     return new CodedInputByteBufferNano(buf, off, len);
61   }
62 
63   // -----------------------------------------------------------------
64 
65   /**
66    * Attempt to read a field tag, returning zero if we have reached EOF.
67    * Protocol message parsers use this to read tags, since a protocol message
68    * may legally end wherever a tag occurs, and zero is not a valid tag number.
69    */
readTag()70   public int readTag() throws IOException {
71     if (isAtEnd()) {
72       lastTag = 0;
73       return 0;
74     }
75 
76     lastTag = readRawVarint32();
77     if (lastTag == 0) {
78       // If we actually read zero, that's not a valid tag.
79       throw InvalidProtocolBufferNanoException.invalidTag();
80     }
81     return lastTag;
82   }
83 
84   /**
85    * Verifies that the last call to readTag() returned the given tag value.
86    * This is used to verify that a nested group ended with the correct
87    * end tag.
88    *
89    * @throws InvalidProtocolBufferNanoException {@code value} does not match the
90    *                                        last tag.
91    */
checkLastTagWas(final int value)92   public void checkLastTagWas(final int value)
93                               throws InvalidProtocolBufferNanoException {
94     if (lastTag != value) {
95       throw InvalidProtocolBufferNanoException.invalidEndTag();
96     }
97   }
98 
99   /**
100    * Reads and discards a single field, given its tag value.
101    *
102    * @return {@code false} if the tag is an endgroup tag, in which case
103    *         nothing is skipped.  Otherwise, returns {@code true}.
104    */
skipField(final int tag)105   public boolean skipField(final int tag) throws IOException {
106     switch (WireFormatNano.getTagWireType(tag)) {
107       case WireFormatNano.WIRETYPE_VARINT:
108         readInt32();
109         return true;
110       case WireFormatNano.WIRETYPE_FIXED64:
111         readRawLittleEndian64();
112         return true;
113       case WireFormatNano.WIRETYPE_LENGTH_DELIMITED:
114         skipRawBytes(readRawVarint32());
115         return true;
116       case WireFormatNano.WIRETYPE_START_GROUP:
117         skipMessage();
118         checkLastTagWas(
119           WireFormatNano.makeTag(WireFormatNano.getTagFieldNumber(tag),
120                              WireFormatNano.WIRETYPE_END_GROUP));
121         return true;
122       case WireFormatNano.WIRETYPE_END_GROUP:
123         return false;
124       case WireFormatNano.WIRETYPE_FIXED32:
125         readRawLittleEndian32();
126         return true;
127       default:
128         throw InvalidProtocolBufferNanoException.invalidWireType();
129     }
130   }
131 
132   /**
133    * Reads and discards an entire message.  This will read either until EOF
134    * or until an endgroup tag, whichever comes first.
135    */
skipMessage()136   public void skipMessage() throws IOException {
137     while (true) {
138       final int tag = readTag();
139       if (tag == 0 || !skipField(tag)) {
140         return;
141       }
142     }
143   }
144 
145   // -----------------------------------------------------------------
146 
147   /** Read a {@code double} field value from the stream. */
readDouble()148   public double readDouble() throws IOException {
149     return Double.longBitsToDouble(readRawLittleEndian64());
150   }
151 
152   /** Read a {@code float} field value from the stream. */
readFloat()153   public float readFloat() throws IOException {
154     return Float.intBitsToFloat(readRawLittleEndian32());
155   }
156 
157   /** Read a {@code uint64} field value from the stream. */
readUInt64()158   public long readUInt64() throws IOException {
159     return readRawVarint64();
160   }
161 
162   /** Read an {@code int64} field value from the stream. */
readInt64()163   public long readInt64() throws IOException {
164     return readRawVarint64();
165   }
166 
167   /** Read an {@code int32} field value from the stream. */
readInt32()168   public int readInt32() throws IOException {
169     return readRawVarint32();
170   }
171 
172   /** Read a {@code fixed64} field value from the stream. */
readFixed64()173   public long readFixed64() throws IOException {
174     return readRawLittleEndian64();
175   }
176 
177   /** Read a {@code fixed32} field value from the stream. */
readFixed32()178   public int readFixed32() throws IOException {
179     return readRawLittleEndian32();
180   }
181 
182   /** Read a {@code bool} field value from the stream. */
readBool()183   public boolean readBool() throws IOException {
184     return readRawVarint32() != 0;
185   }
186 
187   /** Read a {@code string} field value from the stream. */
readString()188   public String readString() throws IOException {
189     final int size = readRawVarint32();
190     if (size <= (bufferSize - bufferPos) && size > 0) {
191       // Fast path:  We already have the bytes in a contiguous buffer, so
192       //   just copy directly from it.
193       final String result = new String(buffer, bufferPos, size, InternalNano.UTF_8);
194       bufferPos += size;
195       return result;
196     } else {
197       // Slow path:  Build a byte array first then copy it.
198       return new String(readRawBytes(size), InternalNano.UTF_8);
199     }
200   }
201 
202   /** Read a {@code group} field value from the stream. */
readGroup(final MessageNano msg, final int fieldNumber)203   public void readGroup(final MessageNano msg, final int fieldNumber)
204       throws IOException {
205     if (recursionDepth >= recursionLimit) {
206       throw InvalidProtocolBufferNanoException.recursionLimitExceeded();
207     }
208     ++recursionDepth;
209     msg.mergeFrom(this);
210     checkLastTagWas(
211       WireFormatNano.makeTag(fieldNumber, WireFormatNano.WIRETYPE_END_GROUP));
212     --recursionDepth;
213   }
214 
readMessage(final MessageNano msg)215   public void readMessage(final MessageNano msg)
216       throws IOException {
217     final int length = readRawVarint32();
218     if (recursionDepth >= recursionLimit) {
219       throw InvalidProtocolBufferNanoException.recursionLimitExceeded();
220     }
221     final int oldLimit = pushLimit(length);
222     ++recursionDepth;
223     msg.mergeFrom(this);
224     checkLastTagWas(0);
225     --recursionDepth;
226     popLimit(oldLimit);
227   }
228 
229   /** Read a {@code bytes} field value from the stream. */
readBytes()230   public byte[] readBytes() throws IOException {
231     final int size = readRawVarint32();
232     if (size <= (bufferSize - bufferPos) && size > 0) {
233       // Fast path:  We already have the bytes in a contiguous buffer, so
234       //   just copy directly from it.
235       final byte[] result = new byte[size];
236       System.arraycopy(buffer, bufferPos, result, 0, size);
237       bufferPos += size;
238       return result;
239     } else if (size == 0) {
240       return WireFormatNano.EMPTY_BYTES;
241     } else {
242       // Slow path:  Build a byte array first then copy it.
243       return readRawBytes(size);
244     }
245   }
246 
247   /** Read a {@code uint32} field value from the stream. */
readUInt32()248   public int readUInt32() throws IOException {
249     return readRawVarint32();
250   }
251 
252   /**
253    * Read an enum field value from the stream.  Caller is responsible
254    * for converting the numeric value to an actual enum.
255    */
readEnum()256   public int readEnum() throws IOException {
257     return readRawVarint32();
258   }
259 
260   /** Read an {@code sfixed32} field value from the stream. */
readSFixed32()261   public int readSFixed32() throws IOException {
262     return readRawLittleEndian32();
263   }
264 
265   /** Read an {@code sfixed64} field value from the stream. */
readSFixed64()266   public long readSFixed64() throws IOException {
267     return readRawLittleEndian64();
268   }
269 
270   /** Read an {@code sint32} field value from the stream. */
readSInt32()271   public int readSInt32() throws IOException {
272     return decodeZigZag32(readRawVarint32());
273   }
274 
275   /** Read an {@code sint64} field value from the stream. */
readSInt64()276   public long readSInt64() throws IOException {
277     return decodeZigZag64(readRawVarint64());
278   }
279 
280   // =================================================================
281 
282   /**
283    * Read a raw Varint from the stream.  If larger than 32 bits, discard the
284    * upper bits.
285    */
readRawVarint32()286   public int readRawVarint32() throws IOException {
287     byte tmp = readRawByte();
288     if (tmp >= 0) {
289       return tmp;
290     }
291     int result = tmp & 0x7f;
292     if ((tmp = readRawByte()) >= 0) {
293       result |= tmp << 7;
294     } else {
295       result |= (tmp & 0x7f) << 7;
296       if ((tmp = readRawByte()) >= 0) {
297         result |= tmp << 14;
298       } else {
299         result |= (tmp & 0x7f) << 14;
300         if ((tmp = readRawByte()) >= 0) {
301           result |= tmp << 21;
302         } else {
303           result |= (tmp & 0x7f) << 21;
304           result |= (tmp = readRawByte()) << 28;
305           if (tmp < 0) {
306             // Discard upper 32 bits.
307             for (int i = 0; i < 5; i++) {
308               if (readRawByte() >= 0) {
309                 return result;
310               }
311             }
312             throw InvalidProtocolBufferNanoException.malformedVarint();
313           }
314         }
315       }
316     }
317     return result;
318   }
319 
320   /** Read a raw Varint from the stream. */
readRawVarint64()321   public long readRawVarint64() throws IOException {
322     int shift = 0;
323     long result = 0;
324     while (shift < 64) {
325       final byte b = readRawByte();
326       result |= (long)(b & 0x7F) << shift;
327       if ((b & 0x80) == 0) {
328         return result;
329       }
330       shift += 7;
331     }
332     throw InvalidProtocolBufferNanoException.malformedVarint();
333   }
334 
335   /** Read a 32-bit little-endian integer from the stream. */
readRawLittleEndian32()336   public int readRawLittleEndian32() throws IOException {
337     final byte b1 = readRawByte();
338     final byte b2 = readRawByte();
339     final byte b3 = readRawByte();
340     final byte b4 = readRawByte();
341     return ((b1 & 0xff)      ) |
342            ((b2 & 0xff) <<  8) |
343            ((b3 & 0xff) << 16) |
344            ((b4 & 0xff) << 24);
345   }
346 
347   /** Read a 64-bit little-endian integer from the stream. */
readRawLittleEndian64()348   public long readRawLittleEndian64() throws IOException {
349     final byte b1 = readRawByte();
350     final byte b2 = readRawByte();
351     final byte b3 = readRawByte();
352     final byte b4 = readRawByte();
353     final byte b5 = readRawByte();
354     final byte b6 = readRawByte();
355     final byte b7 = readRawByte();
356     final byte b8 = readRawByte();
357     return (((long)b1 & 0xff)      ) |
358            (((long)b2 & 0xff) <<  8) |
359            (((long)b3 & 0xff) << 16) |
360            (((long)b4 & 0xff) << 24) |
361            (((long)b5 & 0xff) << 32) |
362            (((long)b6 & 0xff) << 40) |
363            (((long)b7 & 0xff) << 48) |
364            (((long)b8 & 0xff) << 56);
365   }
366 
367   /**
368    * Decode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
369    * into values that can be efficiently encoded with varint.  (Otherwise,
370    * negative values must be sign-extended to 64 bits to be varint encoded,
371    * thus always taking 10 bytes on the wire.)
372    *
373    * @param n An unsigned 32-bit integer, stored in a signed int because
374    *          Java has no explicit unsigned support.
375    * @return A signed 32-bit integer.
376    */
decodeZigZag32(final int n)377   public static int decodeZigZag32(final int n) {
378     return (n >>> 1) ^ -(n & 1);
379   }
380 
381   /**
382    * Decode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
383    * into values that can be efficiently encoded with varint.  (Otherwise,
384    * negative values must be sign-extended to 64 bits to be varint encoded,
385    * thus always taking 10 bytes on the wire.)
386    *
387    * @param n An unsigned 64-bit integer, stored in a signed int because
388    *          Java has no explicit unsigned support.
389    * @return A signed 64-bit integer.
390    */
decodeZigZag64(final long n)391   public static long decodeZigZag64(final long n) {
392     return (n >>> 1) ^ -(n & 1);
393   }
394 
395   // -----------------------------------------------------------------
396 
397   private final byte[] buffer;
398   private int bufferStart;
399   private int bufferSize;
400   private int bufferSizeAfterLimit;
401   private int bufferPos;
402   private int lastTag;
403 
404   /** The absolute position of the end of the current message. */
405   private int currentLimit = Integer.MAX_VALUE;
406 
407   /** See setRecursionLimit() */
408   private int recursionDepth;
409   private int recursionLimit = DEFAULT_RECURSION_LIMIT;
410 
411   /** See setSizeLimit() */
412   private int sizeLimit = DEFAULT_SIZE_LIMIT;
413 
414   private static final int DEFAULT_RECURSION_LIMIT = 64;
415   private static final int DEFAULT_SIZE_LIMIT = 64 << 20;  // 64MB
416 
CodedInputByteBufferNano(final byte[] buffer, final int off, final int len)417   private CodedInputByteBufferNano(final byte[] buffer, final int off, final int len) {
418     this.buffer = buffer;
419     bufferStart = off;
420     bufferSize = off + len;
421     bufferPos = off;
422   }
423 
424   /**
425    * Set the maximum message recursion depth.  In order to prevent malicious
426    * messages from causing stack overflows, {@code CodedInputStream} limits
427    * how deeply messages may be nested.  The default limit is 64.
428    *
429    * @return the old limit.
430    */
setRecursionLimit(final int limit)431   public int setRecursionLimit(final int limit) {
432     if (limit < 0) {
433       throw new IllegalArgumentException(
434         "Recursion limit cannot be negative: " + limit);
435     }
436     final int oldLimit = recursionLimit;
437     recursionLimit = limit;
438     return oldLimit;
439   }
440 
441   /**
442    * Set the maximum message size.  In order to prevent malicious
443    * messages from exhausting memory or causing integer overflows,
444    * {@code CodedInputStream} limits how large a message may be.
445    * The default limit is 64MB.  You should set this limit as small
446    * as you can without harming your app's functionality.  Note that
447    * size limits only apply when reading from an {@code InputStream}, not
448    * when constructed around a raw byte array.
449    * <p>
450    * If you want to read several messages from a single CodedInputStream, you
451    * could call {@link #resetSizeCounter()} after each one to avoid hitting the
452    * size limit.
453    *
454    * @return the old limit.
455    */
setSizeLimit(final int limit)456   public int setSizeLimit(final int limit) {
457     if (limit < 0) {
458       throw new IllegalArgumentException(
459         "Size limit cannot be negative: " + limit);
460     }
461     final int oldLimit = sizeLimit;
462     sizeLimit = limit;
463     return oldLimit;
464   }
465 
466   /**
467    * Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
468    */
resetSizeCounter()469   public void resetSizeCounter() {
470   }
471 
472   /**
473    * Sets {@code currentLimit} to (current position) + {@code byteLimit}.  This
474    * is called when descending into a length-delimited embedded message.
475    *
476    * @return the old limit.
477    */
pushLimit(int byteLimit)478   public int pushLimit(int byteLimit) throws InvalidProtocolBufferNanoException {
479     if (byteLimit < 0) {
480       throw InvalidProtocolBufferNanoException.negativeSize();
481     }
482     byteLimit += bufferPos;
483     final int oldLimit = currentLimit;
484     if (byteLimit > oldLimit) {
485       throw InvalidProtocolBufferNanoException.truncatedMessage();
486     }
487     currentLimit = byteLimit;
488 
489     recomputeBufferSizeAfterLimit();
490 
491     return oldLimit;
492   }
493 
recomputeBufferSizeAfterLimit()494   private void recomputeBufferSizeAfterLimit() {
495     bufferSize += bufferSizeAfterLimit;
496     final int bufferEnd = bufferSize;
497     if (bufferEnd > currentLimit) {
498       // Limit is in current buffer.
499       bufferSizeAfterLimit = bufferEnd - currentLimit;
500       bufferSize -= bufferSizeAfterLimit;
501     } else {
502       bufferSizeAfterLimit = 0;
503     }
504   }
505 
506   /**
507    * Discards the current limit, returning to the previous limit.
508    *
509    * @param oldLimit The old limit, as returned by {@code pushLimit}.
510    */
popLimit(final int oldLimit)511   public void popLimit(final int oldLimit) {
512     currentLimit = oldLimit;
513     recomputeBufferSizeAfterLimit();
514   }
515 
516   /**
517    * Returns the number of bytes to be read before the current limit.
518    * If no limit is set, returns -1.
519    */
getBytesUntilLimit()520   public int getBytesUntilLimit() {
521     if (currentLimit == Integer.MAX_VALUE) {
522       return -1;
523     }
524 
525     final int currentAbsolutePosition = bufferPos;
526     return currentLimit - currentAbsolutePosition;
527   }
528 
529   /**
530    * Returns true if the stream has reached the end of the input.  This is the
531    * case if either the end of the underlying input source has been reached or
532    * if the stream has reached a limit created using {@link #pushLimit(int)}.
533    */
isAtEnd()534   public boolean isAtEnd() {
535     return bufferPos == bufferSize;
536   }
537 
538   /**
539    * Get current position in buffer relative to beginning offset.
540    */
getPosition()541   public int getPosition() {
542     return bufferPos - bufferStart;
543   }
544 
545   /**
546    * Retrieves a subset of data in the buffer. The returned array is not backed by the original
547    * buffer array.
548    *
549    * @param offset the position (relative to the buffer start position) to start at.
550    * @param length the number of bytes to retrieve.
551    */
getData(int offset, int length)552   public byte[] getData(int offset, int length) {
553     if (length == 0) {
554       return WireFormatNano.EMPTY_BYTES;
555     }
556     byte[] copy = new byte[length];
557     int start = bufferStart + offset;
558     System.arraycopy(buffer, start, copy, 0, length);
559     return copy;
560   }
561 
562   /**
563    * Rewind to previous position. Cannot go forward.
564    */
rewindToPosition(int position)565   public void rewindToPosition(int position) {
566     if (position > bufferPos - bufferStart) {
567       throw new IllegalArgumentException(
568               "Position " + position + " is beyond current " + (bufferPos - bufferStart));
569     }
570     if (position < 0) {
571       throw new IllegalArgumentException("Bad position " + position);
572     }
573     bufferPos = bufferStart + position;
574   }
575 
576   /**
577    * Read one byte from the input.
578    *
579    * @throws InvalidProtocolBufferNanoException The end of the stream or the current
580    *                                        limit was reached.
581    */
readRawByte()582   public byte readRawByte() throws IOException {
583     if (bufferPos == bufferSize) {
584       throw InvalidProtocolBufferNanoException.truncatedMessage();
585     }
586     return buffer[bufferPos++];
587   }
588 
589   /**
590    * Read a fixed size of bytes from the input.
591    *
592    * @throws InvalidProtocolBufferNanoException The end of the stream or the current
593    *                                        limit was reached.
594    */
readRawBytes(final int size)595   public byte[] readRawBytes(final int size) throws IOException {
596     if (size < 0) {
597       throw InvalidProtocolBufferNanoException.negativeSize();
598     }
599 
600     if (bufferPos + size > currentLimit) {
601       // Read to the end of the stream anyway.
602       skipRawBytes(currentLimit - bufferPos);
603       // Then fail.
604       throw InvalidProtocolBufferNanoException.truncatedMessage();
605     }
606 
607     if (size <= bufferSize - bufferPos) {
608       // We have all the bytes we need already.
609       final byte[] bytes = new byte[size];
610       System.arraycopy(buffer, bufferPos, bytes, 0, size);
611       bufferPos += size;
612       return bytes;
613     } else {
614       throw InvalidProtocolBufferNanoException.truncatedMessage();
615     }
616   }
617 
618   /**
619    * Reads and discards {@code size} bytes.
620    *
621    * @throws InvalidProtocolBufferNanoException The end of the stream or the current
622    *                                        limit was reached.
623    */
skipRawBytes(final int size)624   public void skipRawBytes(final int size) throws IOException {
625     if (size < 0) {
626       throw InvalidProtocolBufferNanoException.negativeSize();
627     }
628 
629     if (bufferPos + size > currentLimit) {
630       // Read to the end of the stream anyway.
631       skipRawBytes(currentLimit - bufferPos);
632       // Then fail.
633       throw InvalidProtocolBufferNanoException.truncatedMessage();
634     }
635 
636     if (size <= bufferSize - bufferPos) {
637       // We have all the bytes we need already.
638       bufferPos += size;
639     } else {
640       throw InvalidProtocolBufferNanoException.truncatedMessage();
641     }
642   }
643 
644   // Read a primitive type.
readPrimitiveField(int type)645   Object readPrimitiveField(int type) throws IOException {
646     switch (type) {
647       case InternalNano.TYPE_DOUBLE:
648           return readDouble();
649       case InternalNano.TYPE_FLOAT:
650           return readFloat();
651       case InternalNano.TYPE_INT64:
652           return readInt64();
653       case InternalNano.TYPE_UINT64:
654           return readUInt64();
655       case InternalNano.TYPE_INT32:
656           return readInt32();
657       case InternalNano.TYPE_FIXED64:
658           return readFixed64();
659       case InternalNano.TYPE_FIXED32:
660           return readFixed32();
661       case InternalNano.TYPE_BOOL:
662           return readBool();
663       case InternalNano.TYPE_STRING:
664           return readString();
665       case InternalNano.TYPE_BYTES:
666           return readBytes();
667       case InternalNano.TYPE_UINT32:
668           return readUInt32();
669       case InternalNano.TYPE_ENUM:
670           return readEnum();
671       case InternalNano.TYPE_SFIXED32:
672           return readSFixed32();
673       case InternalNano.TYPE_SFIXED64:
674           return readSFixed64();
675       case InternalNano.TYPE_SINT32:
676           return readSInt32();
677       case InternalNano.TYPE_SINT64:
678           return readSInt64();
679       default:
680           throw new IllegalArgumentException("Unknown type " + type);
681     }
682   }
683 }
684