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}