001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.io;
016
017import static java.util.Objects.requireNonNull;
018
019import com.google.common.annotations.GwtIncompatible;
020import com.google.common.annotations.J2ktIncompatible;
021import com.google.common.base.Preconditions;
022import com.google.common.primitives.Ints;
023import com.google.common.primitives.Longs;
024import com.google.errorprone.annotations.CanIgnoreReturnValue;
025import com.google.errorprone.annotations.DoNotCall;
026import java.io.DataInput;
027import java.io.DataInputStream;
028import java.io.EOFException;
029import java.io.FilterInputStream;
030import java.io.IOException;
031import java.io.InputStream;
032
033/**
034 * An implementation of {@link DataInput} that uses little-endian byte ordering for reading {@code
035 * short}, {@code int}, {@code float}, {@code double}, and {@code long} values.
036 *
037 * <p><b>Note:</b> This class intentionally violates the specification of its supertype {@code
038 * DataInput}, which explicitly requires big-endian byte order.
039 *
040 * @author Chris Nokleberg
041 * @author Keith Bottner
042 * @since 8.0
043 */
044@J2ktIncompatible
045@GwtIncompatible
046public final class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
047
048  /**
049   * Creates a {@code LittleEndianDataInputStream} that wraps the given stream.
050   *
051   * @param in the stream to delegate to
052   */
053  public LittleEndianDataInputStream(InputStream in) {
054    super(Preconditions.checkNotNull(in));
055  }
056
057  private InputStream in() {
058    return requireNonNull(in); // guaranteed safe by the constructor
059  }
060
061  /** This method will throw an {@link UnsupportedOperationException}. */
062  @CanIgnoreReturnValue // to skip a line
063  @Override
064  @DoNotCall("Always throws UnsupportedOperationException")
065  public String readLine() {
066    throw new UnsupportedOperationException("readLine is not supported");
067  }
068
069  @Override
070  public void readFully(byte[] b) throws IOException {
071    ByteStreams.readFully(this, b);
072  }
073
074  @Override
075  public void readFully(byte[] b, int off, int len) throws IOException {
076    ByteStreams.readFully(this, b, off, len);
077  }
078
079  @Override
080  public int skipBytes(int n) throws IOException {
081    return (int) in().skip(n);
082  }
083
084  @CanIgnoreReturnValue // to skip a byte
085  @Override
086  public int readUnsignedByte() throws IOException {
087    int b1 = in().read();
088    if (b1 < 0) {
089      throw new EOFException();
090    }
091
092    return b1;
093  }
094
095  /**
096   * Reads an unsigned {@code short} as specified by {@link DataInputStream#readUnsignedShort()},
097   * except using little-endian byte order.
098   *
099   * @return the next two bytes of the input stream, interpreted as an unsigned 16-bit integer in
100   *     little-endian byte order
101   * @throws IOException if an I/O error occurs
102   */
103  @CanIgnoreReturnValue // to skip some bytes
104  @Override
105  public int readUnsignedShort() throws IOException {
106    byte b1 = readAndCheckByte();
107    byte b2 = readAndCheckByte();
108
109    return Ints.fromBytes((byte) 0, (byte) 0, b2, b1);
110  }
111
112  /**
113   * Reads an integer as specified by {@link DataInputStream#readInt()}, except using little-endian
114   * byte order.
115   *
116   * @return the next four bytes of the input stream, interpreted as an {@code int} in little-endian
117   *     byte order
118   * @throws IOException if an I/O error occurs
119   */
120  @CanIgnoreReturnValue // to skip some bytes
121  @Override
122  public int readInt() throws IOException {
123    byte b1 = readAndCheckByte();
124    byte b2 = readAndCheckByte();
125    byte b3 = readAndCheckByte();
126    byte b4 = readAndCheckByte();
127
128    return Ints.fromBytes(b4, b3, b2, b1);
129  }
130
131  /**
132   * Reads a {@code long} as specified by {@link DataInputStream#readLong()}, except using
133   * little-endian byte order.
134   *
135   * @return the next eight bytes of the input stream, interpreted as a {@code long} in
136   *     little-endian byte order
137   * @throws IOException if an I/O error occurs
138   */
139  @CanIgnoreReturnValue // to skip some bytes
140  @Override
141  public long readLong() throws IOException {
142    byte b1 = readAndCheckByte();
143    byte b2 = readAndCheckByte();
144    byte b3 = readAndCheckByte();
145    byte b4 = readAndCheckByte();
146    byte b5 = readAndCheckByte();
147    byte b6 = readAndCheckByte();
148    byte b7 = readAndCheckByte();
149    byte b8 = readAndCheckByte();
150
151    return Longs.fromBytes(b8, b7, b6, b5, b4, b3, b2, b1);
152  }
153
154  /**
155   * Reads a {@code float} as specified by {@link DataInputStream#readFloat()}, except using
156   * little-endian byte order.
157   *
158   * @return the next four bytes of the input stream, interpreted as a {@code float} in
159   *     little-endian byte order
160   * @throws IOException if an I/O error occurs
161   */
162  @CanIgnoreReturnValue // to skip some bytes
163  @Override
164  public float readFloat() throws IOException {
165    return Float.intBitsToFloat(readInt());
166  }
167
168  /**
169   * Reads a {@code double} as specified by {@link DataInputStream#readDouble()}, except using
170   * little-endian byte order.
171   *
172   * @return the next eight bytes of the input stream, interpreted as a {@code double} in
173   *     little-endian byte order
174   * @throws IOException if an I/O error occurs
175   */
176  @CanIgnoreReturnValue // to skip some bytes
177  @Override
178  public double readDouble() throws IOException {
179    return Double.longBitsToDouble(readLong());
180  }
181
182  @CanIgnoreReturnValue // to skip a field
183  @Override
184  public String readUTF() throws IOException {
185    return new DataInputStream(in()).readUTF();
186  }
187
188  /**
189   * Reads a {@code short} as specified by {@link DataInputStream#readShort()}, except using
190   * little-endian byte order.
191   *
192   * @return the next two bytes of the input stream, interpreted as a {@code short} in little-endian
193   *     byte order.
194   * @throws IOException if an I/O error occurs.
195   */
196  @CanIgnoreReturnValue // to skip some bytes
197  @Override
198  public short readShort() throws IOException {
199    return (short) readUnsignedShort();
200  }
201
202  /**
203   * Reads a char as specified by {@link DataInputStream#readChar()}, except using little-endian
204   * byte order.
205   *
206   * @return the next two bytes of the input stream, interpreted as a {@code char} in little-endian
207   *     byte order
208   * @throws IOException if an I/O error occurs
209   */
210  @CanIgnoreReturnValue // to skip some bytes
211  @Override
212  public char readChar() throws IOException {
213    return (char) readUnsignedShort();
214  }
215
216  @CanIgnoreReturnValue // to skip a byte
217  @Override
218  public byte readByte() throws IOException {
219    return (byte) readUnsignedByte();
220  }
221
222  @CanIgnoreReturnValue // to skip a byte
223  @Override
224  public boolean readBoolean() throws IOException {
225    return readUnsignedByte() != 0;
226  }
227
228  /**
229   * Reads a byte from the input stream checking that the end of file (EOF) has not been
230   * encountered.
231   *
232   * @return byte read from input
233   * @throws IOException if an error is encountered while reading
234   * @throws EOFException if the end of file (EOF) is encountered.
235   */
236  private byte readAndCheckByte() throws IOException {
237    int b1 = in().read();
238
239    if (b1 == -1) {
240      throw new EOFException();
241    }
242
243    return (byte) b1;
244  }
245}