1
1
mirror of https://github.com/wader/fq.git synced 2024-11-22 07:16:49 +03:00

interp: Add skip_gaps option for tovalue/-V

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.
This commit is contained in:
Mattias Wadman 2023-04-02 12:20:01 +02:00
parent 1bdf1df8ba
commit c51271399c
9 changed files with 96 additions and 31 deletions

View File

@ -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

View File

@ -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,
)
}
@ -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:
@ -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
}
@ -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()
}
@ -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
@ -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{} })
}

View File

@ -82,7 +82,7 @@ type Scalarable interface {
ScalarValue() any
ScalarSym() any
ScalarDescription() string
ScalarGap() bool
ScalarIsGap() bool
ScalarDisplayFormat() scalar.DisplayFormat
}
@ -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 {
@ -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),
@ -1034,6 +1034,7 @@ type Options struct {
DisplayBytes int
Addrbase int
Sizebase int
SkipGaps bool
Decorator Decorator
BitsFormatFn func(br bitio.ReaderAtSeeker) (any, error)

View File

@ -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,
@ -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",

View File

@ -99,6 +99,7 @@ repl false
show_formats false
show_help options
sizebase 10
skip_gaps false
slurp false
string_input false
unicode false

View File

@ -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,

View File

@ -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

View File

@ -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 {
@ -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 {
@ -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 {
@ -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 {
@ -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 {
@ -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 {
@ -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 {
@ -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 {

View File

@ -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 }}