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