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}