/*****************************************************************************
 * LittleEndianConverter.java
 * Java Source
 *
 * This source is licensed under the GNU LGPL v2.1.
 * Please read http://www.gnu.org/copyleft/lgpl.html for more information.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 *
 * Copyright (c) 2001, 2002 Dipl. Ing. P. Szawlowski
 * University of Vienna, Dept. of Medical Computer Sciences
 ****************************************************************************/

package org.broad.igv.preprocess;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

 /**  
  * Utility to convert little endian data to big endian data. Includes methods
  * to read from an <code>InputStream</code> and write to an
  * <code>OutputStream</code>.
  * <p>
  * Todo: extend to convert big endian to little endain data and write to
  * <code>OutputStream</code>
  * @author  Dipl. Ing. Paul Szawlowski -
  *          University of Vienna, Dept. of Medical Computer Sciences
  * @version $Revision: 1.1 $
  */
public class EndianConverter
{
    /**
     * Converts byte in little/big endian order in <code>srcBuffer</code> to
     * big/little endian signed short (2 bytes long) data.
     * @param srcBuffer Bytes in little/big endian order which shall be
     *      converted. The size of the array must be at least 2.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Max. number of data to be written in
     *      <code>destBuffer</code>
     * @return (even) number of processed bytes of srcBuffer
     */
    public static int convert
    (
        final byte[ ]   srcBuffer,
        final short[ ]  destBuffer,
        final int       srcLength,
        final int       destOffset,
        final int       destLength
    )
    {
        return convert
        (
            srcBuffer,
            destBuffer,
            srcLength,
            destOffset,
            destLength,
            ( short )0xff
        );
    }

    /**
     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
     * big/little endian short (2 bytes long) data. Significant bits can be
     * masked, e. g. to get unsigned 7 bit values use <code>0x7f</code> as mask.
     * @param srcBuffer Bytes in little/big endian order which shall be
     *      converted. The size of the array must be at least 2.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Max. number of data to be written in
     *      <code>destBuffer</code>
     * @param mask Mask for significant bits. Set significant bits to 1.
     * @return (even) number of processed bytes of srcBuffer
     */
    public static int convert
    (
        final byte[ ]   srcBuffer,
        final short[ ]  destBuffer,
        int             srcLength,
        final int       destOffset,
        final int       destLength,
        final short     mask
    )
    {
        srcLength = Math.min( destLength * 2, ( srcLength / 2 ) * 2 );
        for( int i = 0; i < srcLength; i += 2 )
        {
            final int tmp =
                ( srcBuffer[ i ] & 0xff | ( srcBuffer[ i + 1 ] << 8 ) ) & mask;
            destBuffer[ ( i / 2 ) + destOffset ] = ( short )tmp;
        }
        return srcLength;
    }

    /**
     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
     * big/little endian signed integer (4 bytes long) data.
     * @param srcBuffer Bytes in little/big endian order which shall be
     *      converted. The size of the array must be at least 4.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Maximum number of data to be written in
     *      <code>destBuffer</code>
     * @return number of processed bytes of srcBuffer (multiple of 4 )
     */
    public static int convert
    (
        final byte[ ]   srcBuffer,
        final int[ ]    destBuffer,
        final int       srcLength,
        final int       destOffset,
        final int       destLength
    )
    {
        return convert
        (
            srcBuffer,
            destBuffer,
            srcLength,
            destOffset,
            destLength,
            0xffffffff
        );
    }

    /**
     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
     * big/little endian integer (4 bytes long) data. Significant bits can be
     * masked, e. g. to get unsigned 31 bit values use <code>0x7fffffff</code>
     * as mask.
     * @param srcBuffer Bytes in little/big endian order which shall be
     *  converted. The size of the array must be at least 4.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Maximum number of data to be written in
     *      <code>destBuffer</code>
     * @param mask Mask for significant bits. Set significant bits to 1.
     * @return number of processed bytes of srcBuffer (multiple of 4 )
     */
    public static int convert
    (
        final byte[ ]   srcBuffer,
        final int[ ]    destBuffer,
        int             srcLength,
        final int       destOffset,
        final int       destLength,
        final int       mask
    )
    {
        srcLength = Math.min( destLength * 4, ( srcLength / 4 ) * 4 );
        for( int i = 0; i < srcLength; i += 4 )
        {
            destBuffer[ ( i / 4 ) + destOffset ] = ( srcBuffer[ i ] & 0xff
                | ( srcBuffer[ i + 1 ] << 8 ) & 0xff00
                | ( srcBuffer[ i + 2 ] << 16 ) & 0xff0000
                | ( srcBuffer[ i + 3 ] << 24 ) ) & mask;
        }
        return srcLength;
    }

    /**
     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
     * big/little endian long (8 bytes long) data.
     * @param srcBuffer Bytes in little/big endian order which shall be
     *  converted. The size of the array must be at least 8.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Maximum number of data to be written in
     *      <code>destBuffer</code>
     * @return number of processed bytes of srcBuffer (multiple of 8 )
     */
    public static int convert
    (
        final byte[ ]   srcBuffer,
        final long[ ]   destBuffer,
        final int       srcLength,
        final int       destOffset,
        final int       destLength
    )
    {
        return convert
        (
            srcBuffer,
            destBuffer,
            srcLength,
            destOffset,
            destLength,
            0xffffffffffffffffL
        );

    }

