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}