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