Skip to content

Commit

Permalink
interp: Add skip_gaps option for tovalue/-V
Browse files Browse the repository at this point in the history
Skips gap fields in struct and arrays.

Gaps fields are bit ranges that a decoder did not add any fields for.
Note that skipping gaps in arrays will affect indexes.
  • Loading branch information
wader committed Apr 30, 2023
1 parent 1bdf1df commit c512713
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 31 deletions.
26 changes: 23 additions & 3 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -796,16 +796,36 @@ fq has some general options in addition to decode and decoders specific options.

`<value>` is fuzzily parsed based on the type of the option. Ex: a string can be specified as `-o name=string` or `-o name="string"`.

### `bits_format`
### `-o bits_format=<string>`

How to represent raw bits as JSON.
How to represent raw binary as JSON.

- `-o bits_foramt=string` String with raw bytes (zero bit padded). The string is binary safe internally in fq but bytes not representable as UTF-8 will be lost if turn to JSON.
- `-o bits_foramt=string` String with raw bytes (zero bit padded if size is not byte aligned). The string is binary safe internally in fq but bytes not representable as UTF-8 will be lost if turn to JSON.
- `-o bits_format=md5` MD5 hex string (zero bit padded).
- `-o bits_format=base64` Base64 string.
- `-p bits_foramt=truncate` Truncated string.
- `-o bits_format=snippet` Truncated Base64 string prefixed with bit length.

```sh
$ fq -V -o bits_format=base64 . file`
```
In query
```jq
tovalue({bits_format: "md5"})
```

### `-o skip_gaps=<boolean>`

Skip gaps fields (`gap0` etc) when using `tovalue` or `-V`. Note that this might affect array indexes if one more more gaps fields are skipped in an array.

```sh
$ fq -V -o skip_gaps=true . file`
```
In query
```jq
tovalue({skip_gaps: true})
```
## Color and unicode output
fq by default tries to use colors if possible, this can be disabled with `-M`. You can also
Expand Down
48 changes: 38 additions & 10 deletions pkg/interp/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,12 @@ func (i *Interp) _registry(c any) any {
}
}

