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 2023/02/15 15:29:57 UTC

[plc4x] branch develop updated: feat(plc4go/spi): added DiffHex and hex highlighting

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 34ac8a1342 feat(plc4go/spi): added DiffHex and hex highlighting
34ac8a1342 is described below

commit 34ac8a13420fa2147e69726e0591714b80a3feb4
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed Feb 15 16:29:47 2023 +0100

    feat(plc4go/spi): added DiffHex and hex highlighting
---
 plc4go/spi/utils/asciiBox.go |  5 ++++-
 plc4go/spi/utils/hex.go      | 36 ++++++++++++++++++++++++++++++++----
 plc4go/spi/utils/hex_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 79 insertions(+), 5 deletions(-)

diff --git a/plc4go/spi/utils/asciiBox.go b/plc4go/spi/utils/asciiBox.go
index d0e3e9ff30..efacc15691 100644
--- a/plc4go/spi/utils/asciiBox.go
+++ b/plc4go/spi/utils/asciiBox.go
@@ -36,6 +36,9 @@ type AsciiBox struct {
 // DebugAsciiBox set to true to get debug messages
 var DebugAsciiBox bool
 
+// ANSI_PATTERN source: https://github.com/chalk/ansi-regex/blob/main/index.js#L3
+var ANSI_PATTERN = regexp.MustCompile("[\u001b\u009b][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]")
+
 // AsciiBoxer is used to render something in a box
 type AsciiBoxer interface {
 	// Box where int param is the proposed width
@@ -270,7 +273,7 @@ func (a *asciiBoxWriter) hasBorders(box AsciiBox) bool {
 }
 
 func countChars(s string) int {
-	return len([]rune(s))
+	return len([]rune(ANSI_PATTERN.ReplaceAllString(s, "")))
 }
 
 //
diff --git a/plc4go/spi/utils/hex.go b/plc4go/spi/utils/hex.go
index bd493a26d6..fa98f37b99 100644
--- a/plc4go/spi/utils/hex.go
+++ b/plc4go/spi/utils/hex.go
@@ -48,12 +48,12 @@ const pipeWidth = 1
 var DebugHex bool
 
 // Dump dumps a 56 char wide hex string
-func Dump(data []byte) string {
-	return DumpFixedWidth(data, DefaultWidth)
+func Dump(data []byte, highlights ...int) string {
+	return DumpFixedWidth(data, DefaultWidth, highlights...)
 }
 
 // DumpFixedWidth dumps hex as hex string. Min width of string returned is 18 up to supplied charWidth
-func DumpFixedWidth(data []byte, desiredCharWidth int) string {
+func DumpFixedWidth(data []byte, desiredCharWidth int, highlights ...int) string {
 	if data == nil || len(data) < 1 {
 		return ""
 	}
@@ -61,14 +61,23 @@ func DumpFixedWidth(data []byte, desiredCharWidth int) string {
 	data = append(data[:0:0], data...)
 	hexString := ""
 	maxBytesPerRow, indexWidth := calculateBytesPerRowAndIndexWidth(len(data), desiredCharWidth)
-
+	highlightsSet := map[int]struct{}{}
+	for _, highlight := range highlights {
+		highlightsSet[highlight] = struct{}{}
+	}
 	for byteIndex, rowIndex := 0, 0; byteIndex < len(data); byteIndex, rowIndex = byteIndex+maxBytesPerRow, rowIndex+1 {
 		indexString := fmt.Sprintf("%0*d|", indexWidth, byteIndex)
 		hexString += indexString
 		for columnIndex := 0; columnIndex < maxBytesPerRow; columnIndex++ {
 			absoluteIndex := byteIndex + columnIndex
 			if absoluteIndex < len(data) {
+				if _, ok := highlightsSet[absoluteIndex]; ok {
+					hexString += "\033[0;31m"
+				}
 				hexString += fmt.Sprintf("%02x ", data[absoluteIndex])
+				if _, ok := highlightsSet[absoluteIndex]; ok {
+					hexString += "\033[0m"
+				}
 			} else {
 				// align with empty byte representation
 				hexString += strings.Repeat(" ", byteWidth)
@@ -88,6 +97,25 @@ func DumpFixedWidth(data []byte, desiredCharWidth int) string {
 	return hexString[:len(hexString)-1]
 }
 
+// DiffHex produces a hex diff AsciiBox of two byte arrays
+func DiffHex(expectedBytes, actualBytes []byte) AsciiBox {
+	numBytes := int(math.Min(float64(len(expectedBytes)), float64(len(actualBytes))))
+	brokenAt := -1
+	var diffIndexes []int
+	for i := 0; i < numBytes; i++ {
+		if expectedBytes[i] != actualBytes[i] {
+			if brokenAt < 0 {
+				brokenAt = i
+			}
+			diffIndexes = append(diffIndexes, i)
+		}
+	}
+	expectedHex := DumpFixedWidth(expectedBytes, 46, diffIndexes...)
+	actialHex := DumpFixedWidth(actualBytes, 46, diffIndexes...)
+	return AsciiBoxWriterDefault.BoxSideBySide(AsciiBoxWriterDefault.BoxString("expected", expectedHex, 0), AsciiBoxWriterDefault.BoxString("actual", actialHex, 0))
+
+}
+
 func calculateBytesPerRowAndIndexWidth(numberOfBytes, desiredStringWidth int) (int, int) {
 	if DebugHex {
 		log.Debug().Msgf("Calculating max row and index for %d number of bytes and a desired string width of %d", numberOfBytes, desiredStringWidth)
diff --git a/plc4go/spi/utils/hex_test.go b/plc4go/spi/utils/hex_test.go
index 01e5035f22..8bee4efeb2 100644
--- a/plc4go/spi/utils/hex_test.go
+++ b/plc4go/spi/utils/hex_test.go
@@ -256,6 +256,49 @@ func TestDumpFixedWidth(t *testing.T) {
 	}
 }
 
+func TestDiffHex(t *testing.T) {
+	type args struct {
+		expectedBytes, actualBytes []byte
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "two identical",
+			args: args{
+				expectedBytes: []byte{1, 2, 3, 4},
+				actualBytes:   []byte{1, 2, 3, 4},
+			},
+			want: `
+╔═expected═══════════════════════════════════╗╔═actual═════════════════════════════════════╗
+║0|01 02 03 04                   '....      '║║0|01 02 03 04                   '....      '║
+╚════════════════════════════════════════════╝╚════════════════════════════════════════════╝
+`,
+		},
+		{
+			name: "two different",
+			args: args{
+				expectedBytes: []byte{1, 2, 3, 4},
+				actualBytes:   []byte{1, 2, 6, 4},
+			},
+			want: `
+╔═expected═══════════════════════════════════╗╔═actual═════════════════════════════════════╗
+║0|01 02 03 04                   '....      '║║0|01 02 06 04                   '....      '║
+╚════════════════════════════════════════════╝╚════════════════════════════════════════════╝
+`,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := DiffHex(tt.args.expectedBytes, tt.args.actualBytes); got.String() != strings.Trim(tt.want, "\n") {
+				t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want)
+			}
+		})
+	}
+}
+
 func Test_maskString(t *testing.T) {
 	type args struct {
 		data []byte