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

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


The following commit(s) were added to refs/heads/develop by this push:
     new 409175c  plc4go: implement a json based read buffer
409175c is described below

commit 409175cf2951d37bde3ad79778d7001d36f7a463
Author: Sebastian Rühl <[email protected]>
AuthorDate: Wed Apr 21 10:37:55 2021 +0200

    plc4go: implement a json based read buffer
---
 plc4go/internal/plc4go/s7/s7Io_test.go             |  16 +-
 plc4go/internal/plc4go/spi/utils/ReadBuffer.go     |   2 +-
 .../plc4go/spi/utils/ReadBufferJsonBased.go        | 449 +++++++++++++++++++++
 3 files changed, 464 insertions(+), 3 deletions(-)

diff --git a/plc4go/internal/plc4go/s7/s7Io_test.go 
b/plc4go/internal/plc4go/s7/s7Io_test.go
index c3ffdef..4dacc80 100644
--- a/plc4go/internal/plc4go/s7/s7Io_test.go
+++ b/plc4go/internal/plc4go/s7/s7Io_test.go
@@ -408,14 +408,26 @@ func TestS7MessageBytes(t *testing.T) {
                                }
                        }
                        {
-                               // and at least a roundtip
+                               // and at least a for xml roundtip
                                reader := strings.NewReader(tt.wantStringXml)
                                readBuffer := utils.NewXmlReadBuffer(reader)
                                if got, err := 
model.TPKTPacketParse(readBuffer); err != nil || !reflect.DeepEqual(got, 
tt.args.debuggable) {
                                        if err != nil {
                                                t.Error(err)
                                        } else {
-                                               t.Errorf("Roundtrip = '\n%v\n', 
want '\n%v\n'", got, tt.wantDump)
+                                               t.Errorf("Roundtrip(xml) = 
'\n%v\n', want '\n%v\n'", got, tt.wantDump)
+                                       }
+                               }
+                       }
+                       {
+                               // and at least a for json roundtip
+                               reader := strings.NewReader(tt.wantStringJson)
+                               readBuffer := utils.NewJsonReadBuffer(reader)
+                               if got, err := 
model.TPKTPacketParse(readBuffer); err != nil || !reflect.DeepEqual(got, 
tt.args.debuggable) {
+                                       if err != nil {
+                                               t.Error(err)
+                                       } else {
+                                               t.Errorf("Roundtrip(json) = 
'\n%v\n', want '\n%v\n'", got, tt.wantDump)
                                        }
                                }
                        }
diff --git a/plc4go/internal/plc4go/spi/utils/ReadBuffer.go 
b/plc4go/internal/plc4go/spi/utils/ReadBuffer.go
index 3404f44..10c0521 100644
--- a/plc4go/internal/plc4go/spi/utils/ReadBuffer.go
+++ b/plc4go/internal/plc4go/spi/utils/ReadBuffer.go
@@ -41,7 +41,7 @@ type ReadBuffer interface {
        ReadInt64(logicalName string, bitLength uint8, readerArgs 
...WithReaderArgs) (int64, error)
        ReadBigInt(logicalName string, bitLength uint64, readerArgs 
...WithReaderArgs) (*big.Int, error)
        ReadFloat32(logicalName string, signed bool, exponentBitLength uint8, 
mantissaBitLength uint8, readerArgs ...WithReaderArgs) (float32, error)
-       ReadFloat64(logicalName string, singed bool, exponentBitLength uint8, 
mantissaBitLength uint8, readerArgs ...WithReaderArgs) (float64, error)
+       ReadFloat64(logicalName string, signed bool, exponentBitLength uint8, 
mantissaBitLength uint8, readerArgs ...WithReaderArgs) (float64, error)
        ReadBigFloat(logicalName string, signed bool, exponentBitLength uint8, 
mantissaBitLength uint8, readerArgs ...WithReaderArgs) (*big.Float, error)
        ReadString(logicalName string, bitLength uint32, readerArgs 
...WithReaderArgs) (string, error)
        // CloseContext signals that we expect the end of the context with the 
supplied logical name
diff --git a/plc4go/internal/plc4go/spi/utils/ReadBufferJsonBased.go 
b/plc4go/internal/plc4go/spi/utils/ReadBufferJsonBased.go
new file mode 100644
index 0000000..51942e6
--- /dev/null
+++ b/plc4go/internal/plc4go/spi/utils/ReadBufferJsonBased.go
@@ -0,0 +1,449 @@
+//
+// 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 utils
+
+import (
+       "encoding/json"
+       "fmt"
+       "github.com/pkg/errors"
+       "io"
+       "math/big"
+)
+
+// NewJsonReadBuffer return as ReadBuffer which doesn't validate attributes 
and lists
+func NewJsonReadBuffer(reader io.Reader) ReadBuffer {
+       decoder := json.NewDecoder(reader)
+       var rootElement map[string]interface{}
+       err := decoder.Decode(&rootElement)
+       return &jsonReadBuffer{
+               rootElement:    rootElement,
+               pos:            1,
+               doValidateAttr: false,
+               err:            err,
+       }
+}
+
+// NewStrictJsonReadBuffer return as ReadBuffer which does validate attributes 
on the setting
+func NewStrictJsonReadBuffer(reader io.Reader, validateAttr bool) ReadBuffer {
+       decoder := json.NewDecoder(reader)
+       var rootElement map[string]interface{}
+       err := decoder.Decode(&rootElement)
+       return &jsonReadBuffer{
+               rootElement:    rootElement,
+               pos:            1,
+               doValidateAttr: validateAttr,
+               err:            err,
+       }
+}
+
+///////////////////////////////////////
+///////////////////////////////////////
+//
+// Internal section
+//
+
+type jsonReadBuffer struct {
+       bufferCommons
+       stack
+       rootElement    map[string]interface{}
+       pos            uint
+       doValidateAttr bool
+       err            error
+}
+
+//
+// Internal section
+//
+///////////////////////////////////////
+///////////////////////////////////////
+
+func (j *jsonReadBuffer) GetPos() uint16 {
+       return uint16(j.pos / 8)
+}
+
+func (j *jsonReadBuffer) HasMore(bitLength uint8) bool {
+       // TODO: work with x.InputOffset() and check if we are at EOF
+       return true
+}
+
+func (j *jsonReadBuffer) PullContext(logicalName string, readerArgs 
...WithReaderArgs) error {
+       if j.err != nil {
+               return j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       if j.Empty() {
+               if context, ok := j.rootElement[logicalName]; ok {
+                       j.Push(context)
+                       return nil
+               } else {
+                       return errors.Errorf("Required context %s not found in 
%v", logicalName, j.rootElement)
+               }
+       }
+       peek := j.Peek()
+       switch peek.(type) {
+       case []interface{}:
+               pop := j.Pop()
+               contextList := pop.([]interface{})
+               context := contextList[0].(map[string]interface{})
+               if len(contextList) < 2 {
+                       j.Push(make([]interface{}, 0))
+               } else {
+                       j.Push(contextList[1 : len(contextList)-1])
+               }
+               if subContext, ok := context[logicalName]; ok {
+                       j.Push(subContext)
+                       return nil
+               } else {
+                       return errors.Errorf("Required context %s not found in 
%v", logicalName, peek)
+               }
+       }
+       if context, ok := peek.(map[string]interface{})[logicalName]; ok {
+               j.Push(context)
+               return nil
+       } else {
+               return errors.Errorf("Required context %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadBit(logicalName string, readerArgs 
...WithReaderArgs) (bool, error) {
+       if j.err != nil {
+               return false, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(1)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "bit", 1, readerArgs...); err != nil {
+               return false, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return value.(bool), nil
+       } else {
+               return false, errors.Errorf("Required element %s not found in 
%v", logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadUint8(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (uint8, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "uint", bitLength, readerArgs...); 
err != nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return uint8(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadUint16(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (uint16, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "uint", bitLength, readerArgs...); 
err != nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return uint16(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadUint32(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (uint32, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "uint", bitLength, readerArgs...); 
err != nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return uint32(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadUint64(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (uint64, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "uint", bitLength, readerArgs...); 
err != nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return uint64(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadInt8(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (int8, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "int", bitLength, readerArgs...); err 
!= nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return int8(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadInt16(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (int16, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "int", bitLength, readerArgs...); err 
!= nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return int16(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadInt32(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (int32, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "int", bitLength, readerArgs...); err 
!= nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return int32(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadInt64(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (int64, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "int", bitLength, readerArgs...); err 
!= nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return int64(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadBigInt(logicalName string, bitLength uint64, 
readerArgs ...WithReaderArgs) (*big.Int, error) {
+       if j.err != nil {
+               return nil, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       logicalName = j.sanitizeLogicalName(logicalName)
+       peek, element := j.getElement(logicalName)
+       // TODO: not enough bits
+       if err := j.validateAttr(element, "int", uint8(bitLength), 
readerArgs...); err != nil {
+               return nil, err
+       }
+       newInt := big.NewInt(0)
+       if value, ok := element[logicalName]; ok {
+               newInt.SetString(value.(string), 10)
+               return newInt, nil
+       } else {
+               return newInt, errors.Errorf("Required element %s not found in 
%v", logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadFloat32(logicalName string, signed bool, 
exponentBitLength uint8, mantissaBitLength uint8, readerArgs ...WithReaderArgs) 
(float32, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       bitLength := exponentBitLength + mantissaBitLength
+       if signed {
+               bitLength += 1
+       }
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "float", bitLength, readerArgs...); 
err != nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return float32(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadFloat64(logicalName string, signed bool, 
exponentBitLength uint8, mantissaBitLength uint8, readerArgs ...WithReaderArgs) 
(float64, error) {
+       if j.err != nil {
+               return 0, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       bitLength := exponentBitLength + mantissaBitLength
+       if signed {
+               bitLength += 1
+       }
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "float", bitLength, readerArgs...); 
err != nil {
+               return 0, err
+       }
+       if value, ok := element[logicalName]; ok {
+               return float64(value.(float64)), nil
+       } else {
+               return 0, errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadBigFloat(logicalName string, signed bool, 
exponentBitLength uint8, mantissaBitLength uint8, readerArgs ...WithReaderArgs) 
(*big.Float, error) {
+       if j.err != nil {
+               return nil, j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       bitLength := exponentBitLength + mantissaBitLength
+       if signed {
+               bitLength += 1
+       }
+       j.move(bitLength)
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "float", bitLength, readerArgs...); 
err != nil {
+               return nil, err
+       }
+       newFloat := big.NewFloat(0)
+       if value, ok := element[logicalName]; ok {
+               newFloat.SetString(value.(string))
+               return newFloat, nil
+       } else {
+               return newFloat, errors.Errorf("Required element %s not found 
in %v", logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) ReadString(logicalName string, bitLength uint32, 
readerArgs ...WithReaderArgs) (string, error) {
+       if j.err != nil {
+               return "", j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       // TODO: not enough bits
+       j.move(uint8(bitLength))
+       peek, element := j.getElement(logicalName)
+       if err := j.validateAttr(element, "string", uint8(bitLength), 
readerArgs...); err != nil {
+               return "", err
+       }
+       if value, ok := element[logicalName]; ok {
+               return value.(string), nil
+       } else {
+               return "", errors.Errorf("Required element %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) CloseContext(logicalName string, readerArgs 
...WithReaderArgs) error {
+       if j.err != nil {
+               return j.err
+       }
+       logicalName = j.sanitizeLogicalName(logicalName)
+       if j.Empty() {
+               return errors.Errorf("Required context close %s not found in 
%v", logicalName, j.rootElement)
+       }
+       // Delete us from stack
+       _ = j.Pop()
+       if j.Empty() {
+               return nil
+       }
+       peek := j.Peek()
+       switch peek.(type) {
+       case []interface{}:
+               return nil
+       }
+       if _, ok := peek.(map[string]interface{})[logicalName]; ok {
+               delete(peek.(map[string]interface{}), logicalName)
+               return nil
+       } else {
+               return errors.Errorf("Required context %s not found in %v", 
logicalName, peek)
+       }
+}
+
+func (j *jsonReadBuffer) getElement(logicalName string) (interface{}, 
map[string]interface{}) {
+       logicalName = j.sanitizeLogicalName(logicalName)
+       peek := j.Peek()
+       var element map[string]interface{}
+       switch peek.(type) {
+       case []interface{}:
+               pop := j.Pop()
+               elementList := pop.([]interface{})
+               element = elementList[0].(map[string]interface{})
+               if len(elementList) < 2 {
+                       j.Push(make([]interface{}, 0))
+               } else {
+                       j.Push(elementList[1 : len(elementList)-1])
+               }
+       case []map[string]interface{}:
+               pop := j.Pop()
+               elementList := pop.([]map[string]interface{})
+               element = elementList[0]
+               j.Push(elementList[1 : len(elementList)-1])
+       case map[string]interface{}:
+               element = peek.(map[string]interface{})
+       default:
+               panic(fmt.Sprintf("Invalid state at %s with %v", logicalName, 
element))
+       }
+       return peek, element
+}
+
+func (j *jsonReadBuffer) move(bits uint8) {
+       j.pos += uint(bits)
+}
+
+func (j *jsonReadBuffer) validateAttr(element map[string]interface{}, dataType 
string, bitLength uint8, readerArgs ...WithReaderArgs) error {
+       if !j.doValidateAttr {
+               return nil
+       }
+       if value, ok := element[rwDataTypeKey]; !ok || value != dataType {
+               return errors.Errorf("Unexpected %s :%s. Want %s", 
rwDataTypeKey, dataType, dataType)
+       }
+       if value, ok := element[rwBitLengthKey]; !ok || value != 
string(bitLength) {
+               return errors.Errorf("Unexpected %s :%d. Want %s", 
rwBitLengthKey, bitLength, dataType)
+       }
+       return nil
+}

Reply via email to