001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.imaging.common;
018
019import java.nio.ByteOrder;
020import java.util.Arrays;
021
022/**
023 * Convenience methods for converting data types to and from byte arrays.
024 */
025public final class ByteConversions {
026    public static byte[] toBytes(final double value, final ByteOrder byteOrder) {
027        final byte[] result = new byte[8];
028        toBytes(value, byteOrder, result, 0);
029        return result;
030    }
031
032    private static void toBytes(final double value, final ByteOrder byteOrder, final byte[] result, final int offset) {
033        final long bits = Double.doubleToRawLongBits(value);
034        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
035            result[offset + 0] = (byte) (0xff & bits >> 0);
036            result[offset + 1] = (byte) (0xff & bits >> 8);
037            result[offset + 2] = (byte) (0xff & bits >> 16);
038            result[offset + 3] = (byte) (0xff & bits >> 24);
039            result[offset + 4] = (byte) (0xff & bits >> 32);
040            result[offset + 5] = (byte) (0xff & bits >> 40);
041            result[offset + 6] = (byte) (0xff & bits >> 48);
042            result[offset + 7] = (byte) (0xff & bits >> 56);
043        } else {
044            result[offset + 7] = (byte) (0xff & bits >> 0);
045            result[offset + 6] = (byte) (0xff & bits >> 8);
046            result[offset + 5] = (byte) (0xff & bits >> 16);
047            result[offset + 4] = (byte) (0xff & bits >> 24);
048            result[offset + 3] = (byte) (0xff & bits >> 32);
049            result[offset + 2] = (byte) (0xff & bits >> 40);
050            result[offset + 1] = (byte) (0xff & bits >> 48);
051            result[offset + 0] = (byte) (0xff & bits >> 56);
052        }
053    }
054
055    public static byte[] toBytes(final double[] values, final ByteOrder byteOrder) {
056        return toBytes(values, 0, values.length, byteOrder);
057    }
058
059    private static byte[] toBytes(final double[] values, final int offset, final int length, final ByteOrder byteOrder) {
060        final byte[] result = Allocator.byteArray(length * 8);
061        for (int i = 0; i < length; i++) {
062            toBytes(values[offset + i], byteOrder, result, i * 8);
063        }
064        return result;
065    }
066
067    public static byte[] toBytes(final float value, final ByteOrder byteOrder) {
068        final byte[] result = new byte[4];
069        toBytes(value, byteOrder, result, 0);
070        return result;
071    }
072
073    private static void toBytes(final float value, final ByteOrder byteOrder, final byte[] result, final int offset) {
074        final int bits = Float.floatToRawIntBits(value);
075        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
076            result[offset + 0] = (byte) (0xff & bits >> 0);
077            result[offset + 1] = (byte) (0xff & bits >> 8);
078            result[offset + 2] = (byte) (0xff & bits >> 16);
079            result[offset + 3] = (byte) (0xff & bits >> 24);
080        } else {
081            result[offset + 3] = (byte) (0xff & bits >> 0);
082            result[offset + 2] = (byte) (0xff & bits >> 8);
083            result[offset + 1] = (byte) (0xff & bits >> 16);
084            result[offset + 0] = (byte) (0xff & bits >> 24);
085        }
086    }
087
088    public static byte[] toBytes(final float[] values, final ByteOrder byteOrder) {
089        return toBytes(values, 0, values.length, byteOrder);
090    }
091
092    private static byte[] toBytes(final float[] values, final int offset, final int length, final ByteOrder byteOrder) {
093        final byte[] result = Allocator.byteArray(length * 4);
094        for (int i = 0; i < length; i++) {
095            toBytes(values[offset + i], byteOrder, result, i * 4);
096        }
097        return result;
098    }
099
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}