    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}