    /**
     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
     * big/little endian long (8 bytes long) data. Significant bits can be
     * masked, e. g. to get unsigned 63 bit values use
     * <code>0x7fffffffffffffff</code> as mask.
     * @param srcBuffer Bytes in little/big endian order which shall be
     *  converted. The size of the array must be at least 8.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Maximum number of data to be written in
     *      <code>destBuffer</code>
     * @param mask Mask for significant bits. Set significant bits to 1.
     * @return number of processed bytes of srcBuffer (multiple of 8 )
     */
    public static int convert
    (
        final byte[ ]   srcBuffer,
        final long[ ]   destBuffer,
        int             srcLength,
        final int       destOffset,
        final int       destLength,
        final long      mask
    )
    {
        srcLength = Math.min( destLength * 8, ( srcLength / 8 ) * 8 );
        for( int i = 0; i < srcLength; i += 8 )
        {
            destBuffer[ ( i / 8 ) + destOffset ] = ( srcBuffer[ i ] & 0xff
                | ( srcBuffer[ i + 1 ] << 8 ) & 0xff00
                | ( srcBuffer[ i + 2 ] << 16 ) & 0xff0000
                | ( srcBuffer[ i + 3 ] << 24 ) & 0xff000000
                | ( srcBuffer[ i + 3 ] << 32 ) & 0xff00000000L
                | ( srcBuffer[ i + 5 ] << 40 ) & 0xff0000000000L
                | ( srcBuffer[ i + 6 ] << 48 ) & 0xff000000000000L
                | ( srcBuffer[ i + 7 ] << 56 ) ) & mask;
        }
        return srcLength;
    }

    /**
     * Convert a little/big endian signed short to a big/little endian signed
     * short
     * @param value number to convert
     */
    public static short convert( final short value )
    {
        return ( short )( ( value >> 8 ) & 0xff | ( value << 8 ) );
    }

    /**
     * Convert a little/big endian signed integer to a big/little endian signed
     * integer
     * @param value number to convert
     */
    public static int convert( final int value )
    {
        return ( value >> 24 ) & 0xff
            | ( value >> 8 ) & 0xff00
            | ( value << 8 ) & 0xff0000
            | value << 24;
    }

    /**
     * Convert a little/big endian signed long to a big/little endian signed
     * long
     * @param value number to convert
     */
    public static long convert( final long value )
    {
        return ( value >> 56 ) & 0xff
            | ( value >> 40 ) & 0xff00
            | ( value >> 24 ) & 0xff0000
            | ( value >> 8 ) & 0xff000000
            | ( value << 8 ) & 0xff00000000L
            | ( value << 24 ) & 0xff00000000L
            | ( value << 56 ) & 0xff0000000000L;
    }

    /**
     * Converts bytes in <code>srcBuffer</code> in little endian order to float
     * data.
     * @param srcBuffer Bytes in little endian order which shall be
     *  converted. The size of the array must be at least 4.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Maximum number of data to be written in
     *      <code>destBuffer</code>
     * @return number of processed bytes of srcBuffer (multiple of 4 )
     */
    public static int convertLittleEndianToFloat
    (
        final byte[ ]   srcBuffer,
        final float[ ]  destBuffer,
        int             srcLength,
        final int       destOffset,
        final int       destLength
    )
    {
        srcLength = Math.min( destLength * 4, ( srcLength / 4 ) * 4 );
        for( int i = 0; i < srcLength; i += 4 )
        {
            destBuffer[ ( i / 4 ) + destOffset ] = Float.intBitsToFloat
            (
                srcBuffer[ i ] & 0xff
                | ( srcBuffer[ i + 1 ] << 8 ) & 0xff00
                | ( srcBuffer[ i + 2 ] << 16 ) & 0xff0000
                | ( srcBuffer[ i + 3 ] << 24 )
            );
        }
        return srcLength;
    }

    /**
     * Converts bytes in <code>srcBuffer</code> in little endian order to double
     * data.
     * @param srcBuffer Bytes in little endian order which shall be
     *  converted. The size of the array must be at least 8.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Maximum number of data to be written in
     *      <code>destBuffer</code>
     * @return number of processed bytes of srcBuffer (multiple of 8 )
     */
    public static int convertLittleEndianToDouble
    (
        final byte[ ]   srcBuffer,
        final double[ ] destBuffer,
        int             srcLength,
        final int       destOffset,
        final int       destLength
    )
    {
        srcLength = Math.min( destLength * 8, ( srcLength / 8 ) * 8 );
        for( int i = 0; i < srcLength; i += 8 )
        {
            destBuffer[ ( i / 8 ) + destOffset ] = Double.longBitsToDouble
            (
                srcBuffer[ i ] & 0xff
                | ( srcBuffer[ i + 1 ] << 8 ) & 0xff00
                | ( srcBuffer[ i + 2 ] << 16 ) & 0xff0000
                | ( srcBuffer[ i + 3 ] << 24 ) & 0xff000000
                | ( srcBuffer[ i + 3 ] << 32 ) & 0xff00000000L
                | ( srcBuffer[ i + 5 ] << 40 ) & 0xff0000000000L
                | ( srcBuffer[ i + 6 ] << 48 ) & 0xff000000000000L
                | ( srcBuffer[ i + 7 ] << 56 )
            );
        }
        return srcLength;
    }

