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
commit 6ba5bbe7f6c407ec66a296431952764f4ba658f1 Author: Sebastian Rühl <[email protected]> AuthorDate: Sat Apr 17 15:56:18 2021 +0200 plc4go: fixed issues in length calculation in hex + calculated DefaultWidth so that it can contain 10 bytes per line in most cases + removed panic from undumpable items + fixed issues with sizes too small + moved size calculation to separate function --- plc4go/internal/plc4go/spi/utils/asciiBox.go | 12 +- plc4go/internal/plc4go/spi/utils/asciiBox_test.go | 362 ++++++++++------ plc4go/internal/plc4go/spi/utils/hex.go | 93 ++-- plc4go/internal/plc4go/spi/utils/hex_test.go | 503 ++++++++++++++++++++-- 4 files changed, 792 insertions(+), 178 deletions(-) diff --git a/plc4go/internal/plc4go/spi/utils/asciiBox.go b/plc4go/internal/plc4go/spi/utils/asciiBox.go index 9969686..0e2cda4 100644 --- a/plc4go/internal/plc4go/spi/utils/asciiBox.go +++ b/plc4go/internal/plc4go/spi/utils/asciiBox.go @@ -27,8 +27,12 @@ import ( "strings" ) +// AsciiBox is a string surrounded by a ascii border (and a optional name) type AsciiBox string +// DebugAsciiBox set to true to get debug messages +var DebugAsciiBox bool + // Width returns the width of the box without the newlines func (m AsciiBox) Width() int { maxWidth := 0 @@ -41,7 +45,7 @@ func (m AsciiBox) Width() int { return maxWidth } -// Boxer is used to render something in a box +// AsciiBoxer is used to render something in a box type AsciiBoxer interface { // Box where int param is the proposed width Box(string, int) AsciiBox @@ -96,7 +100,7 @@ func BoxAnything(name string, anything interface{}, charWidth int) AsciiBox { } } -// BoxString boxes a box +// BoxBox boxes a box func BoxBox(name string, box AsciiBox, charWidth int) AsciiBox { return BoxString(name, string(box), charWidth) } @@ -105,7 +109,9 @@ func BoxBox(name string, box AsciiBox, charWidth int) AsciiBox { func BoxString(name string, data string, charWidth int) AsciiBox { longestLine := AsciiBox(data).Width() if charWidth < longestLine { - log.Debug().Msgf("Overflow by %d chars", longestLine-charWidth) + if DebugAsciiBox { + log.Debug().Msgf("Overflow by %d chars", longestLine-charWidth) + } charWidth = longestLine + 2 } boxedString := "" diff --git a/plc4go/internal/plc4go/spi/utils/asciiBox_test.go b/plc4go/internal/plc4go/spi/utils/asciiBox_test.go index b746ace..5b8788f 100644 --- a/plc4go/internal/plc4go/spi/utils/asciiBox_test.go +++ b/plc4go/internal/plc4go/spi/utils/asciiBox_test.go @@ -19,7 +19,10 @@ package utils -import "testing" +import ( + "strings" + "testing" +) func TestBoxAnything(t *testing.T) { type args struct { @@ -39,9 +42,11 @@ func TestBoxAnything(t *testing.T) { anything: true, charWidth: 0, }, - want: `╔═exampleBool╗ + want: ` +╔═exampleBool╗ ║ true ║ -╚════════════╝`, +╚════════════╝ +`, }, { name: "test int", @@ -50,9 +55,11 @@ func TestBoxAnything(t *testing.T) { anything: 1, charWidth: 0, }, - want: `╔═exampleInt╗ + want: ` +╔═exampleInt╗ ║ 1 ║ -╚═══════════╝`, +╚═══════════╝ +`, }, { name: "test int 123123123", @@ -61,13 +68,16 @@ func TestBoxAnything(t *testing.T) { anything: 123123123, charWidth: 0, }, - want: `╔═exampleInt╗ + want: ` +╔═exampleInt╗ ║ 123123123 ║ -╚═══════════╝`, +╚═══════════╝ +`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + tt.want = trimBox(tt.want) if got := BoxAnything(tt.args.name, tt.args.anything, tt.args.charWidth); got != tt.want { t.Errorf("BoxAnything() = '\n%v\n', want '\n%v\n'", got, tt.want) } @@ -88,12 +98,15 @@ func TestBoxSideBySide(t *testing.T) { { name: "Test2Boxes", args: args{ - box1: `000 0x: 31 32 33 34 35 36 37 38 '12345678' + box1: ` +000 0x: 31 32 33 34 35 36 37 38 '12345678' 008 0x: 39 30 61 62 63 64 65 66 '90abcdef' 016 0x: 67 68 69 6a 6b 6c 6d 6e 'ghijklmn' 024 0x: 6f 70 71 72 73 74 75 76 'opqrstuv' -032 0x: 77 78 79 7a 'wxyz '`, - box2: `╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ +032 0x: 77 78 79 7a 'wxyz ' +`, + box2: ` +╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ 000 0x: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e '1234567890abcdefghijklmn' ║ ║ 024 0x: 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 'opqrstuvwxyz.1234567890a' ║ ║ 048 0x: 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 'bcdefghijklmnopqrstuvwxy' ║ @@ -101,9 +114,11 @@ func TestBoxSideBySide(t *testing.T) { ║ 096 0x: 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 'klmnopqrstuvwxyz.1234567' ║ ║ 120 0x: 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 '890abcdefghijklmnopqrstu' ║ ║ 144 0x: 76 77 78 79 7a d3 61 61 62 'vwxyz.aab ' ║ -╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝`, +╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ +`, }, - want: `000 0x: 31 32 33 34 35 36 37 38 '12345678'╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ + want: ` +000 0x: 31 32 33 34 35 36 37 38 '12345678'╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ 008 0x: 39 30 61 62 63 64 65 66 '90abcdef'║ 000 0x: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e '1234567890abcdefghijklmn' ║ 016 0x: 67 68 69 6a 6b 6c 6d 6e 'ghijklmn'║ 024 0x: 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 'opqrstuvwxyz.1234567890a' ║ 024 0x: 6f 70 71 72 73 74 75 76 'opqrstuv'║ 048 0x: 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 'bcdefghijklmnopqrstuvwxy' ║ @@ -111,58 +126,78 @@ func TestBoxSideBySide(t *testing.T) { ║ 096 0x: 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 'klmnopqrstuvwxyz.1234567' ║ ║ 120 0x: 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 '890abcdefghijklmnopqrstu' ║ ║ 144 0x: 76 77 78 79 7a d3 61 61 62 'vwxyz.aab ' ║ - ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝`, + ╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ +`, }, { name: "another 2 boxes", args: args{ - box1: `╔═exampleInt╗ + box1: ` +╔═exampleInt╗ ║ 4 ║ -╚═══════════╝`, - box2: `╔═exampleInt╗ +╚═══════════╝ +`, + box2: ` +╔═exampleInt╗ ║ 7 ║ -╚═══════════╝`, +╚═══════════╝ +`, }, - want: `╔═exampleInt╗╔═exampleInt╗ + want: ` +╔═exampleInt╗╔═exampleInt╗ ║ 4 ║║ 7 ║ -╚═══════════╝╚═══════════╝`, +╚═══════════╝╚═══════════╝ +`, }, { name: "size difference first box", args: args{ - box1: `╔═exampleInt╗ + box1: ` +╔═exampleInt╗ ║ 4 ║ ║ 4 ║ -╚═══════════╝`, - box2: `╔═exampleInt╗ +╚═══════════╝ +`, + box2: ` +╔═exampleInt╗ ║ 7 ║ -╚═══════════╝`, +╚═══════════╝ +`, }, - want: `╔═exampleInt╗╔═exampleInt╗ + want: ` +╔═exampleInt╗╔═exampleInt╗ ║ 4 ║║ 7 ║ ║ 4 ║╚═══════════╝ -╚═══════════╝ `, +╚═══════════╝ +`, }, { name: "size difference second box", args: args{ - box1: `╔═exampleInt╗ + box1: ` +╔═exampleInt╗ ║ 4 ║ -╚═══════════╝`, - box2: `╔═exampleInt╗ +╚═══════════╝ +`, + box2: ` +╔═exampleInt╗ ║ 7 ║ ║ 7 ║ -╚═══════════╝`, +╚═══════════╝ +`, }, - want: `╔═exampleInt╗╔═exampleInt╗ + want: ` +╔═exampleInt╗╔═exampleInt╗ ║ 4 ║║ 7 ║ ╚═══════════╝║ 7 ║ - ╚═══════════╝`, + ╚═══════════╝ +`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := BoxSideBySide(tt.args.box1, tt.args.box2); got != tt.want { + tt.want = trimBox(tt.want) + if got := BoxSideBySide(trimBox(tt.args.box1), trimBox(tt.args.box2)); got != tt.want { t.Errorf("BoxSideBySide() = '\n%v\n', want '\n%v\n'", got, tt.want) } }) @@ -182,57 +217,69 @@ func TestBoxBelowBox(t *testing.T) { { name: "Test2Boxes", args: args{ - box1: `000 0x: 31 32 33 34 35 36 37 38 '12345678' -008 0x: 39 30 61 62 63 64 65 66 '90abcdef' -016 0x: 67 68 69 6a 6b 6c 6d 6e 'ghijklmn' -024 0x: 6f 70 71 72 73 74 75 76 'opqrstuv' -032 0x: 77 78 79 7a 'wxyz '`, - box2: `╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ -║ 000 0x: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e '1234567890abcdefghijklmn' ║ -║ 024 0x: 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 'opqrstuvwxyz.1234567890a' ║ -║ 048 0x: 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 'bcdefghijklmnopqrstuvwxy' ║ -║ 072 0x: 7a d3 61 61 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 'z.aa1234567890abcdefghij' ║ -║ 096 0x: 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 'klmnopqrstuvwxyz.1234567' ║ -║ 120 0x: 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 '890abcdefghijklmnopqrstu' ║ -║ 144 0x: 76 77 78 79 7a d3 61 61 62 'vwxyz.aab ' ║ -╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝`, + box1: ` +000 31 32 33 34 35 36 37 38 '12345678' +008 39 30 61 62 63 64 65 66 '90abcdef' +016 67 68 69 6a 6b 6c 6d 6e 'ghijklmn' +024 6f 70 71 72 73 74 75 76 'opqrstuv' +032 77 78 79 7a 'wxyz ' +`, + box2: ` +╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ +║ 000 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e '1234567890abcdefghijklmn' ║ +║ 024 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 'opqrstuvwxyz.1234567890a' ║ +║ 048 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 'bcdefghijklmnopqrstuvwxy' ║ +║ 072 7a d3 61 61 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 'z.aa1234567890abcdefghij' ║ +║ 096 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 'klmnopqrstuvwxyz.1234567' ║ +║ 120 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 '890abcdefghijklmnopqrstu' ║ +║ 144 76 77 78 79 7a d3 61 61 62 'vwxyz.aab ' ║ +╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ +`, }, - want: `000 0x: 31 32 33 34 35 36 37 38 '12345678' -008 0x: 39 30 61 62 63 64 65 66 '90abcdef' -016 0x: 67 68 69 6a 6b 6c 6d 6e 'ghijklmn' -024 0x: 6f 70 71 72 73 74 75 76 'opqrstuv' -032 0x: 77 78 79 7a 'wxyz ' -╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ -║ 000 0x: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e '1234567890abcdefghijklmn' ║ -║ 024 0x: 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 'opqrstuvwxyz.1234567890a' ║ -║ 048 0x: 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 'bcdefghijklmnopqrstuvwxy' ║ -║ 072 0x: 7a d3 61 61 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 'z.aa1234567890abcdefghij' ║ -║ 096 0x: 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 'klmnopqrstuvwxyz.1234567' ║ -║ 120 0x: 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 '890abcdefghijklmnopqrstu' ║ -║ 144 0x: 76 77 78 79 7a d3 61 61 62 'vwxyz.aab ' ║ -╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝`, + want: ` +000 31 32 33 34 35 36 37 38 '12345678' +008 39 30 61 62 63 64 65 66 '90abcdef' +016 67 68 69 6a 6b 6c 6d 6e 'ghijklmn' +024 6f 70 71 72 73 74 75 76 'opqrstuv' +032 77 78 79 7a 'wxyz ' +╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ +║ 000 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e '1234567890abcdefghijklmn' ║ +║ 024 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 'opqrstuvwxyz.1234567890a' ║ +║ 048 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 'bcdefghijklmnopqrstuvwxy' ║ +║ 072 7a d3 61 61 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 'z.aa1234567890abcdefghij' ║ +║ 096 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 'klmnopqrstuvwxyz.1234567' ║ +║ 120 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 '890abcdefghijklmnopqrstu' ║ +║ 144 76 77 78 79 7a d3 61 61 62 'vwxyz.aab ' ║ +╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝`, }, { name: "different sized boxes", args: args{ - box1: `╔═sampleField════════════╗ + box1: ` +╔═sampleField════════════╗ ║123123123123123123123123║ -╚════════════════════════╝`, - box2: `╔═sampleField╗ +╚════════════════════════╝ +`, + box2: ` +╔═sampleField╗ ║123123123123║ -╚════════════╝`, +╚════════════╝ +`, }, - want: `╔═sampleField════════════╗ + want: ` +╔═sampleField════════════╗ ║123123123123123123123123║ ╚════════════════════════╝ ╔═sampleField╗ ║123123123123║ -╚════════════╝ `, +╚════════════╝ +`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := BoxBelowBox(tt.args.box1, tt.args.box2); got != tt.want { + tt.want = trimBox(tt.want) + if got := BoxBelowBox(trimBox(tt.args.box1), trimBox(tt.args.box2)); got != tt.want { t.Errorf("BoxSideBySide() = '\n%v\n', want '\n%v\n'", got, tt.want) } }) @@ -257,9 +304,11 @@ func TestBoxString(t *testing.T) { data: "123123123123", charWidth: 1, }, - want: `╔═sampleField╗ + want: ` +╔═sampleField╗ ║123123123123║ -╚════════════╝`, +╚════════════╝ +`, }, { name: "simplebox-unamed", @@ -268,9 +317,11 @@ func TestBoxString(t *testing.T) { data: "123123123123", charWidth: 1, }, - want: `╔════════════╗ + want: ` +╔════════════╗ ║123123123123║ -╚════════════╝`, +╚════════════╝ +`, }, { name: "simplebox", @@ -279,14 +330,17 @@ func TestBoxString(t *testing.T) { data: "123123123123\n123123123123123123123123", charWidth: 1, }, - want: `╔═sampleField════════════╗ + want: ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123123123123123123123║ -╚════════════════════════╝`, +╚════════════════════════╝ +`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + tt.want = trimBox(tt.want) if got := BoxString(tt.args.name, tt.args.data, tt.args.charWidth); got != tt.want { t.Errorf("BoxString() = '\n%v\n', want '\n%v\n'", got, tt.want) } @@ -308,86 +362,115 @@ func TestAlignBoxes(t *testing.T) { name: "enough space", args: args{ boxes: []AsciiBox{ - `╔═sampleField════════════╗ + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123ABABABABABAB123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123123123123123123123║ -╚════════════════════════╝`, +╚════════════════════════╝ +`, }, desiredWith: 1000, }, - want: `╔═sampleField════════════╗╔═sampleField════════════╗ + want: ` +╔═sampleField════════════╗╔═sampleField════════════╗ ║ 123123123123 ║║ 123123123123 ║ ║123123ABABABABABAB123123║║123123123123123123123123║ -╚════════════════════════╝╚════════════════════════╝`, +╚════════════════════════╝╚════════════════════════╝ +`, }, { name: "not enough space", args: args{ boxes: []AsciiBox{ - `╔═sampleField════════════╗ + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123ABABABABABAB123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123123123123123123123║ -╚════════════════════════╝`, +╚════════════════════════╝ +`, }, desiredWith: 0, }, - want: `╔═sampleField════════════╗ + want: ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123ABABABABABAB123123║ ╚════════════════════════╝ ╔═sampleField════════════╗ ║ 123123123123 ║ ║123123123123123123123123║ -╚════════════════════════╝`, +╚════════════════════════╝ +`, }, { name: "not enough space should result in multiple rows", args: args{ boxes: []AsciiBox{ - `╔═sampleField════════════╗ + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123ABABABABABAB123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123123123123123123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123ABABABABABAB123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123123123123123123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123ABABABABABAB123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123123123123123123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123ABABABABABAB123123║ -╚════════════════════════╝`, - `╔═sampleField════════════╗ +╚════════════════════════╝ +`, + ` +╔═sampleField════════════╗ ║ 123123123123 ║ ║123123123123123123123123║ -╚════════════════════════╝`, +╚════════════════════════╝ +`, }, desiredWith: 65, }, - want: `╔═sampleField════════════╗╔═sampleField════════════╗╔═sampleField════════════╗ + want: ` +╔═sampleField════════════╗╔═sampleField════════════╗╔═sampleField════════════╗ ║ 123123123123 ║║ 123123123123 ║║ 123123123123 ║ ║123123ABABABABABAB123123║║123123123123123123123123║║123123ABABABABABAB123123║ ╚════════════════════════╝╚════════════════════════╝╚════════════════════════╝ @@ -398,11 +481,16 @@ func TestAlignBoxes(t *testing.T) { ╔═sampleField════════════╗╔═sampleField════════════╗ ║ 123123123123 ║║ 123123123123 ║ ║123123ABABABABABAB123123║║123123123123123123123123║ -╚════════════════════════╝╚════════════════════════╝ `, +╚════════════════════════╝╚════════════════════════╝ +`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + for i, box := range tt.args.boxes { + tt.args.boxes[i] = trimBox(box) + } + tt.want = trimBox(tt.want) if got := AlignBoxes(tt.args.boxes, tt.args.desiredWith); got != tt.want { t.Errorf("AlignBoxes() = '\n%v\n', want '\n%v\n'", got, tt.want) } @@ -418,16 +506,20 @@ func TestAsciiBox_width(t *testing.T) { }{ { name: "same width", - m: `123123123123123 + m: ` +123123123123123 123123123123123 -123123123123123`, +123123123123123 +`, want: 15, }, { name: "different width", - m: `123123123123123 + m: ` +123123123123123 123123123123123123123123123123 -123123123123123`, +123123123123123 +`, want: 30, }, } @@ -453,46 +545,70 @@ func Test_mergeHorizontal(t *testing.T) { name: "3 same", args: args{ boxes: []AsciiBox{ - `123123123 + ` +123123123 +123123123 123123123 -123123123`, - `abcabcabc +`, + ` +abcabcabc +abcabcabc abcabcabc -abcabcabc`, - `zxyzxyzxy +`, + ` zxyzxyzxy -zxyzxyzxy`, +zxyzxyzxy +zxyzxyzxy +`, }, }, - want: `123123123abcabcabczxyzxyzxy + want: ` +123123123abcabcabczxyzxyzxy +123123123abcabcabczxyzxyzxy 123123123abcabcabczxyzxyzxy -123123123abcabcabczxyzxyzxy`, +`, }, { name: "3 different", args: args{ boxes: []AsciiBox{ - `123123123 + ` 123123123 -123123123`, - `abcabcabc +123123123 +123123123 +`, + ` +abcabcabc abcabcabcabcabcabcabcabcabc -abcabcabc`, - `zxyzxyzxy +abcabcabc +`, + ` +zxyzxyzxy zxyzxyzxy -zxyzxyzxy`, +zxyzxyzxy +`, }, }, - want: `123123123abcabcabc zxyzxyzxy + want: ` +123123123abcabcabc zxyzxyzxy 123123123abcabcabcabcabcabcabcabcabczxyzxyzxy -123123123abcabcabc zxyzxyzxy`, +123123123abcabcabc zxyzxyzxy +`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + for i, box := range tt.args.boxes { + tt.args.boxes[i] = trimBox(box) + } + tt.want = trimBox(tt.want) if got := mergeHorizontal(tt.args.boxes); got != tt.want { t.Errorf("mergeHorizontal() = '\n%v\n', want '\n%v\n'", got, tt.want) } }) } } + +func trimBox(box AsciiBox) AsciiBox { + return AsciiBox(strings.Trim(string(box), "\n")) +} diff --git a/plc4go/internal/plc4go/spi/utils/hex.go b/plc4go/internal/plc4go/spi/utils/hex.go index 6b1725c..80e9a81 100644 --- a/plc4go/internal/plc4go/spi/utils/hex.go +++ b/plc4go/internal/plc4go/spi/utils/hex.go @@ -24,23 +24,32 @@ import ( "encoding/gob" "fmt" "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "math" "strings" ) // DefaultWidth defaults to a default screen dumps size -const DefaultWidth = 51 +const DefaultWidth = 56 // 10 bytes per line on a []byte < 999 // boxLineOverheat Overheat per line when drawing boxes const boxLineOverheat = 1 + 1 -// Dump dumps a 46 char wide hex string +// byteWidth required size of runes required to print one bytes 2 hex digits + 2 blanks +const byteWidth = 2 + 2 + +// blank size of blank +const blankWidth = 1 + +// DebugHex set to true to get debug messages +var DebugHex bool + +// BoxedDump dumps a 56+2 char wide hex string func BoxedDump(name string, data []byte) string { - // we substract the 2 lines at the side - dumpWidth := DefaultWidth - boxLineOverheat - return string(BoxString(name, DumpFixedWidth(data, dumpWidth), DefaultWidth)) + return string(BoxString(name, DumpFixedWidth(data, DefaultWidth), DefaultWidth+boxLineOverheat)) } -// Dump dumps a 46 char wide hex string +// Dump dumps a 56 char wide hex string func Dump(data []byte) string { return DumpFixedWidth(data, DefaultWidth) } @@ -56,33 +65,25 @@ func BoxedDumpFixedWidth(name string, data []byte, charWidth int) string { func DumpAnything(anything interface{}) string { convertedBytes, err := toBytes(anything) if err != nil { - panic(err) + if DebugHex { + log.Error().Err(err).Msg("Error converting to bytes") + } + return "<undumpable>" } - return DumpFixedWidth(convertedBytes, 1) + return Dump(convertedBytes) } -// TODO: test with charWidht <12 // DumpFixedWidth dumps hex as hex string. Min width of string returned is 18 up to supplied charWidth -func DumpFixedWidth(data []byte, charWidth int) string { - if charWidth <= 0 { - panic("charWidth needs to be greater than 0") +func DumpFixedWidth(data []byte, desiredCharWidth int) string { + if data == nil || len(data) < 1 { + return "" } hexString := "" - // 3 digits index plus one blank - const indexWidth = 3 + 1 - // 2 hex digits + 2 blanks - const byteWidth = 2 + 2 - // strings get quoate by 2 chars - const stringRenderOverheat = 2 - const minWidth = indexWidth + byteWidth + stringRenderOverheat + 1 - if charWidth < minWidth { - charWidth = minWidth + 6 - } - // Formulary to calculate max bytes per row... - maxBytesPerRow := ((charWidth - indexWidth - stringRenderOverheat) / (byteWidth + 1)) - 1 + maxBytesPerRow, indexWidth := calculateBytesPerRowAndIndexWidth(len(data), desiredCharWidth) for byteIndex, rowIndex := 0, 0; byteIndex < len(data); byteIndex, rowIndex = byteIndex+maxBytesPerRow, rowIndex+1 { - hexString += fmt.Sprintf("%03d 0x: ", byteIndex) + indexString := fmt.Sprintf("%0*d ", indexWidth, byteIndex) + hexString += indexString for columnIndex := 0; columnIndex < maxBytesPerRow; columnIndex++ { absoluteIndex := byteIndex + columnIndex if absoluteIndex < len(data) { @@ -106,6 +107,48 @@ func DumpFixedWidth(data []byte, charWidth int) string { return hexString[:len(hexString)-1] } +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) + } + indexDigits := int(math.Log10(float64(numberOfBytes))) + 1 + requiredIndexWidth := indexDigits + blankWidth + if DebugHex { + log.Debug().Msgf("index width %d for indexDigits %d for bytes %d", requiredIndexWidth, indexDigits, numberOfBytes) + } + // strings get quoted by 2 chars + const quoteRune = 1 + const potentialStringRenderRune = 1 + // 0 00 '.' + availableSpace := requiredIndexWidth + byteWidth + quoteRune + potentialStringRenderRune + quoteRune + if DebugHex { + log.Debug().Msgf("calculated %d minimal width for number of bytes %d", availableSpace, numberOfBytes) + } + if desiredStringWidth >= availableSpace { + availableSpace = desiredStringWidth + } else { + if DebugHex { + log.Debug().Msgf("Overflow by %d runes", desiredStringWidth-availableSpace) + } + } + if DebugHex { + log.Debug().Msgf("Actual space %d", availableSpace) + } + + z := float64(availableSpace) + y := float64(requiredIndexWidth) + a := float64(byteWidth) + b := float64(quoteRune) + // c = needed space for bytes x * byteWidth + // x = maxBytesPerRow + // x = (z - (y + b + x * 1 + b)) / a == x = (-2 * b - y + z)/(a + 1) and a + 1!=0 and a!=0 + x := ((-2 * b) - y + z) / (a + 1) + if DebugHex { + log.Debug().Msgf("Calculated number of bytes per row %f in int %d", x, int(x)) + } + return int(x), indexDigits +} + func maskString(data []byte) string { for i := range data { switch { diff --git a/plc4go/internal/plc4go/spi/utils/hex_test.go b/plc4go/internal/plc4go/spi/utils/hex_test.go index 2dbe2d1..e8f5cf2 100644 --- a/plc4go/internal/plc4go/spi/utils/hex_test.go +++ b/plc4go/internal/plc4go/spi/utils/hex_test.go @@ -19,7 +19,15 @@ package utils -import "testing" +import ( + "math" + "strings" + "testing" +) + +func init() { + DebugHex = true +} func TestDump(t *testing.T) { type args struct { @@ -35,23 +43,216 @@ func TestDump(t *testing.T) { args: args{ data: []byte("1234567890abcdefghijklmnopqrstuvwxyz"), }, - want: `000 0x: 31 32 33 34 35 36 37 38 '12345678' -008 0x: 39 30 61 62 63 64 65 66 '90abcdef' -016 0x: 67 68 69 6a 6b 6c 6d 6e 'ghijklmn' -024 0x: 6f 70 71 72 73 74 75 76 'opqrstuv' -032 0x: 77 78 79 7a 'wxyz '`, + want: ` +00 31 32 33 34 35 36 37 38 39 30 '1234567890' +10 61 62 63 64 65 66 67 68 69 6a 'abcdefghij' +20 6b 6c 6d 6e 6f 70 71 72 73 74 'klmnopqrst' +30 75 76 77 78 79 7a 'uvwxyz ' +`, + }, + { + name: "Test Bigger Dump", + args: args{ + data: []byte(strings.Repeat("Lorem ipsum", 90)), + }, + want: ` +000 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +010 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +020 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +030 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +040 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +050 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +060 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +070 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +080 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +090 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +100 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +110 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +120 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +130 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +140 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +150 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +160 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +170 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +180 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +190 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +200 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +210 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +220 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +230 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +240 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +250 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +260 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +270 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +280 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +290 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +300 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +310 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +320 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +330 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +340 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +350 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +360 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +370 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +380 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +390 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +400 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +410 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +420 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +430 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +440 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +450 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +460 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +470 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +480 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +490 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +500 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +510 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +520 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +530 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +540 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +550 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +560 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +570 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +580 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +590 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +600 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +610 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +620 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +630 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +640 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +650 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +660 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +670 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +680 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +690 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +700 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +710 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +720 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +730 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +740 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +750 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +760 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +770 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +780 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +790 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +800 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +810 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +820 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +830 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +840 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +850 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +860 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +870 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +880 4c 6f 72 65 6d 20 69 70 73 75 'Lorem ipsu' +890 6d 4c 6f 72 65 6d 20 69 70 73 'mLorem ips' +900 75 6d 4c 6f 72 65 6d 20 69 70 'umLorem ip' +910 73 75 6d 4c 6f 72 65 6d 20 69 'sumLorem i' +920 70 73 75 6d 4c 6f 72 65 6d 20 'psumLorem ' +930 69 70 73 75 6d 4c 6f 72 65 6d 'ipsumLorem' +940 20 69 70 73 75 6d 4c 6f 72 65 ' ipsumLore' +950 6d 20 69 70 73 75 6d 4c 6f 72 'm ipsumLor' +960 65 6d 20 69 70 73 75 6d 4c 6f 'em ipsumLo' +970 72 65 6d 20 69 70 73 75 6d 4c 'rem ipsumL' +980 6f 72 65 6d 20 69 70 73 75 6d 'orem ipsum' +`, }, { name: "minimum size", args: args{ []byte("a"), }, - want: "000 0x: 61 'a '", + want: "0 61 'a '", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Dump(tt.args.data); got != strings.Trim(tt.want, "\n") { + t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want) + } + }) + } +} + +func BenchmarkTestDump(b *testing.B) { + DebugHex = false + type args struct { + data []byte + } + benchmarks := []struct { + name string + args args + }{ + { + "small", + args{ + data: []byte(strings.Repeat("Lorem ipsum", 1)), + }, + }, + { + "medium", + args{ + data: []byte(strings.Repeat("Lorem ipsum", 100)), + }, + }, + { + "big", + args{ + data: []byte(strings.Repeat("Lorem ipsum", 10000)), + }, + }, + } + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + Dump(bm.args.data) + } + }) + } + DebugHex = true +} + +func TestBoxedDump(t *testing.T) { + type args struct { + name string + data []byte + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Test Dump", + args: args{ + name: "super nice data", + data: []byte("1234567890abcdefghijklmnopqrstuvwxyz\3231234567890abcdefghijklmnopqrstuvwxyz\323aa1234567890abcdefghijklmnopqrstuvwxyz\3231234567890abcdefghijklmnopqrstuvwxyz\323aab"), + }, + want: ` +╔═super nice data════════════════════════════════════════╗ +║000 31 32 33 34 35 36 37 38 39 30 '1234567890'║ +║010 61 62 63 64 65 66 67 68 69 6a 'abcdefghij'║ +║020 6b 6c 6d 6e 6f 70 71 72 73 74 'klmnopqrst'║ +║030 75 76 77 78 79 7a d3 31 32 33 'uvwxyz.123'║ +║040 34 35 36 37 38 39 30 61 62 63 '4567890abc'║ +║050 64 65 66 67 68 69 6a 6b 6c 6d 'defghijklm'║ +║060 6e 6f 70 71 72 73 74 75 76 77 'nopqrstuvw'║ +║070 78 79 7a d3 61 61 31 32 33 34 'xyz.aa1234'║ +║080 35 36 37 38 39 30 61 62 63 64 '567890abcd'║ +║090 65 66 67 68 69 6a 6b 6c 6d 6e 'efghijklmn'║ +║100 6f 70 71 72 73 74 75 76 77 78 'opqrstuvwx'║ +║110 79 7a d3 31 32 33 34 35 36 37 'yz.1234567'║ +║120 38 39 30 61 62 63 64 65 66 67 '890abcdefg'║ +║130 68 69 6a 6b 6c 6d 6e 6f 70 71 'hijklmnopq'║ +║140 72 73 74 75 76 77 78 79 7a d3 'rstuvwxyz.'║ +║150 61 61 62 'aab '║ +╚════════════════════════════════════════════════════════╝ +`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := Dump(tt.args.data); got != tt.want { + if got := BoxedDump(tt.args.name, tt.args.data); got != strings.Trim(tt.want, "\n") { t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want) } }) @@ -76,20 +277,84 @@ func TestBoxedDumpFixedWidth(t *testing.T) { data: []byte("1234567890abcdefghijklmnopqrstuvwxyz\3231234567890abcdefghijklmnopqrstuvwxyz\323aa1234567890abcdefghijklmnopqrstuvwxyz\3231234567890abcdefghijklmnopqrstuvwxyz\323aab"), charWidth: 136, }, - want: `╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ -║ 000 0x: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e '1234567890abcdefghijklmn' ║ -║ 024 0x: 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 'opqrstuvwxyz.1234567890a' ║ -║ 048 0x: 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 'bcdefghijklmnopqrstuvwxy' ║ -║ 072 0x: 7a d3 61 61 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 'z.aa1234567890abcdefghij' ║ -║ 096 0x: 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 'klmnopqrstuvwxyz.1234567' ║ -║ 120 0x: 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 '890abcdefghijklmnopqrstu' ║ -║ 144 0x: 76 77 78 79 7a d3 61 61 62 'vwxyz.aab ' ║ -╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝`, + want: ` +╔═super nice data══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗ +║ 000 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f '1234567890abcdefghijklmno' ║ +║ 025 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 62 63 'pqrstuvwxyz.1234567890abc' ║ +║ 050 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 61 'defghijklmnopqrstuvwxyz.a' ║ +║ 075 61 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 'a1234567890abcdefghijklmn' ║ +║ 100 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 62 'opqrstuvwxyz.1234567890ab' ║ +║ 125 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 'cdefghijklmnopqrstuvwxyz.' ║ +║ 150 61 61 62 'aab ' ║ +╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝ +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := BoxedDumpFixedWidth(tt.args.name, tt.args.data, tt.args.charWidth); got != strings.Trim(tt.want, "\n") { + t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want) + } + }) + } +} + +func TestDumpAnything(t *testing.T) { + type args struct { + anything interface{} + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Random struct", + args: args{ + anything: struct { + A string + B string + C string + D struct { + E string + F string + } + }{A: "a", B: "b", C: "c", D: struct { + E string + F string + }{ + E: "e", + F: "f", + }}, + }, + want: ` +000 25 ff 81 03 01 02 ff 82 00 01 '%.........' +010 04 01 01 41 01 0c 00 01 01 42 '...A.....B' +020 01 0c 00 01 01 43 01 0c 00 01 '.....C....' +030 01 44 01 ff 84 00 00 00 37 ff '.D......7.' +040 83 03 01 01 1d 73 74 72 75 63 '.....struc' +050 74 20 7b 20 45 20 73 74 72 69 't { E stri' +060 6e 67 3b 20 46 20 73 74 72 69 'ng; F stri' +070 6e 67 20 7d 01 ff 84 00 01 02 'ng }......' +080 01 01 45 01 0c 00 01 01 46 01 '..E.....F.' +090 0c 00 00 00 14 ff 82 01 01 61 '.........a' +100 01 01 62 01 01 63 01 01 01 65 '..b..c...e' +110 01 01 66 00 00 '..f.. ' +`, + }, + { + name: "unexported struct gob error", + args: args{ + anything: struct { + a string + }{a: "a"}, + }, + want: "<undumpable>", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := BoxedDumpFixedWidth(tt.args.name, tt.args.data, tt.args.charWidth); got != tt.want { + if got := DumpAnything(tt.args.anything); got != strings.Trim(tt.want, "\n") { t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want) } }) @@ -112,13 +377,14 @@ func TestDumpFixedWidth(t *testing.T) { data: []byte("1234567890abcdefghijklmnopqrstuvwxyz\3231234567890abcdefghijklmnopqrstuvwxyz\323aa1234567890abcdefghijklmnopqrstuvwxyz\3231234567890abcdefghijklmnopqrstuvwxyz\323aab"), charWidth: 136, }, - want: `000 0x: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f '1234567890abcdefghijklmno' -025 0x: 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 62 63 'pqrstuvwxyz.1234567890abc' -050 0x: 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 61 'defghijklmnopqrstuvwxyz.a' -075 0x: 61 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 'a1234567890abcdefghijklmn' -100 0x: 6f 70 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 62 'opqrstuvwxyz.1234567890ab' -125 0x: 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 'cdefghijklmnopqrstuvwxyz.' -150 0x: 61 61 62 'aab '`, + want: ` +000 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 '1234567890abcdefghijklmnop' +026 71 72 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 'qrstuvwxyz.1234567890abcde' +052 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 61 61 31 32 'fghijklmnopqrstuvwxyz.aa12' +078 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 '34567890abcdefghijklmnopqr' +104 73 74 75 76 77 78 79 7a d3 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 67 'stuvwxyz.1234567890abcdefg' +130 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a d3 61 61 62 'hijklmnopqrstuvwxyz.aab ' +`, }, { name: "minimum size", @@ -126,12 +392,12 @@ func TestDumpFixedWidth(t *testing.T) { []byte("a"), 1, }, - want: "000 0x: 61 'a'", + want: "0 61 'a'", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := DumpFixedWidth(tt.args.data, tt.args.charWidth); got != tt.want { + if got := DumpFixedWidth(tt.args.data, tt.args.charWidth); got != strings.Trim(tt.want, "\n") { t.Errorf("Dump() = \n%v\n, want \n%v\n", got, tt.want) } }) @@ -166,3 +432,186 @@ func Test_maskString(t *testing.T) { }) } } + +func Test_MinMax(t *testing.T) { + type args struct { + data []byte + charWidth int + } + tests := []struct { + name string + args args + want string + }{ + { + name: "nil data", + args: args{ + data: nil, + charWidth: math.MinInt32, + }, + want: "", + }, { + name: "empty data", + args: args{ + data: []byte{}, + charWidth: math.MinInt32, + }, + want: "", + }, + { + name: "-1 one byte", + args: args{ + data: []byte{0x1}, + charWidth: -1, + }, + want: "0 01 '.'", + }, + { + name: "12", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := DumpFixedWidth(tt.args.data, tt.args.charWidth); got != tt.want { + t.Errorf("maskString() = %v, want %v", got, tt.want) + } + }) + } +} + +func Benchmark(b *testing.B) { + DebugHex = false + type args struct { + numberOfBytes, desiredStringWidth int + } + benchmarks := []struct { + name string + args args + }{ + { + "small", + args{ + numberOfBytes: 1, + desiredStringWidth: 120, + }, + }, + { + "medium", + args{ + numberOfBytes: 1000, + desiredStringWidth: 120, + }, + }, + { + "big", + args{ + numberOfBytes: 1000000, + desiredStringWidth: 120, + }, + }, + } + for _, bm := range benchmarks { + b.Run(bm.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + calculateBytesPerRowAndIndexWidth(bm.args.numberOfBytes, bm.args.desiredStringWidth) + } + }) + } + DebugHex = true +} + +func Test_calculateBytesPerRowAndIndexWidth(t *testing.T) { + type args struct { + numberOfBytes int + desiredCharWidth int + } + tests := []struct { + name string + args args + wantMaxBytesPerRow int + wantIndexWidth int + }{ + { + name: "1 byte MinInt32 width", + args: args{ + numberOfBytes: 1, + desiredCharWidth: math.MinInt32, + }, + wantMaxBytesPerRow: 1, + wantIndexWidth: 1, + }, + { + name: "10 byte MinInt32 width", + args: args{ + numberOfBytes: 10, + desiredCharWidth: math.MinInt32, + }, + wantMaxBytesPerRow: 1, + wantIndexWidth: 2, + }, + { + name: "100 byte MinInt32 width", + args: args{ + numberOfBytes: 100, + desiredCharWidth: math.MinInt32, + }, + wantMaxBytesPerRow: 1, + wantIndexWidth: 3, + }, + { + name: "100 byte MaxInt32 width", + args: args{ + numberOfBytes: 100, + desiredCharWidth: math.MaxInt32, + }, + wantMaxBytesPerRow: 429496728, + wantIndexWidth: 3, + }, + { + name: "100 byte 12 width", + args: args{ + numberOfBytes: 100, + desiredCharWidth: 12, + }, + wantMaxBytesPerRow: 1, + wantIndexWidth: 3, + }, + { + name: "153 byte 136 width", + args: args{ + numberOfBytes: 153, + desiredCharWidth: 136, + }, + wantMaxBytesPerRow: 26, + wantIndexWidth: 3, + }, + { + name: "153 byte calculated width", + args: args{ + numberOfBytes: 153, + desiredCharWidth: func() int { + const quoteRune = 1 + const numberOfBytes = 153 + const indexWidth = 3 + const charRepresentation = 1 + + // 000 AF FE AF FE ..... '....*' + return indexWidth + blankWidth + (numberOfBytes * byteWidth) + quoteRune + (numberOfBytes * charRepresentation) + quoteRune + }(), + }, + wantMaxBytesPerRow: 153, + wantIndexWidth: 3, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := calculateBytesPerRowAndIndexWidth(tt.args.numberOfBytes, tt.args.desiredCharWidth) + if got != tt.wantMaxBytesPerRow { + t.Errorf("calculateBytesPerRowAndIndexWidth() got max bytes per row = %v, want %v", got, tt.wantMaxBytesPerRow) + } + if got1 != tt.wantIndexWidth { + t.Errorf("calculateBytesPerRowAndIndexWidth() got index width = %v, want %v", got1, tt.wantIndexWidth) + } + }) + } +}
