diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 123023e6ab..2ce3b999b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: "1.20.4" + go-version: "1.20.5" - uses: actions/checkout@v3 - uses: golangci/golangci-lint-action@v3 with: @@ -47,7 +47,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: "1.20.4" + go-version: "1.20.5" - name: Test env: GOARCH: ${{ matrix.goarch }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5bda56cec9..a3301e487c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: "1.20.4" + go-version: "1.20.5" - uses: goreleaser/goreleaser-action@v2 with: distribution: goreleaser diff --git a/Dockerfile b/Dockerfile index 5d22f636ec..2afcb0cfbb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # bump: docker-golang /FROM golang:([\d.]+)/ docker:golang|^1 -FROM golang:1.20.4-bullseye AS base +FROM golang:1.20.5-bullseye AS base # expect is used to test cli RUN \ diff --git a/go.mod b/go.mod index baf71954ff..9d55cc52dd 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( // bump: gomod-golang-x-crypto /golang\.org\/x\/crypto v(.*)/ https://github.com/golang/crypto.git|^0 // bump: gomod-golang-x-crypto command go get -d golang.org/x/crypto@v$LATEST && go mod tidy // bump: gomod-golang-x-crypto link "Tags" https://github.com/golang/crypto/tags - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.10.0 // has no tags // go get -d golang.org/x/exp@master && go mod tidy @@ -61,12 +61,12 @@ require ( // bump: gomod-golang-x-net /golang\.org\/x\/net v(.*)/ https://github.com/golang/net.git|^0 // bump: gomod-golang-x-net command go get -d golang.org/x/net@v$LATEST && go mod tidy // bump: gomod-golang-x-net link "Tags" https://github.com/golang/net/tags - golang.org/x/net v0.10.0 + golang.org/x/net v0.11.0 // bump: gomod-golang/text /golang\.org\/x\/text v(.*)/ https://github.com/golang/text.git|^0 // bump: gomod-golang/text command go get -d golang.org/x/text@v$LATEST && go mod tidy // bump: gomod-golang/text link "Source diff $CURRENT..$LATEST" https://github.com/golang/text/compare/v$CURRENT..v$LATEST - golang.org/x/text v0.9.0 + golang.org/x/text v0.10.0 // bump: gomod-gopkg.in/yaml.v3 /gopkg\.in\/yaml\.v3 v(.*)/ https://github.com/go-yaml/yaml.git|^3 // bump: gomod-gopkg.in/yaml.v3 command go get -d gopkg.in/yaml.v3@v$LATEST && go mod tidy @@ -78,6 +78,6 @@ require ( github.com/itchyny/timefmt-go v0.1.5 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/sys v0.9.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect ) diff --git a/go.sum b/go.sum index 856dd5242b..b3a3a83d32 100644 --- a/go.sum +++ b/go.sum @@ -27,18 +27,18 @@ github.com/wader/gojq v0.12.1-0.20230529153812-b7e613069119 h1:9GNJxrBtaN5wHFGap github.com/wader/gojq v0.12.1-0.20230529153812-b7e613069119/go.mod h1:jQY39j9tgky+JYcJrKNz5OYTe/sPDAw7FvVj13JGqVk= github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448 h1:AzpBtmgdXa3uznrb3esNeEoaLqtNEwckRmaUH0qWD6w= github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE= golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= diff --git a/pkg/interp/dump.go b/pkg/interp/dump.go index f3b66e7d3f..d8bd570a19 100644 --- a/pkg/interp/dump.go +++ b/pkg/interp/dump.go @@ -12,6 +12,7 @@ import ( "github.com/wader/fq/internal/binwriter" "github.com/wader/fq/internal/bitioex" "github.com/wader/fq/internal/columnwriter" + "github.com/wader/fq/internal/hexpairwriter" "github.com/wader/fq/internal/mathex" "github.com/wader/fq/pkg/bitio" "github.com/wader/fq/pkg/decode" @@ -72,6 +73,7 @@ func dumpEx(v *decode.Value, ctx *dumpCtx, depth int, rootV *decode.Value, rootD cprint := func(c int, a ...any) { fmt.Fprint(cw.Columns[c], a...) } + // cfmt: column i fmt.fprintf cfmt := func(c int, format string, a ...any) { fmt.Fprintf(cw.Columns[c], format, a...) } @@ -118,7 +120,9 @@ func dumpEx(v *decode.Value, ctx *dumpCtx, depth int, rootV *decode.Value, rootD // show address bar on root, nested root and format change if depth == 0 || v.IsRoot || v.Format != nil { + // write header: 00 01 02 03 04 cfmt(colHex, "%s", deco.DumpHeader.F(ctx.hexHeader)) + // write header: 012345 cfmt(colASCII, "%s", deco.DumpHeader.F(ctx.asciiHeader)) if willDisplayData { @@ -188,6 +192,10 @@ func dumpEx(v *decode.Value, ctx *dumpCtx, depth int, rootV *decode.Value, rootD cprint(colField, "\n") + // -------------------------------------------------- + // Error handling + // -------------------------------------------------- + if valueErr != nil { var printErrs func(depth int, err error) printErrs = func(depth int, err error) { @@ -223,24 +231,33 @@ func dumpEx(v *decode.Value, ctx *dumpCtx, depth int, rootV *decode.Value, rootD printErrs(depth, valueErr) } + // -------------------------------------------------- + // For a given field, compute various helper variables + // -------------------------------------------------- + rootBitLen, err := bitioex.Len(rootV.RootReader) if err != nil { return err } bufferLastBit := rootBitLen - 1 - startBit := innerRange.Start - stopBit := innerRange.Stop() - 1 - sizeBits := innerRange.Len - lastDisplayBit := stopBit + startBit := innerRange.Start // field's start bit index (for entire file) + stopBit := innerRange.Stop() - 1 // field's end bit index (for entire file); inclusive + sizeBits := innerRange.Len // field's bit length (1, 8, 16, 32, ...) - if opts.DisplayBytes > 0 && sizeBits > int64(opts.DisplayBytes)*8 { - lastDisplayBit = startBit + (int64(opts.DisplayBytes)*8 - 1) - if lastDisplayBit%(int64(opts.LineBytes)*8) != 0 { - lastDisplayBit += (int64(opts.LineBytes) * 8) - lastDisplayBit%(int64(opts.LineBytes)*8) - 1 + // determine lastDisplayBit: + // sometimes the field's bit length overflows the max width of a line; + // cut off the overflow in such cases. + lastDisplayBit := stopBit + displayBits := int64(opts.DisplayBytes) * 8 + lineBits := int64(opts.LineBytes) * 8 + if opts.DisplayBytes > 0 && sizeBits > displayBits { + lastDisplayBit = startBit + (displayBits - 1) + if lastDisplayBit%lineBits != 0 { + lastDisplayBit += lineBits - lastDisplayBit%lineBits - 1 } - if lastDisplayBit > stopBit || stopBit-lastDisplayBit <= int64(opts.LineBytes)*8 { + if lastDisplayBit > stopBit || stopBit-lastDisplayBit <= lineBits { lastDisplayBit = stopBit } } @@ -270,12 +287,17 @@ func dumpEx(v *decode.Value, ctx *dumpCtx, depth int, rootV *decode.Value, rootD startLineByte := startLine * int64(opts.LineBytes) lastDisplayLine := lastDisplayByte / int64(opts.LineBytes) + // -------------------------------------------------- + // Ouput Data + // -------------------------------------------------- + // has length and is not compound or a collapsed struct/array (max depth) if willDisplayData { + // write: 0x00012 (example address) cfmt(colAddr, "%s%s\n", rootIndent, deco.DumpAddr.F(mathex.PadFormatInt(startLineByte, opts.Addrbase, true, addrWidth))) - vBR, err := bitioex.Range(rootV.RootReader, startByte*4, displaySizeBits) + vBR1, err := bitioex.Range(rootV.RootReader, startByte*8, displaySizeBits) if err != nil { return err } @@ -286,29 +308,38 @@ func dumpEx(v *decode.Value, ctx *dumpCtx, depth int, rootV *decode.Value, rootD } addrLines := lastDisplayLine - startLine + 1 - // hexpairFn := func(b byte) string { return deco.ByteColor(b).Wrap(hexpairwriter.Pair(b)) } + hexpairFn := func(b byte) string { return deco.ByteColor(b).Wrap(hexpairwriter.Pair(b)) } binFn := func(b byte) string { return deco.ByteColor(b).Wrap(string("01"[int(b)])) } asciiFn := func(b byte) string { return deco.ByteColor(b).Wrap(asciiwriter.SafeASCII(b)) } - hexBR, err := bitio.CloneReadSeeker(vBR2) - if err != nil { - return err - } - // if _, err := bitioex.CopyBitsBuffer( - // hexpairwriter.New(cw.Columns[colHex], opts.LineBytes, int(startLineByteOffset), hexpairFn), - // hexBR, - // buf); err != nil { - // return err - // } - - if _, err := bitio.CopyBuffer( - binwriter.New(cw.Columns[colHex], opts.LineBytes*8, int(startLineBitOffset), binFn), - hexBR, - buf); err != nil { - return err + switch opts.Base { + case 16: + // write: 89 50 4e 47 0d 0a 1a 0a + hexBR, err := bitio.CloneReadSeeker(vBR1) + if err != nil { + return err + } + if _, err := bitioex.CopyBitsBuffer( + hexpairwriter.New(cw.Columns[colHex], opts.LineBytes, int(startLineByteOffset), hexpairFn), + hexBR, + buf); err != nil { + return err + } + case 2: + // write: 100010010101000 + hexBR, err := bitio.CloneReadSeeker(vBR2) + if err != nil { + return err + } + if _, err := bitio.CopyBuffer( + binwriter.New(cw.Columns[colHex], opts.LineBytes*8, int(startLineBitOffset), binFn), + hexBR, + buf); err != nil { + return err + } } - - asciiBR, err := bitio.CloneReadSeeker(vBR) + // write: .PNG.... + asciiBR, err := bitio.CloneReadSeeker(vBR1) if err != nil { return err } @@ -383,7 +414,13 @@ func dump(v *decode.Value, w io.Writer, opts *Options) error { } addrColumnWidth := maxAddrIndentWidth - hexColumnWidth := opts.LineBytes * 8 + var hexColumnWidth int + switch opts.Base { + case 16: + hexColumnWidth = opts.LineBytes*3 - 1 + case 2: + hexColumnWidth = opts.LineBytes * 8 + } asciiColumnWidth := opts.LineBytes treeColumnWidth := -1 // TODO: set with and truncate/wrap properly @@ -406,11 +443,18 @@ func dump(v *decode.Value, w io.Writer, opts *Options) error { var hexHeader string var asciiHeader string + var spaceLength int + switch opts.Base { + case 16: + spaceLength = 1 + case 2: + spaceLength = 8 - 2 // TODO: adapt for wider screens + } for i := 0; i < opts.LineBytes; i++ { s := mathex.PadFormatInt(int64(i), opts.Addrbase, false, 2) hexHeader += s - if i < opts.LineBytes-1 { - hexHeader += " " + if spaceLength > 1 || i < opts.LineBytes-1 { + hexHeader += strings.Repeat(" ", spaceLength) } asciiHeader += s[len(s)-1:] } diff --git a/pkg/interp/interp.go b/pkg/interp/interp.go index 700674ec61..1acf8a974a 100644 --- a/pkg/interp/interp.go +++ b/pkg/interp/interp.go @@ -1044,6 +1044,7 @@ type Options struct { BitsFormat string LineBytes int DisplayBytes int + Base int Addrbase int Sizebase int SkipGaps bool diff --git a/pkg/interp/options.jq b/pkg/interp/options.jq index 35f14a15ac..4b9ad1db06 100644 --- a/pkg/interp/options.jq +++ b/pkg/interp/options.jq @@ -78,6 +78,7 @@ def _opt_options: argdecode: "array_string_pair", argjson: "array_string_pair", array_truncate: "number", + base: "number", bits_format: "string", byte_colors: "csv_ranges_array", color: "boolean", @@ -528,12 +529,31 @@ def options($opts): + [$opts] ) | add - | ( if .width != 0 then [_intdiv(_intdiv(.width; 8); 2) * 2, 4] | max - else 16 + | ( if .width != 0 then + if .base == 2 then + # set input data bits to ~44.4% of width + .5 * (8 / (8 + 1)) * .width | floor + else + # set input data hex to ~37.5% of width + .5 * (3 / (3 + 1)) * .width | floor + end + else + 16 * 3 + end + ) as $input_data_width + | ( if .base == 2 then + # 100010010101000 + # show at least 1 byte + [_intdiv($input_data_width; 8), 1] | max + else + # 89 50 4e 47 0d 0a 1a 0a + # show an even amount of bytes; and at least 4 + [_intdiv(_intdiv($input_data_width; 3); 2) * 2, 4] | max end ) as $display_bytes # default if not set | .display_bytes |= (. // $display_bytes) | .line_bytes |= (. // $display_bytes) + | .base |= (. // 16) ); def options: options({});