    /**
     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
     * big/little endian signed integer data with a user defined block size of
     * 2, 3, or 4 bytes. <p>
     * @param srcBuffer Bytes in little/big endian order which shall be
     *      converted. The size of the array must be at least
     *      <code>blockSize</code>.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Maximum number of data to be written in
     *      <code>destBuffer</code>
     * @param blockSize May be 2, 3 or 4.
     * @return number of processed bytes of srcBuffer (multiple of
     *      <code>blockSize</code>)
     */
    public static int convert
    (
        final int       blockSize,
        final byte[ ]   srcBuffer,
        final int[ ]    destBuffer,
        final int       srcLength,
        final int       destOffset,
        final int       destLength
    )
    {
        return convert
        (
            blockSize,
            srcBuffer,
            destBuffer,
            srcLength,
            destOffset,
            destLength,
            0xffffffff
        );
    }

    /**
     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
     * big/little endian signed integer data with a user defined block size of
     * 2, 3, or 4 bytes. Significant bits can be masked, e. g. to get unsigned
     * 16 bit values use <code>0xffff</code> as mask.<p>
     * @param blockSize May be 2, 3 or 4.
     * @param srcBuffer Bytes in little/big endian order which shall be
     *  converted. The size of the array must be at least
     *  <code>blockSize</code>.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param srcLength Number of bytes of <code>srcBuffer</code> which shall
     *      be processed. Must be <= length of <code>srcBuffer</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Maximum number of data to be written in
     *      <code>destBuffer</code>
     * @param mask Mask for significant bits. Set significant bits to 1.
     * @return number of processed bytes of srcBuffer (multiple of
     *      <code>blockSize</code>)
     */
    public static int convert
    (
        final int       blockSize,
        final byte[ ]   srcBuffer,
        final int[ ]    destBuffer,
        final int       srcLength,
        final int       destOffset,
        final int       destLength,
        final int       mask
    )
    {
        final int length = Math.min
        (
            destLength * blockSize,
            ( srcLength / blockSize ) * blockSize
        );
        if( blockSize == 2 )
        {
            for( int i = 0; i < length; i += 2 )
            {
                destBuffer[ ( i / 2 ) + destOffset ] =
                    ( srcBuffer[ i ] & 0xff | ( srcBuffer[ i + 1 ] << 8 ) )
                    & mask;
            }
            return length;
        }
        else if( blockSize == 3 )
        {
            for( int i = 0; i < length; i += 3 )
            {
                destBuffer[ ( i / 3 ) + destOffset ] = ( srcBuffer[ i ] & 0xff
                    | ( srcBuffer[ i + 1 ]  << 8 ) & 0xff00
                    | ( srcBuffer[ i + 2 ]  << 24 ) )  & mask;
            }
            return length;
        }
        else if( blockSize == 4 )
        {
            return convert
            (
                srcBuffer,
                destBuffer,
                srcLength,
                destOffset,
                destLength,
                mask
            );
        }
        else
        {
            return 0;
        }
    }

    /**
     * Converts signed short data in <code>srcBuffer</code> to bytes in little
     * endian order.
     * @param srcBuffer Signed short data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param destBuffer Buffer to store the converted data bytes in little
     *      endian order. The first valid byte will start at 0. The size of
     *      the array must be at least 2.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @return number of processed short data from <code>srcBuffer</code>.
     *      Multiply by 2 to get the number of valid bytes in
     *      <code>destBuffer</code>.
     */
    public static int convertToLittleEndian
    (
        final short[ ]  srcBuffer,
        final byte[ ]   destBuffer,
        final int       srcOffset,
        int             srcLength
    )
    {
        srcLength = Math.min( srcLength, destBuffer.length / 2 );
        int index = 0;
        for( int i = 0; i < srcLength; i ++ )
        {
            final short data = srcBuffer[ srcOffset + i ];
            destBuffer[ index ++ ] = ( byte )( data );
            destBuffer[ index ++ ] = ( byte )( data >> 8 );
        }
        return srcLength;
    }

    /**
     * Converts signed int data in <code>srcBuffer</code> to bytes in little
     * endian order.
     * @param srcBuffer Signed int data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param destBuffer Buffer to store the converted data bytes in little
     *      endian order. The first valid byte will start at 0. The size of
     *      the array must be at least 4.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @return number of processed short data from <code>srcBuffer</code>.
     *      Multiply by 4 to get the number of valid bytes in
     *      <code>destBuffer</code>.
     */
    public static int convertToLittleEndian
    (
        final int[ ]    srcBuffer,
        final byte[ ]   destBuffer,
        final int       srcOffset,
        int             srcLength
    )
    {
        srcLength = Math.min( srcLength, destBuffer.length / 4 );
        int index = 0;
        for( int i = 0; i < srcLength; i ++ )
        {
            final int data = srcBuffer[ srcOffset + i ];
            destBuffer[ index ++ ] = ( byte )( data );
            destBuffer[ index ++ ] = ( byte )( data >> 8 );
            destBuffer[ index ++ ] = ( byte )( data >> 16 );
            destBuffer[ index ++ ] = ( byte )( data >> 24 );
        }
        return srcLength;
    }

