content: /*
   * Copyright 2002-2015 Drew Noakes
   *
   * More information about this project is available at:
   *
   *    https://drewnoakes.com/code/exif/
   *    https://github.com/drewnoakes/metadata-extractor
   */
  
  package tim.prune.jpeg.drew;
  
  
  /**
   * Provides methods to read specific values from a byte array,
   * with a consistent, checked exception structure for issues.
   *
   * @author Drew Noakes https://drewnoakes.com
   */
  public class ByteArrayReader
  {
  	private final byte[] _buffer;
  	private boolean _isMotorolaByteOrder = true;
  
  	public ByteArrayReader(byte[] buffer)
  	{
  		if (buffer == null)
  			throw new NullPointerException();
  
  		_buffer = buffer;
  	}
  
  	public void setMotorolaByteOrder(boolean motorolaByteOrder)
  	{
  		_isMotorolaByteOrder = motorolaByteOrder;
  	}
  
  	public long getLength()
  	{
  		return _buffer.length;
  	}
  
  	protected byte getByte(int index)
  	{
  		return _buffer[index];
  	}
  
  	protected void validateIndex(int index, int bytesRequested) throws ExifException
  	{
  		if (!isValidIndex(index, bytesRequested))
  			throw new ExifException("Invalid index " + index);
  	}
  
  	private boolean isValidIndex(int index, int bytesRequested)
  	{
  		return bytesRequested >= 0
  			&& index >= 0
  			&& ((long)index + (long)bytesRequested) <= (long)_buffer.length;
  	}
  
  	public byte[] getBytes(int index, int count) throws ExifException
  	{
  		validateIndex(index, count);
  
  		byte[] bytes = new byte[count];
  		System.arraycopy(_buffer, index, bytes, 0, count);
  		return bytes;
  	}
  
  	/**
  	 * Returns an unsigned 8-bit int calculated from one byte of data at the specified index.
  	 *
  	 * @param index position within the data buffer to read byte
  	 * @return the 8 bit int value, between 0 and 255
  	 * @throws ExifException the buffer does not contain enough bytes to service the request, or index is negative
  	 */
  	public short getUInt8(int index) throws ExifException
  	{
  		validateIndex(index, 1);
  
  		return (short) (getByte(index) & 0xFF);
  	}
  
  	/**
  	 * Returns a signed 8-bit int calculated from one byte of data at the specified index.
  	 *
  	 * @param index position within the data buffer to read byte
  	 * @return the 8 bit int value, between 0x00 and 0xFF
  	 * @throws ExifException the buffer does not contain enough bytes to service the request, or index is negative
  	 */
  	public byte getInt8(int index) throws ExifException
  	{
  		validateIndex(index, 1);
  
  		return getByte(index);
  	}
  
  	/**
  	 * Returns an unsigned 16-bit int calculated from two bytes of data at the specified index.
  	 *
  	 * @param index position within the data buffer to read first byte
  	 * @return the 16 bit int value, between 0x0000 and 0xFFFF
  	 * @throws ExifException the buffer does not contain enough bytes to service the request, or index is negative
  	 */
  	public int getUInt16(int index) throws ExifException
  	{
  		validateIndex(index, 2);
  
  		if (_isMotorolaByteOrder) {
  			// Motorola - MSB first
  			return (getByte(index    ) << 8 & 0xFF00) |
  				   (getByte(index + 1)      & 0xFF);
  		} else {
  			// Intel ordering - LSB first
  			return (getByte(index + 1) << 8 & 0xFF00) |
  				   (getByte(index    )      & 0xFF);
  		}
  	}
  
  	/**
  	 * Returns a signed 16-bit int calculated from two bytes of data at the specified index (MSB, LSB).
  	 *
  	 * @param index position within the data buffer to read first byte
  	 * @return the 16 bit int value, between 0x0000 and 0xFFFF
  	 * @throws ExifException the buffer does not contain enough bytes to service the request, or index is negative
  	 */
  	public short getInt16(int index) throws ExifException
  	{
  		validateIndex(index, 2);
  
  		if (_isMotorolaByteOrder) {
  			// Motorola - MSB first
  			return (short) (((short)getByte(index    ) << 8 & (short)0xFF00) |
  			                ((short)getByte(index + 1)      & (short)0xFF));
  		} else {
  			// Intel ordering - LSB first
  			return (short) (((short)getByte(index + 1) << 8 & (short)0xFF00) |
  			                ((short)getByte(index    )      & (short)0xFF));
  		}
  	}
  
  	/**
  	 * Get a 32-bit unsigned integer from the buffer, returning it as a long.
  	 *
  	 * @param index position within the data buffer to read first byte
  	 * @return the unsigned 32-bit int value as a long, between 0x00000000 and 0xFFFFFFFF
  	 * @throws ExifException the buffer does not contain enough bytes to service the request, or index is negative
  	 */
  	public long getUInt32(int index) throws ExifException
  	{
  		validateIndex(index, 4);
  
  		if (_isMotorolaByteOrder) {
  			// Motorola - MSB first (big endian)
  			return (((long)getByte(index    )) << 24 & 0xFF000000L) |
  				   (((long)getByte(index + 1)) << 16 & 0xFF0000L) |
  				   (((long)getByte(index + 2)) << 8  & 0xFF00L) |
  				   (((long)getByte(index + 3))       & 0xFFL);
  		} else {
  			// Intel ordering - LSB first (little endian)
  			return (((long)getByte(index + 3)) << 24 & 0xFF000000L) |
  				   (((long)getByte(index + 2)) << 16 & 0xFF0000L) |
  				   (((long)getByte(index + 1)) << 8  & 0xFF00L) |
  				   (((long)getByte(index    ))       & 0xFFL);
  		}
  	}
  
  	/**
  	 * Returns a signed 32-bit integer from four bytes of data at the specified index the buffer.
  	 *
  	 * @param index position within the data buffer to read first byte
  	 * @return the signed 32 bit int value, between 0x00000000 and 0xFFFFFFFF
  	 * @throws ExifException the buffer does not contain enough bytes to service the request, or index is negative
  	 */
  	public int getInt32(int index) throws ExifException
  	{
  		validateIndex(index, 4);
  
  		if (_isMotorolaByteOrder) {
  			// Motorola - MSB first (big endian)
  			return (getByte(index    ) << 24 & 0xFF000000) |
  				   (getByte(index + 1) << 16 & 0xFF0000) |
  				   (getByte(index + 2) << 8  & 0xFF00) |
  				   (getByte(index + 3)       & 0xFF);
  		} else {
  			// Intel ordering - LSB first (little endian)
  			return (getByte(index + 3) << 24 & 0xFF000000) |
  				   (getByte(index + 2) << 16 & 0xFF0000) |
  				   (getByte(index + 1) << 8  & 0xFF00) |
  				   (getByte(index    )       & 0xFF);
  		}
  	}
  
  	/**
  	 * Creates a String from the _data buffer starting at the specified index,
  	 * and ending where <code>byte=='\0'</code> or where <code>length==maxLength</code>.
  	 *
  	 * @param index          The index within the buffer at which to start reading the string.
  	 * @param maxLengthBytes The maximum number of bytes to read.  If a zero-byte is not reached within this limit,
  	 *                       reading will stop and the string will be truncated to this length.
  	 * @return The read string.
  	 * @throws ExifException The buffer does not contain enough bytes to satisfy this request.
  	 */
  	public String getNullTerminatedString(int index, int maxLengthBytes) throws ExifException
  	{
  		// NOTE currently only really suited to single-byte character strings
  
  		byte[] bytes = getBytes(index, maxLengthBytes);
  
  		// Count the number of non-null bytes
  		int length = 0;
  		while (length < bytes.length && bytes[length] != '\0')
  			length++;
  
  		return new String(bytes, 0, length);
  	}
  
  	public String getString(int index, int bytesRequested) throws ExifException
  	{
  		// TODO: validate index
  		return new String(getBytes(index, bytesRequested));
  	}
  }
  