EndianUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.io;

  18. import static org.apache.commons.io.IOUtils.EOF;

  19. import java.io.EOFException;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.OutputStream;

  23. /**
  24.  * Helps with reading and writing primitive numeric types ({@code short},
  25.  * {@code int}, {@code long}, {@code float}, and {@code double}) that are
  26.  * encoded in little endian using two's complement or unsigned representations.
  27.  * <p>
  28.  * Different computer architectures have different conventions for
  29.  * byte ordering. In "Little Endian" architectures (e.g. X86),
  30.  * the low-order byte is stored in memory at the lowest address, and
  31.  * subsequent bytes at higher addresses. In "Big Endian" architectures
  32.  * (e.g. Motorola 680X0), the situation is reversed.
  33.  * Most methods and classes throughout Java &mdash; e.g. {@code DataInputStream} and
  34.  * {@code Double.longBitsToDouble()} &mdash; assume data is laid out
  35.  * in big endian order with the most significant byte first.
  36.  * The methods in this class read and write data in little endian order,
  37.  * generally by reversing the bytes and then using the
  38.  * regular Java methods to convert the swapped bytes to a primitive type.
  39.  * </p>
  40.  * <p>
  41.  * Provenance: Excalibur
  42.  * </p>
  43.  *
  44.  * @see org.apache.commons.io.input.SwappedDataInputStream
  45.  */
  46. public class EndianUtils {

  47.     /**
  48.      * Reads the next byte from the input stream.
  49.      * @param input  the stream
  50.      * @return the byte
  51.      * @throws IOException if the end of file is reached
  52.      */
  53.     private static int read(final InputStream input) throws IOException {
  54.         final int value = input.read();
  55.         if (EOF == value) {
  56.             throw new EOFException("Unexpected EOF reached");
  57.         }
  58.         return value;
  59.     }

  60.     /**
  61.      * Reads a little endian {@code double} value from a byte array at a given offset.
  62.      *
  63.      * @param data source byte array
  64.      * @param offset starting offset in the byte array
  65.      * @return the value read
  66.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes
  67.      */
  68.     public static double readSwappedDouble(final byte[] data, final int offset) {
  69.         return Double.longBitsToDouble(readSwappedLong(data, offset));
  70.     }

  71.     /**
  72.      * Reads a little endian {@code double} value from an InputStream.
  73.      *
  74.      * @param input source InputStream
  75.      * @return the value just read
  76.      * @throws IOException in case of an I/O problem
  77.      */
  78.     public static double readSwappedDouble(final InputStream input) throws IOException {
  79.         return Double.longBitsToDouble(readSwappedLong(input));
  80.     }

  81.     /**
  82.      * Reads a little endian {@code float} value from a byte array at a given offset.
  83.      *
  84.      * @param data source byte array
  85.      * @param offset starting offset in the byte array
  86.      * @return the value read
  87.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
  88.      */
  89.     public static float readSwappedFloat(final byte[] data, final int offset) {
  90.         return Float.intBitsToFloat(readSwappedInteger(data, offset));
  91.     }

  92.     /**
  93.      * Reads a little endian {@code float} value from an InputStream.
  94.      *
  95.      * @param input source InputStream
  96.      * @return the value just read
  97.      * @throws IOException in case of an I/O problem
  98.      */
  99.     public static float readSwappedFloat(final InputStream input) throws IOException {
  100.         return Float.intBitsToFloat(readSwappedInteger(input));
  101.     }

  102.     /**
  103.      * Reads a little endian {@code int} value from a byte array at a given offset.
  104.      *
  105.      * @param data source byte array
  106.      * @param offset starting offset in the byte array
  107.      * @return the value read
  108.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
  109.      */
  110.     public static int readSwappedInteger(final byte[] data, final int offset) {
  111.         validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
  112.         return ((data[offset + 0] & 0xff) << 0) +
  113.             ((data[offset + 1] & 0xff) << 8) +
  114.             ((data[offset + 2] & 0xff) << 16) +
  115.             ((data[offset + 3] & 0xff) << 24);
  116.     }

  117.     /**
  118.      * Reads a little endian {@code int} value from an InputStream.
  119.      *
  120.      * @param input source InputStream
  121.      * @return the value just read
  122.      * @throws IOException in case of an I/O problem
  123.      */
  124.     public static int readSwappedInteger(final InputStream input) throws IOException {
  125.         final int value1 = read(input);
  126.         final int value2 = read(input);
  127.         final int value3 = read(input);
  128.         final int value4 = read(input);
  129.         return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16) + ((value4 & 0xff) << 24);
  130.     }

  131.     /**
  132.      * Reads a little endian {@code long} value from a byte array at a given offset.
  133.      *
  134.      * @param data source byte array
  135.      * @param offset starting offset in the byte array
  136.      * @return the value read
  137.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes
  138.      */
  139.     public static long readSwappedLong(final byte[] data, final int offset) {
  140.         validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE);
  141.         final long low = readSwappedInteger(data, offset);
  142.         final long high = readSwappedInteger(data, offset + 4);
  143.         return (high << 32) + (0xffffffffL & low);
  144.     }

  145.     /**
  146.      * Reads a little endian {@code long} value from an InputStream.
  147.      *
  148.      * @param input source InputStream
  149.      * @return the value just read
  150.      * @throws IOException in case of an I/O problem
  151.      */
  152.     public static long readSwappedLong(final InputStream input) throws IOException {
  153.         final byte[] bytes = new byte[8];
  154.         for (int i = 0; i < 8; i++) {
  155.             bytes[i] = (byte) read(input);
  156.         }
  157.         return readSwappedLong(bytes, 0);
  158.     }

  159.     /**
  160.      * Reads a little endian {@code short} value from a byte array at a given offset.
  161.      *
  162.      * @param data source byte array
  163.      * @param offset starting offset in the byte array
  164.      * @return the value read
  165.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes
  166.      */
  167.     public static short readSwappedShort(final byte[] data, final int offset) {
  168.         validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
  169.         return (short) (((data[offset + 0] & 0xff) << 0) + ((data[offset + 1] & 0xff) << 8));
  170.     }

  171.     /**
  172.      * Reads a little endian {@code short} value from an InputStream.
  173.      *
  174.      * @param input source InputStream
  175.      * @return the value just read
  176.      * @throws IOException in case of an I/O problem
  177.      */
  178.     public static short readSwappedShort(final InputStream input) throws IOException {
  179.         return (short) (((read(input) & 0xff) << 0) + ((read(input) & 0xff) << 8));
  180.     }

  181.     /**
  182.      * Reads a little endian unsigned integer (32-bit) value from a byte array at a given
  183.      * offset.
  184.      *
  185.      * @param data source byte array
  186.      * @param offset starting offset in the byte array
  187.      * @return the value read
  188.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
  189.     */
  190.     public static long readSwappedUnsignedInteger(final byte[] data, final int offset) {
  191.         validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
  192.         final long low = ((data[offset + 0] & 0xff) << 0) +
  193.                      ((data[offset + 1] & 0xff) << 8) +
  194.                      ((data[offset + 2] & 0xff) << 16);
  195.         final long high = data[offset + 3] & 0xff;
  196.         return (high << 24) + (0xffffffffL & low);
  197.     }

  198.     /**
  199.      * Reads a little endian unsigned integer (32-bit) from an InputStream.
  200.      *
  201.      * @param input source InputStream
  202.      * @return the value just read
  203.      * @throws IOException in case of an I/O problem
  204.      */
  205.     public static long readSwappedUnsignedInteger(final InputStream input) throws IOException {
  206.         final int value1 = read(input);
  207.         final int value2 = read(input);
  208.         final int value3 = read(input);
  209.         final int value4 = read(input);
  210.         final long low = ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16);
  211.         final long high = value4 & 0xff;
  212.         return (high << 24) + (0xffffffffL & low);
  213.     }

  214.     /**
  215.      * Reads an unsigned short (16-bit) value from a byte array in little endian order at a given
  216.      * offset.
  217.      *
  218.      * @param data source byte array
  219.      * @param offset starting offset in the byte array
  220.      * @return the value read
  221.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes
  222.      */
  223.     public static int readSwappedUnsignedShort(final byte[] data, final int offset) {
  224.         validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
  225.         return ((data[offset + 0] & 0xff) << 0) + ((data[offset + 1] & 0xff) << 8);
  226.     }

  227.     /**
  228.      * Reads an unsigned short (16-bit) from an InputStream in little endian order.
  229.      *
  230.      * @param input source InputStream
  231.      * @return the value just read
  232.      * @throws IOException in case of an I/O problem
  233.      */
  234.     public static int readSwappedUnsignedShort(final InputStream input) throws IOException {
  235.         final int value1 = read(input);
  236.         final int value2 = read(input);

  237.         return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8);
  238.     }

  239.     /**
  240.      * Converts a {@code double} value from big endian to little endian
  241.      * and vice versa. That is, it converts the {@code double} to bytes,
  242.      * reverses the bytes, and then reinterprets those bytes as a new {@code double}.
  243.      * This can be useful if you have a number that was read from the
  244.      * underlying source in the wrong endianness.
  245.      *
  246.      * @param value value to convert
  247.      * @return the converted value
  248.      */
  249.     public static double swapDouble(final double value) {
  250.         return Double.longBitsToDouble(swapLong(Double.doubleToLongBits(value)));
  251.     }

  252.     /**
  253.      * Converts a {@code float} value from big endian to little endian and vice versa.
  254.      *
  255.      * @param value value to convert
  256.      * @return the converted value
  257.      */
  258.     public static float swapFloat(final float value) {
  259.         return Float.intBitsToFloat(swapInteger(Float.floatToIntBits(value)));
  260.     }

  261.     /**
  262.      * Converts an {@code int} value from big endian to little endian and vice versa.
  263.      *
  264.      * @param value value to convert
  265.      * @return the converted value
  266.      */
  267.     public static int swapInteger(final int value) {
  268.         return
  269.             ((value >> 0 & 0xff) << 24) +
  270.             ((value >> 8 & 0xff) << 16) +
  271.             ((value >> 16 & 0xff) << 8) +
  272.             ((value >> 24 & 0xff) << 0);
  273.     }

  274.     /**
  275.      * Converts a {@code long} value from big endian to little endian and vice versa.
  276.      *
  277.      * @param value value to convert
  278.      * @return the converted value
  279.      */
  280.     public static long swapLong(final long value) {
  281.         return
  282.             ((value >> 0 & 0xff) << 56) +
  283.             ((value >> 8 & 0xff) << 48) +
  284.             ((value >> 16 & 0xff) << 40) +
  285.             ((value >> 24 & 0xff) << 32) +
  286.             ((value >> 32 & 0xff) << 24) +
  287.             ((value >> 40 & 0xff) << 16) +
  288.             ((value >> 48 & 0xff) << 8) +
  289.             ((value >> 56 & 0xff) << 0);
  290.     }

  291.     /**
  292.      * Converts a {@code short} value from big endian to little endian and vice versa.
  293.      *
  294.      * @param value value to convert
  295.      * @return the converted value
  296.      */
  297.     public static short swapShort(final short value) {
  298.         return (short) (((value >> 0 & 0xff) << 8) +
  299.             ((value >> 8 & 0xff) << 0));
  300.     }

  301.     /**
  302.      * Validates if the provided byte array has enough data.
  303.      *
  304.      * @param data the input byte array
  305.      * @param offset the input offset
  306.      * @param byteNeeded the needed number of bytes
  307.      * @throws IllegalArgumentException if the byte array does not have enough data
  308.      */
  309.     private static void validateByteArrayOffset(final byte[] data, final int offset, final int byteNeeded) {
  310.         if (data.length < offset + byteNeeded) {
  311.             throw new IllegalArgumentException("Data only has " + data.length + "bytes, needed " + (offset + byteNeeded) + "bytes.");
  312.         }
  313.     }

  314.     /**
  315.      * Writes the 8 bytes of a {@code double} to a byte array at a given offset in little endian order.
  316.      *
  317.      * @param data target byte array
  318.      * @param offset starting offset in the byte array
  319.      * @param value value to write
  320.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes
  321.      */
  322.     public static void writeSwappedDouble(final byte[] data, final int offset, final double value) {
  323.         writeSwappedLong(data, offset, Double.doubleToLongBits(value));
  324.     }

  325.     /**
  326.      * Writes the 8 bytes of a {@code double} to an output stream in little endian order.
  327.      *
  328.      * @param output target OutputStream
  329.      * @param value value to write
  330.      * @throws IOException in case of an I/O problem
  331.      */
  332.     public static void writeSwappedDouble(final OutputStream output, final double value) throws IOException {
  333.         writeSwappedLong(output, Double.doubleToLongBits(value));
  334.     }

  335.     /**
  336.      * Writes the 4 bytes of a {@code float} to a byte array at a given offset in little endian order.
  337.      *
  338.      * @param data target byte array
  339.      * @param offset starting offset in the byte array
  340.      * @param value value to write
  341.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
  342.      */
  343.     public static void writeSwappedFloat(final byte[] data, final int offset, final float value) {
  344.         writeSwappedInteger(data, offset, Float.floatToIntBits(value));
  345.     }

  346.     /**
  347.      * Writes the 4 bytes of a {@code float} to an output stream in little endian order.
  348.      *
  349.      * @param output target OutputStream
  350.      * @param value value to write
  351.      * @throws IOException in case of an I/O problem
  352.     */
  353.     public static void writeSwappedFloat(final OutputStream output, final float value) throws IOException {
  354.         writeSwappedInteger(output, Float.floatToIntBits(value));
  355.     }

  356.     /**
  357.      * Writes the 4 bytes of an {@code int} to a byte array at a given offset in little endian order.
  358.      *
  359.      * @param data target byte array
  360.      * @param offset starting offset in the byte array
  361.      * @param value value to write
  362.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes
  363.      */
  364.     public static void writeSwappedInteger(final byte[] data, final int offset, final int value) {
  365.         validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
  366.         data[offset + 0] = (byte) (value >> 0 & 0xff);
  367.         data[offset + 1] = (byte) (value >> 8 & 0xff);
  368.         data[offset + 2] = (byte) (value >> 16 & 0xff);
  369.         data[offset + 3] = (byte) (value >> 24 & 0xff);
  370.     }

  371.     /**
  372.      * Writes the 4 bytes of an {@code int} to an output stream in little endian order.
  373.      *
  374.      * @param output target OutputStream
  375.      * @param value value to write
  376.      * @throws IOException in case of an I/O problem
  377.      */
  378.     public static void writeSwappedInteger(final OutputStream output, final int value) throws IOException {
  379.         output.write((byte) (value >> 0 & 0xff));
  380.         output.write((byte) (value >> 8 & 0xff));
  381.         output.write((byte) (value >> 16 & 0xff));
  382.         output.write((byte) (value >> 24 & 0xff));
  383.     }

  384.     /**
  385.      * Writes the 8 bytes of a {@code long} to a byte array at a given offset in little endian order.
  386.      *
  387.      * @param data target byte array
  388.      * @param offset starting offset in the byte array
  389.      * @param value value to write
  390.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes
  391.      */
  392.     public static void writeSwappedLong(final byte[] data, final int offset, final long value) {
  393.         validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE);
  394.         data[offset + 0] = (byte) (value >> 0 & 0xff);
  395.         data[offset + 1] = (byte) (value >> 8 & 0xff);
  396.         data[offset + 2] = (byte) (value >> 16 & 0xff);
  397.         data[offset + 3] = (byte) (value >> 24 & 0xff);
  398.         data[offset + 4] = (byte) (value >> 32 & 0xff);
  399.         data[offset + 5] = (byte) (value >> 40 & 0xff);
  400.         data[offset + 6] = (byte) (value >> 48 & 0xff);
  401.         data[offset + 7] = (byte) (value >> 56 & 0xff);
  402.     }

  403.     /**
  404.      * Writes the 8 bytes of a {@code long} to an output stream in little endian order.
  405.      *
  406.      * @param output target OutputStream
  407.      * @param value value to write
  408.      * @throws IOException in case of an I/O problem
  409.      */
  410.     public static void writeSwappedLong(final OutputStream output, final long value) throws IOException {
  411.         output.write((byte) (value >> 0 & 0xff));
  412.         output.write((byte) (value >> 8 & 0xff));
  413.         output.write((byte) (value >> 16 & 0xff));
  414.         output.write((byte) (value >> 24 & 0xff));
  415.         output.write((byte) (value >> 32 & 0xff));
  416.         output.write((byte) (value >> 40 & 0xff));
  417.         output.write((byte) (value >> 48 & 0xff));
  418.         output.write((byte) (value >> 56 & 0xff));
  419.     }

  420.     /**
  421.      * Writes the 2 bytes of a {@code short} to a byte array at a given offset in little endian order.
  422.      *
  423.      * @param data target byte array
  424.      * @param offset starting offset in the byte array
  425.      * @param value value to write
  426.      * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes
  427.      */
  428.     public static void writeSwappedShort(final byte[] data, final int offset, final short value) {
  429.         validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
  430.         data[offset + 0] = (byte) (value >> 0 & 0xff);
  431.         data[offset + 1] = (byte) (value >> 8 & 0xff);
  432.     }

  433.     /**
  434.      * Writes the 2 bytes of a {@code short} to an output stream using little endian encoding.
  435.      *
  436.      * @param output target OutputStream
  437.      * @param value value to write
  438.      * @throws IOException in case of an I/O problem
  439.      */
  440.     public static void writeSwappedShort(final OutputStream output, final short value) throws IOException {
  441.         output.write((byte) (value >> 0 & 0xff));
  442.         output.write((byte) (value >> 8 & 0xff));
  443.     }

  444.     /**
  445.      * Instances should NOT be constructed in standard programming.
  446.      *
  447.      * @deprecated TODO Make private in 3.0.
  448.      */
  449.     @Deprecated
  450.     public EndianUtils() {
  451.         // empty
  452.     }
  453. }