    /**
     * Converts signed int data in <code>srcBuffer</code> to bytes in little
     * endian order with a user defined block size of 1, 2, 3 or 4 (1 is here
     * for convinience).
     * @param blockSize May be 1, 2, 3 or 4.
     * @param srcBuffer Signed int data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param destBuffer Buffer to store the converted data bytes in little
     *      endian order. The first valid byte will start at 0. The size of
     *      the array must be at least <code>blockSize</code>.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @return number of processed short data from <code>srcBuffer</code>.
     *      Multiply by <code>blockSize</code> to get the number of valid bytes
     *      in  <code>destBuffer</code>.
     */
    public static int convertToLittleEndian
    (
        final int       blockSize,
        final int[ ]    srcBuffer,
        final byte[ ]   destBuffer,
        final int       srcOffset,
        int             srcLength
    )
    {
        srcLength = Math.min( srcLength, destBuffer.length / blockSize );
        int index = 0;
        for( int i = 0; i < srcLength; i ++ )
        {
            final int data = srcBuffer[ srcOffset + i ];
            destBuffer[ index ++ ] = ( byte )( data );
            if( blockSize >= 2 )
            {
                destBuffer[ index ++ ] = ( byte )( data >> 8 );
                if( blockSize >= 3 )
                {
                    destBuffer[ index ++ ] = ( byte )( data >> 16 );
                    if( blockSize == 4 )
                    {
                        destBuffer[ index ++ ] = ( byte )( data >> 24 );
                    }
                }
            }
        }
        return srcLength;
    }

    /**
     * Converts signed long data in <code>srcBuffer</code> to bytes in little
     * endian order.
     * @param srcBuffer Signed long data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param destBuffer Buffer to store the converted data bytes in little
     *      endian order. The first valid byte will start at 0. The size of
     *      the array must be at least 8.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @return number of processed short data from <code>srcBuffer</code>.
     *      Multiply by 8 to get the number of valid bytes in
     *      <code>destBuffer</code>.
     */
    public static int convertToLittleEndian
    (
        final long[ ]   srcBuffer,
        final byte[ ]   destBuffer,
        final int       srcOffset,
        int             srcLength
    )
    {
        srcLength = Math.min( srcLength, destBuffer.length / 8 );
        int index = 0;
        for( int i = 0; i < srcLength; i ++ )
        {
            long data = srcBuffer[ srcOffset + i ];
            for( int j = 0; j < 8; j ++ )
            {
                destBuffer[ index ++ ] = ( byte )( data );
                data >>= 8;
            }
        }
        return srcLength;
    }

    /**
     * Converts float data in <code>srcBuffer</code> to bytes in little
     * endian order.For the conversion the <code>Float.floatToIntBits</code>
     * method is used.
     * @param srcBuffer Float data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param destBuffer Buffer to store the converted data bytes in little
     *      endian order. The first valid byte will start at 0. The size of
     *      the array must be at least 4.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @return number of processed short data from <code>srcBuffer</code>.
     *      Multiply by 4 to get the number of valid bytes in
     *      <code>destBuffer</code>.
     */
    public static int convertToLittleEndian
    (
        final float[ ]  srcBuffer,
        final byte[ ]   destBuffer,
        final int       srcOffset,
        int             srcLength
    )
    {
        srcLength = Math.min( srcLength, destBuffer.length / 4 );
        int index = 0;
        for( int i = 0; i < srcLength; i ++ )
        {
            final int data =
                Float.floatToIntBits( srcBuffer[ srcOffset + i ] );
            destBuffer[ index ++ ] = ( byte )( data );
            destBuffer[ index ++ ] = ( byte )( data >> 8 );
            destBuffer[ index ++ ] = ( byte )( data >> 16 );
            destBuffer[ index ++ ] = ( byte )( data >> 24 );
        }
        return srcLength;
    }

    /**
     * Converts double data in <code>srcBuffer</code> to bytes in little
     * endian order. For the conversion the <code>Double.doubleToLongBits</code>
     * method is used.
     * @param srcBuffer Double data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param destBuffer Buffer to store the converted data bytes in little
     *      endian order. The first valid byte will start at 0. The size of
     *      the array must be at least 8.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @return number of processed short data from <code>srcBuffer</code>.
     *      Multiply by 8 to get the number of valid bytes in
     *      <code>destBuffer</code>.
     */
    public static int convertToLittleEndian
    (
        final double[ ] srcBuffer,
        final byte[ ]   destBuffer,
        final int       srcOffset,
        int             srcLength
    )
    {
        srcLength = Math.min( srcLength, destBuffer.length / 8 );
        int index = 0;
        for( int i = 0; i < srcLength; i ++ )
        {
            long data = Double.doubleToLongBits( srcBuffer[ srcOffset + i ] );
            for( int j = 0; j < 8; j ++ )
            {
                destBuffer[ index ++ ] = ( byte )( data );
                data >>= 8;
            }
        }
        return srcLength;
    }


