2 * Copyright 2002-2015 Drew Noakes
4 * More information about this project is available at:
6 * https://drewnoakes.com/code/exif/
7 * https://github.com/drewnoakes/metadata-extractor
10 package tim.prune.jpeg.drew;
12 import java.io.IOException;
16 * Provides methods to read specific values from a byte array,
17 * with a consistent, checked exception structure for issues.
19 * @author Drew Noakes https://drewnoakes.com
21 public class ByteArrayReader
23 private final byte[] _buffer;
24 private boolean _isMotorolaByteOrder = true;
26 public ByteArrayReader(byte[] buffer)
29 throw new NullPointerException();
34 public void setMotorolaByteOrder(boolean motorolaByteOrder)
36 _isMotorolaByteOrder = motorolaByteOrder;
39 public long getLength()
41 return _buffer.length;
44 protected byte getByte(int index)
46 return _buffer[index];
49 protected void validateIndex(int index, int bytesRequested) throws ExifException
51 if (!isValidIndex(index, bytesRequested))
52 throw new ExifException("Invalid index " + index);
55 private boolean isValidIndex(int index, int bytesRequested)
57 return bytesRequested >= 0
59 && ((long)index + (long)bytesRequested) <= (long)_buffer.length;
62 public byte[] getBytes(int index, int count) throws ExifException
64 validateIndex(index, count);
66 byte[] bytes = new byte[count];
67 System.arraycopy(_buffer, index, bytes, 0, count);
72 * Returns an unsigned 8-bit int calculated from one byte of data at the specified index.
74 * @param index position within the data buffer to read byte
75 * @return the 8 bit int value, between 0 and 255
76 * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
78 public short getUInt8(int index) throws ExifException
80 validateIndex(index, 1);
82 return (short) (getByte(index) & 0xFF);
86 * Returns a signed 8-bit int calculated from one byte of data at the specified index.
88 * @param index position within the data buffer to read byte
89 * @return the 8 bit int value, between 0x00 and 0xFF
90 * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
92 public byte getInt8(int index) throws ExifException
94 validateIndex(index, 1);
96 return getByte(index);
100 * Returns an unsigned 16-bit int calculated from two bytes of data at the specified index.
102 * @param index position within the data buffer to read first byte
103 * @return the 16 bit int value, between 0x0000 and 0xFFFF
104 * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
106 public int getUInt16(int index) throws ExifException
108 validateIndex(index, 2);
110 if (_isMotorolaByteOrder) {
111 // Motorola - MSB first
112 return (getByte(index ) << 8 & 0xFF00) |
113 (getByte(index + 1) & 0xFF);
115 // Intel ordering - LSB first
116 return (getByte(index + 1) << 8 & 0xFF00) |
117 (getByte(index ) & 0xFF);
122 * Returns a signed 16-bit int calculated from two bytes of data at the specified index (MSB, LSB).
124 * @param index position within the data buffer to read first byte
125 * @return the 16 bit int value, between 0x0000 and 0xFFFF
126 * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
128 public short getInt16(int index) throws ExifException
130 validateIndex(index, 2);
132 if (_isMotorolaByteOrder) {
133 // Motorola - MSB first
134 return (short) (((short)getByte(index ) << 8 & (short)0xFF00) |
135 ((short)getByte(index + 1) & (short)0xFF));
137 // Intel ordering - LSB first
138 return (short) (((short)getByte(index + 1) << 8 & (short)0xFF00) |
139 ((short)getByte(index ) & (short)0xFF));
144 * Get a 32-bit unsigned integer from the buffer, returning it as a long.
146 * @param index position within the data buffer to read first byte
147 * @return the unsigned 32-bit int value as a long, between 0x00000000 and 0xFFFFFFFF
148 * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
150 public long getUInt32(int index) throws ExifException
152 validateIndex(index, 4);
154 if (_isMotorolaByteOrder) {
155 // Motorola - MSB first (big endian)
156 return (((long)getByte(index )) << 24 & 0xFF000000L) |
157 (((long)getByte(index + 1)) << 16 & 0xFF0000L) |
158 (((long)getByte(index + 2)) << 8 & 0xFF00L) |
159 (((long)getByte(index + 3)) & 0xFFL);
161 // Intel ordering - LSB first (little endian)
162 return (((long)getByte(index + 3)) << 24 & 0xFF000000L) |
163 (((long)getByte(index + 2)) << 16 & 0xFF0000L) |
164 (((long)getByte(index + 1)) << 8 & 0xFF00L) |
165 (((long)getByte(index )) & 0xFFL);
170 * Returns a signed 32-bit integer from four bytes of data at the specified index the buffer.
172 * @param index position within the data buffer to read first byte
173 * @return the signed 32 bit int value, between 0x00000000 and 0xFFFFFFFF
174 * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
176 public int getInt32(int index) throws ExifException
178 validateIndex(index, 4);
180 if (_isMotorolaByteOrder) {
181 // Motorola - MSB first (big endian)
182 return (getByte(index ) << 24 & 0xFF000000) |
183 (getByte(index + 1) << 16 & 0xFF0000) |
184 (getByte(index + 2) << 8 & 0xFF00) |
185 (getByte(index + 3) & 0xFF);
187 // Intel ordering - LSB first (little endian)
188 return (getByte(index + 3) << 24 & 0xFF000000) |
189 (getByte(index + 2) << 16 & 0xFF0000) |
190 (getByte(index + 1) << 8 & 0xFF00) |
191 (getByte(index ) & 0xFF);
196 * Creates a String from the _data buffer starting at the specified index,
197 * and ending where <code>byte=='\0'</code> or where <code>length==maxLength</code>.
199 * @param index The index within the buffer at which to start reading the string.
200 * @param maxLengthBytes The maximum number of bytes to read. If a zero-byte is not reached within this limit,
201 * reading will stop and the string will be truncated to this length.
202 * @return The read string.
203 * @throws IOException The buffer does not contain enough bytes to satisfy this request.
205 public String getNullTerminatedString(int index, int maxLengthBytes) throws ExifException
207 // NOTE currently only really suited to single-byte character strings
209 byte[] bytes = getBytes(index, maxLengthBytes);
211 // Count the number of non-null bytes
213 while (length < bytes.length && bytes[length] != '\0')
216 return new String(bytes, 0, length);
219 public String getString(int index, int bytesRequested) throws ExifException
221 // TODO: validate index
222 return new String(getBytes(index, bytesRequested));