diff --git a/src/zutil/ByteUtil.java b/src/zutil/ByteUtil.java index 2090006..7fa5cd5 100755 --- a/src/zutil/ByteUtil.java +++ b/src/zutil/ByteUtil.java @@ -33,20 +33,20 @@ import zutil.converter.Converter; */ public class ByteUtil { /** Bitmask array used by utility functions **/ - private static final int[][] BYTE_MASK = new int[][]{ - {0b0000_0001}, - {0b0000_0010, 0b0000_0011}, - {0b0000_0100, 0b0000_0110, 0b0000_0111}, - {0b0000_1000, 0b0000_1100, 0b0000_1110, 0b0000_1111}, - {0b0001_0000, 0b0001_1000, 0b0001_1100, 0b0001_1110, 0b0001_1111}, - {0b0010_0000, 0b0011_0000, 0b0011_1000, 0b0011_1100, 0b0011_1110, 0b0011_1111}, - {0b0100_0000, 0b0110_0000, 0b0111_0000, 0b0111_1000, 0b0111_1100, 0b0111_1110, 0b0111_1111}, - {0b1000_0000, 0b1100_0000, 0b1110_0000, 0b1111_0000, 0b1111_1000, 0b1111_1100, 0b1111_1110, 0b1111_1111} + private static final int[/*index*/][/*length*/] BYTE_MASK = new int[][]{ + {0b0000_0001, 0b0000_0011, 0b0000_0111, 0b0000_1111, 0b0001_1111, 0b0011_1111, 0b0111_1111, 0b1111_1111}, + {0b0000_0010, 0b0000_0110, 0b0000_1110, 0b0001_1110, 0b0011_1110, 0b0111_1110, 0b1111_1110}, + {0b0000_0100, 0b0000_1100, 0b0001_1100, 0b0011_1100, 0b0111_1100, 0b1111_1100}, + {0b0000_1000, 0b0001_1000, 0b0011_1000, 0b0111_1000, 0b1111_1000}, + {0b0001_0000, 0b0011_0000, 0b0111_0000, 0b1111_0000}, + {0b0010_0000, 0b0110_0000, 0b1110_0000}, + {0b0100_0000, 0b1100_0000}, + {0b1000_0000}, }; /** - * Creates a new sub byte from index and with the given length length + * Creates a new sub byte from index and with the given length * * @param data is the byte data * @param index is the bit index, valid values 0-7 @@ -58,7 +58,7 @@ public class ByteUtil { } /** - * Creates a new sub byte from index and with a length and shifts the data to the left + * Creates a new sub byte from index and with a length and shifts the data all the way to the right (LSB) * * @param data is the byte data * @param index is the bit index, valid values 0-7 @@ -67,7 +67,7 @@ public class ByteUtil { */ public static byte getShiftedBits(byte data, int index, int length) { int ret = 0xFF & getBits(data, index, length); - ret = ret >>> index+1-length; + ret = ret >>> index; return (byte) ret; } @@ -79,7 +79,7 @@ public class ByteUtil { * @return a new byte containing a sub byte defined by the index and length */ public static byte getBits(byte data, int length) { - return getBits(data, length-1, length); + return getBits(data, 0, length); } /** @@ -105,7 +105,7 @@ public class ByteUtil { * @return a new byte containing a sub byte defined by the index and length */ public static byte getBitsMSB(byte data, int length) { - return getShiftedBits(data, 7, length); + return getShiftedBits(data, 8-length, length); } /** @@ -119,7 +119,7 @@ public class ByteUtil { byte[] dest = new byte[data.length]; if (data.length > 0) for (int i=0; i index || index > 7) - throw new IllegalArgumentException("Invalid index argument, allowed values: 0-7"); - if (length < 0 || index-length < 0) - throw new IllegalArgumentException("Invalid length argument: " + length + ", allowed values: 1 to " + (index+1) + " for index " + index); + throw new IllegalArgumentException("Invalid index argument " + index + ", allowed values: 0-7"); + if (1 > length || length > 8 - index) + throw new IllegalArgumentException("Invalid length argument: " + length + ", allowed values: 1 to " + (8 - index) + " for index " + index); + + --length; return (byte) BYTE_MASK[index][length]; } /** - * Shifts a whole byte array to the left by the specified amount. + * Shifts a whole byte array to the right towards the LSB by the specified amount. * * @param data the array to be shifted * @param shiftBy the amount to shift. Currently only supports maximum value of 8 * @return same data reference as the data input */ - public static byte[] shiftLeft(byte[] data, int shiftBy) { + public static byte[] shiftRight(byte[] data, int shiftBy) { if (0 > shiftBy || shiftBy > 8) throw new IllegalArgumentException("Invalid shiftBy(" + shiftBy + ") argument, allowed values: 0-8"); if (shiftBy == 0) @@ -154,8 +155,9 @@ public class ByteUtil { byte rest; for (int i=0; i>> shiftBy); + rest = getBits(data[i], 0, shiftBy); + rest = (byte)(rest << 8 - shiftBy); + data[i] = (byte)((data[i] & 0xFF) >>> shiftBy); if (i != 0) data[i-1] |= rest; } @@ -164,13 +166,13 @@ public class ByteUtil { } /** - * Shifts a whole byte array to the right by the specified amount. + * Shifts a whole byte array to the left towards the MSB by the specified amount. * * @param data the array to be shifted * @param shiftBy the amount to shift. Currently only supports maximum value of 8 * @return same data reference as the data input */ - public static byte[] shiftRight(byte[] data, int shiftBy) { + public static byte[] shiftLeft(byte[] data, int shiftBy) { if (0 > shiftBy || shiftBy > 8) throw new IllegalArgumentException("Invalid shiftBy(" + shiftBy + ") argument, allowed values: 0-8"); if (shiftBy == 0) @@ -193,7 +195,7 @@ public class ByteUtil { * Presents a binary array in HEX and ASCII * * @param data The source binary data to format - * @return A multiline String with human readable HEX and ASCII + * @return A multiline String with human-readable HEX and ASCII */ public static String toFormattedString(byte[] data) { return toFormattedString(data, 0, data.length); diff --git a/src/zutil/parser/binary/BinaryStructInputStream.java b/src/zutil/parser/binary/BinaryStructInputStream.java index 14c0eb4..96d3e33 100755 --- a/src/zutil/parser/binary/BinaryStructInputStream.java +++ b/src/zutil/parser/binary/BinaryStructInputStream.java @@ -115,12 +115,12 @@ public class BinaryStructInputStream extends InputStream{ dataBitIndex = 7; } int subBitLength = Math.min(dataBitIndex + 1, field.getBitLength(struct) - fieldReadLength); - valueData[valueDataIndex] = ByteUtil.getBits(data, dataBitIndex, subBitLength); + valueData[valueDataIndex] = ByteUtil.getBits(data, dataBitIndex - (subBitLength - 1), subBitLength); fieldReadLength += subBitLength; dataBitIndex -= subBitLength; } // Set value - ByteUtil.shiftLeft(valueData, shiftBy); // shift data so that LSB is at the beginning + ByteUtil.shiftRight(valueData, shiftBy); // shift data so that LSB is at the beginning field.setByteValue(struct, valueData); } } diff --git a/src/zutil/parser/binary/BinaryStructOutputStream.java b/src/zutil/parser/binary/BinaryStructOutputStream.java index 7248d1c..1dafd30 100755 --- a/src/zutil/parser/binary/BinaryStructOutputStream.java +++ b/src/zutil/parser/binary/BinaryStructOutputStream.java @@ -103,15 +103,15 @@ public class BinaryStructOutputStream extends OutputStream { } else { int fieldBitLength = field.getBitLength(struct); byte[] data = field.getByteValue(struct); - data = ByteUtil.shiftRight(data, ((8 - fieldBitLength % 8) % 8)); + data = ByteUtil.shiftLeft(data, ((8 - fieldBitLength % 8) % 8)); - for (int i=(int)Math.ceil(fieldBitLength/8.0)-1; fieldBitLength>0; fieldBitLength-=8, --i) { + for (int i=(int)Math.ceil(fieldBitLength / 8.0) - 1; fieldBitLength>0; fieldBitLength-=8, --i) { byte b = data[i]; if (restBitLength == 0 && fieldBitLength >= 8) out.write(0xFF & b); else { b = (byte) ((b & 0xFF) >> restBitLength); - b &= ByteUtil.getBitMask(7 - restBitLength, fieldBitLength); + b &= ByteUtil.getBitMask(8 - restBitLength - fieldBitLength, fieldBitLength); rest |= b; restBitLength += fieldBitLength; if (restBitLength >= 8) diff --git a/test/zutil/ByteUtilTest.java b/test/zutil/ByteUtilTest.java index 622be55..20a9e2a 100755 --- a/test/zutil/ByteUtilTest.java +++ b/test/zutil/ByteUtilTest.java @@ -42,23 +42,30 @@ public class ByteUtilTest { assertEquals(1, ByteUtil.getShiftedBits((byte)0b0001_0000, 4, 1)); assertEquals(1, ByteUtil.getShiftedBits((byte)0b0000_0001, 0, 1)); - assertEquals(3, ByteUtil.getShiftedBits((byte)0b0110_0000, 6, 2)); + assertEquals(3, ByteUtil.getShiftedBits((byte)0b0110_0000, 5, 2)); - assertEquals((byte)0xFF, ByteUtil.getShiftedBits((byte)0b1111_1111, 7, 8)); + assertEquals((byte)0xFF, ByteUtil.getShiftedBits((byte)0b1111_1111, 0, 8)); } @Test public void getBits() { - assertEquals(0x01, ByteUtil.getBits((byte)0x11, 1)); - assertEquals(0x03, ByteUtil.getBits((byte)0x13, 4)); - assertEquals((byte)0x55, ByteUtil.getBits((byte)0x55, 8)); + assertEquals(0b0000_0001, ByteUtil.getBits((byte)0b0001_0001, 1)); + assertEquals(0b0000_0011, ByteUtil.getBits((byte)0b000_0011, 4)); + assertEquals((byte)0b0101_0101, ByteUtil.getBits((byte)0b0101_0101, 8)); + } + + @Test + public void getBitMask() { + assertEquals((byte)0b0001, ByteUtil.getBitMask(0, 1)); + assertEquals((byte)0b0110, ByteUtil.getBitMask(1, 2)); + assertEquals((byte)0b1111_0000, ByteUtil.getBitMask(4, 4)); } @Test public void getBitsMSB() { assertEquals(0x01, ByteUtil.getBitsMSB((byte)0x80, 1)); - assertEquals(0x05, ByteUtil.getBitsMSB((byte)0x52, 4)); + assertEquals(0b0000_0101, ByteUtil.getBitsMSB((byte)0b0101_0010, 4)); assertEquals((byte)0x55, ByteUtil.getBitsMSB((byte)0x55, 8)); assertEquals((byte)0x03, ByteUtil.getBitsMSB((byte)0xFF, 2)); assertEquals((byte)0x0F, ByteUtil.getBitsMSB((byte)0xFF, 4)); @@ -109,41 +116,45 @@ public class ByteUtilTest { } + @Test + public void shiftRight() { + assertArrayEquals( new byte[]{}, + ByteUtil.shiftRight(new byte[]{}, 4)); + assertArrayEquals( new byte[]{0b0000_0001}, + ByteUtil.shiftRight(new byte[]{0b0000_0001}, 0)); + assertArrayEquals( new byte[]{0b0000_0000}, + ByteUtil.shiftRight(new byte[]{0b0000_0001}, 1)); + assertArrayEquals( new byte[]{0b0000_0001}, + ByteUtil.shiftRight(new byte[]{0b0001_0000}, 4)); + assertArrayEquals( new byte[]{0b0000_1000}, + ByteUtil.shiftRight(new byte[]{(byte)0b1000_0000}, 4)); + assertArrayEquals( new byte[]{0b0001_0001, 0b0000_0000}, + ByteUtil.shiftRight(new byte[]{0b0001_0000, 0b0000_0001}, 4)); + assertArrayEquals( new byte[]{0b0100_1001, 0b0000_0001}, + ByteUtil.shiftRight(new byte[]{0b0111_1111, 0b0101_0010}, 6)); + assertArrayEquals( new byte[]{0b0000_0001,0b0000_0001,0b0000_0001,0b0000_0001}, + ByteUtil.shiftRight(new byte[]{0b0001_0000,0b0001_0000,0b0001_0000,0b0001_0000}, 4)); + } + @Test public void shiftLeft() { assertArrayEquals( new byte[]{}, ByteUtil.shiftLeft(new byte[]{}, 4)); assertArrayEquals( new byte[]{0b0000_0001}, ByteUtil.shiftLeft(new byte[]{0b0000_0001}, 0)); - assertArrayEquals( new byte[]{0b0000_0001}, - ByteUtil.shiftLeft(new byte[]{0b0001_0000}, 4)); - assertArrayEquals( new byte[]{0b0000_1000}, - ByteUtil.shiftLeft(new byte[]{(byte)0b1000_0000}, 4)); - assertArrayEquals( new byte[]{0b0001_0001, 0b0000_0000}, - ByteUtil.shiftLeft(new byte[]{0b0001_0000, 0b0000_0001}, 4)); - assertArrayEquals( new byte[]{0b0100_1001, 0b0000_0001}, + assertArrayEquals( new byte[]{(byte)0b0000_0010}, + ByteUtil.shiftLeft(new byte[]{0b0000_0001}, 1)); + assertArrayEquals( new byte[]{(byte)0b0001_0000}, + ByteUtil.shiftLeft(new byte[]{0b0000_0001}, 4)); + assertArrayEquals( new byte[]{(byte)0b1000_0000}, + ByteUtil.shiftLeft(new byte[]{0b0000_1000}, 4)); + assertArrayEquals( new byte[]{0b0000_0000}, + ByteUtil.shiftLeft(new byte[]{(byte)0b0001_0000}, 4)); + assertArrayEquals( new byte[]{0b0001_0000, 0b0001_0000}, + ByteUtil.shiftLeft(new byte[]{0b0000_0001, 0b0000_0001}, 4)); + assertArrayEquals( new byte[]{(byte)0b1100_0000, (byte)0b1001_1111}, ByteUtil.shiftLeft(new byte[]{0b0111_1111, 0b0101_0010}, 6)); - assertArrayEquals( new byte[]{0b0000_0001,0b0000_0001,0b0000_0001,0b0000_0001}, + assertArrayEquals( new byte[]{0b0000_0000,0b0000_0001,0b0000_0001,0b0000_0001}, ByteUtil.shiftLeft(new byte[]{0b0001_0000,0b0001_0000,0b0001_0000,0b0001_0000}, 4)); } - - @Test - public void shiftRight() { - assertArrayEquals( new byte[]{}, - ByteUtil.shiftRight(new byte[]{}, 4)); - assertArrayEquals( new byte[]{0b0000_0001}, - ByteUtil.shiftRight(new byte[]{0b0000_0001}, 0)); - assertArrayEquals( new byte[]{(byte)0b0001_0000}, - ByteUtil.shiftRight(new byte[]{0b0000_0001}, 4)); - assertArrayEquals( new byte[]{(byte)0b1000_0000}, - ByteUtil.shiftRight(new byte[]{0b0000_1000}, 4)); - assertArrayEquals( new byte[]{0b0000_0000}, - ByteUtil.shiftRight(new byte[]{(byte)0b0001_0000}, 4)); - assertArrayEquals( new byte[]{0b0001_0000, 0b0001_0000}, - ByteUtil.shiftRight(new byte[]{0b0000_0001, 0b0000_0001}, 4)); - assertArrayEquals( new byte[]{(byte)0b1100_0000, (byte)0b1001_1111}, - ByteUtil.shiftRight(new byte[]{0b0111_1111, 0b0101_0010}, 6)); - assertArrayEquals( new byte[]{0b0000_0000,0b0000_0001,0b0000_0001,0b0000_0001}, - ByteUtil.shiftRight(new byte[]{0b0001_0000,0b0001_0000,0b0001_0000,0b0001_0000}, 4)); - } } diff --git a/test/zutil/net/mqtt/MqttBrokerRun.java b/test/zutil/net/mqtt/MqttBrokerRun.java new file mode 100644 index 0000000..450f9d2 --- /dev/null +++ b/test/zutil/net/mqtt/MqttBrokerRun.java @@ -0,0 +1,27 @@ +package zutil.net.mqtt; + +import zutil.log.CompactLogFormatter; +import zutil.log.LogUtil; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class MqttBrokerRun implements MqttSubscriptionListener { + private static final Logger logger = LogUtil.getLogger(); + + public static void main(String[] args) throws IOException { + LogUtil.setGlobalLevel(Level.FINEST); + LogUtil.setGlobalFormatter(new CompactLogFormatter()); + + MqttBroker mqttBroker = new MqttBroker(); + mqttBroker.addGlobalSubscriber(new MqttBrokerRun()); + mqttBroker.start(); + } + + @Override + public void dataPublished(String topic, byte[] data) { + logger.info("MQTT data published(topic: " + topic + "): " + new String(data, StandardCharsets.UTF_8)); + } +} diff --git a/test/zutil/net/mqtt/packet/MqttPacketPublishTest.java b/test/zutil/net/mqtt/packet/MqttPacketPublishTest.java index 3660628..09230ff 100644 --- a/test/zutil/net/mqtt/packet/MqttPacketPublishTest.java +++ b/test/zutil/net/mqtt/packet/MqttPacketPublishTest.java @@ -36,6 +36,47 @@ public class MqttPacketPublishTest { assertArrayEquals(new byte[0], obj.payload); } + @Test + public void decodeFlags() throws IOException { + char[] data = new char[]{ + // Fixed Header + 0b0011_1000, // Packet Type(4) + Reserved(4) + 0xFF & 4, // Variable Header + Payload Length + // Variable Header + 0b0000_0000, // length + 0xFF & 2, // length + 'a', // Topic Name + 'b', // Topic Name + // Payload + }; + + MqttPacketPublish obj = (MqttPacketPublish) MqttPacket.read( + new BinaryStructInputStream(new ByteArrayInputStream(Converter.toBytes(data)))); + + assertTrue(obj.getFlagDup()); + assertEquals(MqttPacketPublish.PUBLISH_QOS_0, obj.getFlagQoS()); + assertFalse(obj.getFlagRetain()); + + data = new char[]{ + // Fixed Header + 0b0011_0101, // Packet Type(4) + Reserved(4) + 0xFF & 4, // Variable Header + Payload Length + // Variable Header + 0b0000_0000, // length + 0xFF & 2, // length + 'a', // Topic Name + 'b', // Topic Name + // Payload + }; + + obj = (MqttPacketPublish) MqttPacket.read( + new BinaryStructInputStream(new ByteArrayInputStream(Converter.toBytes(data)))); + + assertFalse(obj.getFlagDup()); + assertEquals(MqttPacketPublish.PUBLISH_QOS_2, obj.getFlagQoS()); + assertTrue(obj.getFlagRetain()); + } + @Test public void decodePayload() throws IOException { char[] data = new char[]{ diff --git a/test/zutil/parser/binary/BinaryStructOutputStreamTest.java b/test/zutil/parser/binary/BinaryStructOutputStreamTest.java index c103f72..9de7513 100755 --- a/test/zutil/parser/binary/BinaryStructOutputStreamTest.java +++ b/test/zutil/parser/binary/BinaryStructOutputStreamTest.java @@ -61,7 +61,7 @@ public class BinaryStructOutputStreamTest { }; byte[] data = BinaryStructOutputStream.serialize(struct); - assertArrayEquals(new byte[]{(byte)0b0111_1111}, data); + assertArrayEquals(new byte[]{(byte)0b011_11111}, data); } @Test