You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2019/12/12 12:08:44 UTC

[plc4x] 01/01: first draft of big integer support on driver base java

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch feature/big_integer_support_on_driver_base
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit cf9a33fc5c6e982a920bc45efb256dc31e566adb
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Thu Dec 12 13:08:28 2019 +0100

    first draft of big integer support on driver base java
---
 plc4j/utils/driver-base-java/pom.xml               |   6 +
 .../org/apache/plc4x/java/utils/WriteBuffer.java   | 106 ++++++++++----
 .../apache/plc4x/java/utils/WriteBufferTest.java   | 153 +++++++++++++++++++++
 3 files changed, 239 insertions(+), 26 deletions(-)

diff --git a/plc4j/utils/driver-base-java/pom.xml b/plc4j/utils/driver-base-java/pom.xml
index 2adef47..01f6afc 100644
--- a/plc4j/utils/driver-base-java/pom.xml
+++ b/plc4j/utils/driver-base-java/pom.xml
@@ -37,6 +37,12 @@
       <groupId>com.github.jinahya</groupId>
       <artifactId>bit-io</artifactId>
     </dependency>
+
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java
index 8c14102..a0fd385 100644
--- a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java
+++ b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java
@@ -59,10 +59,10 @@ public class WriteBuffer {
     }
 
     public void writeUnsignedByte(int bitLength, byte value) throws ParseException {
-        if(bitLength <= 0) {
+        if (bitLength <= 0) {
             throw new ParseException("unsigned byte must contain at least 1 bit");
         }
-        if(bitLength > 4) {
+        if (bitLength > 4) {
             throw new ParseException("unsigned byte can only contain max 4 bits");
         }
         try {
@@ -73,10 +73,10 @@ public class WriteBuffer {
     }
 
     public void writeUnsignedShort(int bitLength, short value) throws ParseException {
-        if(bitLength <= 0) {
+        if (bitLength <= 0) {
             throw new ParseException("unsigned short must contain at least 1 bit");
         }
-        if(bitLength > 8) {
+        if (bitLength > 8) {
             throw new ParseException("unsigned short can only contain max 8 bits");
         }
         try {
@@ -87,14 +87,14 @@ public class WriteBuffer {
     }
 
     public void writeUnsignedInt(int bitLength, int value) throws ParseException {
-        if(bitLength <= 0) {
+        if (bitLength <= 0) {
             throw new ParseException("unsigned int must contain at least 1 bit");
         }
-        if(bitLength > 16) {
+        if (bitLength > 16) {
             throw new ParseException("unsigned int can only contain max 16 bits");
         }
         try {
-            if(!littleEndian) {
+            if (!littleEndian) {
                 value = Integer.reverseBytes(value) >> 16;
             }
             bo.writeInt(true, bitLength, value);
@@ -104,14 +104,14 @@ public class WriteBuffer {
     }
 
     public void writeUnsignedLong(int bitLength, long value) throws ParseException {
-        if(bitLength <= 0) {
+        if (bitLength <= 0) {
             throw new ParseException("unsigned long must contain at least 1 bit");
         }
-        if(bitLength > 32) {
+        if (bitLength > 32) {
             throw new ParseException("unsigned long can only contain max 32 bits");
         }
         try {
-            if(!littleEndian) {
+            if (!littleEndian) {
                 value = Long.reverseBytes(value) >> 32;
             }
             bo.writeLong(true, bitLength, value);
@@ -120,15 +120,11 @@ public class WriteBuffer {
         }
     }
 
-    public void writeUnsignedBigInteger(int bitLength, BigInteger value) throws ParseException {
-        throw new UnsupportedOperationException("not implemented yet");
-    }
-
     public void writeByte(int bitLength, byte value) throws ParseException {
-        if(bitLength <= 0) {
+        if (bitLength <= 0) {
             throw new ParseException("byte must contain at least 1 bit");
         }
-        if(bitLength > 8) {
+        if (bitLength > 8) {
             throw new ParseException("byte can only contain max 8 bits");
         }
         try {
@@ -139,14 +135,14 @@ public class WriteBuffer {
     }
 
     public void writeShort(int bitLength, short value) throws ParseException {
-        if(bitLength <= 0) {
+        if (bitLength <= 0) {
             throw new ParseException("short must contain at least 1 bit");
         }
-        if(bitLength > 16) {
+        if (bitLength > 16) {
             throw new ParseException("short can only contain max 16 bits");
         }
         try {
-            if(!littleEndian) {
+            if (!littleEndian) {
                 value = Short.reverseBytes(value);
             }
             bo.writeShort(false, bitLength, value);
@@ -156,14 +152,14 @@ public class WriteBuffer {
     }
 
     public void writeInt(int bitLength, int value) throws ParseException {
-        if(bitLength <= 0) {
+        if (bitLength <= 0) {
             throw new ParseException("int must contain at least 1 bit");
         }
-        if(bitLength > 32) {
+        if (bitLength > 32) {
             throw new ParseException("int can only contain max 32 bits");
         }
         try {
-            if(!littleEndian) {
+            if (!littleEndian) {
                 value = Integer.reverseBytes(value);
             }
             bo.writeInt(false, bitLength, value);
@@ -173,14 +169,14 @@ public class WriteBuffer {
     }
 
     public void writeLong(int bitLength, long value) throws ParseException {
-        if(bitLength <= 0) {
+        if (bitLength <= 0) {
             throw new ParseException("long must contain at least 1 bit");
         }
-        if(bitLength > 64) {
+        if (bitLength > 64) {
             throw new ParseException("long can only contain max 64 bits");
         }
         try {
-            if(!littleEndian) {
+            if (!littleEndian) {
                 value = Long.reverseBytes(value);
             }
             bo.writeLong(false, bitLength, value);
@@ -190,9 +186,67 @@ public class WriteBuffer {
     }
 
     public void writeBigInteger(int bitLength, BigInteger value) throws ParseException {
-        throw new UnsupportedOperationException("not implemented yet");
+        int actualBitLength = value.bitLength();
+        boolean negative = value.compareTo(BigInteger.ZERO) < 0;
+        int bitLengthIncludingPossibleSign = actualBitLength + (negative ? 1 : 0);
+        if (bitLength < bitLengthIncludingPossibleSign) {
+            throw new ParseException("bit length including possible sign " + bitLengthIncludingPossibleSign + " exceeds supplied bit length " + bitLength);
+        }
+        byte[] bytes = value.toByteArray();
+        int remainingBitLength = bitLengthIncludingPossibleSign;
+        try {
+            if (!littleEndian) {
+                // MSB in 0
+                for (int i = 0; i < bytes.length; i++) {
+                    int bitsToWrite = Math.max(Math.min(remainingBitLength, 8), 1);
+                    bo.writeByte(false, bitsToWrite, bytes[i]);
+                    remainingBitLength -= bitsToWrite;
+                }
+            } else {
+                // MSB in bytes.length
+                for (int i = bytes.length - 1; i >= 0; i--) {
+                    int bitsToWrite = Math.max(Math.min(remainingBitLength, 8), 1);
+                    bo.writeByte(false, bitsToWrite, bytes[i]);
+                    remainingBitLength -= bitsToWrite;
+                }
+            }
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
+    public void writeUnsignedBigInteger(int bitLength, BigInteger value) throws ParseException {
+        if (value.compareTo(BigInteger.ZERO) < 0) {
+            throw new ParseException("value " + value + " is below 0");
+        }
+        int actualBitLength = value.bitLength();
+        if (bitLength < actualBitLength) {
+            throw new ParseException("bit length" + actualBitLength + " exceeds supplied bit length " + bitLength);
+        }
+        byte[] bytes = value.toByteArray();
+        int remainingBitLength = actualBitLength;
+        try {
+            if (!littleEndian) {
+                // MSB in 0
+                for (int i = 0; i < bytes.length; i++) {
+                    int bitsToWrite = Math.max(Math.min(remainingBitLength, 8), 1);
+                    bo.writeByte(false, bitsToWrite, bytes[i]);
+                    remainingBitLength -= bitsToWrite;
+                }
+            } else {
+                // MSB in bytes.length
+                for (int i = bytes.length - 1; i >= 0; i--) {
+                    int bitsToWrite = Math.max(Math.min(remainingBitLength, 8), 1);
+                    bo.writeByte(false, bitsToWrite, bytes[i]);
+                    remainingBitLength -= bitsToWrite;
+                }
+            }
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
+    }
+
+
     public void writeFloat(int bitLength, float value) throws ParseException {
         throw new UnsupportedOperationException("not implemented yet");
     }
diff --git a/plc4j/utils/driver-base-java/src/test/java/org/apache/plc4x/java/utils/WriteBufferTest.java b/plc4j/utils/driver-base-java/src/test/java/org/apache/plc4x/java/utils/WriteBufferTest.java
new file mode 100644
index 0000000..08ecd77
--- /dev/null
+++ b/plc4j/utils/driver-base-java/src/test/java/org/apache/plc4x/java/utils/WriteBufferTest.java
@@ -0,0 +1,153 @@
+package org.apache.plc4x.java.utils;
+
+import org.apache.commons.io.HexDump;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class WriteBufferTest {
+
+    @Test
+    void getData() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeBit() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeUnsignedByte() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeUnsignedShort() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeUnsignedInt() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeUnsignedLong() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeUnsignedBigInteger() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeByte() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeShort() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeInt() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeLong() {
+        // TODO: implement me
+    }
+
+    @Nested
+    class WriteBigInteger {
+        @Nested
+        class BigEndian {
+
+            @Test
+            void zero() throws Exception {
+                WriteBuffer SUT = new WriteBuffer(1, false);
+                SUT.writeBigInteger(8, BigInteger.ZERO);
+                byte[] data = SUT.getData();
+                System.out.println(toHex(data));
+                // TODO: check right representation
+                assertArrayEquals(new byte[]{0b0000_0000}, data);
+                assertEquals(BigInteger.ZERO, new BigInteger(data));
+            }
+
+            @Test
+            void one() throws Exception {
+                WriteBuffer SUT = new WriteBuffer(1, false);
+                SUT.writeBigInteger(8, BigInteger.ONE);
+                byte[] data = SUT.getData();
+                System.out.println(toHex(data));
+                // TODO: check right representation
+                assertArrayEquals(new byte[]{0b0000_0001}, data);
+                assertEquals(BigInteger.ZERO, new BigInteger(data));
+            }
+
+            @Test
+            void minusOne() throws Exception {
+                WriteBuffer SUT = new WriteBuffer(1, false);
+                SUT.writeBigInteger(8, BigInteger.ZERO.subtract(BigInteger.ONE));
+                byte[] data = SUT.getData();
+                System.out.println(toHex(data));
+                // TODO: check right representation
+                assertArrayEquals(new byte[]{0b0000_0001}, data);
+                assertEquals(BigInteger.ZERO, new BigInteger(data));
+            }
+
+            @Test
+            void minus255() throws Exception {
+                WriteBuffer SUT = new WriteBuffer(1, false);
+                SUT.writeBigInteger(8, BigInteger.valueOf(-255L));
+                byte[] data = SUT.getData();
+                System.out.println(toHex(data));
+                // TODO: check right representation
+                assertArrayEquals(new byte[]{(byte) 0b1000_0000, 0b0000_0001}, data);
+                assertEquals(BigInteger.valueOf(-255L), new BigInteger(data));
+            }
+
+        }
+
+        @Nested
+        class LittleEndian {
+
+            @Test
+            void writeBigInteger_LE() throws Exception {
+                WriteBuffer SUT_LE = new WriteBuffer(8012, true);
+                SUT_LE.writeBigInteger(1, BigInteger.ZERO);
+            }
+        }
+    }
+
+    @Test
+    void writeFloat() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeDouble() {
+        // TODO: implement me
+    }
+
+    @Test
+    void writeBigDecimal() {
+        // TODO: implement me
+    }
+
+    public static String toHex(byte[] bytes) throws Exception {
+        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+            HexDump.dump(bytes, 0, byteArrayOutputStream, 0);
+            return byteArrayOutputStream.toString();
+        }
+    }
+}
\ No newline at end of file