    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 *      https://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     *
    054     * @param input  the stream.
    055     * @return the byte.
    056     * @throws IOException if the end of file is reached.
    057     */
    058    private static int read(final InputStream input) throws IOException {
    059        final int value = input.read();
    060        if (EOF == value) {
    061            throw new EOFException("Unexpected EOF reached");
    062        }
    063        return value;
    064    }
    065
    066    /**
    067     * Reads a little-endian {@code double} value from a byte array at a given offset.
    068     *
    069     * @param data source byte array.
    070     * @param offset starting offset in the byte array.
    071     * @return the value read.
    072     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes.
    073     */
    074    public static double readSwappedDouble(final byte[] data, final int offset) {
    075        return Double.longBitsToDouble(readSwappedLong(data, offset));
    076    }
    077
    078    /**
    079     * Reads a little-endian {@code double} value from an InputStream.
    080     *
    081     * @param input source InputStream.
    082     * @return the value just read.
    083     * @throws IOException in case of an I/O problem.
    084     */
    085    public static double readSwappedDouble(final InputStream input) throws IOException {
    086        return Double.longBitsToDouble(readSwappedLong(input));
    087    }
    088
    089    /**
    090     * Reads a little-endian {@code float} value from a byte array at a given offset.
    091     *
    092     * @param data source byte array.
    093     * @param offset starting offset in the byte array.
    094     * @return the value read.
    095     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
    096     */
    097    public static float readSwappedFloat(final byte[] data, final int offset) {
    098        return Float.intBitsToFloat(readSwappedInteger(data, offset));
    099    }
    100
    101    /**
    102     * Reads a little-endian {@code float} value from an InputStream.
    103     *
    104     * @param input source InputStream.
    105     * @return the value just read.
    106     * @throws IOException in case of an I/O problem.
    107     */
    108    public static float readSwappedFloat(final InputStream input) throws IOException {
    109        return Float.intBitsToFloat(readSwappedInteger(input));
    110    }
    111
    112    /**
    113     * Reads a little-endian {@code int} value from a byte array at a given offset.
    114     *
    115     * @param data source byte array.
    116     * @param offset starting offset in the byte array.
    117     * @return the value read.
    118     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
    119     */
    120    public static int readSwappedInteger(final byte[] data, final int offset) {
    121        validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
    122        return
    123            ((data[offset + 0] & 0xff) << 0) +
    124            ((data[offset + 1] & 0xff) << 8) +
    125            ((data[offset + 2] & 0xff) << 16) +
    126            ((data[offset + 3] & 0xff) << 24);
    127    }
    128
    129    /**
    130     * Reads a little-endian {@code int} value from an InputStream.
    131     *
    132     * @param input source InputStream.
    133     * @return the value just read.
    134     * @throws IOException in case of an I/O problem.
    135     */
    136    public static int readSwappedInteger(final InputStream input) throws IOException {
    137        final int value1 = read(input);
    138        final int value2 = read(input);
    139        final int value3 = read(input);
    140        final int value4 = read(input);
    141        return
    142            ((value1 & 0xff) << 0) +
    143            ((value2 & 0xff) << 8) +
    144            ((value3 & 0xff) << 16) +
    145            ((value4 & 0xff) << 24);
    146    }
    147
    148    /**
    149     * Reads a little-endian {@code long} value from a byte array at a given offset.
    150     *
    151     * @param data source byte array.
    152     * @param offset starting offset in the byte array.
    153     * @return the value read.
    154     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes.
    155     */
    156    public static long readSwappedLong(final byte[] data, final int offset) {
    157        validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE);
    158        final long low = readSwappedInteger(data, offset);
    159        final long high = readSwappedInteger(data, offset + 4);
    160        return (high << 32) + (0xffffffffL & low);
    161    }
    162
    163    /**
    164     * Reads a little-endian {@code long} value from an InputStream.
    165     *
    166     * @param input source InputStream.
    167     * @return the value just read.
    168     * @throws IOException in case of an I/O problem.
    169     */
    170    public static long readSwappedLong(final InputStream input) throws IOException {
    171        final byte[] bytes = new byte[8];
    172        for (int i = 0; i < 8; i++) {
    173            bytes[i] = (byte) read(input);
    174        }
    175        return readSwappedLong(bytes, 0);
    176    }
    177
    178    /**
    179     * Reads a little-endian {@code short} value from a byte array at a given offset.
    180     *
    181     * @param data source byte array.
    182     * @param offset starting offset in the byte array.
    183     * @return the value read.
    184     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes.
    185     */
    186    public static short readSwappedShort(final byte[] data, final int offset) {
    187        validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
    188        return (short) (
    189            ((data[offset + 0] & 0xff) << 0) +
    190            ((data[offset + 1] & 0xff) << 8)
    191        );
    192    }
    193
    194    /**
    195     * Reads a little-endian {@code short} value from an InputStream.
    196     *
    197     * @param input source InputStream.
    198     * @return the value just read.
    199     * @throws IOException in case of an I/O problem.
    200     */
    201    public static short readSwappedShort(final InputStream input) throws IOException {
    202        return (short) (
    203            ((read(input) & 0xff) << 0) +
    204            ((read(input) & 0xff) << 8)
    205        );
    206    }
    207
    208    /**
    209     * Reads a little-endian unsigned integer (32-bit) value from a byte array at a given
    210     * offset.
    211     *
    212     * @param data source byte array.
    213     * @param offset starting offset in the byte array.
    214     * @return the value read.
    215     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
    216    */
    217    public static long readSwappedUnsignedInteger(final byte[] data, final int offset) {
    218        validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
    219        final long low = ((data[offset + 0] & 0xff) << 0) +
    220                         ((data[offset + 1] & 0xff) << 8) +
    221                         ((data[offset + 2] & 0xff) << 16);
    222        final long high = data[offset + 3] & 0xff;
    223        return (high << 24) + (0xffffffffL & low);
    224    }
    225
    226    /**
    227     * Reads a little-endian unsigned integer (32-bit) from an InputStream.
    228     *
    229     * @param input source InputStream.
    230     * @return the value just read.
    231     * @throws IOException in case of an I/O problem.
    232     */
    233    public static long readSwappedUnsignedInteger(final InputStream input) throws IOException {
    234        final int value1 = read(input);
    235        final int value2 = read(input);
    236        final int value3 = read(input);
    237        final int value4 = read(input);
    238        final long low = ((value1 & 0xff) << 0) +
    239                         ((value2 & 0xff) << 8) +
    240                         ((value3 & 0xff) << 16);
    241        final long high = value4 & 0xff;
    242        return (high << 24) + (0xffffffffL & low);
    243    }
    244
    245    /**
    246     * Reads an unsigned short (16-bit) value from a byte array in little-endian order at a given
    247     * offset.
    248     *
    249     * @param data source byte array.
    250     * @param offset starting offset in the byte array.
    251     * @return the value read.
    252     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes.
    253     */
    254    public static int readSwappedUnsignedShort(final byte[] data, final int offset) {
    255        validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
    256        return ((data[offset + 0] & 0xff) << 0) +
    257               ((data[offset + 1] & 0xff) << 8);
    258    }
    259
    260    /**
    261     * Reads an unsigned short (16-bit) from an InputStream in little-endian order.
    262     *
    263     * @param input source InputStream.
    264     * @return the value just read.
    265     * @throws IOException in case of an I/O problem.
    266     */
    267    public static int readSwappedUnsignedShort(final InputStream input) throws IOException {
    268        final int value1 = read(input);
    269        final int value2 = read(input);
    270
    271        return ((value1 & 0xff) << 0) +
    272               ((value2 & 0xff) << 8);
    273    }
    274
    275    /**
    276     * Converts a {@code double} value from big-endian to little-endian
    277     * and vice versa. That is, it converts the {@code double} to bytes,
    278     * reverses the bytes, and then reinterprets those bytes as a new {@code double}.
    279     * This can be useful if you have a number that was read from the
    280     * underlying source in the wrong endianness.
    281     *
    282     * @param value value to convert.
    283     * @return the converted value.
    284     */
    285    public static double swapDouble(final double value) {
    286        return Double.longBitsToDouble(swapLong(Double.doubleToLongBits(value)));
    287    }
    288
    289    /**
    290     * Converts a {@code float} value from big-endian to little-endian and vice versa.
    291     *
    292     * @param value value to convert.
    293     * @return the converted value.
    294     */
    295    public static float swapFloat(final float value) {
    296        return Float.intBitsToFloat(swapInteger(Float.floatToIntBits(value)));
    297    }
    298
    299    /**
    300     * Converts an {@code int} value from big-endian to little-endian and vice versa.
    301     *
    302     * @param value value to convert.
    303     * @return the converted value.
    304     */
    305    public static int swapInteger(final int value) {
    306        return
    307            ((value >> 0 & 0xff) << 24) +
    308            ((value >> 8 & 0xff) << 16) +
    309            ((value >> 16 & 0xff) << 8) +
    310            ((value >> 24 & 0xff) << 0);
    311    }
    312
    313    /**
    314     * Converts a {@code long} value from big-endian to little-endian and vice versa.
    315     *
    316     * @param value value to convert.
    317     * @return the converted value.
    318     */
    319    public static long swapLong(final long value) {
    320        return
    321            ((value >> 0 & 0xff) << 56) +
    322            ((value >> 8 & 0xff) << 48) +
    323            ((value >> 16 & 0xff) << 40) +
    324            ((value >> 24 & 0xff) << 32) +
    325            ((value >> 32 & 0xff) << 24) +
    326            ((value >> 40 & 0xff) << 16) +
    327            ((value >> 48 & 0xff) << 8) +
    328            ((value >> 56 & 0xff) << 0);
    329    }
    330
    331    /**
    332     * Converts a {@code short} value from big-endian to little-endian and vice versa.
    333     *
    334     * @param value value to convert.
    335     * @return the converted value.
    336     */
    337    public static short swapShort(final short value) {
    338        return (short) (
    339            ((value >> 0 & 0xff) << 8) +
    340            ((value >> 8 & 0xff) << 0)
    341        );
    342    }
    343
    344    /**
    345     * Validates if the provided byte array has enough data.
    346     *
    347     * @param data the input byte array.
    348     * @param offset the input offset.
    349     * @param byteNeeded the needed number of bytes.
    350     * @throws IllegalArgumentException if the byte array does not have enough data.
    351     */
    352    private static void validateByteArrayOffset(final byte[] data, final int offset, final int byteNeeded) {
    353        if (data.length < offset + byteNeeded) {
    354            throw new IllegalArgumentException("Data only has " + data.length + "bytes, needed " + (offset + byteNeeded) + "bytes.");
    355        }
    356    }
    357
    358    /**
    359     * Writes the 8 bytes of a {@code double} to a byte array at a given offset in little-endian order.
    360     *
    361     * @param data target byte array.
    362     * @param offset starting offset in the byte array.
    363     * @param value value to write.
    364     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes.
    365     */
    366    public static void writeSwappedDouble(final byte[] data, final int offset, final double value) {
    367        writeSwappedLong(data, offset, Double.doubleToLongBits(value));
    368    }
    369
    370    /**
    371     * Writes the 8 bytes of a {@code double} to an output stream in little-endian order.
    372     *
    373     * @param output target OutputStream.
    374     * @param value value to write.
    375     * @throws IOException in case of an I/O problem.
    376     */
    377    public static void writeSwappedDouble(final OutputStream output, final double value) throws IOException {
    378        writeSwappedLong(output, Double.doubleToLongBits(value));
    379    }
    380
    381    /**
    382     * Writes the 4 bytes of a {@code float} to a byte array at a given offset in little-endian order.
    383     *
    384     * @param data target byte array.
    385     * @param offset starting offset in the byte array.
    386     * @param value value to write.
    387     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
    388     */
    389    public static void writeSwappedFloat(final byte[] data, final int offset, final float value) {
    390        writeSwappedInteger(data, offset, Float.floatToIntBits(value));
    391    }
    392
    393    /**
    394     * Writes the 4 bytes of a {@code float} to an output stream in little-endian order.
    395     *
    396     * @param output target OutputStream.
    397     * @param value value to write.
    398     * @throws IOException in case of an I/O problem.
    399    */
    400    public static void writeSwappedFloat(final OutputStream output, final float value) throws IOException {
    401        writeSwappedInteger(output, Float.floatToIntBits(value));
    402    }
    403
    404    /**
    405     * Writes the 4 bytes of an {@code int} to a byte array at a given offset in little-endian order.
    406     *
    407     * @param data target byte array.
    408     * @param offset starting offset in the byte array.
    409     * @param value value to write.
    410     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
    411     */
    412    public static void writeSwappedInteger(final byte[] data, final int offset, final int value) {
    413        validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
    414        data[offset + 0] = (byte) (value >> 0 & 0xff);
    415        data[offset + 1] = (byte) (value >> 8 & 0xff);
    416        data[offset + 2] = (byte) (value >> 16 & 0xff);
    417        data[offset + 3] = (byte) (value >> 24 & 0xff);
    418    }
    419
    420    /**
    421     * Writes the 4 bytes of an {@code int} to an output stream in little-endian order.
    422     *
    423     * @param output target OutputStream.
    424     * @param value value to write.
    425     * @throws IOException in case of an I/O problem.
    426     */
    427    public static void writeSwappedInteger(final OutputStream output, final int value) throws IOException {
    428        output.write((byte) (value >> 0 & 0xff));
    429        output.write((byte) (value >> 8 & 0xff));
    430        output.write((byte) (value >> 16 & 0xff));
    431        output.write((byte) (value >> 24 & 0xff));
    432    }
    433
    434    /**
    435     * Writes the 8 bytes of a {@code long} to a byte array at a given offset in little-endian order.
    436     *
    437     * @param data target byte array.
    438     * @param offset starting offset in the byte array.
    439     * @param value value to write.
    440     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes.
    441     */
    442    public static void writeSwappedLong(final byte[] data, final int offset, final long value) {
    443        validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE);
    444        data[offset + 0] = (byte) (value >> 0 & 0xff);
    445        data[offset + 1] = (byte) (value >> 8 & 0xff);
    446        data[offset + 2] = (byte) (value >> 16 & 0xff);
    447        data[offset + 3] = (byte) (value >> 24 & 0xff);
    448        data[offset + 4] = (byte) (value >> 32 & 0xff);
    449        data[offset + 5] = (byte) (value >> 40 & 0xff);
    450        data[offset + 6] = (byte) (value >> 48 & 0xff);
    451        data[offset + 7] = (byte) (value >> 56 & 0xff);
    452    }
    453
    454    /**
    455     * Writes the 8 bytes of a {@code long} to an output stream in little-endian order.
    456     *
    457     * @param output target OutputStream.
    458     * @param value value to write.
    459     * @throws IOException in case of an I/O problem.
    460     */
    461    public static void writeSwappedLong(final OutputStream output, final long value) throws IOException {
    462        output.write((byte) (value >> 0 & 0xff));
    463        output.write((byte) (value >> 8 & 0xff));
    464        output.write((byte) (value >> 16 & 0xff));
    465        output.write((byte) (value >> 24 & 0xff));
    466        output.write((byte) (value >> 32 & 0xff));
    467        output.write((byte) (value >> 40 & 0xff));
    468        output.write((byte) (value >> 48 & 0xff));
    469        output.write((byte) (value >> 56 & 0xff));
    470    }
    471
    472    /**
    473     * Writes the 2 bytes of a {@code short} to a byte array at a given offset in little-endian order.
    474     *
    475     * @param data target byte array.
    476     * @param offset starting offset in the byte array.
    477     * @param value value to write.
    478     * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes.
    479     */
    480    public static void writeSwappedShort(final byte[] data, final int offset, final short value) {
    481        validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
    482        data[offset + 0] = (byte) (value >> 0 & 0xff);
    483        data[offset + 1] = (byte) (value >> 8 & 0xff);
    484    }
    485
    486    /**
    487     * Writes the 2 bytes of a {@code short} to an output stream using little-endian encoding.
    488     *
    489     * @param output target OutputStream.
    490     * @param value value to write.
    491     * @throws IOException in case of an I/O problem.
    492     */
    493    public static void writeSwappedShort(final OutputStream output, final short value) throws IOException {
    494        output.write((byte) (value >> 0 & 0xff));
    495        output.write((byte) (value >> 8 & 0xff));
    496    }
    497
    498    /**
    499     * Instances should NOT be constructed in standard programming.
    500     *
    501     * @deprecated TODO Make private in 3.0.
    502     */
    503    @Deprecated
    504    public EndianUtils() {
    505        // empty
    506    }
    507}