    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 *
    017 */
    018
    019package org.apache.commons.compress.utils;
    020
    021import java.io.DataInput;
    022import java.io.DataOutput;
    023import java.io.IOException;
    024import java.io.InputStream;
    025import java.io.OutputStream;
    026
    027/**
    028 * Utility methods for reading and writing bytes.
    029 * @since 1.14
    030 */
    031public final class ByteUtils {
    032
    033    /**
    034     * Empty array.
    035     *
    036     * @since 1.21
    037     */
    038    public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
    039
    040    private ByteUtils() { /* no instances */ }
    041
    042    /**
    043     * Used to supply bytes.
    044     * @since 1.14
    045     */
    046    public interface ByteSupplier {
    047        /**
    048         * The contract is similar to {@link InputStream#read()}, return
    049         * the byte as an unsigned int, -1 if there are no more bytes.
    050         * @return the supplied byte or -1 if there are no more bytes
    051         * @throws IOException if supplying fails
    052         */
    053        int getAsByte() throws IOException;
    054    }
    055
    056    /**
    057     * Used to consume bytes.
    058     * @since 1.14
    059     */
    060    public interface ByteConsumer {
    061        /**
    062         * The contract is similar to {@link OutputStream#write(int)},
    063         * consume the lower eight bytes of the int as a byte.
    064         * @param b the byte to consume
    065         * @throws IOException if consuming fails
    066         */
    067        void accept(int b) throws IOException;
    068    }
    069
    070    /**
    071     * Reads the given byte array as a little endian long.
    072     * @param bytes the byte array to convert
    073     * @return the number read
    074     */
    075    public static long fromLittleEndian(final byte[] bytes) {
    076        return fromLittleEndian(bytes, 0, bytes.length);
    077    }
    078
    079    /**
    080     * Reads the given byte array as a little endian long.
    081     * @param bytes the byte array to convert
    082     * @param off the offset into the array that starts the value
    083     * @param length the number of bytes representing the value
    084     * @return the number read
    085     * @throws IllegalArgumentException if len is bigger than eight
    086     */
    087    public static long fromLittleEndian(final byte[] bytes, final int off, final int length) {
    088        checkReadLength(length);
    089        long l = 0;
    090        for (int i = 0; i < length; i++) {
    091            l |= (bytes[off + i] & 0xffL) << (8 * i);
    092        }
    093        return l;
    094    }
    095
    096    /**
    097     * Reads the given number of bytes from the given stream as a little endian long.
    098     * @param in the stream to read from
    099     * @param length the number of bytes representing the value
    100     * @return the number read
    101     * @throws IllegalArgumentException if len is bigger than eight
    102     * @throws IOException if reading fails or the stream doesn't
    103     * contain the given number of bytes anymore
    104     */
    105    public static long fromLittleEndian(final InputStream in, final int length) throws IOException {
    106        // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object
    107        checkReadLength(length);
    108        long l = 0;
    109        for (int i = 0; i < length; i++) {
    110            final long b = in.read();
    111            if (b == -1) {
    112                throw new IOException("Premature end of data");
    113            }
    114            l |= (b << (i * 8));
    115        }
    116        return l;
    117    }
    118
    119    /**
    120     * Reads the given number of bytes from the given supplier as a little endian long.
    121     *
    122     * <p>Typically used by our InputStreams that need to count the
    123     * bytes read as well.</p>
    124     *
    125     * @param supplier the supplier for bytes
    126     * @param length the number of bytes representing the value
    127     * @return the number read
    128     * @throws IllegalArgumentException if len is bigger than eight
    129     * @throws IOException if the supplier fails or doesn't supply the
    130     * given number of bytes anymore
    131     */
    132    public static long fromLittleEndian(final ByteSupplier supplier, final int length) throws IOException {
    133        checkReadLength(length);
    134        long l = 0;
    135        for (int i = 0; i < length; i++) {
    136            final long b = supplier.getAsByte();
    137            if (b == -1) {
    138                throw new IOException("Premature end of data");
    139            }
    140            l |= (b << (i * 8));
    141        }
    142        return l;
    143    }
    144
    145    /**
    146     * Reads the given number of bytes from the given input as little endian long.
    147     * @param in the input to read from
    148     * @param length the number of bytes representing the value
    149     * @return the number read
    150     * @throws IllegalArgumentException if len is bigger than eight
    151     * @throws IOException if reading fails or the stream doesn't
    152     * contain the given number of bytes anymore
    153     */
    154    public static long fromLittleEndian(final DataInput in, final int length) throws IOException {
    155        // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object
    156        checkReadLength(length);
    157        long l = 0;
    158        for (int i = 0; i < length; i++) {
    159            final long b = in.readUnsignedByte();
    160            l |= (b << (i * 8));
    161        }
    162        return l;
    163    }
    164
    165    /**
    166     * Inserts the given value into the array as a little endian
    167     * sequence of the given length starting at the given offset.
    168     * @param b the array to write into
    169     * @param value the value to insert
    170     * @param off the offset into the array that receives the first byte
    171     * @param length the number of bytes to use to represent the value
    172     */
    173    public static void toLittleEndian(final byte[] b, final long value, final int off, final int length) {
    174        long num = value;
    175        for (int i = 0; i < length; i++) {
    176            b[off + i] = (byte) (num & 0xff);
    177            num >>= 8;
    178        }
    179    }
    180
    181    /**
    182     * Writes the given value to the given stream as a little endian
    183     * array of the given length.
    184     * @param out the stream to write to
    185     * @param value the value to write
    186     * @param length the number of bytes to use to represent the value
    187     * @throws IOException if writing fails
    188     */
    189    public static void toLittleEndian(final OutputStream out, final long value, final int length)
    190        throws IOException {
    191        // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object
    192        long num = value;
    193        for (int i = 0; i < length; i++) {
    194            out.write((int) (num & 0xff));
    195            num >>= 8;
    196        }
    197    }
    198
    199    /**
    200     * Provides the given value to the given consumer as a little endian
    201     * sequence of the given length.
    202     * @param consumer the consumer to provide the bytes to
    203     * @param value the value to provide
    204     * @param length the number of bytes to use to represent the value
    205     * @throws IOException if writing fails
    206     */
    207    public static void toLittleEndian(final ByteConsumer consumer, final long value, final int length)
    208        throws IOException {
    209        long num = value;
    210        for (int i = 0; i < length; i++) {
    211            consumer.accept((int) (num & 0xff));
    212            num >>= 8;
    213        }
    214    }
    215
    216    /**
    217     * Writes the given value to the given stream as a little endian
    218     * array of the given length.
    219     * @param out the output to write to
    220     * @param value the value to write
    221     * @param length the number of bytes to use to represent the value
    222     * @throws IOException if writing fails
    223     */
    224    public static void toLittleEndian(final DataOutput out, final long value, final int length)
    225        throws IOException {
    226        // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object
    227        long num = value;
    228        for (int i = 0; i < length; i++) {
    229            out.write((int) (num & 0xff));
    230            num >>= 8;
    231        }
    232    }
    233
    234    /**
    235     * {@link ByteSupplier} based on {@link InputStream}.
    236     * @since 1.14
    237     */
    238    public static class InputStreamByteSupplier implements ByteSupplier {
    239        private final InputStream is;
    240        public InputStreamByteSupplier(final InputStream is) {
    241            this.is = is;
    242        }
    243        @Override
    244        public int getAsByte() throws IOException {
    245            return is.read();
    246        }
    247    }
    248
    249    /**
    250     * {@link ByteConsumer} based on {@link OutputStream}.
    251     * @since 1.14
    252     */
    253    public static class OutputStreamByteConsumer implements ByteConsumer {
    254        private final OutputStream os;
    255        public OutputStreamByteConsumer(final OutputStream os) {
    256            this.os = os;
    257        }
    258        @Override
    259        public void accept(final int b) throws IOException {
    260            os.write(b);
    261        }
    262    }
    263
    264    private static void checkReadLength(final int length) {
    265        if (length > 8) {
    266            throw new IllegalArgumentException("Can't read more than eight bytes into a long value");
    267        }
    268    }
    269}