    /**
     * Reads little/big endian data from an <code>InputStream</code> and
     * converts it to big/little endian signed short (2 bytes long) data.
     * @param readBuffer Auxilary Buffer to be used to read from
     *      <code>stream</code>. Choose an appropriate size (multiple of 2)
     *      depending on the size of the stream. The size of the array must be
     *      at least 2.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Max. number of data to be written in
     *      <code>destBuffer</code>
     * @param stream <code>InputStream</code> to read from.
     * @return number of data elements written in <code>destBuffer</code>
     *      (will be <= destLength).
     */
    public static int read
    (
        final byte[ ]       readBuffer,
        final short[ ]      destBuffer,
        final int           destOffset,
        final int           destLength,
        final InputStream   stream
    )
    throws IOException
    {
        return read
        (
            readBuffer,
            destBuffer,
            destOffset,
            destLength,
            stream,
            ( short )0xff
        );
    }
    /**
     * Reads little/big endian data from an <code>InputStream</code> and
     * converts it to big/little endian short (2 bytes long) data.
     * Significant bits can be masked, e. g. to get unsigned 7 bit values use
     * <code>0x7f</code> as mask.
     * @param readBuffer Auxilary Buffer to be used to read from
     *      <code>stream</code>. Choose an appropriate size (multiple of 2)
     *      depending on the size of the stream. The size of the array must be
     *      at least 2.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Max. number of data to be written in
     *      <code>destBuffer</code>
     * @param stream <code>InputStream</code> to read from.
     * @param mask Mask for significant bits. Set significant bits to 1.
     * @return number of data elements written in <code>destBuffer</code>
     *      (will be <= destLength).
     */
    public static int read
    (
        final byte[ ]       readBuffer,
        final short[ ]      destBuffer,
        final int           destOffset,
        final int           destLength,
        final InputStream   stream,
        final short         mask
    )
    throws IOException
    {
        int numOfBytesRead = 0;
        int numOfData = 0;
        int offset = 0;
        final int length = ( readBuffer.length / 2 ) * 2;
        while( ( numOfBytesRead >= 0 ) && ( numOfData < destLength ) )
        {
            // calculate how many more bytes can be read so that destBuffer
            // does not overflow; enables to continue reading from same stream
            // without data loss
            final int maxBytesToRead =
                Math.min( ( destLength - numOfData ) * 2, length );
            numOfBytesRead =
                stream.read( readBuffer, offset, maxBytesToRead - offset );
            int numOfProcessedBytes = convert
            (
                readBuffer,
                destBuffer,
                numOfBytesRead + offset,
                destOffset + numOfData,
                destLength - numOfData,
                mask
            );
            // if an uneven number of bytes was read from stream
            if( numOfBytesRead - numOfProcessedBytes == 1 )
            {
                offset = 1;
                readBuffer[ 0 ] = readBuffer[ numOfProcessedBytes ];
            }
            else
            {
                offset = 0;
            }
            numOfData += ( numOfProcessedBytes / 2 );
        }
        return numOfData;
    }

    /**
     * Reads little/big endian data from an <code>InputStream</code> and
     * converts it to big/little endian signed int (4 bytes long) data.
     * @param readBuffer Auxilary Buffer to be used to read from
     *      <code>stream</code>. Choose an appropriate size (multiple of 4)
     *      depending on the size of the stream. The size of the array must be
     *      at least 4.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Max. number of data to be written in
     *      <code>destBuffer</code>
     * @param stream <code>InputStream</code> to read from.
     * @return number of data elements written in <code>destBuffer</code>
     *      (will be <= destLength).
     */
    public static int read
    (
        final byte[ ]       readBuffer,
        final int[ ]        destBuffer,
        final int           destOffset,
        final int           destLength,
        final InputStream   stream
    )
    throws IOException
    {
        return read
        (
            readBuffer,
            destBuffer,
            destOffset,
            destLength,
            stream,
            0xffffffff
        );
    }

    /**
     * Reads little/big endian data from an <code>InputStream</code> and
     * converts it to big/little endian int (4 bytes long) data. Significant
     * bits can be masked, e. g. to get unsigned 31 bit values use
     * <code>0x7fffffff</code> as mask.
     * @param readBuffer Auxilary Buffer to be used to read from
     *      <code>stream</code>. Choose an appropriate size (multiple of 4)
     *      depending on the size of the stream. The size of the array must be
     *      at least 4.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Max. number of data to be written in
     *      <code>destBuffer</code>
     * @param stream <code>InputStream</code> to read from.
     * @param mask Mask for significant bits. Set significant bits to 1.
     * @return number of data elements written in <code>destBuffer</code>
     *      (will be <= destLength).
     */
    public static int read
    (
        final byte[ ]       readBuffer,
        final int[ ]        destBuffer,
        final int           destOffset,
        final int           destLength,
        final InputStream   stream,
        final int           mask
    )
    throws IOException
    {
        int numOfBytesRead = 0;
        int numOfData = 0;
        int offset = 0;
        final int length = ( readBuffer.length / 4 ) * 4;
        while( ( numOfBytesRead >= 0 ) && ( numOfData < destLength ) )
        {
            // calculate how many more bytes can be read so that destBuffer
            // does not overflow; enables to continue reading from same stream
            // without data loss
            final int maxBytesToRead =
                Math.min( ( destLength - numOfData ) * 4, length );
            numOfBytesRead =
                stream.read( readBuffer, offset, maxBytesToRead - offset );
            int numOfProcessedBytes = convert
            (
                readBuffer,
                destBuffer,
                numOfBytesRead + offset,
                destOffset + numOfData,
                destLength - numOfData,
                mask
            );
            offset = numOfBytesRead - numOfProcessedBytes;
            // if a number of bytes was read from stream was not a multiple
            // of 4
            if( offset >= 1 )
            {
                readBuffer[ 0 ] = readBuffer[ numOfProcessedBytes ];
                if( offset >= 2 )
                {
                    readBuffer[ 1 ] = readBuffer[ numOfProcessedBytes + 1 ];
                    if( offset >= 3 )
                    {
                        readBuffer[ 2 ] = readBuffer[ numOfProcessedBytes + 2 ];
                    }
                }
            }
            numOfData += ( numOfProcessedBytes / 4 );
        }
        return numOfData;
    }

