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