/**
  * Skips a set amount of bytes such that reading can be stopped and started again later if not
  * enough bytes are available.
  *
  * <p>Returns {@link #READ_RESULT_CONTINUE} if all bytes have been skipped. Reset {@link
  * #bytesState} to {@code 0} before calling to indicate a new set of bytes should be skipped.
  *
  * @param inputStream The input stream from which bytes should be skipped
  * @param totalBytes The total size of bytes to be skipped
  * @return One of the {@code RESULT_*} flags defined in this class
  */
 private int skipBytesInternal(NonBlockingInputStream inputStream, int totalBytes) {
   if (bytesState >= totalBytes) {
     return READ_RESULT_CONTINUE;
   }
   int remainingBytes = totalBytes - bytesState;
   int additionalBytesRead = inputStream.skip(remainingBytes);
   return updateBytesState(additionalBytesRead, totalBytes);
 }
 /**
  * Reads a set amount of bytes into a {@link ByteBuffer} such that reading can be stopped and
  * started again later if not enough bytes are available.
  *
  * <p>Returns {@link #READ_RESULT_CONTINUE} if all bytes have been read. Reset {@link #bytesState}
  * to {@code 0} before calling to indicate a new set of bytes should be read.
  *
  * @param inputStream The input stream from which bytes should be read
  * @param byteBuffer The {@link ByteBuffer} into which bytes should be read
  * @param totalBytes The total size of bytes to be read
  * @return One of the {@code RESULT_*} flags defined in this class
  */
 private int readBytesInternal(
     NonBlockingInputStream inputStream, ByteBuffer byteBuffer, int totalBytes) {
   if (bytesState == STATE_BEGIN_READING && totalBytes > byteBuffer.capacity()) {
     throw new IllegalArgumentException("Byte buffer not large enough");
   }
   if (bytesState >= totalBytes) {
     return READ_RESULT_CONTINUE;
   }
   int remainingBytes = totalBytes - bytesState;
   int additionalBytesRead = inputStream.read(byteBuffer, remainingBytes);
   return updateBytesState(additionalBytesRead, totalBytes);
 }
 /**
  * Reads a set amount of bytes into a {@code byte[]} such that reading can be stopped and started
  * again later if not enough bytes are available.
  *
  * <p>Returns {@link #READ_RESULT_CONTINUE} if all bytes have been read. Reset {@link #bytesState}
  * to {@code 0} before calling to indicate a new set of bytes should be read.
  *
  * @param inputStream The input stream from which bytes should be read
  * @param byteArray The {@code byte[]} into which bytes should be read
  * @param totalBytes The total size of bytes to be read
  * @return One of the {@code RESULT_*} flags defined in this class
  */
 private int readBytesInternal(
     NonBlockingInputStream inputStream, byte[] byteArray, int totalBytes) {
   if (bytesState == STATE_BEGIN_READING && totalBytes > byteArray.length) {
     throw new IllegalArgumentException("Byte array not large enough");
   }
   if (bytesState >= totalBytes) {
     return READ_RESULT_CONTINUE;
   }
   int remainingBytes = totalBytes - bytesState;
   int additionalBytesRead = inputStream.read(byteArray, bytesState, remainingBytes);
   return updateBytesState(additionalBytesRead, totalBytes);
 }
  @Override
  public int read(NonBlockingInputStream inputStream) throws ParserException {
    Assertions.checkState(eventHandler != null);
    while (true) {
      while (!masterElementsStack.isEmpty()
          && bytesRead >= masterElementsStack.peek().elementEndOffsetBytes) {
        eventHandler.onMasterElementEnd(masterElementsStack.pop().elementId);
        return READ_RESULT_CONTINUE;
      }

      if (state == STATE_BEGIN_READING) {
        int idResult = readElementId(inputStream);
        if (idResult != READ_RESULT_CONTINUE) {
          return idResult;
        }
        int sizeResult = readElementContentSize(inputStream);
        if (sizeResult != READ_RESULT_CONTINUE) {
          return sizeResult;
        }
        state = STATE_READ_CONTENTS;
        bytesState = 0;
      }

      int type = eventHandler.getElementType(elementId);
      switch (type) {
        case TYPE_MASTER:
          int masterHeaderSize = (int) (bytesRead - elementOffset); // Header size is 12 bytes max.
          masterElementsStack.add(new MasterElement(elementId, bytesRead + elementContentSize));
          eventHandler.onMasterElementStart(
              elementId, elementOffset, masterHeaderSize, elementContentSize);
          prepareForNextElement();
          return READ_RESULT_CONTINUE;
        case TYPE_UNSIGNED_INT:
          if (elementContentSize > MAX_INTEGER_ELEMENT_SIZE_BYTES) {
            throw new IllegalStateException("Invalid integer size " + elementContentSize);
          }
          int intResult = readBytesInternal(inputStream, tempByteArray, (int) elementContentSize);
          if (intResult != READ_RESULT_CONTINUE) {
            return intResult;
          }
          long intValue = getTempByteArrayValue((int) elementContentSize, false);
          eventHandler.onIntegerElement(elementId, intValue);
          prepareForNextElement();
          return READ_RESULT_CONTINUE;
        case TYPE_FLOAT:
          if (elementContentSize != VALID_FLOAT32_ELEMENT_SIZE_BYTES
              && elementContentSize != VALID_FLOAT64_ELEMENT_SIZE_BYTES) {
            throw new IllegalStateException("Invalid float size " + elementContentSize);
          }
          int floatResult = readBytesInternal(inputStream, tempByteArray, (int) elementContentSize);
          if (floatResult != READ_RESULT_CONTINUE) {
            return floatResult;
          }
          long valueBits = getTempByteArrayValue((int) elementContentSize, false);
          double floatValue;
          if (elementContentSize == VALID_FLOAT32_ELEMENT_SIZE_BYTES) {
            floatValue = Float.intBitsToFloat((int) valueBits);
          } else {
            floatValue = Double.longBitsToDouble(valueBits);
          }
          eventHandler.onFloatElement(elementId, floatValue);
          prepareForNextElement();
          return READ_RESULT_CONTINUE;
        case TYPE_STRING:
          if (elementContentSize > Integer.MAX_VALUE) {
            throw new IllegalStateException(
                "String element size " + elementContentSize + " is larger than MAX_INT");
          }
          if (stringBytes == null) {
            stringBytes = new byte[(int) elementContentSize];
          }
          int stringResult = readBytesInternal(inputStream, stringBytes, (int) elementContentSize);
          if (stringResult != READ_RESULT_CONTINUE) {
            return stringResult;
          }
          String stringValue = new String(stringBytes, Charset.forName(C.UTF8_NAME));
          stringBytes = null;
          eventHandler.onStringElement(elementId, stringValue);
          prepareForNextElement();
          return READ_RESULT_CONTINUE;
        case TYPE_BINARY:
          if (elementContentSize > Integer.MAX_VALUE) {
            throw new IllegalStateException(
                "Binary element size " + elementContentSize + " is larger than MAX_INT");
          }
          if (inputStream.getAvailableByteCount() < elementContentSize) {
            return READ_RESULT_NEED_MORE_DATA;
          }
          int binaryHeaderSize = (int) (bytesRead - elementOffset); // Header size is 12 bytes max.
          boolean consumed =
              eventHandler.onBinaryElement(
                  elementId,
                  elementOffset,
                  binaryHeaderSize,
                  (int) elementContentSize,
                  inputStream);
          if (consumed) {
            long expectedBytesRead = elementOffset + binaryHeaderSize + elementContentSize;
            if (expectedBytesRead != bytesRead) {
              throw new IllegalStateException(
                  "Incorrect total bytes read. Expected "
                      + expectedBytesRead
                      + " but actually "
                      + bytesRead);
            }
            prepareForNextElement();
          }
          return READ_RESULT_CONTINUE;
        case TYPE_UNKNOWN:
          if (elementContentSize > Integer.MAX_VALUE) {
            throw new IllegalStateException(
                "Unknown element size " + elementContentSize + " is larger than MAX_INT");
          }
          int skipResult = skipBytesInternal(inputStream, (int) elementContentSize);
          if (skipResult != READ_RESULT_CONTINUE) {
            return skipResult;
          }
          prepareForNextElement();
          break;
        default:
          throw new IllegalStateException("Invalid element type " + type);
      }
    }
  }