mirror of
https://github.com/wader/fq.git
synced 2024-12-25 06:12:30 +03:00
205 lines
3.8 KiB
Go
205 lines
3.8 KiB
Go
package decode
|
|
|
|
import (
|
|
"errors"
|
|
"sort"
|
|
|
|
"github.com/wader/fq/pkg/bitio"
|
|
"github.com/wader/fq/pkg/ranges"
|
|
)
|
|
|
|
type DisplayFormat int
|
|
|
|
const (
|
|
NumberDecimal DisplayFormat = iota
|
|
NumberBinary
|
|
NumberOctal
|
|
NumberHex
|
|
)
|
|
|
|
func DisplayFormatToBase(fmt DisplayFormat) int {
|
|
switch fmt {
|
|
case NumberDecimal:
|
|
return 10
|
|
case NumberBinary:
|
|
return 2
|
|
case NumberOctal:
|
|
return 8
|
|
case NumberHex:
|
|
return 16
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
type Struct []*Value
|
|
|
|
type Array []*Value
|
|
|
|
// TODO: encoding? endian, string encoding, compression, etc?
|
|
type Value struct {
|
|
Parent *Value
|
|
V interface{} // int, int64, uint64, float64, string, bool, []byte, *bitio.Buffer, Array, Struct
|
|
Index int // index in parent array/struct
|
|
Range ranges.Range
|
|
RootBitBuf *bitio.Buffer
|
|
IsRoot bool
|
|
Name string
|
|
DisplayFormat DisplayFormat
|
|
Symbol string
|
|
Description string
|
|
Format *Format
|
|
Unknown bool
|
|
Err error
|
|
}
|
|
|
|
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")
|
|
|
|
func (v *Value) walk(preOrder bool, fn WalkFn) error {
|
|
var walkFn WalkFn
|
|
walkFn = func(v *Value, rootV *Value, depth int, rootDepth int) error {
|
|
rootDepthDelta := 0
|
|
if v.IsRoot {
|
|
rootV = v
|
|
rootDepthDelta = 1
|
|
}
|
|
|
|
if preOrder {
|
|
err := fn(v, 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 v := v.V.(type) {
|
|
case Struct:
|
|
for _, wv := range v {
|
|
if err := walkFn(wv, rootV, depth+1, rootDepth+rootDepthDelta); err != nil {
|
|
if errors.Is(err, ErrWalkBreak) {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
case Array:
|
|
for _, wv := range v {
|
|
if err := walkFn(wv, rootV, depth+1, rootDepth+rootDepthDelta); err != nil {
|
|
if errors.Is(err, ErrWalkBreak) {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if !preOrder {
|
|
err := fn(v, 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
|
|
for rootV != nil && !rootV.IsRoot {
|
|
rootV = rootV.Parent
|
|
}
|
|
|
|
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(true, fn)
|
|
}
|
|
|
|
func (v *Value) WalkPostOrder(fn WalkFn) error {
|
|
return v.walk(false, fn)
|
|
}
|
|
|
|
func (v *Value) Errors() []error {
|
|
var errs []error
|
|
_ = v.WalkPreOrder(func(v *Value, rootV *Value, depth int, rootDepth int) error {
|
|
if v.Err != nil {
|
|
errs = append(errs, v.Err)
|
|
}
|
|
return nil
|
|
})
|
|
return errs
|
|
}
|
|
|
|
func (v *Value) postProcess() {
|
|
if err := v.WalkPostOrder(func(v *Value, rootV *Value, depth int, rootDepth int) error {
|
|
switch vv := v.V.(type) {
|
|
case Struct:
|
|
first := true
|
|
for _, f := range vv {
|
|
if f.IsRoot {
|
|
continue
|
|
}
|
|
|
|
if first {
|
|
v.Range = f.Range
|
|
first = false
|
|
} else {
|
|
v.Range = ranges.MinMax(v.Range, f.Range)
|
|
}
|
|
}
|
|
|
|
sort.Slice(vv, func(i, j int) bool {
|
|
return vv[i].Range.Start < vv[j].Range.Start
|
|
})
|
|
|
|
for i, f := range vv {
|
|
f.Index = i
|
|
}
|
|
case Array:
|
|
first := true
|
|
for _, f := range vv {
|
|
if f.IsRoot {
|
|
continue
|
|
}
|
|
|
|
if first {
|
|
v.Range = f.Range
|
|
first = false
|
|
} else {
|
|
v.Range = ranges.MinMax(v.Range, f.Range)
|
|
}
|
|
}
|
|
|
|
for i, f := range vv {
|
|
f.Index = i
|
|
}
|
|
|
|
// TODO: also sort?
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|