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

[plc4x] branch feature/code-gen updated: - Finished implementing a first working version of the serialization.

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

cdutz pushed a commit to branch feature/code-gen
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feature/code-gen by this push:
     new 0dc23da  - Finished implementing a first working version of the serialization.
0dc23da is described below

commit 0dc23da026b9c63fd7dcb93bfd9381b72509a4e5
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Fri Jun 7 15:12:31 2019 +0200

    - Finished implementing a first working version of the serialization.
---
 .../language/java/JavaLanguageTemplateHelper.java  |  18 +--
 .../main/resources/templates/java/io-template.ftlh |  34 +++--
 .../resources/templates/java/pojo-template.ftlh    |  94 ++++++++++++-
 .../codegenerator/parser/imaginary/Imaginary.g4    |   4 +-
 .../src/main/resources/protocols/s7/protocol.spec  |  24 ++--
 .../code-generation/test-java-s7-driver/pom.xml    |   5 +
 .../github/jinahya/bit/io/MyDefaultBitInput.java   |  35 +++++
 .../org/apache/plc4x/java/utils/ReadBuffer.java    |  31 ++---
 .../org/apache/plc4x/java/utils/SizeAware.java     |  26 ++++
 .../org/apache/plc4x/java/utils/WriteBuffer.java   | 155 ++++++++++++++++-----
 .../src/test/java/BenchmarkGeneratedS7.java        |  44 +++++-
 11 files changed, 382 insertions(+), 88 deletions(-)

diff --git a/sandbox/code-generation/language-template-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java b/sandbox/code-generation/language-template-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
index 744bc4c..d57b64c 100644
--- a/sandbox/code-generation/language-template-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
+++ b/sandbox/code-generation/language-template-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
@@ -190,31 +190,31 @@ public class JavaLanguageTemplateHelper {
             }
             case UINT: {
                 if (simpleTypeReference.getSize() <= 4) {
-                    return "writeUnsignedByte(" + simpleTypeReference.getSize() + ", (byte) " + fieldName + ")";
+                    return "writeUnsignedByte(" + simpleTypeReference.getSize() + ", ((Number) " + fieldName + ").byteValue())";
                 }
                 if (simpleTypeReference.getSize() <= 8) {
-                    return "writeUnsignedShort(" + simpleTypeReference.getSize() + ", (short) " + fieldName + ")";
+                    return "writeUnsignedShort(" + simpleTypeReference.getSize() + ", ((Number) " + fieldName + ").shortValue())";
                 }
                 if (simpleTypeReference.getSize() <= 16) {
-                    return "writeUnsignedInt(" + simpleTypeReference.getSize() + ", (int) " + fieldName + ")";
+                    return "writeUnsignedInt(" + simpleTypeReference.getSize() + ", ((Number) " + fieldName + ").intValue())";
                 }
                 if (simpleTypeReference.getSize() <= 32) {
-                    return "writeUnsignedLong(" + simpleTypeReference.getSize() + ", (long) " + fieldName + ")";
+                    return "writeUnsignedLong(" + simpleTypeReference.getSize() + ", ((Number) " + fieldName + ").longValue()";
                 }
                 return "writeUnsignedBigInteger" + simpleTypeReference.getSize() + ", (BigInteger) " + fieldName + ")";
             }
             case INT: {
                 if (simpleTypeReference.getSize() <= 8) {
-                    return "writeByte" + simpleTypeReference.getSize() + ", (byte) " + fieldName + ")";
+                    return "writeByte" + simpleTypeReference.getSize() + ", ((Number) " + fieldName + ").byteValue())";
                 }
                 if (simpleTypeReference.getSize() <= 16) {
-                    return "writeShort" + simpleTypeReference.getSize() + ", (short) " + fieldName + ")";
+                    return "writeShort" + simpleTypeReference.getSize() + ", ((Number) " + fieldName + ").shortValue())";
                 }
                 if (simpleTypeReference.getSize() <= 32) {
-                    return "writeInt" + simpleTypeReference.getSize() + ", (int) " + fieldName + ")";
+                    return "writeInt" + simpleTypeReference.getSize() + ", ((Number) " + fieldName + ").intValue())";
                 }
                 if (simpleTypeReference.getSize() <= 64) {
-                    return "writeLong" + simpleTypeReference.getSize() + ", (long) " + fieldName + ")";
+                    return "writeLong" + simpleTypeReference.getSize() + ", ((Number) " + fieldName + ").longValue())";
                 }
                 return "writeBigInteger" + simpleTypeReference.getSize() + ", (BigInteger) " + fieldName + ")";
             }