    /**
     * Reads little/big endian data from an <code>InputStream</code> and
     * converts it to to big/little endian signed integer data with a user
     * defined block size of 1, 2, 3, or 4 bytes (1 is here for conveniance).<p>
     * @param blockSize May be 1, 2, 3 or 4.
     * @param readBuffer Auxilary Buffer to be used to read from
     *      <code>stream</code>. Choose an appropriate size (multiple of
     *      <code>blockSize</code>) depending on the size of the stream. The
     *      size of the array must be at least <code>blockSize</code>.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Max. number of data to be written in
     *      <code>destBuffer</code>
     * @param stream <code>InputStream</code> to read from.
     * @return number of data elements written in <code>destBuffer</code>
     *      (will be <= destLength).
     */
    public static int read
    (
        final int           blockSize,
        final byte[ ]       readBuffer,
        final int[ ]        destBuffer,
        final int           destOffset,
        final int           destLength,
        final InputStream   stream
    )
    throws IOException
    {
        return read
        (
            blockSize,
            readBuffer,
            destBuffer,
            destOffset,
            destLength,
            stream,
            0xffffffff
        );
    }

    /**
     * Reads little/big endian data from an <code>InputStream</code> and
     * converts it to to big/little endian signed integer data with a user
     * defined block size of 1, 2, 3, or 4 bytes (1 is here for conveniance).
     * Significant bits can be masked, e. g. to get unsigned 16 bit values use
     * <code>0xffff</code> as mask.<p>
     * @param blockSize May be 1, 2, 3 or 4.
     * @param readBuffer Auxilary Buffer to be used to read from
     *      <code>stream</code>. Choose an appropriate size (multiple of
     *      <code>blockSize</code>) depending on the size of the stream. The
     *      size of the array must be at least <code>blockSize</code>.
     * @param destBuffer Buffer to store the converted data. The size of the
     *      array must be at least <code>destOffset</code> +
     *      <code>destLength</code>.
     * @param destOffset Offset for writing converted data in
     *      <code>destBuffer</code>.
     * @param destLength Max. number of data to be written in
     *      <code>destBuffer</code>
     * @param stream <code>InputStream</code> to read from.
     * @param mask Mask for significant bits. Set significant bits to 1.
     * @return number of data elements written in <code>destBuffer</code>
     *      (will be <= destLength).
     */
    public static int read
    (
        final int           blockSize,
        final byte[ ]       readBuffer,
        final int[ ]        destBuffer,
        final int           destOffset,
        final int           destLength,
        final InputStream   stream,
        final int           mask
    )
    throws IOException
    {
        if( blockSize == 2 )
        {
            return read2ByteBlock
            (
                readBuffer,
                destBuffer,
                destOffset,
                destLength,
                stream,
                mask
            );
        }
        else if( blockSize == 3 )
        {
            return read3ByteBlock
            (
                readBuffer,
                destBuffer,
                destOffset,
                destLength,
                stream,
                mask
            );
        }
        else if( blockSize == 4 )
        {
            return read
            (
                readBuffer,
                destBuffer,
                destOffset,
                destLength,
                stream,
                mask
            );
        }
        else
        {
            return 0;
        }
    }

    /**
     * Reads 4 bytes in little/big endian format and converts it to a big/little
     * endian signed int.<p>
     * @throws IOException if EOF occurs and only one, 2 or 3 bytes were read or
     *      if error during reading occurs
     */
    public static int read4ByteBlock( final InputStream stream )
        throws IOException
    {
        return read( stream ) & 0xff
            | ( read( stream ) << 8 ) & 0xff00
            | ( read( stream ) << 16 ) & 0xff0000
            | ( read( stream ) << 24 );
    }

    /**
     * Reads 2 bytes in little/big endian format and converts it to a big/little
     * endian signed int.<p>
     * To Convert it to an unsigned int <code>&</code> the result with
     * <code>0xffff</code>.
     * @throws IOException if EOF occurs and only one bytes was read or
     *      if error during reading occurs
     */
    public static int read2ByteBlock( final InputStream stream )
        throws IOException
    {
        return read( stream ) & 0xff
            | ( read( stream ) << 8 );
    }

    /**
     * Reads 3 bytes in little/big endian format and converts it to a big/little
     * endian signed int.<p>
     * To Convert it to an unsigned int <code>&</code> the result with
     * <code>0xffffff</code>.
     * @throws IOException if EOF occurs and only one or 2 bytes were read or
     *      if error during reading occurs
     */
    public static int read3ByteBlock( final InputStream stream )
        throws IOException
    {
        return read( stream ) & 0xff
            | ( read( stream ) << 8 ) & 0xff00
            | ( read( stream ) << 16 );
    }

    /**
     * Reads 8 bytes in little/big endian format and converts it to a big/little
     * endian signed long.<p>
     * To Convert it to an unsigned int <code>&</code> the result with
     * <code>0xffffff</code>.
     * @throws IOException if EOF occurs and only one or 2 bytes were read or
     *      if error during reading occurs
     */
    public static long read8ByteBlock( final InputStream stream )
        throws IOException
    {
        return read( stream ) & 0xff
            | ( read( stream ) << 8 ) & 0xff00
            | ( read( stream ) << 16 ) & 0xff0000
            | ( read( stream ) << 24 ) & 0xff000000
            | ( read( stream ) << 32 ) & 0xff00000000L
            | ( read( stream ) << 40 ) & 0xff0000000000L
            | ( read( stream ) << 48 ) & 0xff000000000000L
            | ( read( stream ) << 56 );
    }

