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    *      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 &mdash; e.g. {@code DataInputStream} and
37   * {@code Double.longBitsToDouble()} &mdash; 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 }