package org.simantics.databoard.util.binary; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; /** * DataInput and DataOutput serialize primitive numbers with big endian byte * order. This utility gives little endian read and write. * * @author Toni Kalajainen */ public class LittleEndian { public static short readShort(DataInput in) throws IOException { return (short) (in.readUnsignedByte() | (in.readUnsignedByte() << 8)); } public static void writeShort(DataOutput out, int v) throws IOException { v = (((v & 0xFF00) >> 8) | (v << 8)); out.writeShort( v ); } public static int readUnsignedShort(DataInput in) throws IOException { int v = in.readUnsignedShort(); return (((v & 0xFF00) >> 8) | (v << 8)); } public static int readInt(DataInput in) throws IOException { return Integer.reverseBytes( in.readInt() ); } public static void writeInt(DataOutput out, int v) throws IOException { out.writeInt( Integer.reverseBytes( v ) ); } public static long readLong(DataInput in) throws IOException { return Long.reverseBytes( in.readLong() ); } public static void writeLong(DataOutput out, long v) throws IOException { out.writeLong( Long.reverseBytes(v) ); } public static double readDouble(DataInput in) throws IOException { return Double.longBitsToDouble( Long.reverseBytes( in.readLong() ) ); } public static void writeDouble(DataOutput out, double d) throws IOException { out.writeLong( Long.reverseBytes( Double.doubleToLongBits(d) ) ); } public static float readFloat(DataInput in) throws IOException { return Float.intBitsToFloat( Integer.reverseBytes( in.readInt() ) ); } public static void writeFloat(DataOutput out, float v) throws IOException { out.writeInt( Integer.reverseBytes( Float.floatToIntBits(v) ) ); } public static void writeUInt24(DataOutput out, int value) throws IOException { out.write((byte)value); out.write((byte)(value >> 8)); out.write((byte)(value >> 16)); } public static int readUInt24(DataInput in) throws IOException { return ( ( in.readByte() ) | ( in.readByte() << 8) | ( in.readByte() << 16) ) & 0xffffff; } /** * Write UInt32 with dynamic encoding (1-5 bytes). * * @param out * @param length * @throws IOException */ public static void writeDynamicUInt32(DataOutput out, int length) throws IOException { if(length < 0x80) { out.write((byte)length); } else { length -= 0x80; if(length < 0x4000) { out.write((byte)((length&0x3f) | 0x80)); out.write((byte)(length>>>6)); } else { length -= 0x4000; if(length < 0x200000) { out.write((byte)((length&0x1f) | 0xc0)); out.write((byte)((length>>>5)&0xff)); out.write((byte)((length>>>13)&0xff)); } else { length -= 0x200000; if(length < 0x10000000) { out.write((byte)((length&0x0f) | 0xe0)); out.write((byte)((length>>>4)&0xff)); out.write((byte)((length>>>12)&0xff)); out.write((byte)((length>>>20)&0xff)); } else { length -= 0x10000000; out.write((byte)((length&0x07) | 0xf0)); out.write((byte)((length>>>3)&0xff)); out.write((byte)((length>>>11)&0xff)); out.write((byte)((length>>>19)&0xff)); out.write((byte)((length>>>27)&0xff)); } } } } } public static int readDynamicUInt32(DataInput in) throws IOException { int length = in.readByte()&0xff; if(length >= 0x80) { if(length >= 0xc0) { if(length >= 0xe0) { if(length >= 0xf0) { length &= 0x0f; length += ((in.readByte()&0xff)<<3); length += ((in.readByte()&0xff)<<11); length += ((in.readByte()&0xff)<<19); length += 0x10204080; } else { length &= 0x1f; length += ((in.readByte()&0xff)<<4); length += ((in.readByte()&0xff)<<12); length += ((in.readByte()&0xff)<<20); length += 0x204080; } } else { length &= 0x3f; length += ((in.readByte()&0xff)<<5); length += ((in.readByte()&0xff)<<13); length += 0x4080; } } else { length &= 0x7f; length += ((in.readByte()&0xff)<<6); length += 0x80; } } return length; } /** * Get number of bytes for dynamic encoding of UInt32 (1-5 bytes) * * @param length length value * @return bytes required (1-5) */ public static int getDynamicUInt32Length(int length) { if(length < 0x80) return 1; if(length < 0x4080) return 2; if(length < 0x204000) return 3; if(length < 0x10200000) return 4; return 5; } /** * Decode an unsigned integer. The number of bytes read depends on maxValue. * * @param in * @param maxValue * @return int * @throws IOException */ public static int getUInt(DataInput in, int maxValue) throws IOException { if (maxValue==0) return 0; if (maxValue<0x100) { return in.readByte() & 0xFF; } else if (maxValue<0x10000) { return LittleEndian.readShort(in) & 0xFFFF; } else if (maxValue<0x1000000) { return LittleEndian.readUInt24(in) & 0xFFFFFF; } else { return readInt(in); } } /** * Calculate unsigned integer encoding length. * * @param maxValue * @return 0-4 bytes */ public static int getUIntLength(int maxValue) { if (maxValue==0) { return 0; } else if (maxValue<0x100) { return 1; } else if (maxValue<0x10000) { return 2; } else if (maxValue<0x1000000) { return 3; } else { return 4; } } /** * Encode and write an unsigned integer. The number of bytes written * depends on the maxValue. * * @param out * @param value * @param maxValue * @throws IOException */ public static void putUInt(DataOutput out, int value, int maxValue) throws IOException { if (maxValue==0) {} else if (maxValue<0x100) { out.write(value); } else if (maxValue<0x10000) { LittleEndian.writeShort(out, value); } else if (maxValue<0x1000000) { LittleEndian.writeUInt24(out, value); } else { out.writeInt(value); } } }