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