From a33714fbc4dbd8e1fc2634c4a05fdfd692cc4305 Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Sat, 5 Mar 2016 00:42:26 +0100 Subject: [PATCH] Basic support for writing int with BinaryStruct --- src/zutil/parser/binary/BinaryFieldData.java | 83 +++++++++++++ .../binary/BinaryStructInputStream.java | 72 ++++++++++++ .../binary/BinaryStructOutputStream.java | 97 +++++++++++++++ .../parser/binary/BinaryStructParser.java | 110 ------------------ .../parser/binary/BinaryStructWriter.java | 50 -------- ....java => BinaryStructInputStreamTest.java} | 16 +-- .../binary/BinaryStructOutputStreamTest.java | 89 ++++++++++++++ 7 files changed, 349 insertions(+), 168 deletions(-) create mode 100755 src/zutil/parser/binary/BinaryFieldData.java create mode 100755 src/zutil/parser/binary/BinaryStructInputStream.java create mode 100755 src/zutil/parser/binary/BinaryStructOutputStream.java delete mode 100755 src/zutil/parser/binary/BinaryStructParser.java delete mode 100755 src/zutil/parser/binary/BinaryStructWriter.java rename test/zutil/parser/binary/{BinaryStructTest.java => BinaryStructInputStreamTest.java} (86%) create mode 100755 test/zutil/parser/binary/BinaryStructOutputStreamTest.java diff --git a/src/zutil/parser/binary/BinaryFieldData.java b/src/zutil/parser/binary/BinaryFieldData.java new file mode 100755 index 0000000..a367ea1 --- /dev/null +++ b/src/zutil/parser/binary/BinaryFieldData.java @@ -0,0 +1,83 @@ +package zutil.parser.binary; + + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import zutil.converter.Converter; +import zutil.parser.binary.BinaryStruct.BinaryField; + +/** + * A class representing each field in a BinaryStruct. + */ +public class BinaryFieldData implements Comparable { + private static final HashMap> cache = new HashMap<>(); + + private int index; + private int length; + private Field field; + + + protected static List getStructFieldList(Class clazz){ + if (!cache.containsKey(clazz)) { + ArrayList list = new ArrayList<>(); + for (Field field : clazz.getFields()) { + if (field.isAnnotationPresent(BinaryField.class)) + list.add(new BinaryFieldData(field)); + } + Collections.sort(list); + cache.put(clazz, list); + } + return cache.get(clazz); + } + + + private BinaryFieldData(Field f){ + field = f; + BinaryField fieldData = field.getAnnotation(BinaryField.class); + index = fieldData.index(); + length = fieldData.length(); + } + + protected void setValue(Object obj, byte[] data){ + try { + field.setAccessible(true); + if (field.getType() == Boolean.class || field.getType() == boolean.class) + field.set(obj, data[0] != 0); + else if (field.getType() == Integer.class || field.getType() == int.class) + field.set(obj, Converter.toInt(data)); + else if (field.getType() == String.class) + field.set(obj, new String(data)); + } catch (IllegalAccessException e){ + e.printStackTrace(); + } + } + + // TODO: variable length support + protected byte[] getValue(Object obj){ + try { + if (field.getType() == Boolean.class || field.getType() == boolean.class) + return new byte[]{ (byte)(field.getBoolean(obj) ? 0x01 : 0x00) }; + else if (field.getType() == Integer.class || field.getType() == int.class) + return Converter.toBytes(field.getInt(obj)); + else if (field.getType() == String.class) + return ((String)(field.get(obj))).getBytes(); + } catch (IllegalAccessException e){ + e.printStackTrace(); + } + return null; + } + + + public int getBitLength(){ + return length; + } + + @Override + public int compareTo(BinaryFieldData o) { + return this.index - o.index; + } +} \ No newline at end of file diff --git a/src/zutil/parser/binary/BinaryStructInputStream.java b/src/zutil/parser/binary/BinaryStructInputStream.java new file mode 100755 index 0000000..2594085 --- /dev/null +++ b/src/zutil/parser/binary/BinaryStructInputStream.java @@ -0,0 +1,72 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.parser.binary; + +import zutil.ByteUtil; +import zutil.converter.Converter; +import zutil.parser.binary.BinaryStruct.BinaryField; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +/** + * A stream class that parses a byte stream into + * binary struct objects. + * + * @author Ziver + */ +public class BinaryStructInputStream { + + public static int parse(BinaryStruct struct, byte[] data) { + List structDataList = BinaryFieldData.getStructFieldList(struct.getClass()); + int bitOffset = 0; + for (BinaryFieldData field : structDataList){ + + int byteIndex = bitOffset / 8; + int bitIndex = 7 - bitOffset % 8; + int bitLength = Math.min(bitIndex+1, field.getBitLength()); + + int readLength = 0; + byte[] valueData = new byte[(int) Math.ceil(field.getBitLength() / 8.0)]; + for (int index = 0; index < valueData.length; ++index) { + valueData[index] = ByteUtil.getShiftedBits(data[byteIndex], bitIndex, bitLength); + readLength += bitLength; + byteIndex++; + bitIndex = 7; + bitLength = Math.min(bitIndex+1, field.getBitLength() - readLength); + } + field.setValue(struct, valueData); + bitOffset += field.getBitLength(); + } + return bitOffset; + } + + + + +} diff --git a/src/zutil/parser/binary/BinaryStructOutputStream.java b/src/zutil/parser/binary/BinaryStructOutputStream.java new file mode 100755 index 0000000..307addf --- /dev/null +++ b/src/zutil/parser/binary/BinaryStructOutputStream.java @@ -0,0 +1,97 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.parser.binary; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + + +/** + * A stream class that generates a byte stream from + * binary struct objects. + * + * @author Ziver + */ +public class BinaryStructOutputStream { + + private OutputStream out; + private byte rest; + private int restLength; + + + public BinaryStructOutputStream(OutputStream out){ + this.out = out; + + rest = 0; + restLength = 0; + } + + + /** + * Generate a binary byte array from the provided struct. + * The byte array will be left + */ +/* public byte[] serialize(BinaryStruct struct) { + + }*/ + + /** + * Generate a binary stream from the provided struct and + * write the data to the underlying stream. + */ + public void write(BinaryStruct struct) throws IOException { + List structDataList = BinaryFieldData.getStructFieldList(struct.getClass()); + + for (BinaryFieldData field : structDataList){ + byte[] data = field.getValue(struct); + + for (int i=data.length-1; i>=0; --i) { + out.write(data[i]); + } + } + } + + + /** + * Writes any outstanding data to the stream + */ + public void flush() throws IOException { + if(restLength > 0){ + out.write(0xFF & rest); + rest = 0; + restLength = 0; + } + out.flush(); + } + + /** + * Flushes and closes the underlying stream + */ + public void close() throws IOException { + flush(); + out.close(); + } +} diff --git a/src/zutil/parser/binary/BinaryStructParser.java b/src/zutil/parser/binary/BinaryStructParser.java deleted file mode 100755 index 5eeabc2..0000000 --- a/src/zutil/parser/binary/BinaryStructParser.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package zutil.parser.binary; - -import zutil.ByteUtil; -import zutil.converter.Converter; -import zutil.parser.binary.BinaryStruct.BinaryField; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Created by Ziver on 2016-01-28. - */ -public class BinaryStructParser { - - public static int parse(BinaryStruct struct, byte[] data) { - List structDataList = getStructDataList(struct.getClass()); - int bitOffset = 0; - for (BinaryFieldData field : structDataList){ - bitOffset += field.setValue(struct, data, bitOffset); - } - return bitOffset; - } - - - private static List getStructDataList(Class clazz){ - ArrayList list = new ArrayList<>(); - for (Field field : clazz.getFields()){ - if (field.isAnnotationPresent(BinaryField.class)) - list.add(new BinaryFieldData(field)); - } - Collections.sort(list); - return list; - } - - - - public static class BinaryFieldData implements Comparable { - private int index; - private int length; - private Field field; - - protected BinaryFieldData(Field f){ - field = f; - BinaryField fieldData = field.getAnnotation(BinaryField.class); - index = fieldData.index(); - length = fieldData.length(); - } - - protected int setValue(Object obj, byte[] data, int bitOffset){ - try { - int byteIndex = bitOffset / 8; - int bitIndex = 7 - bitOffset % 8; - int bitLength = Math.min(bitIndex+1, length); - - int readLength = 0; - byte[] valueData = new byte[(int) Math.ceil(length / 8.0)]; - for (int index = 0; index < valueData.length; ++index) { - valueData[index] = ByteUtil.getShiftedBits(data[byteIndex], bitIndex, bitLength); - readLength += bitLength; - byteIndex++; - bitIndex = 7; - bitLength = Math.min(bitIndex+1, length - readLength); - } - - field.setAccessible(true); - if (field.getType() == Boolean.class || field.getType() == boolean.class) - field.set(obj, valueData[0] != 0); - else if (field.getType() == Integer.class || field.getType() == int.class) - field.set(obj, Converter.toInt(valueData)); - else if (field.getType() == String.class) - field.set(obj, new String(valueData)); - return readLength; - } catch (IllegalAccessException e){ - e.printStackTrace(); - } - return length; // we return the configured length to not shift the data - } - - @Override - public int compareTo(BinaryFieldData o) { - return this.index - o.index; - } - } -} diff --git a/src/zutil/parser/binary/BinaryStructWriter.java b/src/zutil/parser/binary/BinaryStructWriter.java deleted file mode 100755 index 53674ca..0000000 --- a/src/zutil/parser/binary/BinaryStructWriter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package zutil.parser.binary; - - -/** - * Created by Ziver - */ -/*TODO:*/ public class BinaryStructWriter { - - private byte[] data; - private int length; - - - - public void write(BinaryStruct struct) { - - } - - - public byte[] getByteArray() { - return data; - } - - public int getLength() { - return length; - } -} diff --git a/test/zutil/parser/binary/BinaryStructTest.java b/test/zutil/parser/binary/BinaryStructInputStreamTest.java similarity index 86% rename from test/zutil/parser/binary/BinaryStructTest.java rename to test/zutil/parser/binary/BinaryStructInputStreamTest.java index f254d2e..48237e1 100755 --- a/test/zutil/parser/binary/BinaryStructTest.java +++ b/test/zutil/parser/binary/BinaryStructInputStreamTest.java @@ -31,9 +31,9 @@ import static junit.framework.TestCase.assertFalse; /** - * Created by Ziver on 2016-01-28. + * Created by Ziver */ -public class BinaryStructTest { +public class BinaryStructInputStreamTest { interface BinaryTestStruct extends BinaryStruct{ void assertObj(); } @@ -52,7 +52,7 @@ public class BinaryStructTest { } }; - BinaryStructParser.parse(struct, new byte[]{0,0,0,1, 0,0,0,2}); + BinaryStructInputStream.parse(struct, new byte[]{0,0,0,1, 0,0,0,2}); struct.assertObj(); } @@ -70,7 +70,7 @@ public class BinaryStructTest { } }; - BinaryStructParser.parse(struct, new byte[]{0b0100_000}); + BinaryStructInputStream.parse(struct, new byte[]{0b0100_000}); struct.assertObj(); } @@ -81,11 +81,11 @@ public class BinaryStructTest { public String s1; public void assertObj(){ - assertEquals(s1, "hello world!"); + assertEquals("hello world!", s1); } }; - BinaryStructParser.parse(struct, "hello world!".getBytes()); + BinaryStructInputStream.parse(struct, "hello world!".getBytes()); struct.assertObj(); } @@ -103,7 +103,7 @@ public class BinaryStructTest { } }; - BinaryStructParser.parse(struct, new byte[]{0b0000_0000,0b0001_1000,0b0000_0000}); + BinaryStructInputStream.parse(struct, new byte[]{0b0000_0000,0b0001_1000,0b0000_0000}); struct.assertObj(); } @@ -122,7 +122,7 @@ public class BinaryStructTest { } }; - BinaryStructParser.parse(struct, new byte[]{0b0000_0001,0b0001_1000,0b0000_0000}); + BinaryStructInputStream.parse(struct, new byte[]{0b0000_0001,0b0001_1000,0b0000_0000}); struct.assertObj(); } } diff --git a/test/zutil/parser/binary/BinaryStructOutputStreamTest.java b/test/zutil/parser/binary/BinaryStructOutputStreamTest.java new file mode 100755 index 0000000..daa129a --- /dev/null +++ b/test/zutil/parser/binary/BinaryStructOutputStreamTest.java @@ -0,0 +1,89 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ziver Koc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package zutil.parser.binary; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertArrayEquals; + + +/** + * Created by Ziver + */ +public class BinaryStructOutputStreamTest { + + @Test + public void basicIntTest() throws IOException { + BinaryStruct struct = new BinaryStruct() { + @BinaryField(index=1, length=32) + public int i1 = 1; + @BinaryField(index=2, length=32) + public int i2 = 2; + }; + + byte[] data = serialize(struct); + assertArrayEquals(new byte[]{0,0,0,1, 0,0,0,2}, data); + } +/* + @Test + public void basicBooleanTest() throws IOException { + BinaryStruct struct = new BinaryStruct() { + @BinaryField(index=1, length=1) + public boolean b1 = true; + @BinaryField(index=2, length=1) + public boolean b2 = false; + }; + + byte[] data = serialize(struct); + assertArrayEquals(new byte[]{(byte)0b1000_0000}, data); + } + + @Test + public void basicStringTest(){ + BinaryTestStruct struct = new BinaryTestStruct() { + @BinaryField(index=1, length=8*12) + public String s1; + + public void assertObj(Object... expected){ + assertEquals(s1, "hello world!"); + } + }; + + BinaryStructParser.parse(struct, "hello world!".getBytes()); + struct.assertObj(); + } + */ + + private byte[] serialize(BinaryStruct struct) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + BinaryStructOutputStream out = new BinaryStructOutputStream(buffer); + out.write(struct); + return buffer.toByteArray(); + } +}