gt:         DerValue[]      retval = new DerValue[max];
context: /*
   * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package sun.security.util;
  
  import java.io.InputStream;
  import java.io.IOException;
  import java.math.BigInteger;
  import java.nio.charset.Charset;
  import java.util.Date;
  import java.util.Vector;
  
  import static java.nio.charset.StandardCharsets.*;
  
  /**
   * A DER input stream, used for parsing ASN.1 DER-encoded data such as
   * that found in X.509 certificates.  DER is a subset of BER/1, which has
   * the advantage that it allows only a single encoding of primitive data.
   * (High level data such as dates still support many encodings.)  That is,
   * it uses the "Definite" Encoding Rules (DER) not the "Basic" ones (BER).
   *
   * <P>Note that, like BER/1, DER streams are streams of explicitly
   * tagged data values.  Accordingly, this programming interface does
   * not expose any variant of the java.io.InputStream interface, since
   * that kind of input stream holds untagged data values and using that
   * I/O model could prevent correct parsing of the DER data.
   *
   * <P>At this time, this class supports only a subset of the types of DER
   * data encodings which are defined.  That subset is sufficient for parsing
   * most X.509 certificates.
   *
   *
   * @author David Brownell
   * @author Amit Kapoor
   * @author Hemma Prafullchandra
   */
  
  public class DerInputStream {
  
      /*
       * This version only supports fully buffered DER.  This is easy to
       * work with, though if large objects are manipulated DER becomes
       * awkward to deal with.  That's where BER is useful, since BER
       * handles streaming data relatively well.
       */
      DerInputBuffer      buffer;
  
      /** The DER tag of the value; one of the tag_ constants. */
      public byte         tag;
  
      /**
       * Create a DER input stream from a data buffer.  The buffer is not
       * copied, it is shared.  Accordingly, the buffer should be treated
       * as read-only.
       *
       * @param data the buffer from which to create the string (CONSUMED)
       */
      public DerInputStream(byte[] data) throws IOException {
          init(data, 0, data.length, true);
      }
  
      /**
       * Create a DER input stream from part of a data buffer with
       * additional arg to control whether DER checks are enforced.
       * The buffer is not copied, it is shared.  Accordingly, the
       * buffer should be treated as read-only.
       *
       * @param data the buffer from which to create the string (CONSUMED)
       * @param offset the first index of <em>data</em> which will
       *          be read as DER input in the new stream
       * @param len how long a chunk of the buffer to use,
       *          starting at "offset"
       * @param allowBER whether to allow constructed indefinite-length
       *          encoding as well as tolerate leading 0s
       */
      public DerInputStream(byte[] data, int offset, int len,
          boolean allowBER) throws IOException {
          init(data, offset, len, allowBER);
      }
  
      /**
       * Create a DER input stream from part of a data buffer.
       * The buffer is not copied, it is shared.  Accordingly, the
       * buffer should be treated as read-only.
       *
       * @param data the buffer from which to create the string (CONSUMED)
       * @param offset the first index of <em>data</em> which will
       *          be read as DER input in the new stream
       * @param len how long a chunk of the buffer to use,
       *          starting at "offset"
       */
      public DerInputStream(byte[] data, int offset, int len) throws IOException {
          init(data, offset, len, true);
      }
  
      /*
       * private helper routine
       */
      private void init(byte[] data, int offset, int len, boolean allowBER) throws IOException {
          if ((offset+2 > data.length) || (offset+len > data.length)) {
              throw new IOException("Encoding bytes too short");
          }
          // check for indefinite length encoding
          if (DerIndefLenConverter.isIndefinite(data[offset+1])) {
              if (!allowBER) {
                  throw new IOException("Indefinite length BER encoding found");
              } else {
                  byte[] inData = new byte[len];
                  System.arraycopy(data, offset, inData, 0, len);
  
                  DerIndefLenConverter derIn = new DerIndefLenConverter();
                  byte[] result = derIn.convertBytes(inData);
                  if (result == null) {
                      throw new IOException("not all indef len BER resolved");
                  } else {
                      buffer = new DerInputBuffer(result, allowBER);
                  }
              }
          } else {
              buffer = new DerInputBuffer(data, offset, len, allowBER);
          }
          buffer.mark(Integer.MAX_VALUE);
      }
  
      DerInputStream(DerInputBuffer buf) {
          buffer = buf;
          buffer.mark(Integer.MAX_VALUE);
      }
  
      /**
       * Creates a new DER input stream from part of this input stream.
       *
       * @param len how long a chunk of the current input stream to use,
       *          starting at the current position.
       * @param do_skip true if the existing data in the input stream should
       *          be skipped.  If this value is false, the next data read
       *          on this stream and the newly created stream will be the
       *          same.
       */
      public DerInputStream subStream(int len, boolean do_skip)
      throws IOException {
          DerInputBuffer newbuf = buffer.dup();
  
          newbuf.truncate(len);
          if (do_skip) {
              buffer.skip(len);
          }
          return new DerInputStream(newbuf);
      }
  
      /**
       * Return what has been written to this DerInputStream
       * as a byte array. Useful for debugging.
       */
      public byte[] toByteArray() {
          return buffer.toByteArray();
      }
  
      /*
       * PRIMITIVES -- these are "universal" ASN.1 simple types.
       *
       *  INTEGER, ENUMERATED, BIT STRING, OCTET STRING, NULL
       *  OBJECT IDENTIFIER, SEQUENCE (OF), SET (OF)
       *  UTF8String, PrintableString, T61String, IA5String, UTCTime,
       *  GeneralizedTime, BMPString.
       * Note: UniversalString not supported till encoder is available.
       */
  
      /**
       * Get an integer from the input stream as an integer.
       *
       * @return the integer held in this DER input stream.
       */
      public int getInteger() throws IOException {
          if (buffer.read() != DerValue.tag_Integer) {
              throw new IOException("DER input, Integer tag error");
          }
          return buffer.getInteger(getDefiniteLength(buffer));
      }
  
      /**
       * Get a integer from the input stream as a BigInteger object.
       *
       * @return the integer held in this DER input stream.
       */
      public BigInteger getBigInteger() throws IOException {
          if (buffer.read() != DerValue.tag_Integer) {
              throw new IOException("DER input, Integer tag error");
          }
          return buffer.getBigInteger(getDefiniteLength(buffer), false);
      }
  
      /**
       * Returns an ASN.1 INTEGER value as a positive BigInteger.
       * This is just to deal with implementations that incorrectly encode
       * some values as negative.
       *
       * @return the integer held in this DER value as a BigInteger.
       */
      public BigInteger getPositiveBigInteger() throws IOException {
          if (buffer.read() != DerValue.tag_Integer) {
              throw new IOException("DER input, Integer tag error");
          }
          return buffer.getBigInteger(getDefiniteLength(buffer), true);
      }
  
      /**
       * Get an enumerated from the input stream.
       *
       * @return the integer held in this DER input stream.
       */
      public int getEnumerated() throws IOException {
          if (buffer.read() != DerValue.tag_Enumerated) {
              throw new IOException("DER input, Enumerated tag error");
          }
          return buffer.getInteger(getDefiniteLength(buffer));
      }
  
      /**
       * Get a bit string from the input stream. Padded bits (if any)
       * will be stripped off before the bit string is returned.
       */
      public byte[] getBitString() throws IOException {
          if (buffer.read() != DerValue.tag_BitString)
              throw new IOException("DER input not an bit string");
  
          return buffer.getBitString(getDefiniteLength(buffer));
      }
  
      /**
       * Get a bit string from the input stream.  The bit string need
       * not be byte-aligned.
       */
      public BitArray getUnalignedBitString() throws IOException {
          if (buffer.read() != DerValue.tag_BitString) {
              throw new IOException("DER input not a bit string");
          }
  
          int length = getDefiniteLength(buffer);
  
          if (length == 0) {
              return new BitArray(0);
          }
  
          /*
           * First byte = number of excess bits in the last octet of the
           * representation.
           */
          length--;
          int excessBits = buffer.read();
          if (excessBits < 0) {
              throw new IOException("Unused bits of bit string invalid");
          }
          int validBits = length*8 - excessBits;
          if (validBits < 0) {
              throw new IOException("Valid bits of bit string invalid");
          }
  
          byte[] repn = new byte[length];
  
          if ((length != 0) && (buffer.read(repn) != length)) {
              throw new IOException("Short read of DER bit string");
          }
  
          return new BitArray(validBits, repn);
      }
  
      /**
       * Returns an ASN.1 OCTET STRING from the input stream.
       */
      public byte[] getOctetString() throws IOException {
          if (buffer.read() != DerValue.tag_OctetString)
              throw new IOException("DER input not an octet string");
  
          int length = getDefiniteLength(buffer);
          byte[] retval = new byte[length];
          if ((length != 0) && (buffer.read(retval) != length))
              throw new IOException("Short read of DER octet string");
  
          return retval;
      }
  
      /**
       * Returns the asked number of bytes from the input stream.
       */
      public void getBytes(byte[] val) throws IOException {
          if ((val.length != 0) && (buffer.read(val) != val.length)) {
              throw new IOException("Short read of DER octet string");
          }
      }
  
      /**
       * Reads an encoded null value from the input stream.
       */
      public void getNull() throws IOException {
          if (buffer.read() != DerValue.tag_Null || buffer.read() != 0)
              throw new IOException("getNull, bad data");
      }
  
      /**
       * Reads an X.200 style Object Identifier from the stream.
       */
      public ObjectIdentifier getOID() throws IOException {
          return new ObjectIdentifier(this);
      }
  
      /**
       * Return a sequence of encoded entities.  ASN.1 sequences are
       * ordered, and they are often used, like a "struct" in C or C++,
       * to group data values.  They may have optional or context
       * specific values.
       *
       * @param startLen guess about how long the sequence will be
       *          (used to initialize an auto-growing data structure)
       * @return array of the values in the sequence
       */
      public DerValue[] getSequence(int startLen) throws IOException {
          tag = (byte)buffer.read();
          if (tag != DerValue.tag_Sequence)
              throw new IOException("Sequence tag error");
          return readVector(startLen);
      }
  
      /**
       * Return a set of encoded entities.  ASN.1 sets are unordered,
       * though DER may specify an order for some kinds of sets (such
       * as the attributes in an X.500 relative distinguished name)
       * to facilitate binary comparisons of encoded values.
       *
       * @param startLen guess about how large the set will be
       *          (used to initialize an auto-growing data structure)
       * @return array of the values in the sequence
       */
      public DerValue[] getSet(int startLen) throws IOException {
          tag = (byte)buffer.read();
          if (tag != DerValue.tag_Set)
              throw new IOException("Set tag error");
          return readVector(startLen);
      }
  
      /**
       * Return a set of encoded entities.  ASN.1 sets are unordered,
       * though DER may specify an order for some kinds of sets (such
       * as the attributes in an X.500 relative distinguished name)
       * to facilitate binary comparisons of encoded values.
       *
       * @param startLen guess about how large the set will be
       *          (used to initialize an auto-growing data structure)
       * @param implicit if true tag is assumed implicit.
       * @return array of the values in the sequence
       */
      public DerValue[] getSet(int startLen, boolean implicit)
          throws IOException {
          tag = (byte)buffer.read();
          if (!implicit) {
              if (tag != DerValue.tag_Set) {
                  throw new IOException("Set tag error");
              }
          }
          return (readVector(startLen));
      }
  
      /*
       * Read a "vector" of values ... set or sequence have the
       * same encoding, except for the initial tag, so both use
       * this same helper routine.
       */
      protected DerValue[] readVector(int startLen) throws IOException {
          DerInputStream  newstr;
  
          byte lenByte = (byte)buffer.read();
          int len = getLength(lenByte, buffer);
  
          if (len == -1) {
             // indefinite length encoding found
             buffer = new DerInputBuffer(
                     DerIndefLenConverter.convertStream(buffer, lenByte, tag),
                     buffer.allowBER);
  
             if (tag != buffer.read())
                  throw new IOException("Indefinite length encoding" +
                          " not supported");
             len = DerInputStream.getDefiniteLength(buffer);
          }
  
          if (len == 0)
              // return empty array instead of null, which should be
              // used only for missing optionals
              return new DerValue[0];
  
          /*
           * Create a temporary stream from which to read the data,
           * unless it's not really needed.
           */
          if (buffer.available() == len)
              newstr = this;
          else
              newstr = subStream(len, true);
  
          /*
           * Pull values out of the stream.
           */
          Vector<DerValue> vec = new Vector<>(startLen);
          DerValue value;
  
          do {
              value = new DerValue(newstr.buffer, buffer.allowBER);
              vec.addElement(value);
          } while (newstr.available() > 0);
  
          if (newstr.available() != 0)
              throw new IOException("Extra data at end of vector");
  
          /*
           * Now stick them into the array we're returning.
           */
          int             i, max = vec.size();
  