1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.core.impl.utils; 18 19 import java.io.ByteArrayInputStream; 20 import java.io.DataInput; 21 import java.io.DataInputStream; 22 import java.io.EOFException; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.nio.ByteOrder; 26 27 /** 28 * An input stream to parse EXIF data area, which can be written in either little or big endian 29 * order. 30 */ 31 // Note: This class is adapted from {@link androidx.exifinterface.media.ExifInterface} 32 final class ByteOrderedDataInputStream extends InputStream implements DataInput { 33 private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN; 34 private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN; 35 36 private final DataInputStream mDataInputStream; 37 private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN; 38 @SuppressWarnings("WeakerAccess") /* synthetic access */ 39 final int mLength; 40 @SuppressWarnings("WeakerAccess") /* synthetic access */ 41 int mPosition; 42 ByteOrderedDataInputStream(InputStream in)43 ByteOrderedDataInputStream(InputStream in) throws IOException { 44 this(in, ByteOrder.BIG_ENDIAN); 45 } 46 ByteOrderedDataInputStream(InputStream in, ByteOrder byteOrder)47 ByteOrderedDataInputStream(InputStream in, ByteOrder byteOrder) throws IOException { 48 mDataInputStream = new DataInputStream(in); 49 mLength = mDataInputStream.available(); 50 mPosition = 0; 51 // TODO (b/142218289): Need to handle case where input stream does not support mark 52 mDataInputStream.mark(mLength); 53 mByteOrder = byteOrder; 54 } 55 ByteOrderedDataInputStream(byte[] bytes)56 ByteOrderedDataInputStream(byte[] bytes) throws IOException { 57 this(new ByteArrayInputStream(bytes)); 58 } 59 setByteOrder(ByteOrder byteOrder)60 public void setByteOrder(ByteOrder byteOrder) { 61 mByteOrder = byteOrder; 62 } 63 seek(long byteCount)64 public void seek(long byteCount) throws IOException { 65 if (mPosition > byteCount) { 66 mPosition = 0; 67 mDataInputStream.reset(); 68 // TODO (b/142218289): Need to handle case where input stream does not support mark 69 mDataInputStream.mark(mLength); 70 } else { 71 byteCount -= mPosition; 72 } 73 74 if (skipBytes((int) byteCount) != (int) byteCount) { 75 throw new IOException("Couldn't seek up to the byteCount"); 76 } 77 } 78 peek()79 public int peek() { 80 return mPosition; 81 } 82 83 @Override available()84 public int available() throws IOException { 85 return mDataInputStream.available(); 86 } 87 88 @Override read()89 public int read() throws IOException { 90 ++mPosition; 91 return mDataInputStream.read(); 92 } 93 94 @Override read(byte[] b, int off, int len)95 public int read(byte[] b, int off, int len) throws IOException { 96 int bytesRead = mDataInputStream.read(b, off, len); 97 mPosition += bytesRead; 98 return bytesRead; 99 } 100 101 @Override readUnsignedByte()102 public int readUnsignedByte() throws IOException { 103 ++mPosition; 104 return mDataInputStream.readUnsignedByte(); 105 } 106 107 @Override readLine()108 public String readLine() { 109 throw new UnsupportedOperationException("readLine() not implemented."); 110 } 111 112 @Override readBoolean()113 public boolean readBoolean() throws IOException { 114 ++mPosition; 115 return mDataInputStream.readBoolean(); 116 } 117 118 @Override readChar()119 public char readChar() throws IOException { 120 mPosition += 2; 121 return mDataInputStream.readChar(); 122 } 123 124 @Override readUTF()125 public String readUTF() throws IOException { 126 mPosition += 2; 127 return mDataInputStream.readUTF(); 128 } 129 130 @Override readFully(byte[] buffer, int offset, int length)131 public void readFully(byte[] buffer, int offset, int length) throws IOException { 132 mPosition += length; 133 if (mPosition > mLength) { 134 throw new EOFException(); 135 } 136 if (mDataInputStream.read(buffer, offset, length) != length) { 137 throw new IOException("Couldn't read up to the length of buffer"); 138 } 139 } 140 141 @Override readFully(byte[] buffer)142 public void readFully(byte[] buffer) throws IOException { 143 mPosition += buffer.length; 144 if (mPosition > mLength) { 145 throw new EOFException(); 146 } 147 if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) { 148 throw new IOException("Couldn't read up to the length of buffer"); 149 } 150 } 151 152 @Override readByte()153 public byte readByte() throws IOException { 154 ++mPosition; 155 if (mPosition > mLength) { 156 throw new EOFException(); 157 } 158 int ch = mDataInputStream.read(); 159 if (ch < 0) { 160 throw new EOFException(); 161 } 162 return (byte) ch; 163 } 164 165 @Override readShort()166 public short readShort() throws IOException { 167 mPosition += 2; 168 if (mPosition > mLength) { 169 throw new EOFException(); 170 } 171 int ch1 = mDataInputStream.read(); 172 int ch2 = mDataInputStream.read(); 173 if ((ch1 | ch2) < 0) { 174 throw new EOFException(); 175 } 176 if (mByteOrder == LITTLE_ENDIAN) { 177 return (short) ((ch2 << 8) + ch1); 178 } else if (mByteOrder == BIG_ENDIAN) { 179 return (short) ((ch1 << 8) + ch2); 180 } 181 throw new IOException("Invalid byte order: " + mByteOrder); 182 } 183 184 @Override readInt()185 public int readInt() throws IOException { 186 mPosition += 4; 187 if (mPosition > mLength) { 188 throw new EOFException(); 189 } 190 int ch1 = mDataInputStream.read(); 191 int ch2 = mDataInputStream.read(); 192 int ch3 = mDataInputStream.read(); 193 int ch4 = mDataInputStream.read(); 194 if ((ch1 | ch2 | ch3 | ch4) < 0) { 195 throw new EOFException(); 196 } 197 if (mByteOrder == LITTLE_ENDIAN) { 198 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1); 199 } else if (mByteOrder == BIG_ENDIAN) { 200 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); 201 } 202 throw new IOException("Invalid byte order: " + mByteOrder); 203 } 204 205 @Override skipBytes(int byteCount)206 public int skipBytes(int byteCount) throws IOException { 207 int totalSkip = Math.min(byteCount, mLength - mPosition); 208 int skipped = 0; 209 while (skipped < totalSkip) { 210 skipped += mDataInputStream.skipBytes(totalSkip - skipped); 211 } 212 mPosition += skipped; 213 return skipped; 214 } 215 216 @Override readUnsignedShort()217 public int readUnsignedShort() throws IOException { 218 mPosition += 2; 219 if (mPosition > mLength) { 220 throw new EOFException(); 221 } 222 int ch1 = mDataInputStream.read(); 223 int ch2 = mDataInputStream.read(); 224 if ((ch1 | ch2) < 0) { 225 throw new EOFException(); 226 } 227 if (mByteOrder == LITTLE_ENDIAN) { 228 return ((ch2 << 8) + ch1); 229 } else if (mByteOrder == BIG_ENDIAN) { 230 return ((ch1 << 8) + ch2); 231 } 232 throw new IOException("Invalid byte order: " + mByteOrder); 233 } 234 readUnsignedInt()235 public long readUnsignedInt() throws IOException { 236 return readInt() & 0xffffffffL; 237 } 238 239 @Override readLong()240 public long readLong() throws IOException { 241 mPosition += 8; 242 if (mPosition > mLength) { 243 throw new EOFException(); 244 } 245 int ch1 = mDataInputStream.read(); 246 int ch2 = mDataInputStream.read(); 247 int ch3 = mDataInputStream.read(); 248 int ch4 = mDataInputStream.read(); 249 int ch5 = mDataInputStream.read(); 250 int ch6 = mDataInputStream.read(); 251 int ch7 = mDataInputStream.read(); 252 int ch8 = mDataInputStream.read(); 253 if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { 254 throw new EOFException(); 255 } 256 if (mByteOrder == LITTLE_ENDIAN) { 257 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40) 258 + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16) 259 + ((long) ch2 << 8) + (long) ch1); 260 } else if (mByteOrder == BIG_ENDIAN) { 261 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40) 262 + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16) 263 + ((long) ch7 << 8) + (long) ch8); 264 } 265 throw new IOException("Invalid byte order: " + mByteOrder); 266 } 267 268 @Override readFloat()269 public float readFloat() throws IOException { 270 return Float.intBitsToFloat(readInt()); 271 } 272 273 @Override readDouble()274 public double readDouble() throws IOException { 275 return Double.longBitsToDouble(readLong()); 276 } 277 278 @Override mark(int readlimit)279 public void mark(int readlimit) { 280 synchronized (mDataInputStream) { 281 mDataInputStream.mark(readlimit); 282 } 283 } 284 getLength()285 public int getLength() { 286 return mLength; 287 } 288 } 289