mirror of
https://github.com/wader/fq.git
synced 2024-11-27 14:14:58 +03:00
06245d1295
Rename buffer to binary. Still some work left what to call buffer/binary in decode code. Document decode value and binary type Fix proper unit padding for tobytes and add still undocumenated extra padding argument. Add some additional binary tests
245 lines
4.8 KiB
Go
245 lines
4.8 KiB
Go
package decode
|
|
|
|
// TODO: Encoding, u16le, varint etc, encode?
|
|
// TODO: Value/Compound interface? can have per type and save memory
|
|
|
|
import (
|
|
"errors"
|
|
"sort"
|
|
|
|
"github.com/wader/fq/pkg/bitio"
|
|
"github.com/wader/fq/pkg/ranges"
|
|
"github.com/wader/fq/pkg/scalar"
|
|
)
|
|
|
|
type Compound struct {
|
|
IsArray bool
|
|
Children []*Value
|
|
|
|
Description string
|
|
Format *Format
|
|
Err error
|
|
}
|
|
|
|
type Value struct {
|
|
Parent *Value
|
|
Name string
|
|
V interface{} // scalar.S or Compound (array/struct)
|
|
Index int // index in parent array/struct
|
|
Range ranges.Range
|
|
RootReader bitio.ReaderAtSeeker
|
|
IsRoot bool // TODO: rework?
|
|
}
|
|
|
|
type WalkFn func(v *Value, rootV *Value, depth int, rootDepth int) error
|
|
|
|
var ErrWalkSkipChildren = errors.New("skip children")
|
|
var ErrWalkBreak = errors.New("break")
|
|
var ErrWalkStop = errors.New("stop")
|
|
|
|
type WalkOpts struct {
|
|
PreOrder bool
|
|
OneRoot bool
|
|
Fn WalkFn
|
|
}
|
|
|
|
func (v *Value) Walk(opts WalkOpts) error {
|
|
var walkFn WalkFn
|
|
|
|
walkFn = func(wv *Value, rootV *Value, depth int, rootDepth int) error {
|
|
if opts.OneRoot && wv != v && wv.IsRoot {
|
|
return nil
|
|
}
|
|
|
|
rootDepthDelta := 0
|
|
// only count switching to a new root
|
|
if wv.IsRoot && wv != rootV {
|
|
rootV = wv
|
|
rootDepthDelta = 1
|
|
}
|
|
|
|
if opts.PreOrder {
|
|
err := opts.Fn(wv, rootV, depth, rootDepth+rootDepthDelta)
|
|
switch {
|
|
case errors.Is(err, ErrWalkSkipChildren):
|
|
return nil
|
|
case errors.Is(err, ErrWalkStop):
|
|
fallthrough
|
|
default:
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
switch wvv := wv.V.(type) {
|
|
case *Compound:
|
|
for _, wv := range wvv.Children {
|
|
if err := walkFn(wv, rootV, depth+1, rootDepth+rootDepthDelta); err != nil {
|
|
if errors.Is(err, ErrWalkBreak) {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if !opts.PreOrder {
|
|
err := opts.Fn(wv, rootV, depth, rootDepth+rootDepthDelta)
|
|
switch {
|
|
case errors.Is(err, ErrWalkSkipChildren):
|
|
return errors.New("can't skip children in post-order")
|
|
case errors.Is(err, ErrWalkStop):
|
|
fallthrough
|
|
default:
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// figure out root value for v as it might not be a root itself
|
|
rootV := v.BufferRoot()
|
|
|
|
err := walkFn(v, rootV, 0, 0)
|
|
if errors.Is(err, ErrWalkStop) {
|
|
err = nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (v *Value) WalkPreOrder(fn WalkFn) error {
|
|
return v.Walk(WalkOpts{
|
|
PreOrder: true,
|
|
Fn: fn,
|
|
})
|
|
}
|
|
|
|
func (v *Value) WalkPostOrder(fn WalkFn) error {
|
|
return v.Walk(WalkOpts{
|
|
PreOrder: false,
|
|
Fn: fn,
|
|
})
|
|
}
|
|
|
|
func (v *Value) WalkRootPreOrder(fn WalkFn) error {
|
|
return v.Walk(WalkOpts{
|
|
PreOrder: true,
|
|
OneRoot: true,
|
|
Fn: fn,
|
|
})
|
|
}
|
|
|
|
func (v *Value) WalkRootPostOrder(fn WalkFn) error {
|
|
return v.Walk(WalkOpts{
|
|
PreOrder: false,
|
|
OneRoot: true,
|
|
Fn: fn,
|
|
})
|
|
}
|
|
|
|
func (v *Value) root(findSubRoot bool, findFormatRoot bool) *Value {
|
|
rootV := v
|
|
for rootV.Parent != nil {
|
|
if findSubRoot && rootV.IsRoot {
|
|
break
|
|
}
|
|
if findFormatRoot {
|
|
if c, ok := rootV.V.(*Compound); ok {
|
|
if c.Format != nil {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
rootV = rootV.Parent
|
|
}
|
|
return rootV
|
|
}
|
|
|
|
func (v *Value) Root() *Value { return v.root(false, false) }
|
|
func (v *Value) BufferRoot() *Value { return v.root(true, false) }
|
|
func (v *Value) FormatRoot() *Value { return v.root(true, true) }
|
|
|
|
func (v *Value) Errors() []error {
|
|
var errs []error
|
|
_ = v.WalkPreOrder(func(v *Value, rootV *Value, depth int, rootDepth int) error {
|
|
switch vv := rootV.V.(type) {
|
|
case *Compound:
|
|
if vv.Err != nil {
|
|
errs = append(errs, vv.Err)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
return errs
|
|
}
|
|
|
|
func (v *Value) InnerRange() ranges.Range {
|
|
if v.IsRoot {
|
|
return ranges.Range{Start: 0, Len: v.Range.Len}
|
|
}
|
|
return v.Range
|
|
}
|
|
|
|
func (v *Value) postProcess() {
|
|
if err := v.WalkRootPostOrder(func(v *Value, rootV *Value, depth int, rootDepth int) error {
|
|
switch vv := v.V.(type) {
|
|
case *Compound:
|
|
first := true
|
|
for _, f := range vv.Children {
|
|
if f.IsRoot {
|
|
continue
|
|
}
|
|
|
|
if first {
|
|
v.Range = f.Range
|
|
first = false
|
|
} else {
|
|
v.Range = ranges.MinMax(v.Range, f.Range)
|
|
}
|
|
}
|
|
|
|
// TODO: really sort array? if sort it needs to be stable to keep the order
|
|
// of value with same range start, think null values etc
|
|
sort.SliceStable(vv.Children, func(i, j int) bool {
|
|
return (vv.Children)[i].Range.Start < (vv.Children)[j].Range.Start
|
|
})
|
|
|
|
v.Index = -1
|
|
if vv.IsArray {
|
|
for i, f := range vv.Children {
|
|
f.Index = i
|
|
}
|
|
} else {
|
|
for _, f := range vv.Children {
|
|
f.Index = -1
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (v *Value) TryScalarFn(sms ...scalar.Mapper) error {
|
|
var err error
|
|
sr, ok := v.V.(*scalar.S)
|
|
if !ok {
|
|
panic("not a scalar value")
|
|
}
|
|
s := *sr
|
|
for _, sm := range sms {
|
|
s, err = sm.MapScalar(s)
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
v.V = &s
|
|
return err
|
|
}
|