View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.commons.compress.utils;
21  
22  import java.io.DataInput;
23  import java.io.DataOutput;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  
28  /**
29   * Utility methods for reading and writing bytes.
30   *
31   * @since 1.14
32   */
33  public final class ByteUtils {
34  
35      /**
36       * Used to consume bytes.
37       *
38       * @since 1.14
39       */
40      public interface ByteConsumer {
41          /**
42           * The contract is similar to {@link OutputStream#write(int)}, consume the lower eight bytes of the int as a byte.
43           *
44           * @param b the byte to consume
45           * @throws IOException if consuming fails
46           */
47          void accept(int b) throws IOException;
48      }
49  
50      /**
51       * Used to supply bytes.
52       *
53       * @since 1.14
54       */
55      public interface ByteSupplier {
56          /**
57           * The contract is similar to {@link InputStream#read()}, return the byte as an unsigned int, -1 if there are no more bytes.
58           *
59           * @return the supplied byte or -1 if there are no more bytes
60           * @throws IOException if supplying fails
61           */
62          int getAsByte() throws IOException;
63      }
64  
65      /**
66       * {@link ByteSupplier} based on {@link InputStream}.
67       *
68       * @since 1.14
69       * @deprecated Unused
70       */
71      @Deprecated
72      public static class InputStreamByteSupplier implements ByteSupplier {
73          private final InputStream is;
74  
75          /**
76           * Constructs a new instance.
77           *
78           * @param is an input stream.
79           */
80          public InputStreamByteSupplier(final InputStream is) {
81              this.is = is;
82          }
83  
84          @Override
85          public int getAsByte() throws IOException {
86              return is.read();
87          }
88      }
89  
90      /**
91       * {@link ByteConsumer} based on {@link OutputStream}.
92       *
93       * @since 1.14
94       */
95      public static class OutputStreamByteConsumer implements ByteConsumer {
96          private final OutputStream os;
97  
98          /**
99           * Constructs a new instance.
100          *
101          * @param os an output stream.
102          */
103         public OutputStreamByteConsumer(final OutputStream os) {
104             this.os = os;
105         }
106 
107         @Override
108         public void accept(final int b) throws IOException {
109             os.write(b);
110         }
111     }
112 
113     /**
114      * Empty array.
115      *
116      * @since 1.21
117      */
118     public static final byte[] EMPTY_BYTE_ARRAY = {};
119 
120     private static void checkReadLength(final int length) {
121         if (length > 8) {
122             throw new IllegalArgumentException("Can't read more than eight bytes into a long value");
123         }
124     }
125 
126     /**
127      * Reads the given byte array as a little-endian long.
128      *
129      * @param bytes the byte array to convert
130      * @return the number read
131      */
132     public static long fromLittleEndian(final byte[] bytes) {
133         return fromLittleEndian(bytes, 0, bytes.length);
134     }
135 
136     /**
137      * Reads the given byte array as a little-endian long.
138      *
139      * @param bytes  the byte array to convert
140      * @param off    the offset into the array that starts the value
141      * @param length the number of bytes representing the value
142      * @return the number read
143      * @throws IllegalArgumentException if len is bigger than eight
144      */
145     public static long fromLittleEndian(final byte[] bytes, final int off, final int length) {
146         checkReadLength(length);
147         long l = 0;
148         for (int i = 0; i < length; i++) {
149             l |= (bytes[off + i] & 0xffL) << 8 * i;
150         }
151         return l;
152     }
153 
154     /**
155      * Reads the given number of bytes from the given supplier as a little-endian long.
156      *
157      * <p>
158      * Typically used by our InputStreams that need to count the bytes read as well.
159      * </p>
160      *
161      * @param supplier the supplier for bytes
162      * @param length   the number of bytes representing the value
163      * @return the number read
164      * @throws IllegalArgumentException if len is bigger than eight
165      * @throws IOException              if the supplier fails or doesn't supply the given number of bytes anymore
166      */
167     public static long fromLittleEndian(final ByteSupplier supplier, final int length) throws IOException {
168         checkReadLength(length);
169         long l = 0;
170         for (int i = 0; i < length; i++) {
171             final long b = supplier.getAsByte();
172             if (b == -1) {
173                 throw new IOException("Premature end of data");
174             }
175             l |= b << i * 8;
176         }
177         return l;
178     }
179 
180     /**
181      * Reads the given number of bytes from the given input as little-endian long.
182      *
183      * @param in     the input to read from
184      * @param length the number of bytes representing the value
185      * @return the number read
186      * @throws IllegalArgumentException if len is bigger than eight
187      * @throws IOException              if reading fails or the stream doesn't contain the given number of bytes anymore
188      */
189     public static long fromLittleEndian(final DataInput in, final int length) throws IOException {
190         // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object
191         checkReadLength(length);
192         long l = 0;
193         for (int i = 0; i < length; i++) {
194             final long b = in.readUnsignedByte();
195             l |= b << i * 8;
196         }
197         return l;
198     }
199 
200     /**
201      * Reads the given number of bytes from the given stream as a little-endian long.
202      *
203      * @param in     the stream to read from
204      * @param length the number of bytes representing the value
205      * @return the number read
206      * @throws IllegalArgumentException if len is bigger than eight
207      * @throws IOException              if reading fails or the stream doesn't contain the given number of bytes anymore
208      * @deprecated Unused.
209      */
210     @Deprecated
211     public static long fromLittleEndian(final InputStream in, final int length) throws IOException {
212         // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object
213         checkReadLength(length);
214         long l = 0;
215         for (int i = 0; i < length; i++) {
216             final long b = in.read();
217             if (b == -1) {
218                 throw new IOException("Premature end of data");
219             }
220             l |= b << i * 8;
221         }
222         return l;
223     }
224 
225     /**
226      * Inserts the given value into the array as a little-endian sequence of the given length starting at the given offset.
227      *
228      * @param b      the array to write into
229      * @param value  the value to insert
230      * @param off    the offset into the array that receives the first byte
231      * @param length the number of bytes to use to represent the value
232      */
233     public static void toLittleEndian(final byte[] b, final long value, final int off, final int length) {
234         long num = value;
235         for (int i = 0; i < length; i++) {
236             b[off + i] = (byte) (num & 0xff);
237             num >>= 8;
238         }
239     }
240 
241     /**
242      * Provides the given value to the given consumer as a little-endian sequence of the given length.
243      *
244      * @param consumer the consumer to provide the bytes to
245      * @param value    the value to provide
246      * @param length   the number of bytes to use to represent the value
247      * @throws IOException if writing fails
248      */
249     public static void toLittleEndian(final ByteConsumer consumer, final long value, final int length) throws IOException {
250         long num = value;
251         for (int i = 0; i < length; i++) {
252             consumer.accept((int) (num & 0xff));
253             num >>= 8;
254         }
255     }
256 
257     /**
258      * Writes the given value to the given stream as a little-endian array of the given length.
259      *
260      * @param out    the output to write to
261      * @param value  the value to write
262      * @param length the number of bytes to use to represent the value
263      * @throws IOException if writing fails
264      * @deprecated Unused.
265      */
266     @Deprecated
267     public static void toLittleEndian(final DataOutput out, final long value, final int length) throws IOException {
268         // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object
269         long num = value;
270         for (int i = 0; i < length; i++) {
271             out.write((int) (num & 0xff));
272             num >>= 8;
273         }
274     }
275 
276     /**
277      * Writes the given value to the given stream as a little-endian array of the given length.
278      *
279      * @param out    the stream to write to
280      * @param value  the value to write
281      * @param length the number of bytes to use to represent the value
282      * @throws IOException if writing fails
283      */
284     public static void toLittleEndian(final OutputStream out, final long value, final int length) throws IOException {
285         // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object
286         long num = value;
287         for (int i = 0; i < length; i++) {
288             out.write((int) (num & 0xff));
289             num >>= 8;
290         }
291     }
292 
293     private ByteUtils() {
294         /* no instances */ }
295 }