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 }