    /**
     * Writes signed short data in <code>srcBuffer</code> to <code>stream</code>
     * in little endian order.
     * @param srcBuffer Signed short data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @param tempBuffer Temporary buffer to store the converted data bytes in
     *      little endian order. The size of the array must be at least 2 and
     *      should be a multiple of 2. Use an appropriate size according to
     *      <code>srcLength</code>.
     */
    public static void writeLittleEndian
    (
        final short[ ]      srcBuffer,
        int                 srcOffset,
        int                 srcLength,
        final OutputStream  stream,
        final byte[ ]       tempBuffer
    )
    throws IOException
    {
        while( srcLength >= 0 )
        {
            final int processedData = convertToLittleEndian
            (
                srcBuffer,
                tempBuffer,
                srcOffset,
                srcLength
            );
            stream.write( tempBuffer, 0, processedData * 2 );
            srcOffset += processedData;
            srcLength -= processedData;
        }
    }

    /**
     * Writes signed int data in <code>srcBuffer</code> to <code>stream</code>
     * in little endian order.
     * @param srcBuffer Signed short data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @param tempBuffer Temporary buffer to store the converted data bytes in
     *      little endian order. The size of the array must be at least 4 and
     *      should be a multiple of 4. Use an appropriate size according to
     *      <code>srcLength</code>.
     */
    public static void writeLittleEndian
    (
        final int[ ]        srcBuffer,
        int                 srcOffset,
        int                 srcLength,
        final OutputStream  stream,
        final byte[ ]       tempBuffer
    )
    throws IOException
    {
        while( srcLength > 0 )
        {
            final int processedData = convertToLittleEndian
            (
                srcBuffer,
                tempBuffer,
                srcOffset,
                srcLength
            );
            stream.write( tempBuffer, 0, processedData * 4 );
            srcOffset += processedData;
            srcLength -= processedData;
        }
    }

    /**
     * Writes signed long data in <code>srcBuffer</code> to <code>stream</code>
     * in little endian order.
     * @param srcBuffer Signed short data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @param tempBuffer Temporary buffer to store the converted data bytes in
     *      little endian order. The size of the array must be at least 8 and
     *      should be a multiple of 8. Use an appropriate size according to
     *      <code>srcLength</code>.
     */
    public static void writeLittleEndian
    (
        final long[ ]       srcBuffer,
        int                 srcOffset,
        int                 srcLength,
        final OutputStream  stream,
        final byte[ ]       tempBuffer
    )
    throws IOException
    {
        while( srcLength > 0 )
        {
            final int processedData = convertToLittleEndian
            (
                srcBuffer,
                tempBuffer,
                srcOffset,
                srcLength
            );
            stream.write( tempBuffer, 0, processedData * 8 );
            srcOffset += processedData;
            srcLength -= processedData;
        }
    }

    /**
     * Writes signed int data in <code>srcBuffer</code> to <code>stream</code>
     * in little endian order.
     * @param srcBuffer Signed short data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @param tempBuffer Temporary buffer to store the converted data bytes in
     *      little endian order. The size of the array must be at least
     *      <code>blockSize</code> and should be a multiple of
     *      <code>blockSize</code>. Use an appropriate size according to
     *      <code>srcLength</code>.
     */
    public static void writeLittleEndian
    (
        final int           blockSize,
        final int[ ]        srcBuffer,
        int                 srcOffset,
        int                 srcLength,
        final OutputStream  stream,
        final byte[ ]       tempBuffer
    )
    throws IOException
    {
        while( srcLength > 0 )
        {
            final int processedData = convertToLittleEndian
            (
                blockSize,
                srcBuffer,
                tempBuffer,
                srcOffset,
                srcLength
            );
            stream.write( tempBuffer, 0, processedData * blockSize );
            srcOffset += processedData;
            srcLength -= processedData;
        }
    }

    /**
     * Writes float data in <code>srcBuffer</code> to <code>stream</code>
     * in little endian order. For the conversion the
     * <code>Float.floatToIntBits</code> method is used.
     * @param srcBuffer Signed short data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @param tempBuffer Temporary buffer to store the converted data bytes in
     *      little endian order. The size of the array must be at least 4 and
     *      should be a multiple of 4. Use an appropriate size according to
     *      <code>srcLength</code>.
     */
    public static void writeLittleEndian
    (
        final float[ ]      srcBuffer,
        int                 srcOffset,
        int                 srcLength,
        final OutputStream  stream,
        final byte[ ]       tempBuffer
    )
    throws IOException
    {
        while( srcLength > 0 )
        {
            final int processedData = convertToLittleEndian
            (
                srcBuffer,
                tempBuffer,
                srcOffset,
                srcLength
            );
            stream.write( tempBuffer, 0, processedData * 4 );
            srcOffset += processedData;
            srcLength -= processedData;
        }
    }

