View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.compress.utils;
19  
20  import java.io.DataInput;
21  import java.io.DataOutput;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  
26  /**
27   * Utility methods for reading and writing bytes.
28   *
29   * @since 1.14
30   */
31  public final class ByteUtils {
32  
33      /**
34       * Used to consume bytes.
35       *
36       * @since 1.14
37       */
38      public interface ByteConsumer {
39          /**
40           * The contract is similar to {@link OutputStream#write(int)}, consume the lower eight bytes of the int as a byte.
41           *
42           * @param b the byte to consume
43           * @throws IOException if consuming fails
44           */
45          void accept(int b) throws IOException;
46      }
47  
48      /**
49       * Used to supply bytes.
50       *
51       * @since 1.14
52       */
53      public interface ByteSupplier {
54          /**
55           * The contract is similar to {@link InputStream#read()}, return the byte as an unsigned int, -1 if there are no more bytes.
56           *
57           * @return the supplied byte or -1 if there are no more bytes
58           * @throws IOException if supplying fails
59           */
60          int getAsByte() throws IOException;
61      }
62  
63      /**
64       * {@link ByteSupplier} based on {@link InputStream}.
65       *
66       * @since 1.14
67       * @deprecated Unused
68       */
69      @Deprecated
70      public static class InputStreamByteSupplier implements ByteSupplier {
71          private final InputStream is;
72  
73          public InputStreamByteSupplier(final InputStream is) {
74              this.is = is;
75          }
76  
77          @Override
78          public int getAsByte() throws IOException {
79              return is.read();
80          }
81      }
82  
83      /**
84       * {@link ByteConsumer} based on {@link OutputStream}.
85       *
86       * @since 1.14
87       */
88      public static class OutputStreamByteConsumer implements ByteConsumer {
89          private final OutputStream os;
90  
91          public OutputStreamByteConsumer(final OutputStream os) {
92              this.os = os;
93          }
94  
95          @Override
96          public void accept(final int b) throws IOException {
97              os.write(b);
98          }
99      }
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 }