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