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  /**
080   * {@inheritDoc}
081   *
082   * <p>{@link LittleEndianDataInputStream} ignores negative values of {@code n} (returning zero).
083   * Otherwise, it skips the full requested number of bytes unless it reaches end of file or throws
084   * an exception.
085   */
086  @Override
087  public int skipBytes(int n) throws IOException {
088    return (int) ByteStreams.skipUpTo(in(), n);
089  }
090
091  @CanIgnoreReturnValue // to skip a byte
092  @Override
093  public int readUnsignedByte() throws IOException {
094    int b1 = in().read();
095    if (b1 < 0) {
096      throw new EOFException();
097    }
098
099    return b1;
100  }
101
102  /**
103   * Reads an unsigned {@code short} as specified by {@link DataInputStream#readUnsignedShort()},
104   * except using little-endian byte order.
105   *
106   * @return the next two bytes of the input stream, interpreted as an unsigned 16-bit integer in
107   *     little-endian byte order
108   * @throws IOException if an I/O error occurs
109   */
110  @CanIgnoreReturnValue // to skip some bytes
111  @Override
112  public int readUnsignedShort() throws IOException {
113    byte b1 = readAndCheckByte();
114    byte b2 = readAndCheckByte();
115
116    return Ints.fromBytes((byte) 0, (byte) 0, b2, b1);
117  }
118
119  /**
120   * Reads an integer as specified by {@link DataInputStream#readInt()}, except using little-endian
121   * byte order.
122   *
123   * @return the next four bytes of the input stream, interpreted as an {@code int} in little-endian
124   *     byte order
125   * @throws IOException if an I/O error occurs
126   */
127  @CanIgnoreReturnValue // to skip some bytes
128  @Override
129  public int readInt() throws IOException {
130    byte b1 = readAndCheckByte();
131    byte b2 = readAndCheckByte();
132    byte b3 = readAndCheckByte();
133    byte b4 = readAndCheckByte();
134
135    return Ints.fromBytes(b4, b3, b2, b1);
136  }
137
138  /**
139   * Reads a {@code long} as specified by {@link DataInputStream#readLong()}, except using
140   * little-endian byte order.
141   *
142   * @return the next eight bytes of the input stream, interpreted as a {@code long} in
143   *     little-endian byte order
144   * @throws IOException if an I/O error occurs
145   */
146  @CanIgnoreReturnValue // to skip some bytes
147  @Override
148  public long readLong() throws IOException {
149    byte b1 = readAndCheckByte();
150    byte b2 = readAndCheckByte();
151    byte b3 = readAndCheckByte();
152    byte b4 = readAndCheckByte();
153    byte b5 = readAndCheckByte();
154    byte b6 = readAndCheckByte();
155    byte b7 = readAndCheckByte();
156    byte b8 = readAndCheckByte();
157
158    return Longs.fromBytes(b8, b7, b6, b5, b4, b3, b2, b1);
159  }
160
161  /**
162   * Reads a {@code float} as specified by {@link DataInputStream#readFloat()}, except using
163   * little-endian byte order.
164   *
165   * @return the next four bytes of the input stream, interpreted as a {@code float} in
166   *     little-endian byte order
167   * @throws IOException if an I/O error occurs
168   */
169  @CanIgnoreReturnValue // to skip some bytes
170  @Override
171  public float readFloat() throws IOException {
172    return Float.intBitsToFloat(readInt());
173  }
174
175  /**
176   * Reads a {@code double} as specified by {@link DataInputStream#readDouble()}, except using
177   * little-endian byte order.
178   *
179   * @return the next eight bytes of the input stream, interpreted as a {@code double} in
180   *     little-endian byte order
181   * @throws IOException if an I/O error occurs
182   */
183  @CanIgnoreReturnValue // to skip some bytes
184  @Override
185  public double readDouble() throws IOException {
186    return Double.longBitsToDouble(readLong());
187  }
188
189  @CanIgnoreReturnValue // to skip a field
190  @Override
191  public String readUTF() throws IOException {
192    return new DataInputStream(in()).readUTF();
193  }
194
195  /**
196   * Reads a {@code short} as specified by {@link DataInputStream#readShort()}, except using
197   * little-endian byte order.
198   *
199   * @return the next two bytes of the input stream, interpreted as a {@code short} in little-endian
200   *     byte order.
201   * @throws IOException if an I/O error occurs.
202   */
203  @CanIgnoreReturnValue // to skip some bytes
204  @Override
205  public short readShort() throws IOException {
206    return (short) readUnsignedShort();
207  }
208
209  /**
210   * Reads a char as specified by {@link DataInputStream#readChar()}, except using little-endian
211   * byte order.
212   *
213   * @return the next two bytes of the input stream, interpreted as a {@code char} in little-endian
214   *     byte order
215   * @throws IOException if an I/O error occurs
216   */
217  @CanIgnoreReturnValue // to skip some bytes
218  @Override
219  public char readChar() throws IOException {
220    return (char) readUnsignedShort();
221  }
222
223  @CanIgnoreReturnValue // to skip a byte
224  @Override
225  public byte readByte() throws IOException {
226    return (byte) readUnsignedByte();
227  }
228
229  @CanIgnoreReturnValue // to skip a byte
230  @Override
231  public boolean readBoolean() throws IOException {
232    return readUnsignedByte() != 0;
233  }
234
235  /**
236   * Reads a byte from the input stream checking that the end of file (EOF) has not been
237   * encountered.
238   *
239   * @return byte read from input
240   * @throws IOException if an error is encountered while reading
241   * @throws EOFException if the end of file (EOF) is encountered.
242   */
243  private byte readAndCheckByte() throws IOException {
244    int b1 = in().read();
245
246    if (b1 == -1) {
247      throw new EOFException();
248    }
249
250    return (byte) b1;
251  }
252}