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  package org.apache.commons.imaging.common;
18  
19  import java.nio.ByteOrder;
20  import java.util.Arrays;
21  
22  /**
23   * Convenience methods for converting data types to and from byte arrays.
24   */
25  public final class ByteConversions {
26      public static byte[] toBytes(final double value, final ByteOrder byteOrder) {
27          final byte[] result = new byte[8];
28          toBytes(value, byteOrder, result, 0);
29          return result;
30      }
31  
32      private static void toBytes(final double value, final ByteOrder byteOrder, final byte[] result, final int offset) {
33          final long bits = Double.doubleToRawLongBits(value);
34          if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
35              result[offset + 0] = (byte) (0xff & bits >> 0);
36              result[offset + 1] = (byte) (0xff & bits >> 8);
37              result[offset + 2] = (byte) (0xff & bits >> 16);
38              result[offset + 3] = (byte) (0xff & bits >> 24);
39              result[offset + 4] = (byte) (0xff & bits >> 32);
40              result[offset + 5] = (byte) (0xff & bits >> 40);
41              result[offset + 6] = (byte) (0xff & bits >> 48);
42              result[offset + 7] = (byte) (0xff & bits >> 56);
43          } else {
44              result[offset + 7] = (byte) (0xff & bits >> 0);
45              result[offset + 6] = (byte) (0xff & bits >> 8);
46              result[offset + 5] = (byte) (0xff & bits >> 16);
47              result[offset + 4] = (byte) (0xff & bits >> 24);
48              result[offset + 3] = (byte) (0xff & bits >> 32);
49              result[offset + 2] = (byte) (0xff & bits >> 40);
50              result[offset + 1] = (byte) (0xff & bits >> 48);
51              result[offset + 0] = (byte) (0xff & bits >> 56);
52          }
53      }
54  
55      public static byte[] toBytes(final double[] values, final ByteOrder byteOrder) {
56          return toBytes(values, 0, values.length, byteOrder);
57      }
58  
59      private static byte[] toBytes(final double[] values, final int offset, final int length, final ByteOrder byteOrder) {
60          final byte[] result = Allocator.byteArray(length * 8);
61          for (int i = 0; i < length; i++) {
62              toBytes(values[offset + i], byteOrder, result, i * 8);
63          }
64          return result;
65      }
66  
67      public static byte[] toBytes(final float value, final ByteOrder byteOrder) {
68          final byte[] result = new byte[4];
69          toBytes(value, byteOrder, result, 0);
70          return result;
71      }
72  
73      private static void toBytes(final float value, final ByteOrder byteOrder, final byte[] result, final int offset) {
74          final int bits = Float.floatToRawIntBits(value);
75          if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
76              result[offset + 0] = (byte) (0xff & bits >> 0);
77              result[offset + 1] = (byte) (0xff & bits >> 8);
78              result[offset + 2] = (byte) (0xff & bits >> 16);
79              result[offset + 3] = (byte) (0xff & bits >> 24);
80          } else {
81              result[offset + 3] = (byte) (0xff & bits >> 0);
82              result[offset + 2] = (byte) (0xff & bits >> 8);
83              result[offset + 1] = (byte) (0xff & bits >> 16);
84              result[offset + 0] = (byte) (0xff & bits >> 24);
85          }
86      }
87  
88      public static byte[] toBytes(final float[] values, final ByteOrder byteOrder) {
89          return toBytes(values, 0, values.length, byteOrder);
90      }
91  
92      private static byte[] toBytes(final float[] values, final int offset, final int length, final ByteOrder byteOrder) {
93          final byte[] result = Allocator.byteArray(length * 4);
94          for (int i = 0; i < length; i++) {
95              toBytes(values[offset + i], byteOrder, result, i * 4);
96          }
97          return result;
98      }
99  
100     public static byte[] toBytes(final int value, final ByteOrder byteOrder) {
101         final byte[] result = new byte[4];
102         toBytes(value, byteOrder, result, 0);
103         return result;
104     }
105 
106     private static void toBytes(final int value, final ByteOrder byteOrder, final byte[] result, final int offset) {
107         if (byteOrder == ByteOrder.BIG_ENDIAN) {
108             result[offset + 0] = (byte) (value >> 24);
109             result[offset + 1] = (byte) (value >> 16);
110             result[offset + 2] = (byte) (value >> 8);
111             result[offset + 3] = (byte) (value >> 0);
112         } else {
113             result[offset + 3] = (byte) (value >> 24);
114             result[offset + 2] = (byte) (value >> 16);
115             result[offset + 1] = (byte) (value >> 8);
116             result[offset + 0] = (byte) (value >> 0);
117         }
118     }
119 
120     public static byte[] toBytes(final int[] values, final ByteOrder byteOrder) {
121         return toBytes(values, 0, values.length, byteOrder);
122     }
123 
124     private static byte[] toBytes(final int[] values, final int offset, final int length, final ByteOrder byteOrder) {
125         final byte[] result = Allocator.byteArray(length * 4);
126         for (int i = 0; i < length; i++) {
127             toBytes(values[offset + i], byteOrder, result, i * 4);
128         }
129         return result;
130     }
131 
132     /**
133      * Encodes an eight-byte (long) into an array of bytes based on the specified byte order.
134      *
135      * @param value     a standard data primitive of type long
136      * @param byteOrder the byte order to be used for encoding
137      * @return an array of length 8
138      */
139     public static byte[] toBytes(final long value, final ByteOrder byteOrder) {
140         final byte[] result = new byte[8];
141         toBytes(value, byteOrder, result, 0);
142         return result;
143     }
144 
145     private static void toBytes(final long value, final ByteOrder byteOrder, final byte[] result, final int offset) {
146         if (byteOrder == ByteOrder.BIG_ENDIAN) {
147             result[offset + 0] = (byte) (value >> 56);
148             result[offset + 1] = (byte) (value >> 48);
149             result[offset + 2] = (byte) (value >> 40);
150             result[offset + 3] = (byte) (value >> 32);
151             result[offset + 4] = (byte) (value >> 24);
152             result[offset + 5] = (byte) (value >> 16);
153             result[offset + 6] = (byte) (value >> 8);
154             result[offset + 7] = (byte) value;
155         } else {
156             result[offset + 7] = (byte) (value >> 56);
157             result[offset + 6] = (byte) (value >> 48);
158             result[offset + 5] = (byte) (value >> 40);
159             result[offset + 4] = (byte) (value >> 32);
160             result[offset + 3] = (byte) (value >> 24);
161             result[offset + 2] = (byte) (value >> 16);
162             result[offset + 1] = (byte) (value >> 8);
163             result[offset + 0] = (byte) value;
164         }
165     }
166 
167     public static byte[] toBytes(final RationalNumber value, final ByteOrder byteOrder) {
168         final byte[] result = new byte[8];
169         toBytes(value, byteOrder, result, 0);
170         return result;
171     }
172 
173     private static void toBytes(final RationalNumber value, final ByteOrder byteOrder, final byte[] result, final int offset) {
174         if (byteOrder == ByteOrder.BIG_ENDIAN) {
175             result[offset + 0] = (byte) (value.numerator >> 24);
176             result[offset + 1] = (byte) (value.numerator >> 16);
177             result[offset + 2] = (byte) (value.numerator >> 8);
178             result[offset + 3] = (byte) (value.numerator >> 0);
179             result[offset + 4] = (byte) (value.divisor >> 24);
180             result[offset + 5] = (byte) (value.divisor >> 16);
181             result[offset + 6] = (byte) (value.divisor >> 8);
182             result[offset + 7] = (byte) (value.divisor >> 0);
183         } else {
184             result[offset + 3] = (byte) (value.numerator >> 24);
185             result[offset + 2] = (byte) (value.numerator >> 16);
186             result[offset + 1] = (byte) (value.numerator >> 8);
187             result[offset + 0] = (byte) (value.numerator >> 0);
188             result[offset + 7] = (byte) (value.divisor >> 24);
189             result[offset + 6] = (byte) (value.divisor >> 16);
190             result[offset + 5] = (byte) (value.divisor >> 8);
191             result[offset + 4] = (byte) (value.divisor >> 0);
192         }
193     }
194 
195     public static byte[] toBytes(final RationalNumber[] values, final ByteOrder byteOrder) {
196         return toBytes(values, 0, values.length, byteOrder);
197     }
198 
199     private static byte[] toBytes(final RationalNumber[] values, final int offset, final int length, final ByteOrder byteOrder) {
200         final byte[] result = Allocator.byteArray(length * 8);
201         for (int i = 0; i < length; i++) {
202             toBytes(values[offset + i], byteOrder, result, i * 8);
203         }
204         return result;
205     }
206 
207     public static byte[] toBytes(final short value, final ByteOrder byteOrder) {
208         final byte[] result = new byte[2];
209         toBytes(value, byteOrder, result, 0);
210         return result;
211     }
212 
213     private static void toBytes(final short value, final ByteOrder byteOrder, final byte[] result, final int offset) {
214         if (byteOrder == ByteOrder.BIG_ENDIAN) {
215             result[offset + 0] = (byte) (value >> 8);
216             result[offset + 1] = (byte) (value >> 0);
217         } else {
218             result[offset + 1] = (byte) (value >> 8);
219             result[offset + 0] = (byte) (value >> 0);
220         }
221     }
222 
223     public static byte[] toBytes(final short[] values, final ByteOrder byteOrder) {
224         return toBytes(values, 0, values.length, byteOrder);
225     }
226 
227     private static byte[] toBytes(final short[] values, final int offset, final int length, final ByteOrder byteOrder) {
228         final byte[] result = Allocator.byteArray(length * 2);
229         for (int i = 0; i < length; i++) {
230             toBytes(values[offset + i], byteOrder, result, i * 2);
231         }
232         return result;
233     }
234 
235     public static double toDouble(final byte[] bytes, final ByteOrder byteOrder) {
236         return toDouble(bytes, 0, byteOrder);
237     }
238 
239     private static double toDouble(final byte[] bytes, final int offset, final ByteOrder byteOrder) {
240         final long byte0 = 0xffL & bytes[offset + 0];
241         final long byte1 = 0xffL & bytes[offset + 1];
242         final long byte2 = 0xffL & bytes[offset + 2];
243         final long byte3 = 0xffL & bytes[offset + 3];
244         final long byte4 = 0xffL & bytes[offset + 4];
245         final long byte5 = 0xffL & bytes[offset + 5];
246         final long byte6 = 0xffL & bytes[offset + 6];
247         final long byte7 = 0xffL & bytes[offset + 7];
248         final long bits;
249         if (byteOrder == ByteOrder.BIG_ENDIAN) {
250             bits = byte0 << 56 | byte1 << 48 | byte2 << 40 | byte3 << 32 | byte4 << 24 | byte5 << 16 | byte6 << 8 | byte7 << 0;
251         } else {
252             bits = byte7 << 56 | byte6 << 48 | byte5 << 40 | byte4 << 32 | byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0 << 0;
253         }
254         return Double.longBitsToDouble(bits);
255     }
256 
257     public static double[] toDoubles(final byte[] bytes, final ByteOrder byteOrder) {
258         return toDoubles(bytes, 0, bytes.length, byteOrder);
259     }
260 
261     private static double[] toDoubles(final byte[] bytes, final int offset, final int length, final ByteOrder byteOrder) {
262         final double[] result = Allocator.doubleArray(length / 8);
263         Arrays.setAll(result, i -> toDouble(bytes, offset + 8 * i, byteOrder));
264         return result;
265     }
266 
267     public static float toFloat(final byte[] bytes, final ByteOrder byteOrder) {
268         return toFloat(bytes, 0, byteOrder);
269     }
270 
271     private static float toFloat(final byte[] bytes, final int offset, final ByteOrder byteOrder) {
272         final int byte0 = 0xff & bytes[offset + 0];
273         final int byte1 = 0xff & bytes[offset + 1];
274         final int byte2 = 0xff & bytes[offset + 2];
275         final int byte3 = 0xff & bytes[offset + 3];
276         final int bits;
277         if (byteOrder == ByteOrder.BIG_ENDIAN) {
278             bits = byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3 << 0;
279         } else {
280             bits = byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0 << 0;
281         }
282         return Float.intBitsToFloat(bits);
283     }
284 
285     public static float[] toFloats(final byte[] bytes, final ByteOrder byteOrder) {
286         return toFloats(bytes, 0, bytes.length, byteOrder);
287     }
288 
289     private static float[] toFloats(final byte[] bytes, final int offset, final int length, final ByteOrder byteOrder) {
290         final float[] result = Allocator.floatArray(length / 4);
291         for (int i = 0; i < result.length; i++) {
292             result[i] = toFloat(bytes, offset + 4 * i, byteOrder);
293         }
294         return result;
295     }
296 
297     public static int toInt(final byte[] bytes, final ByteOrder byteOrder) {
298         return toInt(bytes, 0, byteOrder);
299     }
300 
301     public static int toInt(final byte[] bytes, final int offset, final ByteOrder byteOrder) {
302         final int byte0 = 0xff & bytes[offset + 0];
303         final int byte1 = 0xff & bytes[offset + 1];
304         final int byte2 = 0xff & bytes[offset + 2];
305         final int byte3 = 0xff & bytes[offset + 3];
306         if (byteOrder == ByteOrder.BIG_ENDIAN) {
307             return byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3;
308         }
309         return byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0;
310     }
311 
312     public static int[] toInts(final byte[] bytes, final ByteOrder byteOrder) {
313         return toInts(bytes, 0, bytes.length, byteOrder);
314     }
315 
316     private static int[] toInts(final byte[] bytes, final int offset, final int length, final ByteOrder byteOrder) {
317         final int[] result = Allocator.intArray(length / 4);
318         Arrays.setAll(result, i -> toInt(bytes, offset + 4 * i, byteOrder));
319         return result;
320     }
321 
322     /**
323      * Extracts an eight-byte long integer from the specified byte array. This method assumes that the byte array is of sufficiently large size to encode a long
324      * integer.
325      *
326      * @param bytes     an array of size at least 8
327      * @param byteOrder the byte-order for interpreting the input bytes
328      * @return an eight-byte signed integer
329      */
330     public static long toLong(final byte[] bytes, final ByteOrder byteOrder) {
331         return toLong(bytes, 0, byteOrder);
332     }
333 
334     private static long toLong(final byte[] bytes, final int offset, final ByteOrder byteOrder) {
335         final long byte0 = 0xffL & bytes[offset + 0];
336         final long byte1 = 0xffL & bytes[offset + 1];
337         final long byte2 = 0xffL & bytes[offset + 2];
338         final long byte3 = 0xffL & bytes[offset + 3];
339         final long byte4 = 0xffL & bytes[offset + 4];
340         final long byte5 = 0xffL & bytes[offset + 5];
341         final long byte6 = 0xffL & bytes[offset + 6];
342         final long byte7 = 0xffL & bytes[offset + 7];
343 
344         if (byteOrder == ByteOrder.BIG_ENDIAN) {
345             return byte0 << 56 | byte1 << 48 | byte2 << 40 | byte3 << 32 | byte4 << 24 | byte5 << 16 | byte6 << 8 | byte7;
346         }
347         return byte7 << 56 | byte6 << 48 | byte5 << 40 | byte4 << 32 | byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0;
348     }
349 
350     /**
351      * Extracts an array of eight-byte long integers from the specified array of bytes. The size of the result array is computed based on the size of the input
352      * byte array.
353      *
354      * @param bytes     a valid array
355      * @param byteOrder the byte-order for interpreting the input bytes
356      * @return an array of zero or more eight-byte signed integers
357      */
358     public static long[] toLongs(final byte[] bytes, final ByteOrder byteOrder) {
359         return toLongs(bytes, 0, bytes.length, byteOrder);
360     }
361 
362     private static long[] toLongs(final byte[] bytes, final int offset, final int length, final ByteOrder byteOrder) {
363         final long[] result = Allocator.longArray(length / 8);
364         Arrays.setAll(result, i -> toLong(bytes, offset + 8 * i, byteOrder));
365         return result;
366     }
367 
368     /**
369      * Interprets the content of a specified bytes array to create an instance of the RationalNumber class.
370      *
371      * @param bytes        a valid array dimensioned to at least 8.
372      * @param byteOrder    the byte order for integer conversion
373      * @param unsignedType indicates whether the extracted value is an unsigned type.
374      * @return a valid instance
375      */
376     public static RationalNumber toRational(final byte[] bytes, final ByteOrder byteOrder, final boolean unsignedType) {
377         return toRational(bytes, 0, byteOrder, unsignedType);
378     }
379 
380     private static RationalNumber toRational(final byte[] bytes, final int offset, final ByteOrder byteOrder, final boolean unsignedType) {
381         final int byte0 = 0xff & bytes[offset + 0];
382         final int byte1 = 0xff & bytes[offset + 1];
383         final int byte2 = 0xff & bytes[offset + 2];
384         final int byte3 = 0xff & bytes[offset + 3];
385         final int byte4 = 0xff & bytes[offset + 4];
386         final int byte5 = 0xff & bytes[offset + 5];
387         final int byte6 = 0xff & bytes[offset + 6];
388         final int byte7 = 0xff & bytes[offset + 7];
389         final int numerator;
390         final int divisor;
391         if (byteOrder == ByteOrder.BIG_ENDIAN) {
392             numerator = byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3;
393             divisor = byte4 << 24 | byte5 << 16 | byte6 << 8 | byte7;
394         } else {
395             numerator = byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0;
396             divisor = byte7 << 24 | byte6 << 16 | byte5 << 8 | byte4;
397         }
398         return new RationalNumber(numerator, divisor, unsignedType);
399     }
400 
401     public static RationalNumber[] toRationals(final byte[] bytes, final ByteOrder byteOrder, final boolean unsignedType) {
402         return toRationals(bytes, 0, bytes.length, byteOrder, unsignedType);
403     }
404 
405     private static RationalNumber[] toRationals(final byte[] bytes, final int offset, final int length, final ByteOrder byteOrder, final boolean unsignedType) {
406         final RationalNumber[] result = new RationalNumber[length / 8];
407         Arrays.setAll(result, i -> toRational(bytes, offset + 8 * i, byteOrder, unsignedType));
408         return result;
409     }
410 
411     public static short toShort(final byte[] bytes, final ByteOrder byteOrder) {
412         return toShort(bytes, 0, byteOrder);
413     }
414 
415     private static short toShort(final byte[] bytes, final int offset, final ByteOrder byteOrder) {
416         return (short) toUInt16(bytes, offset, byteOrder);
417     }
418 
419     public static short[] toShorts(final byte[] bytes, final ByteOrder byteOrder) {
420         return toShorts(bytes, 0, bytes.length, byteOrder);
421     }
422 
423     private static short[] toShorts(final byte[] bytes, final int offset, final int length, final ByteOrder byteOrder) {
424         final short[] result = Allocator.shortArray(length / 2);
425         for (int i = 0; i < result.length; i++) {
426             result[i] = toShort(bytes, offset + 2 * i, byteOrder);
427         }
428         return result;
429     }
430 
431     public static int toUInt16(final byte[] bytes, final ByteOrder byteOrder) {
432         return toUInt16(bytes, 0, byteOrder);
433     }
434 
435     public static int toUInt16(final byte[] bytes, final int offset, final ByteOrder byteOrder) {
436         final int byte0 = 0xff & bytes[offset + 0];
437         final int byte1 = 0xff & bytes[offset + 1];
438         if (byteOrder == ByteOrder.BIG_ENDIAN) {
439             return byte0 << 8 | byte1;
440         }
441         return byte1 << 8 | byte0;
442     }
443 
444     public static int[] toUInt16s(final byte[] bytes, final ByteOrder byteOrder) {
445         return toUInt16s(bytes, 0, bytes.length, byteOrder);
446     }
447 
448     private static int[] toUInt16s(final byte[] bytes, final int offset, final int length, final ByteOrder byteOrder) {
449         final int[] result = Allocator.intArray(length / 2);
450         Arrays.setAll(result, i -> toUInt16(bytes, offset + 2 * i, byteOrder));
451         return result;
452     }
453 
454     private ByteConversions() {
455     }
456 }