package iamrescue.communication.messages.codec; import iamrescue.communication.BitStream; import iamrescue.communication.messages.codec.property.PropertyCodec; import iamrescue.communication.messages.codec.property.PropertyEncoderStore; import iamrescue.util.ArrayUtils; import java.math.BigInteger; import org.apache.commons.lang.Validate; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.EntityID; import rescuecore2.worldmodel.Property; public class BitStreamEncoder { protected static final boolean[] BYTE_PREFIX = { true }; protected static final boolean[] SHORT_PREFIX = { false, true }; protected static final boolean[] INT_PREFIX = { false, false }; private BitStream outputStream; private ICommunicationBeliefBaseAdapter beliefBase; private PropertyEncoderStore encoders; public BitStreamEncoder(ICommunicationBeliefBaseAdapter beliefBase) { outputStream = new BitStream(); this.beliefBase = beliefBase; this.encoders = beliefBase.getEncoders(); } public ICommunicationBeliefBaseAdapter getBeliefBase() { return beliefBase; } public void appendByte(byte b) { outputStream.append(CodecUtils.encodeByte(b)); } public void appendInt(int integer) { outputStream.append(CodecUtils.encodeInt(integer)); } public void appendNumber(int number) { if (number == (byte) number) { outputStream.append(BYTE_PREFIX); appendByte((byte) number); } else if (number == (short) number) { outputStream.append(SHORT_PREFIX); appendShort((short) number); } else { outputStream.append(INT_PREFIX); appendInt(number); } } // public void appendNumberSpecial(int number) { // boolean sign = number < 0; // number = number > 0 ? number : -number; // // int bitsNeeded = bitsNeeded(number); // System.out.println(bitsNeeded); // // // Math.ceil(bitsneeded / 4) // int blocksNeeded = ((bitsNeeded + 3) / 4); // System.out.println(blocksNeeded); // // int actualBits = blocksNeeded * 4; // // int padding = actualBits - bitsNeeded; // System.out.println("padding " + padding); // // appendBoolean(((blocksNeeded) & 1) != 0); // appendBoolean(((blocksNeeded) & 2) != 0); // appendBoolean(((blocksNeeded) & 4) != 0); // // encode the sign of the number // appendBoolean(sign); // // number = number > 0 ? number : -number; // // while (number != 0) { // appendBoolean((number & 1) == 1); // number >>= 1; // } // } public void appendShort(int integer) { outputStream.append(CodecUtils.encodeShort(integer)); } public byte[] toByteArray() { return outputStream.toByteArray(); } /** * Append an integer array to the byte stream. Note that the size of the * array should be smaller than 127 * * @param array */ public void appendIntArray(int[] array) { Validate.isTrue(array.length < Byte.MAX_VALUE, "Does not support arrays larger than " + Byte.MAX_VALUE); appendByte((byte) array.length); for (int i = 0; i < array.length; i++) { appendNumber(array[i]); } } // public void appendNumber(Class<? extends Number> valueType, // Number propertyValue) { // int encodedValue; // // if (valueType.equals(Integer.class)) { // encodedValue = propertyValue.intValue(); // appendInt(encodedValue); // } else if (valueType.equals(Short.class)) { // encodedValue = propertyValue.shortValue(); // appendShort(propertyValue.shortValue()); // } else if (valueType.equals(Byte.class)) { // encodedValue = propertyValue.byteValue(); // appendByte(propertyValue.byteValue()); // } else { // throw new IllegalArgumentException(valueType + " not supported"); // } // // if (propertyValue.intValue() != encodedValue) // throw new IllegalArgumentException("Losing precision. Original: " // + propertyValue.intValue() + ". Encoded as " + valueType // + ": " + encodedValue); // public void appendByteArray(byte[] array) { Validate.isTrue(array.length < Byte.MAX_VALUE, "Does not support arrays larger than " + Byte.MAX_VALUE); appendByte((byte) array.length); for (int i = 0; i < array.length; i++) { appendByte(array[i]); } } /** * This is more efficient than appendByteArray * * @param b */ public void appendByteArraySpecial(byte[] b) { Validate.isTrue(b.length < Byte.MAX_VALUE, "Does not support arrays larger than " + Byte.MAX_VALUE); if (b.length == 0) { appendByte(Byte.MIN_VALUE); return; } byte[] array = new byte[b.length]; // get rid of zeros for (int i = 0; i < array.length; i++) array[i] = (byte) (b[i] + 1); byte max = (byte) (ArrayUtils.max(array) + 1); appendByte(max); BigInteger encoded = BigInteger.valueOf(array[array.length - 1]); BigInteger maxBI = BigInteger.valueOf(max); for (int i = array.length - 2; i >= 0; i--) { encoded = encoded.multiply(maxBI); encoded = encoded.add(BigInteger.valueOf(array[i])); } appendByteArray(encoded.toByteArray()); } public void appendBoolean(boolean b) { outputStream.append(b); } public void appendProperty(Entity object, Property property) { PropertyCodec propertyEncoder = encoders.get(property.getURN()); propertyEncoder.encode(object, property, this); } public void appendEntityID(Entity object) { if (beliefBase.isShortIDAvailable(object.getClass())) { appendBoolean(true); appendShort(beliefBase.getShortID(object)); } else { appendBoolean(false); appendNumber(object.getID().getValue() + Short.MIN_VALUE); } } public void appendEntityID(EntityID id) { if (beliefBase.getShortIndex().knowsAboutLongID(id)) { appendBoolean(true); appendShort(beliefBase.getShortIndex().getShortID(id)); } else { appendBoolean(false); appendNumber(id.getValue() + Short.MIN_VALUE); } } public BitStream getBitStream() { return outputStream; } public static int bitsNeeded(int number) { if (number == Integer.MIN_VALUE) { throw new IllegalArgumentException("Can't encode Integer.MIN_VALUE"); } int count = 0; while (number != 0) { number >>= 1; count++; } return count; } // // public static void main(String[] args) { // // BitStreamEncoder bitStreamEncoder = new BitStreamEncoder(null); // // bitStreamEncoder.appendNumberSpecial(121); // // System.out.println(bitStreamEncoder.getBitStream().size()); // System.out.println(bitStreamEncoder.getBitStream()); // // bitStreamEncoder = new BitStreamEncoder(null); // bitStreamEncoder.appendNumberSpecial(-121); // // System.out.println(bitStreamEncoder.getBitStream().size()); // System.out.println(bitStreamEncoder.getBitStream()); // // // int val = Integer.MIN_VALUE + 1; // // // // System.out.println(bitsNeeded(val)); // // // // int number = -1; // // // // System.out.println(Integer.toBinaryString(number)); // // System.out.println(Integer.toBinaryString(Integer.MAX_VALUE)); // // System.out.println(Integer.toBinaryString(Integer.MIN_VALUE)); // // // // System.out.println(Integer.bitCount(Integer.MAX_VALUE)); // // System.out.println(Integer.bitCount(Integer.MIN_VALUE)); // } }