@@ -272,7 +272,7 @@ public class JavaLanguageTemplateHelper {
     }
 
     public String toWriteExpression(String expression) {
-        return null;
+        return expression;
     }
 
 }
diff --git a/sandbox/code-generation/language-template-java/src/main/resources/templates/java/io-template.ftlh b/sandbox/code-generation/language-template-java/src/main/resources/templates/java/io-template.ftlh
index f167a8a..5b14c11 100644
--- a/sandbox/code-generation/language-template-java/src/main/resources/templates/java/io-template.ftlh
+++ b/sandbox/code-generation/language-template-java/src/main/resources/templates/java/io-template.ftlh
@@ -66,11 +66,16 @@ public class ${typeName}IO  {
     private static JexlExpression ex${field.name?cap_first};
     <#assign needJexl=true>
     <#break>
-    </#switch>
+    <#case "implicit">
+    private static JexlExpression ex${field.name?cap_first};
+    <#assign needJexl=true>
+    <#break>
+</#switch>
 </#list>
 <#if needJexl>
+    private static JexlEngine jexl;
     static {
-        JexlEngine jexl = new JexlBuilder().create();
+        jexl = new JexlBuilder().create();
 <#list type.fields as field>
 <#switch field.typeName>
     <#case "array">
@@ -79,6 +84,9 @@ public class ${typeName}IO  {
     <#case "optional">
         ex${field.name?cap_first} = jexl.createExpression("${field.conditionExpression}");
         <#break>
+    <#case "implicit">
+        ex${field.name?cap_first} = jexl.createExpression("${field.serializationExpression}");
+        <#break>
 </#switch>
 </#list>
     }
@@ -86,11 +94,13 @@ public class ${typeName}IO  {
 
     public static <#if helper.isDiscriminatedType(type)>${typeName}Builder<#else>${typeName}</#if> parse(ReadBuffer io<#if type.parserArguments?has_content>, <#list type.parserArguments as parserArgument>${helper.getLanguageTypeNameForSpecType(parserArgument.type)} ${parserArgument.name}<#sep>, </#sep></#list></#if>) throws ParseException {
         int startPos = io.getPos();
+<#if needJexl>
         JexlContext jc = new MapContext();
         <#if type.parserArguments?has_content>
         <#list type.parserArguments as parserArgument>
         jc.set("${parserArgument.name}", ${parserArgument.name});
         </#list></#if>
+</#if>
 <#list type.fields as field>
 <#switch field.typeName>
     <#case "array">
@@ -122,19 +132,25 @@ public class ${typeName}IO  {
         if(${field.name} != ${typeName}.${field.name?upper_case}) {
             throw new ParseException("Expected constant value " + ${typeName}.${field.name?upper_case} + " but got " + ${field.name});
         }
+<#if needJexl>
         jc.set("${field.name}", ${field.name});
+</#if>
         <#break>
     <#case "discriminator">
 
         // Discriminator Field (Used as input to a switch field)
         ${helper.getLanguageTypeNameForSpecType(field.type)} ${field.name} = io.${helper.getReadMethodName(field.type)}(${field.type.size});
+<#if needJexl>
         jc.set("${field.name}", ${field.name});
-        <#break>
+</#if>
+    <#break>
     <#case "implicit">
 
         // Implicit Field (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
         ${helper.getLanguageTypeNameForSpecType(field.type)} ${field.name} = io.${helper.getReadMethodName(field.type)}(${field.type.size});
+<#if needJexl>
         jc.set("${field.name}", ${field.name});
+</#if>
         <#break>
     <#case "optional">
 
@@ -159,7 +175,9 @@ public class ${typeName}IO  {
 
         // Simple field
         ${helper.getLanguageTypeNameForSpecType(field.type)} ${field.name} = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>${parserArgument}<#sep>, </#sep></#list></#if>)</#if>;
+<#if needJexl>
         jc.set("${field.name}", ${field.name});
+</#if>
         <#break>
     <#case "switch">
 
@@ -188,9 +206,9 @@ public class ${typeName}IO  {
     }
 
     public static void serialize(WriteBuffer io, ${typeName} value) throws ParseException {
-        JexlEngine jexl = new JexlBuilder().create();
-        JexlContext jc = new MapContext();
-        jc.set("this", value);
+<#if needJexl>
+        JexlContext jc = new ObjectContext<>(jexl, value);
+</#if>
 <#list type.fields as field>
 <#switch field.typeName>
     <#case "array">
@@ -223,13 +241,11 @@ public class ${typeName}IO  {
     <#case "implicit">
 
         // Implicit Field (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
-        JexlExpression ex${field.name?cap_first} = jexl.createExpression("${field.serializationExpression}");
         io.${helper.getWriteBufferReadMethodCall(field.type, "ex" + field.name?cap_first + ".evaluate(jc)")};
         <#break>
     <#case "optional">
 
         // Optional Field (Can be skipped, if a given expression evaluates to false)
-        JexlExpression ex${field.name?cap_first} = jexl.createExpression("${field.conditionExpression}");
         if(ex${field.name?cap_first}.evaluate(jc) == Boolean.TRUE) {
             <#if helper.isSimpleType(field.type)>
             io.${helper.getWriteBufferReadMethodCall(field.type, "value.get" + field.name?cap_first + "()")};
@@ -240,7 +256,7 @@ public class ${typeName}IO  {
         <#break>
     <#case "reserved">
 
-        // Reserved Field (Compartmentalized so the "reserved" variable can't leak)
+        // Reserved Field.
         io.${helper.getWriteBufferReadMethodCall(field.type, field.referenceValue)};
         <#break>
     <#case "simple">
diff --git a/sandbox/code-generation/language-template-java/src/main/resources/templates/java/pojo-template.ftlh b/sandbox/code-generation/language-template-java/src/main/resources/templates/java/pojo-template.ftlh
index 2beed05..45bdf89 100644
--- a/sandbox/code-generation/language-template-java/src/main/resources/templates/java/pojo-template.ftlh
+++ b/sandbox/code-generation/language-template-java/src/main/resources/templates/java/pojo-template.ftlh
@@ -38,7 +38,34 @@ ${packageName?replace(".", "/")}/${typeName}.java
 
 package ${packageName};
 
-public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??> extends ${type.parentType.name}</#if> {
+import org.apache.commons.jexl3.*;
+import org.apache.plc4x.java.utils.SizeAware;
+
+public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??> extends ${type.parentType.name}</#if> implements SizeAware {
+
+<#assign needJexl=false>
+<#list type.fields as field>
+<#switch field.typeName>
+    <#case "optional">
+    private static JexlExpression ex${field.name?cap_first};
+    <#assign needJexl=true>
+    <#break>
+</#switch>
+</#list>
+<#if needJexl>
+    private static JexlEngine jexl;
+    static {
+    jexl = new JexlBuilder().create();
+<#list type.fields as field>
+<#switch field.typeName>
+    <#case "optional">
+    ex${field.name?cap_first} = jexl.createExpression("${field.conditionExpression}");
+    <#break>
+</#switch>
+</#list>
+    }
+</#if>
+
 <#if helper.isDiscriminatedType(type)>
 
     // Discriminator values used by the parser to determine the type to be used. All values have to apply.
@@ -88,4 +115,69 @@ public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??
     }
 
 </#list>
+    @Override
+    public int getLengthInBytes() {
+<#if needJexl>
+        JexlContext jc = new ObjectContext<>(jexl, this);
+</#if>
+        int lengthInBits = <#if type.parentType??>super.getLengthInBytes() * 8<#else>0</#if>;
+<#list type.fields as field>
+<#switch field.typeName>
+    <#case "array">
+
+        // Array field
+        <#if helper.isSimpleType(field.type)>
+            lengthInBits += ${field.type.size} * ${field.name}.length;
+        <#else>
+        for(SizeAware element : ${field.name}) {
+            lengthInBits += element.getLengthInBytes() * 8;
+        }
+        </#if>
+        <#break>
+    <#case "const">
+
+        // Const Field (${field.name})
+        lengthInBits += ${field.type.size};
+        <#break>
+    <#case "discriminator">
+
+        // Discriminator Field (${field.name})
+        lengthInBits += ${field.type.size};
+        <#break>
+    <#case "implicit">
+
+        // Implicit Field (${field.name})
+        lengthInBits += ${field.type.size};
+        <#break>
+    <#case "optional">
+
+        // Optional Field (${field.name})
+        if(ex${field.name?cap_first}.evaluate(jc) == Boolean.TRUE) {
+            lengthInBits += ${field.type.size};
+        }
+        <#break>
+    <#case "reserved">
+
+        // Reserved Field
+        lengthInBits += ${field.type.size};
+        <#break>
+    <#case "simple">
+
+        // Simple field (${field.name})
+        <#if helper.isSimpleType(field.type)>
+        lengthInBits += ${field.type.size};
+        <#else>
+        lengthInBits += ${field.name}.getLengthInBytes() * 8;
+        </#if>
+        <#break>
+    <#case "switch">
+
+        // Length of sub-type elements will be added by sub-type...
+        <#break>
+</#switch>
+</#list>
+
+        return lengthInBits / 8;
+    }
+
 }
\ No newline at end of file
diff --git a/sandbox/code-generation/plc4x-maven-plugin/src/main/antlr4/org/apache/plc4x/codegenerator/parser/imaginary/Imaginary.g4 b/sandbox/code-generation/plc4x-maven-plugin/src/main/antlr4/org/apache/plc4x/codegenerator/parser/imaginary/Imaginary.g4
index 6a866a4..1448442 100644
--- a/sandbox/code-generation/plc4x-maven-plugin/src/main/antlr4/org/apache/plc4x/codegenerator/parser/imaginary/Imaginary.g4
+++ b/sandbox/code-generation/plc4x-maven-plugin/src/main/antlr4/org/apache/plc4x/codegenerator/parser/imaginary/Imaginary.g4
@@ -117,8 +117,8 @@ multipleExpressions
 innerExpression
  : HEX_LITERAL
  | INTEGER_LITERAL
- | IDENTIFIER   // Variable reference
- | innerExpression '.' IDENTIFIER // Field Reference
+ | IDENTIFIER '()'?   // Variable reference or method call
+ | innerExpression '.' IDENTIFIER '()'? // Field Reference or method call
  | innerExpression BinaryOperator innerExpression  // Addition
  | innerExpression LBRACKET INTEGER_LITERAL RBRACKET
  | '(' innerExpression ')'
diff --git a/sandbox/code-generation/protocol-s7/src/main/resources/protocols/s7/protocol.spec b/sandbox/code-generation/protocol-s7/src/main/resources/protocols/s7/protocol.spec
index 66e9fe9..18bcab0 100644
--- a/sandbox/code-generation/protocol-s7/src/main/resources/protocols/s7/protocol.spec
+++ b/sandbox/code-generation/protocol-s7/src/main/resources/protocols/s7/protocol.spec
@@ -5,7 +5,7 @@
 [type 'TPKTPacket'
     [const    uint 8     'protocolId' '0x03']
     [reserved uint 8     '0x00']
-    [implicit uint 16    'len'        'payload.size + 4']
+    [implicit uint 16    'len'        'payload.lengthInBytes + 4']
     [field    COTPPacket 'payload']
 ]
 
@@ -14,7 +14,7 @@
 ////////////////////////////////////////////////////////////////
 
 [discriminatedType 'COTPPacket'
-    [implicit      uint 8 'headerLength' 'size - (payload.size + 1)']
+    [implicit      uint 8 'headerLength' 'lengthInBytes - (payload.lengthInBytes + 1)']
     [discriminator uint 8 'tpduCode']
     [typeSwitch 'tpduCode'
         ['0xF0' COTPPacketData
@@ -79,8 +79,8 @@
     [discriminator uint 8  'messageType']
     [reserved      uint 16 '0x0000']
     [field         uint 16 'tpduReference']
-    [implicit      uint 16 'parameterLength' 'parameters.size']
-    [implicit      uint 16 'payloadLength'   'payloads.size']
+    [implicit      uint 16 'parameterLength' 'parameter.lengthInBytes']
+    [implicit      uint 16 'payloadLength'   'payload.lengthInBytes']
     [typeSwitch 'messageType'
         ['0x01' S7MessageRequest
         ]
@@ -108,21 +108,21 @@
             [field    uint 16 'pduLength']
         ]
         ['0x04','0x01' S7ParameterReadVarRequest
-            [implicit   uint 8                    'numItems' 'items.size']
+            [implicit   uint 8                    'numItems' 'items.size()']
             [arrayField S7VarRequestParameterItem 'items'    count 'numItems']
         ]
         ['0x04','0x03' S7ParameterReadVarResponse
             [field uint 8 'numItems']
         ]
         ['0x05','0x01' S7ParameterWriteVarRequest
-            [implicit   uint 8                    'numItems' 'items.size']
+            [implicit   uint 8                    'numItems' 'items.size()']
             [arrayField S7VarRequestParameterItem 'items'    count 'numItems']
         ]
         ['0x05','0x03' S7ParameterWriteVarResponse
             [field uint 8 'numItems']
         ]
         ['0x00','0x07' S7ParameterUserData
-            [implicit   uint 8       'numItems' 'items.size']
+            [implicit   uint 8       'numItems' 'items.size()']
             [arrayField UserDataItem 'items' count 'numItems']
         ]
     ]
@@ -132,7 +132,7 @@
     [discriminator uint 8 'parameterItemType']
     [typeSwitch 'parameterItemType'
         ['0x12' S7VarRequestParameterItemAddress
-            [implicit uint 8    'addressLength' 'address.size']
+            [implicit uint 8    'addressLength' 'address.lengthInBytes']
             [field    S7Address 'address']
         ]
     ]
@@ -158,13 +158,13 @@
     [discriminator uint 8 'itemType']
     [typeSwitch 'itemType'
         ['0x12' UserDataItemCPUFunctions
-            [implicit      uint 8  'parameterLength' 'size']
+            [implicit      uint 8  'parameterLength' 'lengthInBytes']
             [field         uint 16 'cpuFunctionType']
             [field         uint 8  'subFunctionGroup']
             [field         uint 8  'sequenceNumber']
-            [optionalField uint 8  'dataUnitReferenceNumber' 'size == 8']
-            [optionalField uint 8  'lastDataUnit' 'size == 8']
-            [optionalField uint 8  'errorCode' 'size == 8']
+            [optionalField uint 8  'dataUnitReferenceNumber' 'lengthInBytes == 8']
+            [optionalField uint 8  'lastDataUnit' 'lengthInBytes == 8']
+            [optionalField uint 8  'errorCode' 'lengthInBytes == 8']
         ]
     ]
 ]
diff --git a/sandbox/code-generation/test-java-s7-driver/pom.xml b/sandbox/code-generation/test-java-s7-driver/pom.xml
index 5933593..b1ca6bf 100644
--- a/sandbox/code-generation/test-java-s7-driver/pom.xml
+++ b/sandbox/code-generation/test-java-s7-driver/pom.xml
@@ -66,6 +66,11 @@
       <artifactId>commons-compress</artifactId>
       <version>1.18</version>
     </dependency>
+    <dependency>
+      <groupId>com.github.jinahya</groupId>
+      <artifactId>bit-io</artifactId>
+      <version>1.4.2</version>
+    </dependency>
 
     <dependency>
       <groupId>commons-codec</groupId>
diff --git a/sandbox/code-generation/test-java-s7-driver/src/main/java/com/github/jinahya/bit/io/MyDefaultBitInput.java b/sandbox/code-generation/test-java-s7-driver/src/main/java/com/github/jinahya/bit/io/MyDefaultBitInput.java
new file mode 100644
index 0000000..eb538d9
--- /dev/null
+++ b/sandbox/code-generation/test-java-s7-driver/src/main/java/com/github/jinahya/bit/io/MyDefaultBitInput.java
@@ -0,0 +1,35 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package com.github.jinahya.bit.io;
+
+/**
+ * Modified version that exposes the position.
+ */
+public class MyDefaultBitInput extends DefaultBitInput<ArrayByteInput> {
+
+    public MyDefaultBitInput(ArrayByteInput delegate) {
+        super(delegate);
+    }
+
+    public long getPos() {
+        return count;
+    }
+
+}
diff --git a/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java b/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java
index ff059dc..17ac0c1 100644
--- a/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java
+++ b/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java
@@ -19,31 +19,28 @@
 
 package org.apache.plc4x.java.utils;
 
-import org.apache.commons.compress.utils.BitInputStream;
+import com.github.jinahya.bit.io.*;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.nio.ByteOrder;
 
 public class ReadBuffer {
 
-    private BitInputStream bis;
+    private MyDefaultBitInput bi;
 
-    
     public ReadBuffer(byte[] input) {
-        ByteArrayInputStream bais = new ByteArrayInputStream(input);
-        bis = new BitInputStream(bais, ByteOrder.BIG_ENDIAN);
+        ArrayByteInput abi = new ArrayByteInput(input);
+        bi = new MyDefaultBitInput(abi);
     }
 
     public int getPos() {
-        return (int) bis.getBytesRead();
+        return (int) bi.getPos();
     }
 
     public boolean readBit() throws ParseException {
         try {
-            return bis.readBits(1) == 1;
+            return bi.readBoolean();
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
@@ -57,7 +54,7 @@ public class ReadBuffer {
             throw new ParseException("unsigned byte can only contain max 4 bits");
         }
         try {
-            return (byte) bis.readBits(bitLength);
+            return bi.readByte(true, bitLength);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
@@ -71,7 +68,7 @@ public class ReadBuffer {
             throw new ParseException("unsigned short can only contain max 8 bits");
         }
         try {
-            return (short) bis.readBits(bitLength);
+            return bi.readShort(true, bitLength);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
@@ -85,7 +82,7 @@ public class ReadBuffer {
             throw new ParseException("unsigned int can only contain max 16 bits");
         }
         try {
-            return (int) bis.readBits(bitLength);
+            return bi.readInt(true, bitLength);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
@@ -99,7 +96,7 @@ public class ReadBuffer {
             throw new ParseException("unsigned long can only contain max 32 bits");
         }
         try {
-            return bis.readBits(bitLength);
+            return bi.readLong(true, bitLength);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
@@ -117,7 +114,7 @@ public class ReadBuffer {
             throw new ParseException("byte can only contain max 8 bits");
         }
         try {
-            return (byte) bis.readBits(bitLength);
+            return bi.readByte(false, bitLength);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
@@ -131,7 +128,7 @@ public class ReadBuffer {
             throw new ParseException("short can only contain max 16 bits");
         }
         try {
-            return (short) bis.readBits(bitLength);
+            return bi.readShort(false, bitLength);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
@@ -145,7 +142,7 @@ public class ReadBuffer {
             throw new ParseException("int can only contain max 32 bits");
         }
         try {
-            return (int) bis.readBits(bitLength);
+            return bi.readInt(false, bitLength);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
@@ -159,7 +156,7 @@ public class ReadBuffer {
             throw new ParseException("long can only contain max 64 bits");
         }
         try {
-            return bis.readBits(bitLength);
+            return bi.readLong(false, bitLength);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
         }
diff --git a/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/SizeAware.java b/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/SizeAware.java
new file mode 100644
index 0000000..c11d379
--- /dev/null
+++ b/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/SizeAware.java
@@ -0,0 +1,26 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package org.apache.plc4x.java.utils;
+
+public interface SizeAware {
+
+    int getLengthInBytes();
+
+}
diff --git a/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java b/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java
index 211239d..bfe6de5 100644
--- a/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java
+++ b/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java
@@ -19,75 +19,168 @@
 
 package org.apache.plc4x.java.utils;
 
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
+import com.github.jinahya.bit.io.BitOutput;
+import com.github.jinahya.bit.io.BufferByteOutput;
+import com.github.jinahya.bit.io.DefaultBitOutput;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.nio.ByteBuffer;
 
 public class WriteBuffer {
 
-    private int bytePos;
-    private byte bitPos;
-    private ByteArrayOutputStream baos;
-    private DataOutputStream dos;
+    private ByteBuffer bb;
+    private BufferByteOutput bbo;
+    private BitOutput bo;
 
-    public WriteBuffer() {
-        bytePos = 0;
-        bitPos = 0;
-        baos = new ByteArrayOutputStream();
-        dos = new DataOutputStream(baos);
+    public WriteBuffer(int size) {
+        bb = ByteBuffer.allocate(size);
+        bbo = new BufferByteOutput<>(bb);
+        bo = new DefaultBitOutput<>(bbo);
     }
 
-    public byte[] getMessage() throws ParseException {
+    public byte[] getData() {
+        return bb.array();
+    }
+
+    public void writeBit(boolean value) throws ParseException {
         try {
-            dos.flush();
+            bo.writeBoolean(value);
         } catch (IOException e) {
-            throw new ParseException("Unable to flush buffer");
+            throw new ParseException("Error reading", e);
         }
-        return baos.toByteArray();
-    }
-
-    public void writeBit(boolean value) {
     }
 
-    public void writeUnsignedByte(int bitLength, byte value) {
+    public void writeUnsignedByte(int bitLength, byte value) throws ParseException {
+        if(bitLength <= 0) {
+            throw new ParseException("unsigned byte must contain at least 1 bit");
+        }
+        if(bitLength > 4) {
+            throw new ParseException("unsigned byte can only contain max 4 bits");
+        }
+        try {
+            bo.writeByte(true, bitLength, value);
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
-    public void writeUnsignedShort(int bitLength, short value) {
+    public void writeUnsignedShort(int bitLength, short value) throws ParseException {
+        if(bitLength <= 0) {
+            throw new ParseException("unsigned short must contain at least 1 bit");
+        }
+        if(bitLength > 8) {
+            throw new ParseException("unsigned short can only contain max 8 bits");
+        }
+        try {
+            bo.writeShort(true, bitLength, value);
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
-    public void writeUnsignedInt(int bitLength, int value) {
+    public void writeUnsignedInt(int bitLength, int value) throws ParseException {
+        if(bitLength <= 0) {
+            throw new ParseException("unsigned int must contain at least 1 bit");
+        }
+        if(bitLength > 16) {
+            throw new ParseException("unsigned int can only contain max 16 bits");
+        }
+        try {
+            bo.writeInt(true, bitLength, value);
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
-    public void writeUnsignedLong(int bitLength, long value) {
+    public void writeUnsignedLong(int bitLength, long value) throws ParseException {
+        if(bitLength <= 0) {
+            throw new ParseException("unsigned long must contain at least 1 bit");
+        }
+        if(bitLength > 32) {
+            throw new ParseException("unsigned long can only contain max 32 bits");
+        }
+        try {
+            bo.writeLong(true, bitLength, value);
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
-    public void writeUnsignedBigInteger(int bitLength, BigInteger value) {
+    public void writeUnsignedBigInteger(int bitLength, BigInteger value) throws ParseException {
+        throw new UnsupportedOperationException("not implemented yet");
     }
 
-    public void writeByte(int bitLength, byte value) {
+    public void writeByte(int bitLength, byte value) throws ParseException {
+        if(bitLength <= 0) {
+            throw new ParseException("byte must contain at least 1 bit");
+        }
+        if(bitLength > 8) {
+            throw new ParseException("byte can only contain max 8 bits");
+        }
+        try {
+            bo.writeByte(false, bitLength, value);
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
-    public void writeShort(int bitLength, short value) {
+    public void writeShort(int bitLength, short value) throws ParseException {
+        if(bitLength <= 0) {
+            throw new ParseException("short must contain at least 1 bit");
+        }
+        if(bitLength > 16) {
+            throw new ParseException("short can only contain max 16 bits");
+        }
+        try {
+            bo.writeShort(false, bitLength, value);
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
-    public void writeInt(int bitLength, int value) {
+    public void writeInt(int bitLength, int value) throws ParseException {
+        if(bitLength <= 0) {
+            throw new ParseException("int must contain at least 1 bit");
+        }
+        if(bitLength > 32) {
+            throw new ParseException("int can only contain max 32 bits");
+        }
+        try {
+            bo.writeInt(false, bitLength, value);
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
-    public void writeLong(int bitLength, long value) {
+    public void writeLong(int bitLength, long value) throws ParseException {
+        if(bitLength <= 0) {
+            throw new ParseException("long must contain at least 1 bit");
+        }
+        if(bitLength > 64) {
+            throw new ParseException("long can only contain max 64 bits");
+        }
+        try {
+            bo.writeLong(false, bitLength, value);
+        } catch (IOException e) {
+            throw new ParseException("Error reading", e);
+        }
     }
 
-    public void writeBigInteger(int bitLength, BigInteger value) {
+    public void writeBigInteger(int bitLength, BigInteger value) throws ParseException {
+        throw new UnsupportedOperationException("not implemented yet");
     }
 
-    public void writeFloat(int bitLength, float value) {
+    public void writeFloat(int bitLength, float value) throws ParseException {
+        throw new UnsupportedOperationException("not implemented yet");
     }
 
-    public void writeDouble(int bitLength, double value) {
+    public void writeDouble(int bitLength, double value) throws ParseException {
+        throw new UnsupportedOperationException("not implemented yet");
     }
 
-    public void writeBigDecimal(int bitLength, BigDecimal value) {
+    public void writeBigDecimal(int bitLength, BigDecimal value) throws ParseException {
+        throw new UnsupportedOperationException("not implemented yet");
     }
 
 }
diff --git a/sandbox/code-generation/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java b/sandbox/code-generation/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
index 9ba58b3..a0a7fb3 100644
--- a/sandbox/code-generation/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
+++ b/sandbox/code-generation/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
@@ -21,20 +21,50 @@ import org.apache.commons.codec.binary.Hex;
 import org.apache.plc4x.java.s7.TPKTPacket;
 import org.apache.plc4x.java.s7.io.TPKTPacketIO;
 import org.apache.plc4x.java.utils.ReadBuffer;
+import org.apache.plc4x.java.utils.WriteBuffer;
+
+import java.util.Arrays;
 
 public class BenchmarkGeneratedS7 {
 
     public static void main(String[] args) throws Exception {
-        byte[] data = Hex.decodeHex("0300006702f080320100000001005600000407120a10060001032b84000160120a10020001032b840001a0120a10010001032b840001a9120a10050001032b84000150120a10020001032b84000198120a10040001032b84000140120a10020001032b84000190");
+                                    //        00
+        byte[] rData = Hex.decodeHex("0300006702f080320100000001005600000407120a10060001032b84000160120a10020001032b840001a0120a10010001032b840001a9120a10050001032b84000150120a10020001032b84000198120a10040001032b84000140120a10020001032b84000190");
         long start = System.currentTimeMillis();
-        int numRuns = 20000;
+        int numRuns = 1;
+
+        // Benchmark the parsing code
+        TPKTPacket packet = null;
+        for(int i = 0; i < numRuns; i++) {
+            ReadBuffer rBuf = new ReadBuffer(rData);
+            packet = TPKTPacketIO.parse(rBuf);
+        }
+        long endParsing = System.currentTimeMillis();
+
+        System.out.println("Parsed " + numRuns + " packets in " + (endParsing - start) + "ms");
+        System.out.println("That's " + ((float) (endParsing - start) / numRuns) + "ms per packet");
+
+        // Benchmark the serializing code
+        byte[] oData = null;
         for(int i = 0; i < numRuns; i++) {
-            ReadBuffer buf = new ReadBuffer(data);
-            TPKTPacket packet = TPKTPacketIO.parse(buf);
+            WriteBuffer wBuf = new WriteBuffer(packet.getLengthInBytes());
+            TPKTPacketIO.serialize(wBuf, packet);
+            oData = wBuf.getData();
+        }
+        long endSerializing = System.currentTimeMillis();
+
+        System.out.println("Serialized " + numRuns + " packets in " + (endSerializing - endParsing) + "ms");
+        System.out.println("That's " + ((float) (endSerializing - endParsing) / numRuns) + "ms per packet");
+        if(!Arrays.equals(rData, oData)) {
+            for(int i = 0; i < rData.length; i++) {
+                if(rData[i] != oData[i]) {
+                    System.out.println("Difference in byte " + i);
+                }
+            }
+            System.out.println("Not equals");
+        } else {
+            System.out.println("Bytes equal");
         }
-        long end = System.currentTimeMillis();
-        System.out.println("Parsed " + numRuns + " packets in " + (end - start) + "ms");
-        System.out.println("That's " + ((float) (end - start) / numRuns) + "ms per packet");
     }
 
 }