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