/*
* JGrass - Free Open Source Java GIS http://www.jgrass.org
* (C) HydroloGIS - www.hydrologis.com
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Library General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
* details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; if not, write to the Free Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.jgrasstools.gears.io.dxfdwg.libs.dwg.utils;
import java.math.BigInteger;
import java.util.Arrays;
/**
* Clase que engloba mtodos para trabajar con bytes.
*
* @author Vicente Caballero Navarro
*/
public class ByteUtils {
public static final int SIZE_BOOL = 1;
public static final int SIZE_SHORT = 2;
public static final int SIZE_INT = 4;
public static final int SIZE_LONG = 8;
public static final int SIZE_DOUBLE = 8;
/** A nibble->char mapping for printing out bytes. */
public static final char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* Return the <code>int</code> represented by the bytes in
* <code>data</code> staring at offset <code>offset[0]</code>.
*
* @param data the array from which to read
* @param offset A single element array whose first element is the index in
* data from which to begin reading on function entry, and which on
* function exit has been incremented by the number of bytes read.
*
* @return the value of the <code>int</code> decoded
*/
public static final int bytesToInt( byte[] data, int[] offset ) {
/**
* TODO: We use network-order within OceanStore, but temporarily
* supporting intel-order to work with some JNI code until JNI code is
* set to interoperate with network-order.
*/
int result = 0;
for( int i = 0; i < SIZE_INT; ++i ) {
result <<= 8;
result |= byteToUnsignedInt(data[offset[0]++]);
}
return result;
}
/**
* Write the bytes representing <code>i</code> into the byte array
* <code>data</code>, starting at index <code>offset [0]</code>, and
* increment <code>offset [0]</code> by the number of bytes written; if
* <code>data == null</code>, increment <code>offset [0]</code> by the
* number of bytes that would have been written otherwise.
*
* @param i the <code>int</code> to encode
* @param data The byte array to store into, or <code>null</code>.
* @param offset A single element array whose first element is the index in
* data to begin writing at on function entry, and which on
* function exit has been incremented by the number of bytes
* written.
*/
public static final void intToBytes( int i, byte[] data, int[] offset ) {
/**
* TODO: We use network-order within OceanStore, but temporarily
* supporting intel-order to work with some JNI code until JNI code is
* set to interoperate with network-order.
*/
if (data != null) {
for( int j = (offset[0] + SIZE_INT) - 1; j >= offset[0]; --j ) {
data[j] = (byte) i;
i >>= 8;
}
}
offset[0] += SIZE_INT;
}
/**
* Return the <code>short</code> represented by the bytes in
* <code>data</code> staring at offset <code>offset[0]</code>.
*
* @param data the array from which to read
* @param offset A single element array whose first element is the index in
* data from which to begin reading on function entry, and which on
* function exit has been incremented by the number of bytes read.
*
* @return the value of the <code>short</code> decoded
*/
public static final short bytesToShort( byte[] data, int[] offset ) {
/**
* TODO: We use network-order within OceanStore, but temporarily
* supporting intel-order to work with some JNI code until JNI code is
* set to interoperate with network-order.
*/
short result = 0;
for( int i = 0; i < SIZE_SHORT; ++i ) {
result <<= 8;
result |= (short) byteToUnsignedInt(data[offset[0]++]);
}
return result;
}
/**
* Write the bytes representing <code>s</code> into the byte array
* <code>data</code>, starting at index <code>offset [0]</code>, and
* increment <code>offset [0]</code> by the number of bytes written; if
* <code>data == null</code>, increment <code>offset [0]</code> by the
* number of bytes that would have been written otherwise.
*
* @param s the <code>short</code> to encode
* @param data The byte array to store into, or <code>null</code>.
* @param offset A single element array whose first element is the index in
* data to begin writing at on function entry, and which on
* function exit has been incremented by the number of bytes
* written.
*/
public static final void shortToBytes( short s, byte[] data, int[] offset ) {
/**
* TODO: We use network-order within OceanStore, but temporarily
* supporting intel-order to work with some JNI code until JNI code is
* set to interoperate with network-order.
*/
if (data != null) {
data[offset[0] + 1] = (byte) s;
data[offset[0]] = (byte) (s >> 8);
}
offset[0] += SIZE_SHORT;
}
/**
* Return the <code>long</code> represented by the bytes in
* <code>data</code> staring at offset <code>offset[0]</code>.
*
* @param data the array from which to read
* @param offset A single element array whose first element is the index in
* data from which to begin reading on function entry, and which
* on function exit has been incremented by the number of bytes
* read.
*
* @return the value of the <code>long</code> decoded
*/
public static final long bytesToLong( byte[] data, int[] offset ) {
long result = 0;
for( int i = 0; i < SIZE_LONG; ++i ) {
result <<= 8;
int res = byteToUnsignedInt(data[offset[0]++]);
result = result | res;
}
return result;
}
/**
* Write the bytes representing <code>l</code> into the byte array
* <code>data</code>, starting at index <code>offset [0]</code>, and
* increment <code>offset [0]</code> by the number of bytes written; if
* <code>data == null</code>, increment <code>offset [0]</code> by the
* number of bytes that would have been written otherwise.
*
* @param l the <code>long</code> to encode
* @param data The byte array to store into, or <code>null</code>.
* @param offset A single element array whose first element is the index in
* data to begin writing at on function entry, and which on
* function exit has been incremented by the number of bytes
* written.
*/
public static final void longToBytes( long l, byte[] data, int[] offset ) {
/**
* TODO: We use network-order within OceanStore, but temporarily
* supporting intel-order to work with some JNI code until JNI code is
* set to interoperate with network-order.
*/
if (data != null) {
for( int j = (offset[0] + SIZE_LONG) - 1; j >= offset[0]; --j ) {
data[j] = (byte) l;
l >>= 8;
}
}
offset[0] += SIZE_LONG;
}
/**
* Return the <code>double</code> represented by the bytes in
* <code>data</code> staring at offset <code>offset[0]</code>.
*
* @param data the array from which to read
* @param offset A single element array whose first element is the index in
* data from which to begin reading on function entry, and which
* on function exit has been incremented by the number of bytes
* read.
*
* @return the value of the <code>double</code> decoded
*/
public static final double bytesToDouble( byte[] data, int[] offset ) {
long bits = bytesToLong(data, offset);
return Double.longBitsToDouble(bits);
}
/**
* Write the bytes representing <code>d</code> into the byte array
* <code>data</code>, starting at index <code>offset [0]</code>, and
* increment <code>offset [0]</code> by the number of bytes written; if
* <code>data == null</code>, increment <code>offset [0]</code> by the
* number of bytes that would have been written otherwise.
*
* @param d the <code>double</code> to encode
* @param data The byte array to store into, or <code>null</code>.
* @param offset A single element array whose first element is the index in
* data to begin writing at on function entry, and which on
* function exit has been incremented by the number of bytes
* written.
*/
public static final void doubleToBytes( double d, byte[] data, int[] offset ) {
long bits = Double.doubleToLongBits(d);
longToBytes(bits, data, offset);
}
/**
* Return the <code>String</code> represented by the bytes in
* <code>data</code> staring at offset <code>offset[0]</code>. This method
* relies on the user using the corresponding <code>stringToBytes</code>
* method to encode the <code>String</code>, so that it may properly
* retrieve the <code>String</code> length.
*
* @param data the array from which to read
* @param offset A single element array whose first element is the index in
* data from which to begin reading on function entry, and which on
* function exit has been incremented by the number of bytes read.
*
* @return the value of the <code>String</code> decoded
*/
public static final String bytesToString( byte[] data, int[] offset ) {
offset[0] = 0;
int length = bytesToInt(data, offset);
String st = null;
if ((length < 0) || (length > data.length)) {
st = new String(data);
} else {
st = new String(data, offset[0], length);
}
offset[0] += length;
return st;
}
/**
* Write the bytes representing <code>s</code> into the byte array
* <code>data</code>, starting at index <code>offset [0]</code>, and
* increment <code>offset [0]</code> by the number of bytes written; if
* <code>data == null</code>, increment <code>offset [0]</code> by the
* number of bytes that would have been written otherwise.
*
* @param s the <code>String</code> to encode
* @param data The byte array to store into, or <code>null</code>.
* @param offset A single element array whose first element is the index in
* data to begin writing at on function entry, and which on
* function exit has been incremented by the number of bytes
* written.
*/
public static final void stringToBytes( String s, byte[] data, int[] offset ) {
byte[] s_bytes = s.getBytes();
if (data != null) {
intToBytes(s_bytes.length, data, offset);
memcpy(data, offset[0], s_bytes, 0, s_bytes.length);
} else {
offset[0] += SIZE_INT;
}
offset[0] += s_bytes.length;
}
/**
* Return the <code>boolean</code> represented by the bytes in
* <code>data</code> staring at offset <code>offset[0]</code>.
*
* @param data the array from which to read
* @param offset A single element array whose first element is the index in
* data from which to begin reading on function entry, and which
* on function exit has been incremented by the number of bytes
* read.
*
* @return the value of the <code>boolean</code> decoded
*/
public static final boolean bytesToBool( byte[] data, int[] offset ) {
boolean result = true;
if (data[offset[0]] == 0) {
result = false;
}
offset[0] += SIZE_BOOL;
return result;
}
/**
* Write the bytes representing <code>b</code> into the byte array
* <code>data</code>, starting at index <code>offset [0]</code>, and
* increment <code>offset [0]</code> by the number of bytes written; if
* <code>data == null</code>, increment <code>offset [0]</code> by the
* number of bytes that would have been written otherwise.
*
* @param b the <code>boolean</code> to encode
* @param data The byte array to store into, or <code>null</code>.
* @param offset A single element array whose first element is the index in
* data to begin writing at on function entry, and which on
* function exit has been incremented by the number of bytes
* written.
*/
public static final void boolToBytes( boolean b, byte[] data, int[] offset ) {
if (data != null) {
data[offset[0]] = (byte) (b ? 1 : 0);
}
offset[0] += SIZE_BOOL;
}
/**
* Return the <code>BigInteger</code> represented by the bytes in
* <code>data</code> staring at offset <code>offset[0]</code>.
*
* @param data the array from which to read
* @param offset A single element array whose first element is the index in
* data from which to begin reading on function entry, and which
* on function exit has been incremented by the number of bytes
* read.
*
* @return the <code>BigInteger</code> decoded
*/
public static final BigInteger bytesToBigInteger( byte[] data, int[] offset ) {
int length = bytesToInt(data, offset);
byte[] bytes = new byte[length];
offset[0] += memcpy(bytes, 0, data, offset[0], length);
return new BigInteger(bytes);
}
/**
* Write the bytes representing <code>n</code> into the byte array
* <code>data</code>, starting at index <code>offset [0]</code>, and
* increment <code>offset [0]</code> by the number of bytes written; if
* <code>data == null</code>, increment <code>offset [0]</code> by the
* number of bytes that would have been written otherwise.
*
* @param n the <code>BigInteger</code> to encode
* @param data The byte array to store into, or <code>null</code>.
* @param offset A single element array whose first element is the index in
* data to begin writing at on function entry, and which on
* function exit has been incremented by the number of bytes
* written.
*/
public static final void bigIntegerToBytes( BigInteger n, byte[] data, int[] offset ) {
byte[] bytes = n.toByteArray();
intToBytes(bytes.length, data, offset);
offset[0] += memcpy(data, offset[0], bytes, 0, bytes.length);
}
/**
* Convert an array of <code>bytes</code>s into an array of
* <code>ints</code>.
*
* @param dst the array to write
* @param dst_offset the start offset in <code>dst</code>, times 4. This
* measures the offset as if <code>dst</code> were an array of
* <code>byte</code>s (rather than <code>int</code>s).
* @param src the array to read
* @param src_offset the start offset in <code>src</code>
* @param length the number of <code>byte</code>s to copy.
*/
public static final void bytesToInts( int[] dst, int dst_offset, byte[] src, int src_offset, int length ) {
if ((src == null) || (dst == null) || ((src_offset + length) > src.length) || ((dst_offset + length) > (dst.length * 4))
|| ((dst_offset % 4) != 0) || ((length % 4) != 0)) {
croak("bytesToInts parameters are invalid"
+ " src=="
+ Arrays.toString(src)
+ " dst=="
+ Arrays.toString(dst)
+ (((src == null) || (dst == null)) ? " " : (" (src_offset+length)>src.length==" + (src_offset + length)
+ ">" + src.length + " (dst_offset+length)>(dst.length*4)==" + (dst_offset + length) + ">"
+ (dst.length * 4) + " (dst_offset%4)==" + (dst_offset % 4) + " (length%4)==" + (length % 4)
+ " dest.length==" + dst.length + " length==" + length)));
}
// Convert parameters to normal format
int[] offset = new int[1];
offset[0] = src_offset;
int int_dst_offset = dst_offset / 4;
for( int i = 0; i < (length / 4); ++i ) {
dst[int_dst_offset++] = bytesToInt(src, offset);
}
}
/**
* Convert an array of <code>int</code>s into an array of
* <code>bytes</code>.
*
* @param dst the array to write
* @param dst_offset the start offset in <code>dst</code>
* @param src the array to read
* @param src_offset the start offset in <code>src</code>, times 4. This
* measures the offset as if <code>src</code> were an array of
* <code>byte</code>s (rather than <code>int</code>s).
* @param length the number of <code>byte</code>s to copy.
*/
public static final void intsToBytes( byte[] dst, int dst_offset, int[] src, int src_offset, int length ) {
if ((src == null) || (dst == null) || ((dst_offset + length) > dst.length) || ((src_offset + length) > (src.length * 4))
|| ((src_offset % 4) != 0) || ((length % 4) != 0)) {
croak("intsToBytes parameters are invalid:" + " src=" + Arrays.toString(src) + " dst=" + Arrays.toString(dst)
+ " (dst_offset=" + dst_offset + " + length=" + length + ")=" + (dst_offset + length) + " > dst.length="
+ ((dst == null) ? 0 : dst.length) + " (src_offset=" + src_offset + " + length=" + length + ")="
+ (src_offset + length) + " > (src.length=" + ((src == null) ? 0 : src.length) + "*4)="
+ ((src == null) ? 0 : (src.length * 4)) + " (src_offset=" + src_offset + " % 4)=" + (src_offset % 4)
+ " != 0" + " (length=" + length + " % 4)=" + (length % 4) + " != 0");
}
// Convert parameters to normal format
int[] offset = new int[1];
offset[0] = dst_offset;
int int_src_offset = src_offset / 4;
for( int i = 0; i < (length / 4); ++i ) {
intToBytes(src[int_src_offset++], dst, offset);
}
}
/**
* Convert a <code>byte</code> into an unsigned integer.
*
* @param b the <code>byte</code> to cast
*
* @return a postiive <code>int</code> whose lowest byte contains the bits
* of <code>b</code>.
*/
public static final int byteToUnsignedInt( byte b ) {
return ((int) b) & 0xff;
}
/**
* Copy contents of one array of <code>bytes</code> into another. If either
* array is <code>null</code>, simply return the <code>length</code>
* parameter directly.
*
* @param dst the array to write, or <code>null</code>
* @param dst_offset the start offset in <code>dst</code>
* @param src the array to read, or <code>null</code>
* @param src_offset the start offset in <code>src</code>
* @param length the number of <code>byte</code>s to copy.
*
* @return DOCUMENT ME!
*/
public static int memcpy( byte[] dst, int dst_offset, byte[] src, int src_offset, int length ) {
if ((dst != null) && (src != null)) {
if (dst.length < (dst_offset + length)) {
croak("dst.length = " + dst.length + ", but " + "dst_offset = " + dst_offset + " and length = " + length + ".");
}
if (src.length < (src_offset + length)) {
croak("src.length = " + src.length + ", but " + "src_offset = " + src_offset + " and length = " + length + ".");
}
for( int i = 0; i < length; ++i, ++dst_offset, ++src_offset )
dst[dst_offset] = src[src_offset];
}
return length;
}
/**
* Compare the contents of one array of <code>bytes</code> to another.
*
* @param a the first array
* @param a_offset the start offset in <code>a</code>
* @param b the second array
* @param b_offset the start offset in <code>b</code>
* @param length the number of <code>byte</code>s to compare.
*
* @return DOCUMENT ME!
*/
public static boolean memcmp( byte[] a, int a_offset, byte[] b, int b_offset, int length ) {
if ((a == null) && (b == null)) {
return true;
}
if ((a == null) || (b == null)) {
return false;
}
for( int i = 0; i < length; ++i, ++a_offset, ++b_offset )
if (a[a_offset] != b[b_offset]) {
return false;
}
return true;
}
/**
* Fill the given array with zeros.
*
* @param array the array to clear
* @param offset the start offset
* @param length the number of <code>byte</code>s to clear.
*/
public static void memclr( byte[] array, int offset, int length ) {
for( int i = 0; i < length; ++i, ++offset )
array[offset] = 0;
}
/**
* Round a number up to a given multiple.
*
* @param value the number to be rounded
* @param multiple the number to which to be rounded
*
* @return the smallest <code>int</code> greater than or equal to
* <code>value</code> which divides <code>multiple</code> exactly.
*/
public static int round_up( int value, int multiple ) {
return (((value - 1) / multiple) + 1) * multiple;
}
/**
* Return a new array equal to original except zero-padded to an integral
* mulitple of blocks. If the original is already an integral multiple of
* blocks, just return it.
*
* @param original the array of <code>byte</code>s to be padded
* @param block_size the size of the blocks
*
* @return an array whose size divides <code>block_size</code> exactly. The
* array is either <code>original</code> itself, or a copy whose
* first <code>original.length</code> bytes are equal to
* <code>original</code>.
*/
public static byte[] zero_pad( byte[] original, int block_size ) {
if ((original.length % block_size) == 0) {
return original;
}
byte[] result = new byte[round_up(original.length, block_size)];
memcpy(result, 0, original, 0, original.length);
// Unnecessary - jvm sets bytes to 0.
// memclr (result, original.length, result.length - original.length);
return result;
}
/**
* Determines whether two arrays of <code>byte</code>s contain the same
* contents.
*
* @param b1 The first array
* @param b2 The second array
*
* @return <code>true</code> if both arrays are <code>null</code>, both
* empty, or both of the same length with equal contents.
*/
public static boolean equals( byte[] b1, byte[] b2 ) {
if (b1 == b2) {
return true;
}
if ((b1 == null) || (b2 == null)) { // only one is null
return false;
}
if (b1.length != b2.length) {
return false;
}
for( int i = 0; i < b1.length; ++i )
if (b1[i] != b2[i]) {
return false;
}
return true;
}
/**
* Produce a <code>String</code> representation for the specified array of
* <code>byte</code>s. Print each <code>byte</code> as two hexadecimal
* digits.
*
* @param data The array to print
* @param offset the start offset in <code>data</code>
* @param length the number of <code>byte</code>s to print
*
* @return DOCUMENT ME!
*/
public static String print_bytes( byte[] data, int offset, int length ) {
int size = 2 * length;
size += ((size / 8) + (size / 64));
char[] buf = new char[size];
int low_mask = 0x0f;
int high_mask = 0xf0;
int buf_pos = 0;
byte b;
int j = 0;
for( int i = offset; i < (offset + length); ++i ) {
b = data[i];
buf[buf_pos++] = digits[(high_mask & b) >> 4];
buf[buf_pos++] = digits[(low_mask & b)];
if ((j % 4) == 3) {
buf[buf_pos++] = ' ';
}
if ((j % 32) == 31) {
buf[buf_pos++] = '\n';
}
++j;
}
return new String(buf);
}
/**
* DOCUMENT ME!
*
* @param data DOCUMENT ME!
* @param offset DOCUMENT ME!
* @param length DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static String print_bytes_exact( byte[] data, int offset, int length ) {
int size = 2 * length;
char[] buf = new char[size];
int low_mask = 0x0f;
int high_mask = 0xf0;
int buf_pos = 0;
byte b;
int j = 0;
for( int i = offset; i < (offset + length); ++i ) {
b = data[i];
buf[buf_pos++] = digits[(high_mask & b) >> 4];
buf[buf_pos++] = digits[(low_mask & b)];
++j;
}
return new String(buf);
}
/**
* DOCUMENT ME!
*
* @param data DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static String print_bytes( byte[] data ) {
return print_bytes(data, 0, data.length);
}
/**
* DOCUMENT ME!
*
* @param msg DOCUMENT ME!
*/
private static void croak( String msg ) {
// throw new java.AssertionViolatedException(msg);
}
/**
* DOCUMENT ME!
*
* @param b DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static int getUnsigned( byte b ) {
return ((b & 0xff)); // >> 8);
}
}