    /**
     * Writes double data in <code>srcBuffer</code> to <code>stream</code>
     * in little endian order. For the conversion the
     * <code>Double.doubleToLongBits</code> method is used.
     * @param srcBuffer Signed short data to be converted. The size of array
     *      must be at least <code>srcOffset</code> + <code>srcLength</code>.
     * @param srcOffset Offset for reading data from <code>srcBuffer</code>.
     * @param srcLength Max. Number of data to be processed from
     *      <code>srcBuffer</code>.
     * @param tempBuffer Temporary buffer to store the converted data bytes in
     *      little endian order. The size of the array must be at least 8 and
     *      should be a multiple of 8. Use an appropriate size according to
     *      <code>srcLength</code>.
     */
    public static void writeLittleEndian
    (
        final double[ ]     srcBuffer,
        int                 srcOffset,
        int                 srcLength,
        final OutputStream  stream,
        final byte[ ]       tempBuffer
    )
    throws IOException
    {
        while( srcLength > 0 )
        {
            final int processedData = convertToLittleEndian
            (
                srcBuffer,
                tempBuffer,
                srcOffset,
                srcLength
            );
            stream.write( tempBuffer, 0, processedData * 8 );
            srcOffset += processedData;
            srcLength -= processedData;
        }
    }

    /**
     * Writes signed short data to <code>stream</code> in little endian order.
     * @param stream <code>OutputStream</code> to write to.
     * @param value Signed short data to be written.
     */
    public static void writeLittleEndian
    (
        final OutputStream  stream,
        final short         value
    )
    throws IOException
    {
        stream.write( value );
        stream.write( value >> 8 );
    }

    /**
     * Writes signed int data to <code>stream</code> in little endian order.
     * @param stream <code>OutputStream</code> to write to.
     * @param value Signed int data to be written.
     */
    public static void writeLittleEndian
    (
        final OutputStream  stream,
        final int           value
    )
    throws IOException
    {
        stream.write( value );
        stream.write( value >> 8 );
        stream.write( value >> 16 );
        stream.write( value >> 24 );
    }

    /**
     * Writes signed long data to <code>stream</code> in little endian order.
     * @param stream <code>OutputStream</code> to write to.
     * @param value Signed long data to be written.
     */
    public static void writeLittleEndian
    (
        final OutputStream  stream,
        long                value
    )
    throws IOException
    {
        for( int i = 0; i < 8; i ++ )
        {
            stream.write( ( int )value );
            value >>= 8;
        }
    }

    private static int read2ByteBlock
    (
        final byte[ ]       readBuffer,
        final int[ ]        destBuffer,
        final int           destOffset,
        final int           destLength,
        final InputStream   stream,
        final int           mask
    )
    throws IOException
    {
        int numOfBytesRead = 0;
        int numOfData = 0;
        int offset = 0;
        final int length = ( readBuffer.length / 2 ) * 2;
        while( ( numOfBytesRead >= 0 ) && ( numOfData < destLength ) )
        {
            // calculate how many more bytes can be read so that destBuffer
            // does not overflow; enables to continue reading from same stream
            // without data loss
            final int maxBytesToRead =
                Math.max( ( destLength - numOfData ) * 2, length );
            numOfBytesRead =
                stream.read( readBuffer, offset, maxBytesToRead - offset );
            int numOfProcessedBytes = convert
            (
                2,
                readBuffer,
                destBuffer,
                numOfBytesRead + offset,
                destOffset + numOfData,
                destLength - numOfData,
                mask
            );
            // if an uneven number of bytes was read from stream
            if( numOfBytesRead - numOfProcessedBytes == 1 )
            {
                offset = 1;
                readBuffer[ 0 ] = readBuffer[ numOfProcessedBytes ];
            }
            else
            {
                offset = 0;
            }
            numOfData += ( numOfProcessedBytes / 2 );
        }
        return numOfData;
    }

    private static int read3ByteBlock
    (
        final byte[ ]       readBuffer,
        final int[ ]        destBuffer,
        final int           destOffset,
        final int           destLength,
        final InputStream   stream,
        final int           mask
    )
    throws IOException
    {
        int numOfBytesRead = 0;
        int numOfData = 0;
        int offset = 0;
        final int length = ( readBuffer.length / 3 ) * 3;
        while( ( numOfBytesRead >= 0 ) && ( numOfData < destLength ) )
        {
            // calculate how many more bytes can be read so that destBuffer
            // does not overflow; enables to continue reading from same stream
            // without data loss
            final int maxBytesToRead =
                Math.max( ( destLength - numOfData ) * 3, length );
            numOfBytesRead =
                stream.read( readBuffer, offset, maxBytesToRead - offset );
            int numOfProcessedBytes = convert
            (
                3,
                readBuffer,
                destBuffer,
                numOfBytesRead + offset,
                destOffset + numOfData,
                destLength - numOfData,
                mask
            );
            offset = numOfBytesRead - numOfProcessedBytes;
            // if a number of bytes was read from stream was not a multiple
            // of 3
            if( offset >= 1 )
            {
                readBuffer[ 0 ] = readBuffer[ numOfProcessedBytes ];
                if( offset >= 2 )
                {
                    readBuffer[ 1 ] = readBuffer[ numOfProcessedBytes + 1 ];
                }
            }
            numOfData += ( numOfProcessedBytes / 3 );
        }
        return numOfData;
    }

    private static int read( final InputStream stream ) throws IOException
    {
        final int tempValue = stream.read( );
        if( tempValue == -1 )
        {
            throw new IOException( "Filesize does not match blocksize" );
        }
        return tempValue;
    }
}