001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.io;
018
019import static org.apache.commons.io.IOUtils.EOF;
020
021import java.io.EOFException;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025
026/**
027 * Helps with reading and writing primitive numeric types ({@code short},
028 * {@code int}, {@code long}, {@code float}, and {@code double}) that are
029 * encoded in little endian using two's complement or unsigned representations.
030 * <p>
031 * Different computer architectures have different conventions for
032 * byte ordering. In "Little Endian" architectures (e.g. X86),
033 * the low-order byte is stored in memory at the lowest address, and
034 * subsequent bytes at higher addresses. In "Big Endian" architectures
035 * (e.g. Motorola 680X0), the situation is reversed.
036 * Most methods and classes throughout Java &mdash; e.g. {@code DataInputStream} and
037 * {@code Double.longBitsToDouble()} &mdash; assume data is laid out
038 * in big endian order with the most significant byte first.
039 * The methods in this class read and write data in little endian order,
040 * generally by reversing the bytes and then using the
041 * regular Java methods to convert the swapped bytes to a primitive type.
042 * </p>
043 * <p>
044 * Provenance: Excalibur
045 * </p>
046 *
047 * @see org.apache.commons.io.input.SwappedDataInputStream
048 */
049public class EndianUtils {
050
051    /**
052     * Reads the next byte from the input stream.
053     * @param input  the stream
054     * @return the byte
055     * @throws IOException if the end of file is reached
056     */
057    private static int read(final InputStream input) throws IOException {
058        final int value = input.read();
059        if (EOF == value) {
060            throw new EOFException("Unexpected EOF reached");
061        }
062        return value;
063    }
064
065    /**
066     * Reads a little endian {@code double} value from a byte array at a given offset.
067     *
068     * @param data source byte array
069     * @param offset starting offset in the byte array
070     * @return the value read
071     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes
072     */
073    public static double readSwappedDouble(final byte[] data, final int offset) {
074        return Double.longBitsToDouble(readSwappedLong(data, offset));
075    }
076
077    /**
078     * Reads a little endian {@code double} value from an InputStream.
079     *
080     * @param input source InputStream
081     * @return the value just read
082     * @throws IOException in case of an I/O problem
083     */
084    public static double readSwappedDouble(final InputStream input) throws IOException {
085        return Double.longBitsToDouble(readSwappedLong(input));
086    }
087
088    /**
089     * Reads a little endian {@code float} value from a byte array at a given offset.
090     *
091     * @param data source byte array
092     * @param offset starting offset in the byte array
093     * @return the value read
094     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
095     */
096    public static float readSwappedFloat(final byte[] data, final int offset) {
097        return Float.intBitsToFloat(readSwappedInteger(data, offset));
098    }
099
100    /**
101     * Reads a little endian {@code float} value from an InputStream.
102     *
103     * @param input source InputStream
104     * @return the value just read
105     * @throws IOException in case of an I/O problem
106     */
107    public static float readSwappedFloat(final InputStream input) throws IOException {
108        return Float.intBitsToFloat(readSwappedInteger(input));
109    }
110
111    /**
112     * Reads a little endian {@code int} value from a byte array at a given offset.
113     *
114     * @param data source byte array
115     * @param offset starting offset in the byte array
116     * @return the value read
117     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
118     */
119    public static int readSwappedInteger(final byte[] data, final int offset) {
120        validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
121        return ((data[offset + 0] & 0xff) << 0) +
122            ((data[offset + 1] & 0xff) << 8) +
123            ((data[offset + 2] & 0xff) << 16) +
124            ((data[offset + 3] & 0xff) << 24);
125    }
126
127    /**
128     * Reads a little endian {@code int} value from an InputStream.
129     *
130     * @param input source InputStream
131     * @return the value just read
132     * @throws IOException in case of an I/O problem
133     */
134    public static int readSwappedInteger(final InputStream input) throws IOException {
135        final int value1 = read(input);
136        final int value2 = read(input);
137        final int value3 = read(input);
138        final int value4 = read(input);
139        return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16) + ((value4 & 0xff) << 24);
140    }
141
142    /**
143     * Reads a little endian {@code long} value from a byte array at a given offset.
144     *
145     * @param data source byte array
146     * @param offset starting offset in the byte array
147     * @return the value read
148     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes
149     */
150    public static long readSwappedLong(final byte[] data, final int offset) {
151        validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE);
152        final long low = readSwappedInteger(data, offset);
153        final long high = readSwappedInteger(data, offset + 4);
154        return (high << 32) + (0xffffffffL & low);
155    }
156
157    /**
158     * Reads a little endian {@code long} value from an InputStream.
159     *
160     * @param input source InputStream
161     * @return the value just read
162     * @throws IOException in case of an I/O problem
163     */
164    public static long readSwappedLong(final InputStream input) throws IOException {
165        final byte[] bytes = new byte[8];
166        for (int i = 0; i < 8; i++) {
167            bytes[i] = (byte) read(input);
168        }
169        return readSwappedLong(bytes, 0);
170    }
171
172    /**
173     * Reads a little endian {@code short} value from a byte array at a given offset.
174     *
175     * @param data source byte array
176     * @param offset starting offset in the byte array
177     * @return the value read
178     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes
179     */
180    public static short readSwappedShort(final byte[] data, final int offset) {
181        validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
182        return (short) (((data[offset + 0] & 0xff) << 0) + ((data[offset + 1] & 0xff) << 8));
183    }
184
185    /**
186     * Reads a little endian {@code short} value from an InputStream.
187     *
188     * @param input source InputStream
189     * @return the value just read
190     * @throws IOException in case of an I/O problem
191     */
192    public static short readSwappedShort(final InputStream input) throws IOException {
193        return (short) (((read(input) & 0xff) << 0) + ((read(input) & 0xff) << 8));
194    }
195
196    /**
197     * Reads a little endian unsigned integer (32-bit) value from a byte array at a given
198     * offset.
199     *
200     * @param data source byte array
201     * @param offset starting offset in the byte array
202     * @return the value read
203     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
204    */
205    public static long readSwappedUnsignedInteger(final byte[] data, final int offset) {
206        validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
207        final long low = ((data[offset + 0] & 0xff) << 0) +
208                     ((data[offset + 1] & 0xff) << 8) +
209                     ((data[offset + 2] & 0xff) << 16);
210        final long high = data[offset + 3] & 0xff;
211        return (high << 24) + (0xffffffffL & low);
212    }
213
214    /**
215     * Reads a little endian unsigned integer (32-bit) from an InputStream.
216     *
217     * @param input source InputStream
218     * @return the value just read
219     * @throws IOException in case of an I/O problem
220     */
221    public static long readSwappedUnsignedInteger(final InputStream input) throws IOException {
222        final int value1 = read(input);
223        final int value2 = read(input);
224        final int value3 = read(input);
225        final int value4 = read(input);
226        final long low = ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16);
227        final long high = value4 & 0xff;
228        return (high << 24) + (0xffffffffL & low);
229    }
230
231    /**
232     * Reads an unsigned short (16-bit) value from a byte array in little endian order at a given
233     * offset.
234     *
235     * @param data source byte array
236     * @param offset starting offset in the byte array
237     * @return the value read
238     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes
239     */
240    public static int readSwappedUnsignedShort(final byte[] data, final int offset) {
241        validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
242        return ((data[offset + 0] & 0xff) << 0) + ((data[offset + 1] & 0xff) << 8);
243    }
244
245    /**
246     * Reads an unsigned short (16-bit) from an InputStream in little endian order.
247     *
248     * @param input source InputStream
249     * @return the value just read
250     * @throws IOException in case of an I/O problem
251     */
252    public static int readSwappedUnsignedShort(final InputStream input) throws IOException {
253        final int value1 = read(input);
254        final int value2 = read(input);
255
256        return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8);
257    }
258
259    /**
260     * Converts a {@code double} value from big endian to little endian
261     * and vice versa. That is, it converts the {@code double} to bytes,
262     * reverses the bytes, and then reinterprets those bytes as a new {@code double}.
263     * This can be useful if you have a number that was read from the
264     * underlying source in the wrong endianness.
265     *
266     * @param value value to convert
267     * @return the converted value
268     */
269    public static double swapDouble(final double value) {
270        return Double.longBitsToDouble(swapLong(Double.doubleToLongBits(value)));
271    }
272
273    /**
274     * Converts a {@code float} value from big endian to little endian and vice versa.
275     *
276     * @param value value to convert
277     * @return the converted value
278     */
279    public static float swapFloat(final float value) {
280        return Float.intBitsToFloat(swapInteger(Float.floatToIntBits(value)));
281    }
282
283    /**
284     * Converts an {@code int} value from big endian to little endian and vice versa.
285     *
286     * @param value value to convert
287     * @return the converted value
288     */
289    public static int swapInteger(final int value) {
290        return
291            ((value >> 0 & 0xff) << 24) +
292            ((value >> 8 & 0xff) << 16) +
293            ((value >> 16 & 0xff) << 8) +
294            ((value >> 24 & 0xff) << 0);
295    }
296
297    /**
298     * Converts a {@code long} value from big endian to little endian and vice versa.
299     *
300     * @param value value to convert
301     * @return the converted value
302     */
303    public static long swapLong(final long value) {
304        return
305            ((value >> 0 & 0xff) << 56) +
306            ((value >> 8 & 0xff) << 48) +
307            ((value >> 16 & 0xff) << 40) +
308            ((value >> 24 & 0xff) << 32) +
309            ((value >> 32 & 0xff) << 24) +
310            ((value >> 40 & 0xff) << 16) +
311            ((value >> 48 & 0xff) << 8) +
312            ((value >> 56 & 0xff) << 0);
313    }
314
315    /**
316     * Converts a {@code short} value from big endian to little endian and vice versa.
317     *
318     * @param value value to convert
319     * @return the converted value
320     */
321    public static short swapShort(final short value) {
322        return (short) (((value >> 0 & 0xff) << 8) +
323            ((value >> 8 & 0xff) << 0));
324    }
325
326    /**
327     * Validates if the provided byte array has enough data.
328     *
329     * @param data the input byte array
330     * @param offset the input offset
331     * @param byteNeeded the needed number of bytes
332     * @throws IllegalArgumentException if the byte array does not have enough data
333     */
334    private static void validateByteArrayOffset(final byte[] data, final int offset, final int byteNeeded) {
335        if (data.length < offset + byteNeeded) {
336            throw new IllegalArgumentException("Data only has " + data.length + "bytes, needed " + (offset + byteNeeded) + "bytes.");
337        }
338    }
339
340    /**
341     * Writes the 8 bytes of a {@code double} to a byte array at a given offset in little endian order.
342     *
343     * @param data target byte array
344     * @param offset starting offset in the byte array
345     * @param value value to write
346     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes
347     */
348    public static void writeSwappedDouble(final byte[] data, final int offset, final double value) {
349        writeSwappedLong(data, offset, Double.doubleToLongBits(value));
350    }
351
352    /**
353     * Writes the 8 bytes of a {@code double} to an output stream in little endian order.
354     *
355     * @param output target OutputStream
356     * @param value value to write
357     * @throws IOException in case of an I/O problem
358     */
359    public static void writeSwappedDouble(final OutputStream output, final double value) throws IOException {
360        writeSwappedLong(output, Double.doubleToLongBits(value));
361    }
362
363    /**
364     * Writes the 4 bytes of a {@code float} to a byte array at a given offset in little endian order.
365     *
366     * @param data target byte array
367     * @param offset starting offset in the byte array
368     * @param value value to write
369     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
370     */
371    public static void writeSwappedFloat(final byte[] data, final int offset, final float value) {
372        writeSwappedInteger(data, offset, Float.floatToIntBits(value));
373    }
374
375    /**
376     * Writes the 4 bytes of a {@code float} to an output stream in little endian order.
377     *
378     * @param output target OutputStream
379     * @param value value to write
380     * @throws IOException in case of an I/O problem
381    */
382    public static void writeSwappedFloat(final OutputStream output, final float value) throws IOException {
383        writeSwappedInteger(output, Float.floatToIntBits(value));
384    }
385
386    /**
387     * Writes the 4 bytes of an {@code int} to a byte array at a given offset in little endian order.
388     *
389     * @param data target byte array
390     * @param offset starting offset in the byte array
391     * @param value value to write
392     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
393     */
394    public static void writeSwappedInteger(final byte[] data, final int offset, final int value) {
395        validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
396        data[offset + 0] = (byte) (value >> 0 & 0xff);
397        data[offset + 1] = (byte) (value >> 8 & 0xff);
398        data[offset + 2] = (byte) (value >> 16 & 0xff);
399        data[offset + 3] = (byte) (value >> 24 & 0xff);
400    }
401
402    /**
403     * Writes the 4 bytes of an {@code int} to an output stream in little endian order.
404     *
405     * @param output target OutputStream
406     * @param value value to write
407     * @throws IOException in case of an I/O problem
408     */
409    public static void writeSwappedInteger(final OutputStream output, final int value) throws IOException {
410        output.write((byte) (value >> 0 & 0xff));
411        output.write((byte) (value >> 8 & 0xff));
412        output.write((byte) (value >> 16 & 0xff));
413        output.write((byte) (value >> 24 & 0xff));
414    }
415
416    /**
417     * Writes the 8 bytes of a {@code long} to a byte array at a given offset in little endian order.
418     *
419     * @param data target byte array
420     * @param offset starting offset in the byte array
421     * @param value value to write
422     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes
423     */
424    public static void writeSwappedLong(final byte[] data, final int offset, final long value) {
425        validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE);
426        data[offset + 0] = (byte) (value >> 0 & 0xff);
427        data[offset + 1] = (byte) (value >> 8 & 0xff);
428        data[offset + 2] = (byte) (value >> 16 & 0xff);
429        data[offset + 3] = (byte) (value >> 24 & 0xff);
430        data[offset + 4] = (byte) (value >> 32 & 0xff);
431        data[offset + 5] = (byte) (value >> 40 & 0xff);
432        data[offset + 6] = (byte) (value >> 48 & 0xff);
433        data[offset + 7] = (byte) (value >> 56 & 0xff);
434    }
435
436    /**
437     * Writes the 8 bytes of a {@code long} to an output stream in little endian order.
438     *
439     * @param output target OutputStream
440     * @param value value to write
441     * @throws IOException in case of an I/O problem
442     */
443    public static void writeSwappedLong(final OutputStream output, final long value) throws IOException {
444        output.write((byte) (value >> 0 & 0xff));
445        output.write((byte) (value >> 8 & 0xff));
446        output.write((byte) (value >> 16 & 0xff));
447        output.write((byte) (value >> 24 & 0xff));
448        output.write((byte) (value >> 32 & 0xff));
449        output.write((byte) (value >> 40 & 0xff));
450        output.write((byte) (value >> 48 & 0xff));
451        output.write((byte) (value >> 56 & 0xff));
452    }
453
454    /**
455     * Writes the 2 bytes of a {@code short} to a byte array at a given offset in little endian order.
456     *
457     * @param data target byte array
458     * @param offset starting offset in the byte array
459     * @param value value to write
460     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes
461     */
462    public static void writeSwappedShort(final byte[] data, final int offset, final short value) {
463        validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
464        data[offset + 0] = (byte) (value >> 0 & 0xff);
465        data[offset + 1] = (byte) (value >> 8 & 0xff);
466    }
467
468    /**
469     * Writes the 2 bytes of a {@code short} to an output stream using little endian encoding.
470     *
471     * @param output target OutputStream
472     * @param value value to write
473     * @throws IOException in case of an I/O problem
474     */
475    public static void writeSwappedShort(final OutputStream output, final short value) throws IOException {
476        output.write((byte) (value >> 0 & 0xff));
477        output.write((byte) (value >> 8 & 0xff));
478    }
479
480    /**
481     * Instances should NOT be constructed in standard programming.
482     *
483     * @deprecated TODO Make private in 3.0.
484     */
485    @Deprecated
486    public EndianUtils() {
487        // empty
488    }
489}