func (i *Interp) _toValue(c any, opts map[string]any) any {
func (i *Interp) _toValue(c any, om map[string]any) any {
return toValue(
func() Options { return OptionsFromValue(opts) },
func() *Options {
opts := OptionsFromValue(om)
return &opts
},
c,
)
}
Expand Down Expand Up @@ -307,7 +310,7 @@ func valueHas(key any, a func(name string) any, b func(key any) any) any {

// TODO: make more efficient somehow? shallow values but might be hard
// when things like tovalue.key should behave like a jq value and not a decode value etc
func toValue(optsFn func() Options, v any) any {
func toValue(optsFn func() *Options, v any) any {
nv, _ := gojqex.ToGoJQValueFn(v, func(v any) (any, bool) {
switch v := v.(type) {
case JQValueEx:
Expand Down Expand Up @@ -540,7 +543,7 @@ func (dvb decodeValueBase) JQValueKey(name string) any {
case "_gap":
switch vv := dv.V.(type) {
case Scalarable:
return vv.ScalarGap()
return vv.ScalarIsGap()
default:
return false
}
Expand Down Expand Up @@ -606,7 +609,7 @@ func (v decodeValue) JQValueKey(name string) any {
func (v decodeValue) JQValueHas(key any) any {
return valueHas(key, v.decodeValueBase.JQValueKey, v.JQValue.JQValueHas)
}
func (v decodeValue) JQValueToGoJQEx(optsFn func() Options) any {
func (v decodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
if !v.bitsFormat {
return v.JQValueToGoJQ()
}
Expand Down Expand Up @@ -695,13 +698,26 @@ func (v ArrayDecodeValue) JQValueHas(key any) any {
return intKey >= 0 && intKey < len(v.Compound.Children)
})
}
func (v ArrayDecodeValue) JQValueToGoJQ() any {
vs := make([]any, len(v.Compound.Children))
for i, f := range v.Compound.Children {
vs[i] = makeDecodeValue(f, decodeValueValue)
func (v ArrayDecodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
opts := optsFn()

vs := make([]any, 0, len(v.Compound.Children))
for _, f := range v.Compound.Children {
switch s := f.V.(type) {
case Scalarable:
if s.ScalarIsGap() && opts.SkipGaps {
// skip, note for arrays this will affect indexes
continue
}
}

vs = append(vs, makeDecodeValue(f, decodeValueValue))
}
return vs
}
func (v ArrayDecodeValue) JQValueToGoJQ() any {
return v.JQValueToGoJQEx(func() *Options { return &Options{} })
}

// decode value struct

Expand Down Expand Up @@ -767,10 +783,22 @@ func (v StructDecodeValue) JQValueHas(key any) any {
},
)
}
func (v StructDecodeValue) JQValueToGoJQ() any {
func (v StructDecodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
opts := optsFn()

vm := make(map[string]any, len(v.Compound.Children))
for _, f := range v.Compound.Children {
switch s := f.V.(type) {
case Scalarable:
if s.ScalarIsGap() && opts.SkipGaps {
continue
}
}

vm[f.Name] = makeDecodeValue(f, decodeValueValue)
}
return vm
}
func (v StructDecodeValue) JQValueToGoJQ() any {
return v.JQValueToGoJQEx(func() *Options { return &Options{} })
}
7 changes: 4 additions & 3 deletions pkg/interp/interp.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ type Scalarable interface {
ScalarValue() any
ScalarSym() any
ScalarDescription() string
ScalarGap() bool
ScalarIsGap() bool
ScalarDisplayFormat() scalar.DisplayFormat
}

Expand Down Expand Up @@ -210,7 +210,7 @@ type Display interface {

type JQValueEx interface {
gojq.JQValue
JQValueToGoJQEx(optsFn func() Options) any
JQValueToGoJQEx(optsFn func() *Options) any
}

func valuePath(v *decode.Value) []any {
Expand Down Expand Up @@ -682,7 +682,7 @@ func (i *Interp) _printColorJSON(c any, v any) gojq.Iter {
Color: opts.Color,
Tab: false,
Indent: indent,
ValueFn: func(v any) any { return toValue(func() Options { return opts }, v) },
ValueFn: func(v any) any { return toValue(func() *Options { return &opts }, v) },
Colors: colorjson.Colors{
Reset: []byte(ansi.Reset.SetString),
Null: []byte(opts.Decorator.Null.SetString),
Expand Down Expand Up @@ -1034,6 +1034,7 @@ type Options struct {
DisplayBytes int
Addrbase int
Sizebase int
SkipGaps bool

Decorator Decorator
BitsFormatFn func(br bitio.ReaderAtSeeker) (any, error)
Expand Down
4 changes: 3 additions & 1 deletion pkg/interp/options.jq
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def _opt_build_default_fixed:
raw_output: ($stdout.is_terminal | not),
raw_string: false,
repl: false,
skip_gaps: false,
sizebase: 10,
show_formats: false,
show_help: false,
Expand Down Expand Up @@ -101,9 +102,10 @@ def _opt_options:
raw_output: "boolean",
raw_string: "boolean",
repl: "boolean",
sizebase: "number",
show_formats: "boolean",
show_help: "boolean",
sizebase: "number",
skip_gaps: "boolean",
slurp: "boolean",
string_input: "boolean",
unicode: "boolean",
Expand Down
1 change: 1 addition & 0 deletions pkg/interp/testdata/args.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ repl false
show_formats false
show_help options
sizebase 10
skip_gaps false
slurp false
string_input false
unicode false
Expand Down
1 change: 1 addition & 0 deletions pkg/interp/testdata/options.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ $ fq -n options
"show_formats": false,
"show_help": false,
"sizebase": 10,
"skip_gaps": false,
"slurp": false,
"string_input": false,
"unicode": false,
Expand Down
22 changes: 17 additions & 5 deletions pkg/interp/testdata/tovalue.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,21 @@
$ fq -i . test.mp3
mp3> .headers[0] | tovalue.header.magic
"ID3"
mp3> [tobytes, "abc"] | mp3 | tovalue, tovalue({skip_gaps: true}) | keys
[
"footers",
"frames",
"gap0",
"headers"
]
[
"footers",
"frames",
"headers"
]
mp3> "abc" | mpeg_ts | tovalue, tovalue({skip_gaps: true}) | keys
[
"gap0"
]
[]
mp3> ^D
$ fq -i
null> "aaa" | mp3_frame | .gap0 | tovalue, tovalue({sizebase: 2})
"aaa"
"aaa"
null> ^D
16 changes: 8 additions & 8 deletions pkg/scalar/scalar_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (s Any) ScalarValue() any {
}
func (s Any) ScalarSym() any { return s.Sym }
func (s Any) ScalarDescription() string { return s.Description }
func (s Any) ScalarGap() bool { return s.Gap }
func (s Any) ScalarIsGap() bool { return s.Gap }
func (s Any) ScalarDisplayFormat() DisplayFormat { return 0 }

func AnyActual(v any) AnyMapper {
Expand Down Expand Up @@ -224,7 +224,7 @@ func (s BigInt) ScalarValue() any {
}
func (s BigInt) ScalarSym() any { return s.Sym }
func (s BigInt) ScalarDescription() string { return s.Description }
func (s BigInt) ScalarGap() bool { return s.Gap }
func (s BigInt) ScalarIsGap() bool { return s.Gap }
func (s BigInt) ScalarDisplayFormat() DisplayFormat { return s.DisplayFormat }

func BigIntActual(v *big.Int) BigIntMapper {
Expand Down Expand Up @@ -420,7 +420,7 @@ func (s BitBuf) ScalarValue() any {
}
func (s BitBuf) ScalarSym() any { return s.Sym }
func (s BitBuf) ScalarDescription() string { return s.Description }
func (s BitBuf) ScalarGap() bool { return s.Gap }
func (s BitBuf) ScalarIsGap() bool { return s.Gap }
func (s BitBuf) ScalarDisplayFormat() DisplayFormat { return 0 }

func BitBufActual(v bitio.ReaderAtSeeker) BitBufMapper {
Expand Down Expand Up @@ -616,7 +616,7 @@ func (s Bool) ScalarValue() any {
}
func (s Bool) ScalarSym() any { return s.Sym }
func (s Bool) ScalarDescription() string { return s.Description }
func (s Bool) ScalarGap() bool { return s.Gap }
func (s Bool) ScalarIsGap() bool { return s.Gap }
func (s Bool) ScalarDisplayFormat() DisplayFormat { return 0 }

func BoolActual(v bool) BoolMapper {
Expand Down Expand Up @@ -812,7 +812,7 @@ func (s Flt) ScalarValue() any {
}
func (s Flt) ScalarSym() any { return s.Sym }
func (s Flt) ScalarDescription() string { return s.Description }
func (s Flt) ScalarGap() bool { return s.Gap }
func (s Flt) ScalarIsGap() bool { return s.Gap }
func (s Flt) ScalarDisplayFormat() DisplayFormat { return 0 }

func FltActual(v float64) FltMapper {
Expand Down Expand Up @@ -1009,7 +1009,7 @@ func (s Sint) ScalarValue() any {
}
func (s Sint) ScalarSym() any { return s.Sym }
func (s Sint) ScalarDescription() string { return s.Description }
func (s Sint) ScalarGap() bool { return s.Gap }
func (s Sint) ScalarIsGap() bool { return s.Gap }
func (s Sint) ScalarDisplayFormat() DisplayFormat { return s.DisplayFormat }

func SintActual(v int64) SintMapper {
Expand Down Expand Up @@ -1205,7 +1205,7 @@ func (s Str) ScalarValue() any {
}
func (s Str) ScalarSym() any { return s.Sym }
func (s Str) ScalarDescription() string { return s.Description }
func (s Str) ScalarGap() bool { return s.Gap }
func (s Str) ScalarIsGap() bool { return s.Gap }
func (s Str) ScalarDisplayFormat() DisplayFormat { return 0 }

func StrActual(v string) StrMapper {
Expand Down Expand Up @@ -1402,7 +1402,7 @@ func (s Uint) ScalarValue() any {
}
func (s Uint) ScalarSym() any { return s.Sym }
func (s Uint) ScalarDescription() string { return s.Description }
func (s Uint) ScalarGap() bool { return s.Gap }
func (s Uint) ScalarIsGap() bool { return s.Gap }
func (s Uint) ScalarDisplayFormat() DisplayFormat { return s.DisplayFormat }

func UintActual(v uint64) UintMapper {
Expand Down
2 changes: 1 addition & 1 deletion pkg/scalar/scalar_gen.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
}
func (s {{$name}}) ScalarSym() any { return s.Sym }
func (s {{$name}}) ScalarDescription() string { return s.Description }
func (s {{$name}}) ScalarGap() bool { return s.Gap }
func (s {{$name}}) ScalarIsGap() bool { return s.Gap }
{{- if $t.display_format}}
func (s {{$name}}) ScalarDisplayFormat() DisplayFormat { return s.DisplayFormat }
{{- else }}
Expand Down

0 comments on commit c512713

Please sign in to comment.