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 com.google.common.annotations.Beta;
018import com.google.common.annotations.GwtIncompatible;
019import com.google.common.base.Preconditions;
020import com.google.common.primitives.Longs;
021import java.io.DataOutput;
022import java.io.DataOutputStream;
023import java.io.FilterOutputStream;
024import java.io.IOException;
025import java.io.OutputStream;
026
027/**
028 * An implementation of {@link DataOutput} that uses little-endian byte ordering for writing {@code
029 * char}, {@code short}, {@code int}, {@code float}, {@code double}, and {@code long} values.
030 *
031 * <p><b>Note:</b> This class intentionally violates the specification of its supertype {@code
032 * DataOutput}, which explicitly requires big-endian byte order.
033 *
034 * @author Chris Nokleberg
035 * @author Keith Bottner
036 * @since 8.0
037 */
038@Beta
039@GwtIncompatible
040@ElementTypesAreNonnullByDefault
041public final class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput {
042
043  /**
044   * Creates a {@code LittleEndianDataOutputStream} that wraps the given stream.
045   *
046   * @param out the stream to delegate to
047   */
048  public LittleEndianDataOutputStream(OutputStream out) {
049    super(new DataOutputStream(Preconditions.checkNotNull(out)));
050  }
051
052  @Override
053  public void write(byte[] b, int off, int len) throws IOException {
054    // Override slow FilterOutputStream impl
055    out.write(b, off, len);
056  }
057
058  @Override
059  public void writeBoolean(boolean v) throws IOException {
060    ((DataOutputStream) out).writeBoolean(v);
061  }
062
063  @Override
064  public void writeByte(int v) throws IOException {
065    ((DataOutputStream) out).writeByte(v);
066  }
067
068  /**
069   * @deprecated The semantics of {@code writeBytes(String s)} are considered dangerous. Please use
070   *     {@link #writeUTF(String s)}, {@link #writeChars(String s)} or another write method instead.
071   */
072  @Deprecated
073  @Override
074  public void writeBytes(String s) throws IOException {
075    ((DataOutputStream) out).writeBytes(s);
076  }
077
078  /**
079   * Writes a char as specified by {@link DataOutputStream#writeChar(int)}, except using
080   * little-endian byte order.
081   *
082   * @throws IOException if an I/O error occurs
083   */
084  @Override
085  public void writeChar(int v) throws IOException {
086    writeShort(v);
087  }
088
089  /**
090   * Writes a {@code String} as specified by {@link DataOutputStream#writeChars(String)}, except
091   * each character is written using little-endian byte order.
092   *
093   * @throws IOException if an I/O error occurs
094   */
095  @Override
096  public void writeChars(String s) throws IOException {
097    for (int i = 0; i < s.length(); i++) {
098      writeChar(s.charAt(i));
099    }
100  }
101
102  /**
103   * Writes a {@code double} as specified by {@link DataOutputStream#writeDouble(double)}, except
104   * using little-endian byte order.
105   *
106   * @throws IOException if an I/O error occurs
107   */
108  @Override
109  public void writeDouble(double v) throws IOException {
110    writeLong(Double.doubleToLongBits(v));
111  }
112
113  /**
114   * Writes a {@code float} as specified by {@link DataOutputStream#writeFloat(float)}, except using
115   * little-endian byte order.
116   *
117   * @throws IOException if an I/O error occurs
118   */
119  @Override
120  public void writeFloat(float v) throws IOException {
121    writeInt(Float.floatToIntBits(v));
122  }
123
124  /**
125   * Writes an {@code int} as specified by {@link DataOutputStream#writeInt(int)}, except using
126   * little-endian byte order.
127   *
128   * @throws IOException if an I/O error occurs
129   */
130  @Override
131  public void writeInt(int v) throws IOException {
132    out.write(0xFF & v);
133    out.write(0xFF & (v >> 8));
134    out.write(0xFF & (v >> 16));
135    out.write(0xFF & (v >> 24));
136  }
137
138  /**
139   * Writes a {@code long} as specified by {@link DataOutputStream#writeLong(long)}, except using
140   * little-endian byte order.
141   *
142   * @throws IOException if an I/O error occurs
143   */
144  @Override
145  public void writeLong(long v) throws IOException {
146    byte[] bytes = Longs.toByteArray(Long.reverseBytes(v));
147    write(bytes, 0, bytes.length);
148  }
149
150  /**
151   * Writes a {@code short} as specified by {@link DataOutputStream#writeShort(int)}, except using
152   * little-endian byte order.
153   *
154   * @throws IOException if an I/O error occurs
155   */
156  @Override
157  public void writeShort(int v) throws IOException {
158    out.write(0xFF & v);
159    out.write(0xFF & (v >> 8));
160  }
161
162  @Override
163  public void writeUTF(String str) throws IOException {
164    ((DataOutputStream) out).writeUTF(str);
165  }
166
167  // Overriding close() because FilterOutputStream's close() method pre-JDK8 has bad behavior:
168  // it silently ignores any exception thrown by flush(). Instead, just close the delegate stream.
169  // It should flush itself if necessary.
170  @Override
171  public void close() throws IOException {
172    out.close();
173  }
174}