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}