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 * https://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 package org.apache.commons.io;
18
19 import static org.apache.commons.io.IOUtils.EOF;
20
21 import java.io.EOFException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25
26 /**
27 * Helps with reading and writing primitive numeric types ({@code short},
28 * {@code int}, {@code long}, {@code float}, and {@code double}) that are
29 * encoded in little-endian using two's complement or unsigned representations.
30 * <p>
31 * Different computer architectures have different conventions for
32 * byte ordering. In "Little Endian" architectures (e.g. X86),
33 * the low-order byte is stored in memory at the lowest address, and
34 * subsequent bytes at higher addresses. In "Big Endian" architectures
35 * (e.g. Motorola 680X0), the situation is reversed.
36 * Most methods and classes throughout Java — e.g. {@code DataInputStream} and
37 * {@code Double.longBitsToDouble()} — assume data is laid out
38 * in big-endian order with the most significant byte first.
39 * The methods in this class read and write data in little-endian order,
40 * generally by reversing the bytes and then using the
41 * regular Java methods to convert the swapped bytes to a primitive type.
42 * </p>
43 * <p>
44 * Provenance: Excalibur
45 * </p>
46 *
47 * @see org.apache.commons.io.input.SwappedDataInputStream
48 */
49 public class EndianUtils {
50
51 /**
52 * Reads the next byte from the input stream.
53 *
54 * @param input the stream.
55 * @return the byte.
56 * @throws IOException if the end of file is reached.
57 */
58 private static int read(final InputStream input) throws IOException {
59 final int value = input.read();
60 if (EOF == value) {
61 throw new EOFException("Unexpected EOF reached");
62 }
63 return value;
64 }
65
66 /**
67 * Reads a little-endian {@code double} value from a byte array at a given offset.
68 *
69 * @param data source byte array.
70 * @param offset starting offset in the byte array.
71 * @return the value read.
72 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes.
73 */
74 public static double readSwappedDouble(final byte[] data, final int offset) {
75 return Double.longBitsToDouble(readSwappedLong(data, offset));
76 }
77
78 /**
79 * Reads a little-endian {@code double} value from an InputStream.
80 *
81 * @param input source InputStream.
82 * @return the value just read.
83 * @throws IOException in case of an I/O problem.
84 */
85 public static double readSwappedDouble(final InputStream input) throws IOException {
86 return Double.longBitsToDouble(readSwappedLong(input));
87 }
88
89 /**
90 * Reads a little-endian {@code float} value from a byte array at a given offset.
91 *
92 * @param data source byte array.
93 * @param offset starting offset in the byte array.
94 * @return the value read.
95 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
96 */
97 public static float readSwappedFloat(final byte[] data, final int offset) {
98 return Float.intBitsToFloat(readSwappedInteger(data, offset));
99 }
100
101 /**
102 * Reads a little-endian {@code float} value from an InputStream.
103 *
104 * @param input source InputStream.
105 * @return the value just read.
106 * @throws IOException in case of an I/O problem.
107 */
108 public static float readSwappedFloat(final InputStream input) throws IOException {
109 return Float.intBitsToFloat(readSwappedInteger(input));
110 }
111
112 /**
113 * Reads a little-endian {@code int} value from a byte array at a given offset.
114 *
115 * @param data source byte array.
116 * @param offset starting offset in the byte array.
117 * @return the value read.
118 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
119 */
120 public static int readSwappedInteger(final byte[] data, final int offset) {
121 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
122 return
123 ((data[offset + 0] & 0xff) << 0) +
124 ((data[offset + 1] & 0xff) << 8) +
125 ((data[offset + 2] & 0xff) << 16) +
126 ((data[offset + 3] & 0xff) << 24);
127 }
128
129 /**
130 * Reads a little-endian {@code int} value from an InputStream.
131 *
132 * @param input source InputStream.
133 * @return the value just read.
134 * @throws IOException in case of an I/O problem.
135 */
136 public static int readSwappedInteger(final InputStream input) throws IOException {
137 final int value1 = read(input);
138 final int value2 = read(input);
139 final int value3 = read(input);
140 final int value4 = read(input);
141 return
142 ((value1 & 0xff) << 0) +
143 ((value2 & 0xff) << 8) +
144 ((value3 & 0xff) << 16) +
145 ((value4 & 0xff) << 24);
146 }
147
148 /**
149 * Reads a little-endian {@code long} value from a byte array at a given offset.
150 *
151 * @param data source byte array.
152 * @param offset starting offset in the byte array.
153 * @return the value read.
154 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes.
155 */
156 public static long readSwappedLong(final byte[] data, final int offset) {
157 validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE);
158 final long low = readSwappedInteger(data, offset);
159 final long high = readSwappedInteger(data, offset + 4);
160 return (high << 32) + (0xffffffffL & low);
161 }
162
163 /**
164 * Reads a little-endian {@code long} value from an InputStream.
165 *
166 * @param input source InputStream.
167 * @return the value just read.
168 * @throws IOException in case of an I/O problem.
169 */
170 public static long readSwappedLong(final InputStream input) throws IOException {
171 final byte[] bytes = new byte[8];
172 for (int i = 0; i < 8; i++) {
173 bytes[i] = (byte) read(input);
174 }
175 return readSwappedLong(bytes, 0);
176 }
177
178 /**
179 * Reads a little-endian {@code short} value from a byte array at a given offset.
180 *
181 * @param data source byte array.
182 * @param offset starting offset in the byte array.
183 * @return the value read.
184 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes.
185 */
186 public static short readSwappedShort(final byte[] data, final int offset) {
187 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
188 return (short) (
189 ((data[offset + 0] & 0xff) << 0) +
190 ((data[offset + 1] & 0xff) << 8)
191 );
192 }
193
194 /**
195 * Reads a little-endian {@code short} value from an InputStream.
196 *
197 * @param input source InputStream.
198 * @return the value just read.
199 * @throws IOException in case of an I/O problem.
200 */
201 public static short readSwappedShort(final InputStream input) throws IOException {
202 return (short) (
203 ((read(input) & 0xff) << 0) +
204 ((read(input) & 0xff) << 8)
205 );
206 }
207
208 /**
209 * Reads a little-endian unsigned integer (32-bit) value from a byte array at a given
210 * offset.
211 *
212 * @param data source byte array.
213 * @param offset starting offset in the byte array.
214 * @return the value read.
215 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
216 */
217 public static long readSwappedUnsignedInteger(final byte[] data, final int offset) {
218 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
219 final long low = ((data[offset + 0] & 0xff) << 0) +
220 ((data[offset + 1] & 0xff) << 8) +
221 ((data[offset + 2] & 0xff) << 16);
222 final long high = data[offset + 3] & 0xff;
223 return (high << 24) + (0xffffffffL & low);
224 }
225
226 /**
227 * Reads a little-endian unsigned integer (32-bit) from an InputStream.
228 *
229 * @param input source InputStream.
230 * @return the value just read.
231 * @throws IOException in case of an I/O problem.
232 */
233 public static long readSwappedUnsignedInteger(final InputStream input) throws IOException {
234 final int value1 = read(input);
235 final int value2 = read(input);
236 final int value3 = read(input);
237 final int value4 = read(input);
238 final long low = ((value1 & 0xff) << 0) +
239 ((value2 & 0xff) << 8) +
240 ((value3 & 0xff) << 16);
241 final long high = value4 & 0xff;
242 return (high << 24) + (0xffffffffL & low);
243 }
244
245 /**
246 * Reads an unsigned short (16-bit) value from a byte array in little-endian order at a given
247 * offset.
248 *
249 * @param data source byte array.
250 * @param offset starting offset in the byte array.
251 * @return the value read.
252 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes.
253 */
254 public static int readSwappedUnsignedShort(final byte[] data, final int offset) {
255 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
256 return ((data[offset + 0] & 0xff) << 0) +
257 ((data[offset + 1] & 0xff) << 8);
258 }
259
260 /**
261 * Reads an unsigned short (16-bit) from an InputStream in little-endian order.
262 *
263 * @param input source InputStream.
264 * @return the value just read.
265 * @throws IOException in case of an I/O problem.
266 */
267 public static int readSwappedUnsignedShort(final InputStream input) throws IOException {
268 final int value1 = read(input);
269 final int value2 = read(input);
270
271 return ((value1 & 0xff) << 0) +
272 ((value2 & 0xff) << 8);
273 }
274
275 /**
276 * Converts a {@code double} value from big-endian to little-endian
277 * and vice versa. That is, it converts the {@code double} to bytes,
278 * reverses the bytes, and then reinterprets those bytes as a new {@code double}.
279 * This can be useful if you have a number that was read from the
280 * underlying source in the wrong endianness.
281 *
282 * @param value value to convert.
283 * @return the converted value.
284 */
285 public static double swapDouble(final double value) {
286 return Double.longBitsToDouble(swapLong(Double.doubleToLongBits(value)));
287 }
288
289 /**
290 * Converts a {@code float} value from big-endian to little-endian and vice versa.
291 *
292 * @param value value to convert.
293 * @return the converted value.
294 */
295 public static float swapFloat(final float value) {
296 return Float.intBitsToFloat(swapInteger(Float.floatToIntBits(value)));
297 }
298
299 /**
300 * Converts an {@code int} value from big-endian to little-endian and vice versa.
301 *
302 * @param value value to convert.
303 * @return the converted value.
304 */
305 public static int swapInteger(final int value) {
306 return
307 ((value >> 0 & 0xff) << 24) +
308 ((value >> 8 & 0xff) << 16) +
309 ((value >> 16 & 0xff) << 8) +
310 ((value >> 24 & 0xff) << 0);
311 }
312
313 /**
314 * Converts a {@code long} value from big-endian to little-endian and vice versa.
315 *
316 * @param value value to convert.
317 * @return the converted value.
318 */
319 public static long swapLong(final long value) {
320 return
321 ((value >> 0 & 0xff) << 56) +
322 ((value >> 8 & 0xff) << 48) +
323 ((value >> 16 & 0xff) << 40) +
324 ((value >> 24 & 0xff) << 32) +
325 ((value >> 32 & 0xff) << 24) +
326 ((value >> 40 & 0xff) << 16) +
327 ((value >> 48 & 0xff) << 8) +
328 ((value >> 56 & 0xff) << 0);
329 }
330
331 /**
332 * Converts a {@code short} value from big-endian to little-endian and vice versa.
333 *
334 * @param value value to convert.
335 * @return the converted value.
336 */
337 public static short swapShort(final short value) {
338 return (short) (
339 ((value >> 0 & 0xff) << 8) +
340 ((value >> 8 & 0xff) << 0)
341 );
342 }
343
344 /**
345 * Validates if the provided byte array has enough data.
346 *
347 * @param data the input byte array.
348 * @param offset the input offset.
349 * @param byteNeeded the needed number of bytes.
350 * @throws IllegalArgumentException if the byte array does not have enough data.
351 */
352 private static void validateByteArrayOffset(final byte[] data, final int offset, final int byteNeeded) {
353 if (data.length < offset + byteNeeded) {
354 throw new IllegalArgumentException("Data only has " + data.length + "bytes, needed " + (offset + byteNeeded) + "bytes.");
355 }
356 }
357
358 /**
359 * Writes the 8 bytes of a {@code double} to a byte array at a given offset in little-endian order.
360 *
361 * @param data target byte array.
362 * @param offset starting offset in the byte array.
363 * @param value value to write.
364 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes.
365 */
366 public static void writeSwappedDouble(final byte[] data, final int offset, final double value) {
367 writeSwappedLong(data, offset, Double.doubleToLongBits(value));
368 }
369
370 /**
371 * Writes the 8 bytes of a {@code double} to an output stream in little-endian order.
372 *
373 * @param output target OutputStream.
374 * @param value value to write.
375 * @throws IOException in case of an I/O problem.
376 */
377 public static void writeSwappedDouble(final OutputStream output, final double value) throws IOException {
378 writeSwappedLong(output, Double.doubleToLongBits(value));
379 }
380
381 /**
382 * Writes the 4 bytes of a {@code float} to a byte array at a given offset in little-endian order.
383 *
384 * @param data target byte array.
385 * @param offset starting offset in the byte array.
386 * @param value value to write.
387 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
388 */
389 public static void writeSwappedFloat(final byte[] data, final int offset, final float value) {
390 writeSwappedInteger(data, offset, Float.floatToIntBits(value));
391 }
392
393 /**
394 * Writes the 4 bytes of a {@code float} to an output stream in little-endian order.
395 *
396 * @param output target OutputStream.
397 * @param value value to write.
398 * @throws IOException in case of an I/O problem.
399 */
400 public static void writeSwappedFloat(final OutputStream output, final float value) throws IOException {
401 writeSwappedInteger(output, Float.floatToIntBits(value));
402 }
403
404 /**
405 * Writes the 4 bytes of an {@code int} to a byte array at a given offset in little-endian order.
406 *
407 * @param data target byte array.
408 * @param offset starting offset in the byte array.
409 * @param value value to write.
410 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes.
411 */
412 public static void writeSwappedInteger(final byte[] data, final int offset, final int value) {
413 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE);
414 data[offset + 0] = (byte) (value >> 0 & 0xff);
415 data[offset + 1] = (byte) (value >> 8 & 0xff);
416 data[offset + 2] = (byte) (value >> 16 & 0xff);
417 data[offset + 3] = (byte) (value >> 24 & 0xff);
418 }
419
420 /**
421 * Writes the 4 bytes of an {@code int} to an output stream in little-endian order.
422 *
423 * @param output target OutputStream.
424 * @param value value to write.
425 * @throws IOException in case of an I/O problem.
426 */
427 public static void writeSwappedInteger(final OutputStream output, final int value) throws IOException {
428 output.write((byte) (value >> 0 & 0xff));
429 output.write((byte) (value >> 8 & 0xff));
430 output.write((byte) (value >> 16 & 0xff));
431 output.write((byte) (value >> 24 & 0xff));
432 }
433
434 /**
435 * Writes the 8 bytes of a {@code long} to a byte array at a given offset in little-endian order.
436 *
437 * @param data target byte array.
438 * @param offset starting offset in the byte array.
439 * @param value value to write.
440 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes.
441 */
442 public static void writeSwappedLong(final byte[] data, final int offset, final long value) {
443 validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE);
444 data[offset + 0] = (byte) (value >> 0 & 0xff);
445 data[offset + 1] = (byte) (value >> 8 & 0xff);
446 data[offset + 2] = (byte) (value >> 16 & 0xff);
447 data[offset + 3] = (byte) (value >> 24 & 0xff);
448 data[offset + 4] = (byte) (value >> 32 & 0xff);
449 data[offset + 5] = (byte) (value >> 40 & 0xff);
450 data[offset + 6] = (byte) (value >> 48 & 0xff);
451 data[offset + 7] = (byte) (value >> 56 & 0xff);
452 }
453
454 /**
455 * Writes the 8 bytes of a {@code long} to an output stream in little-endian order.
456 *
457 * @param output target OutputStream.
458 * @param value value to write.
459 * @throws IOException in case of an I/O problem.
460 */
461 public static void writeSwappedLong(final OutputStream output, final long value) throws IOException {
462 output.write((byte) (value >> 0 & 0xff));
463 output.write((byte) (value >> 8 & 0xff));
464 output.write((byte) (value >> 16 & 0xff));
465 output.write((byte) (value >> 24 & 0xff));
466 output.write((byte) (value >> 32 & 0xff));
467 output.write((byte) (value >> 40 & 0xff));
468 output.write((byte) (value >> 48 & 0xff));
469 output.write((byte) (value >> 56 & 0xff));
470 }
471
472 /**
473 * Writes the 2 bytes of a {@code short} to a byte array at a given offset in little-endian order.
474 *
475 * @param data target byte array.
476 * @param offset starting offset in the byte array.
477 * @param value value to write.
478 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes.
479 */
480 public static void writeSwappedShort(final byte[] data, final int offset, final short value) {
481 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE);
482 data[offset + 0] = (byte) (value >> 0 & 0xff);
483 data[offset + 1] = (byte) (value >> 8 & 0xff);
484 }
485
486 /**
487 * Writes the 2 bytes of a {@code short} to an output stream using little-endian encoding.
488 *
489 * @param output target OutputStream.
490 * @param value value to write.
491 * @throws IOException in case of an I/O problem.
492 */
493 public static void writeSwappedShort(final OutputStream output, final short value) throws IOException {
494 output.write((byte) (value >> 0 & 0xff));
495 output.write((byte) (value >> 8 & 0xff));
496 }
497
498 /**
499 * Instances should NOT be constructed in standard programming.
500 *
501 * @deprecated TODO Make private in 3.0.
502 */
503 @Deprecated
504 public EndianUtils() {
505 // empty
506 }
507 }