    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.io.ByteArrayOutputStream;
    020import java.io.IOException;
    021import java.io.InputStream;
    022import java.io.OutputStream;
    023import java.io.PrintWriter;
    024import java.io.RandomAccessFile;
    025import java.nio.ByteOrder;
    026import java.util.logging.Logger;
    027
    028import org.apache.commons.imaging.ImageReadException;
    029
    030/**
    031 * Convenience methods for various binary and I/O operations.
    032 */
    033public final class BinaryFunctions {
    034
    035    private static final Logger LOGGER = Logger.getLogger(BinaryFunctions.class.getName());
    036
    037    private BinaryFunctions() {
    038    }
    039
    040    public static boolean startsWith(final byte[] haystack, final byte[] needle) {
    041        if (needle == null) {
    042            return false;
    043        }
    044        if (haystack == null) {
    045            return false;
    046        }
    047        if (needle.length > haystack.length) {
    048            return false;
    049        }
    050
    051        for (int i = 0; i < needle.length; i++) {
    052            if (needle[i] != haystack[i]) {
    053                return false;
    054            }
    055        }
    056
    057        return true;
    058    }
    059
    060    public static boolean startsWith(final byte[] haystack, final BinaryConstant needle) {
    061        if ((haystack == null) || (haystack.length < needle.size())) {
    062            return false;
    063        }
    064
    065        for (int i = 0; i < needle.size(); i++) {
    066            if (haystack[i] != needle.get(i)) {
    067                return false;
    068            }
    069        }
    070
    071        return true;
    072    }
    073
    074    public static byte readByte(final String name, final InputStream is, final String exception)
    075            throws IOException {
    076        final int result = is.read();
    077        if ((result < 0)) {
    078            throw new IOException(exception);
    079        }
    080        return (byte) (0xff & result);
    081    }
    082
    083    public static byte[] readBytes(final String name, final InputStream is, final int length)
    084            throws IOException {
    085        final String exception = name + " could not be read.";
    086        return readBytes(name, is, length, exception);
    087    }
    088
    089    public static byte[] readBytes(final String name, final InputStream is, final int length,
    090            final String exception) throws IOException {
    091        final byte[] result = new byte[length];
    092        int read = 0;
    093        while (read < length) {
    094            final int count = is.read(result, read, length - read);
    095            if (count < 0) {
    096                throw new IOException(exception + " count: " + count
    097                        + " read: " + read + " length: " + length);
    098            }
    099
    100            read += count;
    101        }
    102
    103        return result;
    104    }
    105
    106    public static byte[] readBytes(final InputStream is, final int count) throws IOException {
    107        return readBytes("", is, count, "Unexpected EOF");
    108    }
    109
    110    public static void readAndVerifyBytes(final InputStream is, final byte[] expected,
    111            final String exception) throws ImageReadException, IOException {
    112        for (final byte element : expected) {
    113            final int data = is.read();
    114            final byte b = (byte) (0xff & data);
    115
    116            if (data < 0) {
    117                throw new ImageReadException("Unexpected EOF.");
    118            }
    119
    120            if (b != element) {
    121                throw new ImageReadException(exception);
    122            }
    123        }
    124    }
    125
    126    public static void readAndVerifyBytes(final InputStream is,
    127            final BinaryConstant expected, final String exception)
    128            throws ImageReadException, IOException {
    129        for (int i = 0; i < expected.size(); i++) {
    130            final int data = is.read();
    131            final byte b = (byte) (0xff & data);
    132
    133            if (data < 0) {
    134                throw new ImageReadException("Unexpected EOF.");
    135            }
    136
    137            if (b != expected.get(i)) {
    138                throw new ImageReadException(exception);
    139            }
    140        }
    141    }
    142
    143    public static void skipBytes(final InputStream is, final long length, final String exception)
    144            throws IOException {
    145        long total = 0;
    146        while (length != total) {
    147            final long skipped = is.skip(length - total);
    148            if (skipped < 1) {
    149                throw new IOException(exception + " (" + skipped + ")");
    150            }
    151            total += skipped;
    152        }
    153    }
    154
    155    public static byte[] remainingBytes(final String name, final byte[] bytes, final int count) {
    156        return slice(bytes, count, bytes.length - count);
    157    }
    158
    159    public static byte[] slice(final byte[] bytes, final int start, final int count) {
    160        final byte[] result = new byte[count];
    161        System.arraycopy(bytes, start, result, 0, count);
    162        return result;
    163    }
    164
    165    public static byte[] head(final byte[] bytes, int count) {
    166        if (count > bytes.length) {
    167            count = bytes.length;
    168        }
    169        return slice(bytes, 0, count);
    170    }
    171
    172    public static boolean compareBytes(final byte[] a, final int aStart, final byte[] b,
    173            final int bStart, final int length) {
    174        if (a.length < (aStart + length)) {
    175            return false;
    176        }
    177        if (b.length < (bStart + length)) {
    178            return false;
    179        }
    180
    181        for (int i = 0; i < length; i++) {
    182            if (a[aStart + i] != b[bStart + i]) {
    183                return false;
    184            }
    185        }
    186
    187        return true;
    188    }
    189
    190    public static int read4Bytes(final String name, final InputStream is,
    191            final String exception, final ByteOrder byteOrder) throws IOException {
    192        final int byte0 = is.read();
    193        final int byte1 = is.read();
    194        final int byte2 = is.read();
    195        final int byte3 = is.read();
    196        if ((byte0 | byte1 | byte2 | byte3) < 0) {
    197            throw new IOException(exception);
    198        }
    199
    200        final int result;
    201        if (byteOrder == ByteOrder.BIG_ENDIAN) {
    202            result = (byte0 << 24) | (byte1 << 16)
    203                    | (byte2 << 8) | (byte3 << 0);
    204        } else {
    205            result = (byte3 << 24) | (byte2 << 16)
    206                    | (byte1 << 8) | (byte0 << 0);
    207        }
    208
    209        return result;
    210    }
    211
    212    public static int read3Bytes(final String name, final InputStream is,
    213            final String exception, final ByteOrder byteOrder) throws IOException {
    214        final int byte0 = is.read();
    215        final int byte1 = is.read();
    216        final int byte2 = is.read();
    217        if ((byte0 | byte1 | byte2) < 0) {
    218            throw new IOException(exception);
    219        }
    220
    221        final int result;
    222        if (byteOrder == ByteOrder.BIG_ENDIAN) {
    223            result = (byte0 << 16) | (byte1 << 8)
    224                    | (byte2 << 0);
    225        } else {
    226            result = (byte2 << 16) | (byte1 << 8)
    227                    | (byte0 << 0);
    228        }
    229
    230        return result;
    231    }
    232
    233    public static int read2Bytes(final String name, final InputStream is,
    234            final String exception, final ByteOrder byteOrder) throws IOException {
    235        final int byte0 = is.read();
    236        final int byte1 = is.read();
    237        if ((byte0 | byte1) < 0) {
    238            throw new IOException(exception);
    239        }
    240
    241        final int result;
    242        if (byteOrder == ByteOrder.BIG_ENDIAN) {
    243            result = (byte0 << 8) | byte1;
    244        } else {
    245            result = (byte1 << 8) | byte0;
    246        }
    247
    248        return result;
    249    }
    250
    251    public static void printCharQuad(final String msg, final int i) {
    252        LOGGER.finest(msg + ": '" + (char) (0xff & (i >> 24))
    253                + (char) (0xff & (i >> 16)) + (char) (0xff & (i >> 8))
    254                + (char) (0xff & (i >> 0)) + "'");
    255
    256    }
    257
    258    public static void printCharQuad(final PrintWriter pw, final String msg, final int i) {
    259        pw.println(msg + ": '" + (char) (0xff & (i >> 24))
    260                + (char) (0xff & (i >> 16)) + (char) (0xff & (i >> 8))
    261                + (char) (0xff & (i >> 0)) + "'");
    262
    263    }
    264
    265    public static void printByteBits(final String msg, final byte i) {
    266        LOGGER.finest(msg + ": '" + Integer.toBinaryString(0xff & i));
    267    }
    268
    269    public static int charsToQuad(final char c1, final char c2, final char c3, final char c4) {
    270        return (((0xff & c1) << 24) | ((0xff & c2) << 16) | ((0xff & c3) << 8) | ((0xff & c4) << 0));
    271    }
    272
    273    /**
    274     * Convert a quad into a byte array.
    275     * @param quad quad
    276     * @return a byte array
    277     */
    278    public static byte[] quadsToByteArray(int quad) {
    279        byte[] arr = new byte[4];
    280        arr[0] = (byte) (quad >> 24);
    281        arr[1] = (byte) (quad >> 16);
    282        arr[2] = (byte) (quad >> 8);
    283        arr[3] = (byte) quad;
    284        return arr;
    285    }
    286
    287    /**
    288     * Consumes the {@code InputStream} (without closing it) searching for a quad. It will
    289     * stop either when the quad is found, or when there are no more bytes in the input stream.
    290     *
    291     * <p>Returns {@code true} if it found the quad, and {@code false} otherwise.
    292     *
    293     * @param quad a quad (the needle)
    294     * @param bis an input stream (the haystack)
    295     * @return {@code true} if it found the quad, and {@code false} otherwise
    296     * @throws IOException
    297     */
    298    public static boolean searchQuad(int quad, InputStream bis) throws IOException {
    299        byte[] needle = BinaryFunctions.quadsToByteArray(quad);
    300        int b = -1;
    301        int position = 0;
    302        while ((b = bis.read()) != -1) {
    303            if (needle[position] == b) {
    304                position++;
    305                if (position == needle.length) {
    306                    return true;
    307                }
    308            } else {
    309                position = 0;
    310            }
    311        }
    312        return false;
    313    }
    314
    315    public static int findNull(final byte[] src) {
    316        return findNull(src, 0);
    317    }
    318
    319    public static int findNull(final byte[] src, final int start) {
    320        for (int i = start; i < src.length; i++) {
    321            if (src[i] == 0) {
    322                return i;
    323            }
    324        }
    325        return -1;
    326    }
    327
    328    public static byte[] getRAFBytes(final RandomAccessFile raf, final long pos,
    329            final int length, final String exception) throws IOException {
    330        final byte[] result = new byte[length];
    331
    332        raf.seek(pos);
    333
    334        int read = 0;
    335        while (read < length) {
    336            final int count = raf.read(result, read, length - read);
    337            if (count < 0) {
    338                throw new IOException(exception);
    339            }
    340
    341            read += count;
    342        }
    343
    344        return result;
    345
    346    }
    347
    348    public static void skipBytes(final InputStream is, final long length) throws IOException {
    349        skipBytes(is, length, "Couldn't skip bytes");
    350    }
    351
    352    public static void copyStreamToStream(final InputStream is, final OutputStream os)
    353            throws IOException {
    354        final byte[] buffer = new byte[1024];
    355        int read;
    356        while ((read = is.read(buffer)) > 0) {
    357            os.write(buffer, 0, read);
    358        }
    359    }
    360
    361    public static byte[] getStreamBytes(final InputStream is) throws IOException {
    362        final ByteArrayOutputStream os = new ByteArrayOutputStream();
    363        copyStreamToStream(is, os);
    364        return os.toByteArray();
    365    }
    366}