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}