ByteUtils.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.compress.utils;

  18. import java.io.DataInput;
  19. import java.io.DataOutput;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.OutputStream;

  23. /**
  24.  * Utility methods for reading and writing bytes.
  25.  *
  26.  * @since 1.14
  27.  */
  28. public final class ByteUtils {

  29.     /**
  30.      * Used to consume bytes.
  31.      *
  32.      * @since 1.14
  33.      */
  34.     public interface ByteConsumer {
  35.         /**
  36.          * The contract is similar to {@link OutputStream#write(int)}, consume the lower eight bytes of the int as a byte.
  37.          *
  38.          * @param b the byte to consume
  39.          * @throws IOException if consuming fails
  40.          */
  41.         void accept(int b) throws IOException;
  42.     }

  43.     /**
  44.      * Used to supply bytes.
  45.      *
  46.      * @since 1.14
  47.      */
  48.     public interface ByteSupplier {
  49.         /**
  50.          * The contract is similar to {@link InputStream#read()}, return the byte as an unsigned int, -1 if there are no more bytes.
  51.          *
  52.          * @return the supplied byte or -1 if there are no more bytes
  53.          * @throws IOException if supplying fails
  54.          */
  55.         int getAsByte() throws IOException;
  56.     }

  57.     /**
  58.      * {@link ByteSupplier} based on {@link InputStream}.
  59.      *
  60.      * @since 1.14
  61.      * @deprecated Unused
  62.      */
  63.     @Deprecated
  64.     public static class InputStreamByteSupplier implements ByteSupplier {
  65.         private final InputStream is;

  66.         public InputStreamByteSupplier(final InputStream is) {
  67.             this.is = is;
  68.         }

  69.         @Override
  70.         public int getAsByte() throws IOException {
  71.             return is.read();
  72.         }
  73.     }

  74.     /**
  75.      * {@link ByteConsumer} based on {@link OutputStream}.
  76.      *
  77.      * @since 1.14
  78.      */
  79.     public static class OutputStreamByteConsumer implements ByteConsumer {
  80.         private final OutputStream os;

  81.         public OutputStreamByteConsumer(final OutputStream os) {
  82.             this.os = os;
  83.         }

  84.         @Override
  85.         public void accept(final int b) throws IOException {
  86.             os.write(b);
  87.         }
  88.     }

  89.     /**
  90.      * Empty array.
  91.      *
  92.      * @since 1.21
  93.      */
  94.     public static final byte[] EMPTY_BYTE_ARRAY = {};

  95.     private static void checkReadLength(final int length) {
  96.         if (length > 8) {
  97.             throw new IllegalArgumentException("Can't read more than eight bytes into a long value");
  98.         }
  99.     }

  100.     /**
  101.      * Reads the given byte array as a little-endian long.
  102.      *
  103.      * @param bytes the byte array to convert
  104.      * @return the number read
  105.      */
  106.     public static long fromLittleEndian(final byte[] bytes) {
  107.         return fromLittleEndian(bytes, 0, bytes.length);
  108.     }

  109.     /**
  110.      * Reads the given byte array as a little-endian long.
  111.      *
  112.      * @param bytes  the byte array to convert
  113.      * @param off    the offset into the array that starts the value
  114.      * @param length the number of bytes representing the value
  115.      * @return the number read
  116.      * @throws IllegalArgumentException if len is bigger than eight
  117.      */
  118.     public static long fromLittleEndian(final byte[] bytes, final int off, final int length) {
  119.         checkReadLength(length);
  120.         long l = 0;
  121.         for (int i = 0; i < length; i++) {
  122.             l |= (bytes[off + i] & 0xffL) << 8 * i;
  123.         }
  124.         return l;
  125.     }

  126.     /**
  127.      * Reads the given number of bytes from the given supplier as a little-endian long.
  128.      *
  129.      * <p>
  130.      * Typically used by our InputStreams that need to count the bytes read as well.
  131.      * </p>
  132.      *
  133.      * @param supplier the supplier for bytes
  134.      * @param length   the number of bytes representing the value
  135.      * @return the number read
  136.      * @throws IllegalArgumentException if len is bigger than eight
  137.      * @throws IOException              if the supplier fails or doesn't supply the given number of bytes anymore
  138.      */
  139.     public static long fromLittleEndian(final ByteSupplier supplier, final int length) throws IOException {
  140.         checkReadLength(length);
  141.         long l = 0;
  142.         for (int i = 0; i < length; i++) {
  143.             final long b = supplier.getAsByte();
  144.             if (b == -1) {
  145.                 throw new IOException("Premature end of data");
  146.             }
  147.             l |= b << i * 8;
  148.         }
  149.         return l;
  150.     }

  151.     /**
  152.      * Reads the given number of bytes from the given input as little-endian long.
  153.      *
  154.      * @param in     the input to read from
  155.      * @param length the number of bytes representing the value
  156.      * @return the number read
  157.      * @throws IllegalArgumentException if len is bigger than eight
  158.      * @throws IOException              if reading fails or the stream doesn't contain the given number of bytes anymore
  159.      */
  160.     public static long fromLittleEndian(final DataInput in, final int length) throws IOException {
  161.         // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object
  162.         checkReadLength(length);
  163.         long l = 0;
  164.         for (int i = 0; i < length; i++) {
  165.             final long b = in.readUnsignedByte();
  166.             l |= b << i * 8;
  167.         }
  168.         return l;
  169.     }

  170.     /**
  171.      * Reads the given number of bytes from the given stream as a little-endian long.
  172.      *
  173.      * @param in     the stream to read from
  174.      * @param length the number of bytes representing the value
  175.      * @return the number read
  176.      * @throws IllegalArgumentException if len is bigger than eight
  177.      * @throws IOException              if reading fails or the stream doesn't contain the given number of bytes anymore
  178.      * @deprecated Unused
  179.      */
  180.     @Deprecated
  181.     public static long fromLittleEndian(final InputStream in, final int length) throws IOException {
  182.         // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object
  183.         checkReadLength(length);
  184.         long l = 0;
  185.         for (int i = 0; i < length; i++) {
  186.             final long b = in.read();
  187.             if (b == -1) {
  188.                 throw new IOException("Premature end of data");
  189.             }
  190.             l |= b << i * 8;
  191.         }
  192.         return l;
  193.     }

  194.     /**
  195.      * Inserts the given value into the array as a little-endian sequence of the given length starting at the given offset.
  196.      *
  197.      * @param b      the array to write into
  198.      * @param value  the value to insert
  199.      * @param off    the offset into the array that receives the first byte
  200.      * @param length the number of bytes to use to represent the value
  201.      */
  202.     public static void toLittleEndian(final byte[] b, final long value, final int off, final int length) {
  203.         long num = value;
  204.         for (int i = 0; i < length; i++) {
  205.             b[off + i] = (byte) (num & 0xff);
  206.             num >>= 8;
  207.         }
  208.     }

  209.     /**
  210.      * Provides the given value to the given consumer as a little-endian sequence of the given length.
  211.      *
  212.      * @param consumer the consumer to provide the bytes to
  213.      * @param value    the value to provide
  214.      * @param length   the number of bytes to use to represent the value
  215.      * @throws IOException if writing fails
  216.      */
  217.     public static void toLittleEndian(final ByteConsumer consumer, final long value, final int length) throws IOException {
  218.         long num = value;
  219.         for (int i = 0; i < length; i++) {
  220.             consumer.accept((int) (num & 0xff));
  221.             num >>= 8;
  222.         }
  223.     }

  224.     /**
  225.      * Writes the given value to the given stream as a little-endian array of the given length.
  226.      *
  227.      * @param out    the output to write to
  228.      * @param value  the value to write
  229.      * @param length the number of bytes to use to represent the value
  230.      * @throws IOException if writing fails
  231.      * @deprecated Unused
  232.      */
  233.     @Deprecated
  234.     public static void toLittleEndian(final DataOutput out, final long value, final int length) throws IOException {
  235.         // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object
  236.         long num = value;
  237.         for (int i = 0; i < length; i++) {
  238.             out.write((int) (num & 0xff));
  239.             num >>= 8;
  240.         }
  241.     }

  242.     /**
  243.      * Writes the given value to the given stream as a little-endian array of the given length.
  244.      *
  245.      * @param out    the stream to write to
  246.      * @param value  the value to write
  247.      * @param length the number of bytes to use to represent the value
  248.      * @throws IOException if writing fails
  249.      */
  250.     public static void toLittleEndian(final OutputStream out, final long value, final int length) throws IOException {
  251.         // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object
  252.         long num = value;
  253.         for (int i = 0; i < length; i++) {
  254.             out.write((int) (num & 0xff));
  255.             num >>= 8;
  256.         }
  257.     }

  258.     private ByteUtils() {
  259.         /* no instances */ }
  260. }