Skip to content

Commit

Permalink
interp: Add hex bits format
Browse files Browse the repository at this point in the history
$ cat random.bin.gz | fq -rV -o bits_format=hex .uncompressed
f6f2074cf77d449d

Also made unknown bit formats an error.
  • Loading branch information
wader committed May 15, 2023
1 parent 070d5f4 commit 8a468f4
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 88 deletions.
1 change: 1 addition & 0 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ How to represent raw binary as 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=hex` Hex string.
- `-o bits_format=base64` Base64 string.
- `-p bits_foramt=truncate` Truncated string.
- `-o bits_format=snippet` Truncated Base64 string prefixed with bit length.
Expand Down
6 changes: 3 additions & 3 deletions format/json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ func makeEncoder(opts ToJSONOpts) *colorjson.Encoder {
Color: false,
Tab: false,
Indent: opts.Indent,
ValueFn: func(v any) any {
ValueFn: func(v any) (any, error) {
switch v := v.(type) {
case gojq.JQValue:
return v.JQValueToGoJQ()
return v.JQValueToGoJQ(), nil
default:
return v
return v, nil
}
},
Colors: colorjson.Colors{},
Expand Down
8 changes: 6 additions & 2 deletions internal/colorjson/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type Options struct {
Color bool
Tab bool
Indent int
ValueFn func(v any) any
ValueFn func(v any) (any, error)
Colors Colors
}

Expand Down Expand Up @@ -120,7 +120,11 @@ func (e *Encoder) encode(v any) error {
if e.opts.ValueFn == nil {
panic(fmt.Sprintf("unknown type and to ValueFn set: %[1]T (%[1]v)", v))
}
return e.encode(e.opts.ValueFn(v))
vv, err := e.opts.ValueFn(v)
if err != nil {
return err
}
return e.encode(vv)
}
if e.w.Len() > 8*1024 {
return e.flush()
Expand Down
61 changes: 32 additions & 29 deletions internal/gojqex/totype.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package gojqex

import (
"fmt"
"math"
"math/big"

Expand All @@ -23,75 +24,77 @@ func IsNull(x any) bool {
}
}

func ToGoJQValue(v any) (any, bool) {
return ToGoJQValueFn(v, func(v any) (any, bool) {
func ToGoJQValue(v any) (any, error) {
return ToGoJQValueFn(v, func(v any) (any, error) {
switch v := v.(type) {
case gojq.JQValue:
return v.JQValueToGoJQ(), true
return v.JQValueToGoJQ(), nil
default:
return nil, false
return nil, fmt.Errorf("not a JQValue")
}
})
}

func ToGoJQValueFn(v any, valueFn func(v any) (any, bool)) (any, bool) {
func ToGoJQValueFn(v any, valueFn func(v any) (any, error)) (any, error) {
switch vv := v.(type) {
case nil:
return vv, true
return vv, nil
case bool:
return vv, true
return vv, nil
case int:
return vv, true
return vv, nil
case int64:
if vv >= math.MinInt && vv <= math.MaxInt {
return int(vv), true
return int(vv), nil
}
return big.NewInt(vv), true
return big.NewInt(vv), nil
case uint64:
if vv <= math.MaxInt {
return int(vv), true
return int(vv), nil
}
return new(big.Int).SetUint64(vv), true
return new(big.Int).SetUint64(vv), nil
case float32:
return float64(vv), true
return float64(vv), nil
case float64:
return vv, true
return vv, nil
case *big.Int:
if vv.IsInt64() {
vv := vv.Int64()
if vv >= math.MinInt && vv <= math.MaxInt {
return int(vv), true
return int(vv), nil
}
}
return vv, true
return vv, nil
case string:
return vv, true
return vv, nil
case []byte:
return string(vv), true
return string(vv), nil
case []any:
vvs := make([]any, len(vv))
for i, v := range vv {
v, ok := ToGoJQValueFn(v, valueFn)
if !ok {
return nil, false
v, err := ToGoJQValueFn(v, valueFn)
if err != nil {
return nil, err
}
vvs[i] = v
}
return vvs, true
return vvs, nil
case map[string]any:
vvs := make(map[string]any, len(vv))
for k, v := range vv {
v, ok := ToGoJQValueFn(v, valueFn)
if !ok {
return nil, false
v, err := ToGoJQValueFn(v, valueFn)
if err != nil {
return nil, err
}
vvs[k] = v
}
return vvs, true
return vvs, nil
default:
if nv, ok := valueFn(vv); ok {
return ToGoJQValueFn(nv, valueFn)
nv, err := valueFn(vv)
if err != nil {
return nil, err
}
return nil, false

return ToGoJQValueFn(nv, valueFn)
}
}
6 changes: 3 additions & 3 deletions internal/gojqex/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,14 @@ func CastFn[T any](v any, structFn func(input any, result any) error) (T, bool)
ft := reflect.TypeOf(&t)
if ft.Elem().Kind() == reflect.Struct {
// TODO: some way to allow decode value passthru?
m, ok := ToGoJQValue(v)
if !ok {
m, err := ToGoJQValue(v)
if err != nil {
return t, false
}
if structFn == nil {
panic("structFn nil")
}
err := structFn(m, &t)
err = structFn(m, &t)
if err != nil {
return t, false
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/interp/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ type openFile struct {
var _ Value = (*openFile)(nil)
var _ ToBinary = (*openFile)(nil)

func (of *openFile) Display(w io.Writer, opts Options) error {
func (of *openFile) Display(w io.Writer, opts *Options) error {
_, err := fmt.Fprintf(w, "<openfile %q>\n", of.filename)
return err
}
Expand Down Expand Up @@ -405,7 +405,7 @@ func (b Binary) JQValueToGoJQ() any {
return buf.String()
}

func (b Binary) Display(w io.Writer, opts Options) error {
func (b Binary) Display(w io.Writer, opts *Options) error {
if opts.RawOutput {
br, err := b.toReader()
if err != nil {
Expand Down
59 changes: 36 additions & 23 deletions pkg/interp/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,16 @@ func (i *Interp) _registry(c any) any {
}

func (i *Interp) _toValue(c any, om map[string]any) any {
return toValue(
func() *Options {
opts := OptionsFromValue(om)
return &opts
},
c,
)
opts, err := OptionsFromValue(om)
if err != nil {
return err
}

v, err := toValue(func() (*Options, error) { return opts, nil }, c)
if err != nil {
return err
}
return v
}

type decodeOpts struct {
Expand Down Expand Up @@ -310,21 +313,20 @@ 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 {
nv, _ := gojqex.ToGoJQValueFn(v, func(v any) (any, bool) {
func toValue(optsFn func() (*Options, error), v any) (any, error) {
return gojqex.ToGoJQValueFn(v, func(v any) (any, error) {
switch v := v.(type) {
case JQValueEx:
if optsFn == nil {
return v.JQValueToGoJQ(), true
return v.JQValueToGoJQ(), nil
}
return v.JQValueToGoJQEx(optsFn), true
return v.JQValueToGoJQEx(optsFn), nil
case gojq.JQValue:
return v.JQValueToGoJQ(), true
return v.JQValueToGoJQ(), nil
default:
return v, true
return v, nil
}
})
return nv
}

type decodeValueKind int
Expand Down Expand Up @@ -453,7 +455,7 @@ func (dvb decodeValueBase) DecodeValue() *decode.Value {
return dvb.dv
}

func (dvb decodeValueBase) Display(w io.Writer, opts Options) error { return dump(dvb.dv, w, opts) }
func (dvb decodeValueBase) Display(w io.Writer, opts *Options) error { return dump(dvb.dv, w, opts) }
func (dvb decodeValueBase) ToBinary() (Binary, error) {
return Binary{br: dvb.dv.RootReader, r: dvb.dv.InnerRange(), unit: 8}, nil
}
Expand Down Expand Up @@ -606,7 +608,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, error)) any {
if !v.bitsFormat {
return v.JQValueToGoJQ()
}
Expand All @@ -625,7 +627,12 @@ func (v decodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
return err
}

s, err := optsFn().BitsFormatFn(brC)
opts, err := optsFn()
if err != nil {
return err
}

s, err := opts.BitsFormatFn(brC)
if err != nil {
return err
}
Expand Down Expand Up @@ -695,8 +702,11 @@ func (v ArrayDecodeValue) JQValueHas(key any) any {
return intKey >= 0 && intKey < len(v.Compound.Children)
})
}
func (v ArrayDecodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
opts := optsFn()
func (v ArrayDecodeValue) JQValueToGoJQEx(optsFn func() (*Options, error)) any {
opts, err := optsFn()
if err != nil {
return err
}

vs := make([]any, 0, len(v.Compound.Children))
for _, f := range v.Compound.Children {
Expand All @@ -713,7 +723,7 @@ func (v ArrayDecodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
return vs
}
func (v ArrayDecodeValue) JQValueToGoJQ() any {
return v.JQValueToGoJQEx(func() *Options { return &Options{} })
return v.JQValueToGoJQEx(func() (*Options, error) { return &Options{}, nil })
}

// decode value struct
Expand Down Expand Up @@ -780,8 +790,11 @@ func (v StructDecodeValue) JQValueHas(key any) any {
},
)
}
func (v StructDecodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
opts := optsFn()
func (v StructDecodeValue) JQValueToGoJQEx(optsFn func() (*Options, error)) any {
opts, err := optsFn()
if err != nil {
return err
}

vm := make(map[string]any, len(v.Compound.Children))
for _, f := range v.Compound.Children {
Expand All @@ -797,5 +810,5 @@ func (v StructDecodeValue) JQValueToGoJQEx(optsFn func() *Options) any {
return vm
}
func (v StructDecodeValue) JQValueToGoJQ() any {
return v.JQValueToGoJQEx(func() *Options { return &Options{} })
return v.JQValueToGoJQEx(func() (*Options, error) { return &Options{}, nil })
}
6 changes: 3 additions & 3 deletions pkg/interp/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func isCompound(v *decode.Value) bool {
}

type dumpCtx struct {
opts Options
opts *Options
buf []byte
cw *columnwriter.Writer
hexHeader string
Expand Down Expand Up @@ -336,7 +336,7 @@ func dumpEx(v *decode.Value, ctx *dumpCtx, depth int, rootV *decode.Value, rootD
return nil
}

func dump(v *decode.Value, w io.Writer, opts Options) error {
func dump(v *decode.Value, w io.Writer, opts *Options) error {
maxAddrIndentWidth := 0
makeWalkFn := func(fn decode.WalkFn) decode.WalkFn {
return func(v *decode.Value, rootV *decode.Value, depth int, rootDepth int) error {
Expand Down Expand Up @@ -409,7 +409,7 @@ func dump(v *decode.Value, w io.Writer, opts Options) error {
}))
}

func hexdump(w io.Writer, bv Binary, opts Options) error {
func hexdump(w io.Writer, bv Binary, opts *Options) error {
br, err := bitioex.Range(bv.br, bv.r.Start, bv.r.Len)
if err != nil {
return err
Expand Down

0 comments on commit 8a468f4

Please sign in to comment.