1
1
mirror of https://github.com/wader/fq.git synced 2024-12-28 08:02:28 +03:00
fq/pkg/decode/decode.go
Mattias Wadman e2eb667091 html: Add to probe group
As decoder now can know they are decoding as part of probing we can now
use some heuristics to see if we should decode as html.
The reason heuristics is needed is that x/html parser will alwaus succeed.

Add lazyre package to help delay compile of RE and make it concurrency safe.
2023-05-11 19:07:18 +02:00

1309 lines
30 KiB
Go

package decode
import (
"bytes"
"context"
"fmt"
"io"
"reflect"
"regexp"
"github.com/wader/fq/internal/bitioex"
"github.com/wader/fq/internal/ioex"
"github.com/wader/fq/internal/recoverfn"
"github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/ranges"
"github.com/wader/fq/pkg/scalar"
)
//go:generate sh -c "cat decode_gen.go.tmpl | go run ../../dev/tmpl.go types.json | gofmt > decode_gen.go"
type Endian int
const (
// BigEndian byte order
BigEndian = iota
// LittleEndian byte order
LittleEndian
)
type Options struct {
Name string
Description string
Force bool
FillGaps bool
IsRoot bool
Range ranges.Range // if zero use whole buffer
InArg any
ParseOptsFn func(init any) any
ReadBuf *[]byte
}
// Decode try decode group and return first success and all other decoder errors
func Decode(ctx context.Context, br bitio.ReaderAtSeeker, group *Group, opts Options) (*Value, any, error) {
return decode(ctx, br, group, opts)
}
func decode(ctx context.Context, br bitio.ReaderAtSeeker, group *Group, opts Options) (*Value, any, error) {
brLen, err := bitioex.Len(br)
if err != nil {
return nil, nil, err
}
decodeRange := opts.Range
if decodeRange.IsZero() {
decodeRange = ranges.Range{Len: brLen}
}
if group == nil {
panic("group is nil, failed to register format?")
}
hasGroupOpts := false
groupArg := group.DefaultInArg
if opts.ParseOptsFn != nil && groupArg != nil {
if groupOptArg := opts.ParseOptsFn(groupArg); groupOptArg != nil {
hasGroupOpts = true
groupArg = groupOptArg
}
}
formatsErr := FormatsError{}
for _, f := range group.Formats {
var inArgs []any
// figure out if there are format specific arg passed as options
hasFormatOpts := false
formatArg := f.DefaultInArg
if opts.ParseOptsFn != nil && formatArg != nil {
if formatOptArg := opts.ParseOptsFn(formatArg); formatOptArg != nil {
hasFormatOpts = true
formatArg = formatOptArg
}
}
// format options have priority
if hasFormatOpts {
inArgs = append(inArgs, formatArg)
}
if opts.InArg != nil {
inArgs = append(inArgs, opts.InArg)
}
if !hasFormatOpts && f.DefaultInArg != nil {
inArgs = append(inArgs, f.DefaultInArg)
}
if hasGroupOpts {
inArgs = append(inArgs, groupArg)
}
cBR, err := bitioex.Range(br, decodeRange.Start, decodeRange.Len)
if err != nil {
return nil, nil, IOError{Err: err, Op: "BitBufRange", ReadSize: decodeRange.Len, Pos: decodeRange.Start}
}
d := newDecoder(ctx, f, cBR, opts)
d.inArgs = inArgs
var decodeV any
r, rOk := recoverfn.Run(func() {
decodeV = f.DecodeFn(d)
})
if ctx != nil && ctx.Err() != nil {
return nil, nil, ctx.Err()
}
if !rOk {
var panicErr error
if err, ok := r.RecoverV.(error); ok {
panicErr = err
} else {
panicErr = fmt.Errorf("recoverable non-panic error :%v", r.RecoverV)
}
formatErr := FormatError{
Err: panicErr,
Format: f,
Stacktrace: r,
}
formatsErr.Errs = append(formatsErr.Errs, formatErr)
switch vv := d.Value.V.(type) {
case *Compound:
// TODO: hack, changes V
d.Value.V = vv
d.Value.Err = formatErr
}
if len(group.Formats) != 1 {
continue
}
}
// TODO: maybe move to Format* funcs?
if opts.FillGaps {
d.FillGaps(ranges.Range{Start: 0, Len: decodeRange.Len}, "gap")
}
var minMaxRange ranges.Range
if err := d.Value.WalkRootPreOrder(func(v *Value, _ *Value, _ int, _ int) error {
minMaxRange = ranges.MinMax(minMaxRange, v.Range)
v.Range.Start += decodeRange.Start
v.RootReader = br
return nil
}); err != nil {
return nil, nil, err
}
d.Value.Range = ranges.Range{Start: decodeRange.Start, Len: minMaxRange.Len}
if opts.IsRoot {
d.Value.postProcess()
}
if len(formatsErr.Errs) > 0 {
return d.Value, decodeV, formatsErr
}
return d.Value, decodeV, nil
}
return nil, nil, formatsErr
}
type D struct {
Ctx context.Context
Endian Endian
Value *Value
Options Options
bitBuf bitio.ReaderAtSeeker
readBuf *[]byte
inArgs []any
}
// TODO: new struct decoder?
// note br is assumed to be a non-shared buffer
func newDecoder(ctx context.Context, format *Format, br bitio.ReaderAtSeeker, opts Options) *D {
name := format.RootName
if opts.Name != "" {
name = opts.Name
}
rootV := &Compound{
IsArray: format.RootArray,
Children: nil,
Description: opts.Description,
}
return &D{
Ctx: ctx,
Endian: BigEndian,
Value: &Value{
Name: name,
V: rootV,
RootReader: br,
Range: ranges.Range{Start: 0, Len: 0},
IsRoot: opts.IsRoot,
Format: format,
},
Options: opts,
bitBuf: br,
readBuf: opts.ReadBuf,
}
}
// ArgAs looks for a decoder option with type of target, similar to errors.As
// return true if found
func (d *D) ArgAs(target any) bool {
targeType := reflect.TypeOf(target)
for _, in := range d.inArgs {
// target will be &format.<Format>In
// in format.<Format>In so have to get ptr type
inType := reflect.PtrTo(reflect.TypeOf(in))
if inType.AssignableTo(targeType) {
targetVal := reflect.ValueOf(target)
targetVal.Elem().Set(reflect.ValueOf(in))
return true
}
}
return false
}
func (d *D) fieldDecoder(name string, bitBuf bitio.ReaderAtSeeker, v any) *D {
return &D{
Ctx: d.Ctx,
Endian: d.Endian,
Value: &Value{
Name: name,
V: v,
Range: ranges.Range{Start: d.Pos(), Len: 0},
RootReader: bitBuf,
},
Options: d.Options,
bitBuf: bitBuf,
readBuf: d.readBuf,
}
}
func (d *D) TryCopyBits(w io.Writer, r bitio.Reader) (int64, error) {
// TODO: what size? now same as io.Copy
buf := d.SharedReadBuf(32 * 1024)
return bitioex.CopyBitsBuffer(w, r, buf)
}
func (d *D) CopyBits(w io.Writer, r bitio.Reader) int64 {
n, err := d.TryCopyBits(w, r)
if err != nil {
d.IOPanic(err, "CopyBits: Copy")
}
return n
}
func (d *D) TryCopy(w io.Writer, r io.Reader) (int64, error) {
// TODO: what size? now same as io.Copy
buf := d.SharedReadBuf(32 * 1024)
return io.CopyBuffer(w, r, buf)
}
func (d *D) Copy(w io.Writer, r io.Reader) int64 {
n, err := d.TryCopy(w, r)
if err != nil {
d.IOPanic(err, "Copy")
}
return n
}
func (d *D) CloneReadSeeker(br bitio.ReadSeeker) bitio.ReadSeeker {
br, err := bitio.CloneReadSeeker(br)
if err != nil {
d.IOPanic(err, "CloneReadSeeker")
}
return br
}
func (d *D) NewBitBufFromReader(r io.Reader) bitio.ReaderAtSeeker {
b := &bytes.Buffer{}
d.Copy(b, r)
return bitio.NewBitReader(b.Bytes(), -1)
}
func (d *D) TryReadAllBits(r bitio.Reader) ([]byte, error) {
bb := &bytes.Buffer{}
buf := d.SharedReadBuf(32 * 1024)
if _, err := bitioex.CopyBitsBuffer(bb, r, buf); err != nil {
return nil, err
}
return bb.Bytes(), nil
}
func (d *D) ReadAllBits(r bitio.Reader) []byte {
buf, err := d.TryReadAllBits(r)
if err != nil {
d.IOPanic(err, "Bytes ReadAllBytes")
}
return buf
}
func (d *D) SharedReadBuf(n int) []byte {
if d.readBuf == nil {
d.readBuf = new([]byte)
}
if len(*d.readBuf) < n {
*d.readBuf = make([]byte, n)
}
return (*d.readBuf)[:n]
}
func (d *D) FillGaps(r ranges.Range, namePrefix string) {
makeWalkFn := func(fn func(iv *Value)) func(iv *Value, rootV *Value, depth int, rootDepth int) error {
return func(iv *Value, _ *Value, _ int, _ int) error {
switch iv.V.(type) {
case *Compound:
default:
fn(iv)
}
return nil
}
}
// TODO: redo this, tries to get rid of slice grow
// TODO: pre-sorted somehow?
n := 0
_ = d.Value.WalkRootPreOrder(makeWalkFn(func(_ *Value) { n++ }))
valueRanges := make([]ranges.Range, n)
i := 0
_ = d.Value.WalkRootPreOrder(makeWalkFn(func(iv *Value) {
valueRanges[i] = iv.Range
i++
}))
gaps := ranges.Gaps(r, valueRanges)
for i, gap := range gaps {
br, err := bitioex.Range(d.bitBuf, gap.Start, gap.Len)
if err != nil {
d.IOPanic(err, "FillGaps: Range")
}
v := &Value{
Name: fmt.Sprintf("%s%d", namePrefix, i),
V: &scalar.BitBuf{
Actual: br,
Gap: true,
},
RootReader: d.bitBuf,
Range: gap,
}
// TODO: for arrays not great that we just append gap fields
d.AddChild(v)
}
}
// Errorf stops decode with a reason unless forced
func (d *D) Errorf(format string, a ...any) {
if !d.Options.Force {
panic(DecoderError{Reason: fmt.Sprintf(format, a...), Pos: d.Pos()})
}
}
// Fatalf stops decode with a reason regardless of forced
func (d *D) Fatalf(format string, a ...any) {
panic(DecoderError{Reason: fmt.Sprintf(format, a...), Pos: d.Pos()})
}
func (d *D) IOPanic(err error, op string) {
panic(IOError{Err: err, Pos: d.Pos(), Op: op})
}
// TryBits reads nBits bits from buffer
func (d *D) TryBits(nBits int) ([]byte, error) {
if nBits < 0 {
return nil, fmt.Errorf("nBits must be >= 0 (%d)", nBits)
}
buf := d.SharedReadBuf(int(bitio.BitsByteCount(int64(nBits))))
_, err := bitio.ReadFull(d.bitBuf, buf, int64(nBits))
if err != nil {
return nil, err
}
return buf[:], nil
}
// Bits reads nBits bits from buffer
func (d *D) Bits(nBits int) []byte {
b, err := d.TryBits(nBits)
if err != nil {
panic(IOError{Err: err, Op: "Bits", ReadSize: int64(nBits), Pos: d.Pos()})
}
return b
}
// TryUintBits reads nBits bits as a uint64 from buffer
func (d *D) TryUintBits(nBits int) (uint64, error) {
if nBits < 0 || nBits > 64 {
return 0, fmt.Errorf("nBits must be 0-64 (%d)", nBits)
}
buf, err := d.TryBits(nBits)
if err != nil {
return 0, err
}
return bitio.Read64(buf[:], 0, int64(nBits)), nil // TODO: int64
}
// UintBits reads nBits bits as uint64 from buffer
func (d *D) UintBits(nBits int) uint64 {
n, err := d.TryUintBits(nBits)
if err != nil {
panic(IOError{Err: err, Op: "UintBits", ReadSize: int64(nBits), Pos: d.Pos()})
}
return n
}
func (d *D) PeekUintBits(nBits int) uint64 {
n, err := d.TryPeekBits(nBits)
if err != nil {
panic(IOError{Err: err, Op: "PeekUintBits", ReadSize: int64(nBits), Pos: d.Pos()})
}
return n
}
func (d *D) TryPeekBytes(nBytes int) ([]byte, error) {
start, err := d.bitBuf.SeekBits(0, io.SeekCurrent)
if err != nil {
return nil, err
}
bs, err := d.TryBytesLen(nBytes)
if _, err := d.bitBuf.SeekBits(start, io.SeekStart); err != nil {
return nil, err
}
return bs, err
}
func (d *D) PeekBytes(nBytes int) []byte {
bs, err := d.TryPeekBytes(nBytes)
if err != nil {
panic(IOError{Err: err, Op: "PeekBytes", ReadSize: int64(nBytes) * 8, Pos: d.Pos()})
}
return bs
}
func (d *D) TryHasBytes(hb []byte) bool {
lenHb := len(hb)
if d.BitsLeft() < int64(lenHb*8) {
return false
}
bs := d.PeekBytes(lenHb)
return bytes.Equal(hb, bs)
}
// PeekFindByte number of bytes to next v
func (d *D) PeekFindByte(findV uint8, maxLen int64) int64 {
peekBits, _, err := d.TryPeekFind(8, 8, maxLen*8, func(v uint64) bool {
return uint64(findV) == v
})
if err != nil {
panic(IOError{Err: err, Op: "PeekFindByte", ReadSize: 0, Pos: d.Pos()})
}
return peekBits / 8
}
// PeekBits peek nBits bits from buffer
// TODO: share code?
func (d *D) TryPeekBits(nBits int) (uint64, error) {
start, err := d.bitBuf.SeekBits(0, io.SeekCurrent)
if err != nil {
return 0, err
}
n, err := d.TryUintBits(nBits)
if _, err := d.bitBuf.SeekBits(start, io.SeekStart); err != nil {
return 0, err
}
return n, err
}
func (d *D) TryPeekFind(nBits int, seekBits int64, maxLen int64, fn func(v uint64) bool) (int64, uint64, error) {
start, err := d.bitBuf.SeekBits(0, io.SeekCurrent)
if err != nil {
return 0, 0, err
}
var count int64
if seekBits < 0 {
count = int64(-nBits)
if _, err := d.bitBuf.SeekBits(start+count, io.SeekStart); err != nil {
return 0, 0, err
}
}
found := false
var v uint64
for {
if (seekBits > 0 && maxLen > 0 && count >= maxLen) || (seekBits < 0 && maxLen > 0 && count < -maxLen) {
break
}
v, err = d.TryU(nBits)
if err != nil {
if _, err := d.bitBuf.SeekBits(start, io.SeekStart); err != nil {
return 0, 0, err
}
return 0, 0, err
}
if fn(v) {
found = true
break
}
count += seekBits
if _, err := d.bitBuf.SeekBits(start+count, io.SeekStart); err != nil {
return 0, 0, err
}
}
if _, err := d.bitBuf.SeekBits(start, io.SeekStart); err != nil {
return 0, 0, err
}
if !found {
return -1, 0, nil
}
return count, v, nil
}
func (d *D) PeekFind(nBits int, seekBits int64, maxLen int64, fn func(v uint64) bool) (int64, uint64) {
peekBits, v, err := d.TryPeekFind(nBits, seekBits, maxLen, fn)
if err != nil {
d.IOPanic(err, "PeekFind: TryPeekFind")
}
if peekBits == -1 {
d.Errorf("peek not found")
}
return peekBits, v
}
// BytesRange reads nBytes bytes starting bit position start
// Does not update current position.
// TODO: nBytes -1?
func (d *D) TryBytesRange(bitOffset int64, nBytes int) ([]byte, error) {
if nBytes < 0 {
return nil, fmt.Errorf("negative nBytes %d", nBytes)
}
buf := make([]byte, nBytes)
n, err := bitio.ReadAtFull(d.bitBuf, buf, int64(nBytes)*8, bitOffset)
if n == int64(nBytes)*8 {
err = nil
}
return buf, err
}
func (d *D) BytesRange(firstBit int64, nBytes int) []byte {
bs, err := d.TryBytesRange(firstBit, nBytes)
if err != nil {
panic(IOError{Err: err, Op: "BytesRange", ReadSize: int64(nBytes) * 8, Pos: firstBit})
}
return bs
}
func (d *D) TryBytesLen(nBytes int) ([]byte, error) {
buf := make([]byte, nBytes)
_, err := bitio.ReadFull(d.bitBuf, buf, int64(nBytes)*8)
return buf, err
}
func (d *D) BytesLen(nBytes int) []byte {
bs, err := d.TryBytesLen(nBytes)
if err != nil {
panic(IOError{Err: err, Op: "BytesLen", ReadSize: int64(nBytes) * 8, Pos: d.Pos()})
}
return bs
}
// TODO: rename/remove BitBuf name?
func (d *D) TryBitBufRange(firstBit int64, nBits int64) (bitio.ReaderAtSeeker, error) {
return bitioex.Range(d.bitBuf, firstBit, nBits)
}
func (d *D) BitBufRange(firstBit int64, nBits int64) bitio.ReaderAtSeeker {
br, err := bitioex.Range(d.bitBuf, firstBit, nBits)
if err != nil {
panic(IOError{Err: err, Op: "BitBufRange", ReadSize: nBits, Pos: firstBit})
}
return br
}
func (d *D) TryPos() (int64, error) {
return d.bitBuf.SeekBits(0, io.SeekCurrent)
}
func (d *D) Pos() int64 {
bPos, err := d.TryPos()
if err != nil {
panic(IOError{Err: err, Op: "Pos", ReadSize: 0, Pos: bPos})
}
return bPos
}
func (d *D) TryLen() (int64, error) {
return bitioex.Len(d.bitBuf)
}
func (d *D) Len() int64 {
l, err := d.TryLen()
if err != nil {
panic(IOError{Err: err, Op: "Len"})
}
return l
}
////
// BitBufLen reads nBits
func (d *D) TryBitBufLen(nBits int64) (bitio.ReaderAtSeeker, error) {
bPos, err := d.TryPos()
if err != nil {
return nil, err
}
br, err := d.TryBitBufRange(bPos, nBits)
if err != nil {
return nil, err
}
if _, err := d.TrySeekRel(nBits); err != nil {
return nil, err
}
return br, nil
}
// End is true if current position is at the end
func (d *D) TryEnd() (bool, error) {
bPos, err := d.TryPos()
if err != nil {
return false, err
}
bLen, err := d.TryLen()
if err != nil {
return false, err
}
return bPos >= bLen, nil
}
func (d *D) End() bool {
bEnd, err := d.TryEnd()
if err != nil {
panic(IOError{Err: err, Op: "End", ReadSize: 0, Pos: d.Pos()})
}
return bEnd
}
func (d *D) NotEnd() bool { return !d.End() }
// BitsLeft number of bits left until end
func (d *D) TryBitsLeft() (int64, error) {
bPos, err := d.TryPos()
if err != nil {
return 0, err
}
bLen, err := d.TryLen()
if err != nil {
return 0, err
}
return bLen - bPos, nil
}
func (d *D) BitsLeft() int64 {
bBitsLeft, err := d.TryBitsLeft()
if err != nil {
panic(IOError{Err: err, Op: "BitsLeft", ReadSize: 0, Pos: d.Pos()})
}
return bBitsLeft
}
// AlignBits number of bits to next nBits align
func (d *D) TryAlignBits(nBits int) (int, error) {
bPos, err := d.TryPos()
if err != nil {
return 0, err
}
return int((int64(nBits) - (bPos % int64(nBits))) % int64(nBits)), nil
}
func (d *D) AlignBits(nBits int) int {
bByteAlignBits, err := d.TryAlignBits(nBits)
if err != nil {
panic(IOError{Err: err, Op: "AlignBits", ReadSize: 0, Pos: d.Pos()})
}
return bByteAlignBits
}
// ByteAlignBits number of bits to next byte align
func (d *D) TryByteAlignBits() (int, error) {
return d.TryAlignBits(8)
}
func (d *D) ByteAlignBits() int {
bByteAlignBits, err := d.TryByteAlignBits()
if err != nil {
panic(IOError{Err: err, Op: "ByteAlignBits", ReadSize: 0, Pos: d.Pos()})
}
return bByteAlignBits
}
// BytePos byte position of current bit position
func (d *D) TryBytePos() (int64, error) {
bPos, err := d.TryPos()
if err != nil {
return 0, err
}
return bPos & 0x7, nil
}
func (d *D) BytePos() int64 {
bBytePos, err := d.TryBytePos()
if err != nil {
panic(IOError{Err: err, Op: "BytePos", ReadSize: 0, Pos: d.Pos()})
}
return bBytePos
}
func (d *D) trySeekAbs(pos int64, fns ...func(d *D)) (int64, error) {
var oldPos int64
if len(fns) > 0 {
oldPos = d.Pos()
}
pos, err := d.bitBuf.SeekBits(pos, io.SeekStart)
if err != nil {
return 0, err
}
if len(fns) > 0 {
for _, fn := range fns {
fn(d)
}
_, err := d.bitBuf.SeekBits(oldPos, io.SeekStart)
if err != nil {
return 0, err
}
}
return pos, nil
}
// TrySeekRel seeks relative to current bit position. If one or more functions is
// passed, the original seek position will be restored after decoding.
func (d *D) TrySeekRel(delta int64, fns ...func(d *D)) (int64, error) {
return d.trySeekAbs(d.Pos()+delta, fns...)
}
// SeekRel seeks relative to current bit position. If one or more functions is
// passed, the original seek position will be restored after decoding. Panics if
// an error occurs while seeking.
func (d *D) SeekRel(delta int64, fns ...func(d *D)) int64 {
n, err := d.trySeekAbs(d.Pos()+delta, fns...)
if err != nil {
d.IOPanic(err, "SeekRel")
}
return n
}
// TrySeekAbs seeks to absolute position. If one or more functions is passed,
// the original seek position will be restored after decoding.
func (d *D) TrySeekAbs(pos int64, fns ...func(d *D)) (int64, error) {
return d.trySeekAbs(pos, fns...)
}
func (d *D) SeekAbs(pos int64, fns ...func(d *D)) int64 {
n, err := d.trySeekAbs(pos, fns...)
if err != nil {
d.IOPanic(err, "SeekAbs")
}
return n
}
func (d *D) AddChild(v *Value) {
v.Parent = d.Value
switch fv := d.Value.V.(type) {
case *Compound:
if !fv.IsArray {
if fv.ByName == nil {
fv.ByName = make(map[string]*Value)
}
if _, ok := fv.ByName[v.Name]; ok {
d.Fatalf("%q already exist in struct %s", v.Name, d.Value.Name)
}
fv.ByName[v.Name] = v
}
fv.Children = append(fv.Children, v)
}
}
func (d *D) FieldGet(name string) *Value {
switch fv := d.Value.V.(type) {
case *Compound:
if fv.IsArray {
for _, ff := range fv.Children {
if ff.Name == name {
return ff
}
}
} else {
if fv.ByName != nil {
if ff, ok := fv.ByName[name]; ok {
return ff
}
}
return nil
}
default:
panic(fmt.Sprintf("%s is not a struct", d.Value.Name))
}
return nil
}
func (d *D) FieldMustGet(name string) *Value {
if v := d.FieldGet(name); v != nil {
return v
}
panic(fmt.Sprintf("%s not found in struct %s", name, d.Value.Name))
}
// FieldArray decode array of fields. Will not be range sorted.
func (d *D) FieldArray(name string, fn func(d *D)) *D {
c := &Compound{IsArray: true}
cd := d.fieldDecoder(name, d.bitBuf, c)
d.AddChild(cd.Value)
fn(cd)
return cd
}
// FieldArrayValue decode array of fields. Will not be range sorted.
func (d *D) FieldArrayValue(name string) *D {
return d.FieldArray(name, func(d *D) {})
}
// FieldStruct decode array of fields. Will be range sorted.
func (d *D) FieldStruct(name string, fn func(d *D)) *D {
c := &Compound{IsArray: false}
cd := d.fieldDecoder(name, d.bitBuf, c)
d.AddChild(cd.Value)
fn(cd)
return cd
}
// FieldStructValue decode array of fields. Will be range sorted.
func (d *D) FieldStructValue(name string) *D {
return d.FieldStruct(name, func(d *D) {})
}
func (d *D) FieldStructArrayLoop(name string, structName string, condFn func() bool, fn func(d *D)) *D {
return d.FieldArray(name, func(d *D) {
for condFn() {
d.FieldStruct(structName, fn)
}
})
}
// FieldStructNArray decodes an array of elements with a known count.
func (d *D) FieldStructNArray(name string, structName string, count int64, fn func(d *D)) *D {
return d.FieldArray(name, func(d *D) {
for i := int64(0); i < count; i++ {
d.FieldStruct(structName, fn)
}
})
}
func (d *D) FieldArrayLoop(name string, condFn func() bool, fn func(d *D)) *D {
return d.FieldArray(name, func(d *D) {
for condFn() {
fn(d)
}
})
}
func (d *D) FieldRangeFn(name string, firstBit int64, nBits int64, fn func() *Value) *Value {
v := fn()
v.Name = name
v.RootReader = d.bitBuf
v.Range = ranges.Range{Start: firstBit, Len: nBits}
d.AddChild(v)
return v
}
func (d *D) AssertPos(pos int64) {
if d.Pos() != pos {
panic(DecoderError{Reason: fmt.Sprintf("expected bits position %d", pos), Pos: d.Pos()})
}
}
func (d *D) AssertAtLeastBitsLeft(nBits int64) {
if d.Options.Force {
return
}
bl := d.BitsLeft()
if bl < nBits {
// TODO:
panic(DecoderError{Reason: fmt.Sprintf("expected bits left %d, found %d", nBits, bl), Pos: d.Pos()})
}
}
func (d *D) AssertLeastBytesLeft(nBytes int64) {
if d.Options.Force {
return
}
bl := d.BitsLeft()
if bl < nBytes*8 {
// TODO:
panic(DecoderError{Reason: fmt.Sprintf("expected bytes left %d, found %d bits", nBytes, bl), Pos: d.Pos()})
}
}
// FramedFn decode from current position nBits forward. When done position will be nBits forward.
func (d *D) FramedFn(nBits int64, fn func(d *D)) int64 {
if nBits < 0 {
d.Fatalf("%d nBits < 0", nBits)
}
decodeLen := d.RangeFn(d.Pos(), nBits, fn)
d.SeekRel(nBits)
return decodeLen
}
// LimitedFn decode from current position nBits forward. When done position will after last bit decoded.
func (d *D) LimitedFn(nBits int64, fn func(d *D)) int64 {
if nBits < 0 {
d.Fatalf("%d nBits < 0", nBits)
}
decodeLen := d.RangeFn(d.Pos(), nBits, fn)
d.SeekRel(decodeLen)
return decodeLen
}
// RangeFn decode from firstBit position nBits forward. Position will not change.
func (d *D) RangeFn(firstBit int64, nBits int64, fn func(d *D)) int64 {
startPos := d.Pos()
// TODO: do some kind of DecodeLimitedLen/RangeFn?
br := d.BitBufRange(0, firstBit+nBits)
if _, err := br.SeekBits(firstBit, io.SeekStart); err != nil {
d.IOPanic(err, "RangeFn: SeekAbs")
}
nd := *d
nd.bitBuf = br
fn(&nd)
endPos := nd.Pos()
return endPos - startPos
}
func (d *D) Format(group *Group, inArg any) any {
dv, v, err := decode(d.Ctx, d.bitBuf, group, Options{
Force: d.Options.Force,
FillGaps: false,
IsRoot: false,
Range: ranges.Range{Start: d.Pos(), Len: d.BitsLeft()},
InArg: inArg,
ParseOptsFn: d.Options.ParseOptsFn,
ReadBuf: d.readBuf,
})
if dv == nil || dv.Errors() != nil {
d.IOPanic(err, "Format: decode")
}
switch vv := dv.V.(type) {
case *Compound:
for _, f := range vv.Children {
d.AddChild(f)
}
default:
panic("unreachable")
}
if _, err := d.bitBuf.SeekBits(dv.Range.Len, io.SeekCurrent); err != nil {
d.IOPanic(err, "Format: SeekRel")
}
return v
}
func (d *D) TryFieldFormat(name string, group *Group, inArg any) (*Value, any, error) {
dv, v, err := decode(d.Ctx, d.bitBuf, group, Options{
Name: name,
Force: d.Options.Force,
FillGaps: false,
IsRoot: false,
Range: ranges.Range{Start: d.Pos(), Len: d.BitsLeft()},
InArg: inArg,
ParseOptsFn: d.Options.ParseOptsFn,
ReadBuf: d.readBuf,
})
if dv == nil || dv.Errors() != nil {
return nil, nil, err
}
d.AddChild(dv)
if _, err := d.bitBuf.SeekBits(dv.Range.Len, io.SeekCurrent); err != nil {
d.IOPanic(err, "TryFieldFormat: SeekRel")
}
return dv, v, err
}
func (d *D) FieldFormat(name string, group *Group, inArg any) (*Value, any) {
dv, v, err := d.TryFieldFormat(name, group, inArg)
if dv == nil || dv.Errors() != nil {
d.IOPanic(err, "FieldFormat: TryFieldFormat")
}
return dv, v
}
func (d *D) FieldFormatOrRaw(name string, group *Group, inArg any) (*Value, any) {
dv, v, _ := d.TryFieldFormat(name, group, inArg)
if dv == nil {
d.FieldRawLen(name, d.BitsLeft())
}
return dv, v
}
func (d *D) TryFieldFormatLen(name string, nBits int64, group *Group, inArg any) (*Value, any, error) {
dv, v, err := decode(d.Ctx, d.bitBuf, group, Options{
Name: name,
Force: d.Options.Force,
FillGaps: true,
IsRoot: false,
Range: ranges.Range{Start: d.Pos(), Len: nBits},
InArg: inArg,
ParseOptsFn: d.Options.ParseOptsFn,
ReadBuf: d.readBuf,
})
if dv == nil || dv.Errors() != nil {
return nil, nil, err
}
d.AddChild(dv)
if _, err := d.bitBuf.SeekBits(nBits, io.SeekCurrent); err != nil {
d.IOPanic(err, "TryFieldFormatLen: SeekRel")
}
return dv, v, err
}
func (d *D) FieldFormatLen(name string, nBits int64, group *Group, inArg any) (*Value, any) {
dv, v, err := d.TryFieldFormatLen(name, nBits, group, inArg)
if dv == nil || dv.Errors() != nil {
d.IOPanic(err, "FieldFormatLen: TryFieldFormatLen")
}
return dv, v
}
func (d *D) FieldFormatOrRawLen(name string, nBits int64, group *Group, inArg any) (*Value, any) {
dv, v, _ := d.TryFieldFormatLen(name, nBits, group, inArg)
if dv == nil {
d.FieldRawLen(name, nBits)
}
return dv, v
}
// TODO: return decooder?
func (d *D) TryFieldFormatRange(name string, firstBit int64, nBits int64, group *Group, inArg any) (*Value, any, error) {
dv, v, err := decode(d.Ctx, d.bitBuf, group, Options{
Name: name,
Force: d.Options.Force,
FillGaps: true,
IsRoot: false,
Range: ranges.Range{Start: firstBit, Len: nBits},
InArg: inArg,
ParseOptsFn: d.Options.ParseOptsFn,
ReadBuf: d.readBuf,
})
if dv == nil || dv.Errors() != nil {
return nil, nil, err
}
d.AddChild(dv)
return dv, v, err
}
func (d *D) FieldFormatRange(name string, firstBit int64, nBits int64, group *Group, inArg any) (*Value, any) {
dv, v, err := d.TryFieldFormatRange(name, firstBit, nBits, group, inArg)
if dv == nil || dv.Errors() != nil {
d.IOPanic(err, "FieldFormatRange: TryFieldFormatRange")
}
return dv, v
}
func (d *D) TryFieldFormatBitBuf(name string, br bitio.ReaderAtSeeker, group *Group, inArg any) (*Value, any, error) {
dv, v, err := decode(d.Ctx, br, group, Options{
Name: name,
Force: d.Options.Force,
FillGaps: true,
IsRoot: true,
InArg: inArg,
ParseOptsFn: d.Options.ParseOptsFn,
ReadBuf: d.readBuf,
})
if dv == nil || dv.Errors() != nil {
return nil, nil, err
}
dv.Range.Start = d.Pos()
d.AddChild(dv)
return dv, v, err
}
func (d *D) FieldFormatBitBuf(name string, br bitio.ReaderAtSeeker, group *Group, inArg any) (*Value, any) {
dv, v, err := d.TryFieldFormatBitBuf(name, br, group, inArg)
if dv == nil || dv.Errors() != nil {
d.IOPanic(err, "FieldFormatBitBuf: TryFieldFormatBitBuf")
}
return dv, v
}
// TODO: rethink these
func (d *D) FieldRootBitBuf(name string, br bitio.ReaderAtSeeker, sms ...scalar.BitBufMapper) *Value {
brLen, err := bitioex.Len(br)
if err != nil {
d.IOPanic(err, "br Len")
}
v := &Value{}
v.V = &scalar.BitBuf{Actual: br}
v.Name = name
v.RootReader = br
v.IsRoot = true
v.Range = ranges.Range{Start: d.Pos(), Len: brLen}
// if err := v.TryScalarFn(sms...); err != nil {
// d.Fatalf("%v", err)
// }
d.AddChild(v)
return v
}
func (d *D) FieldArrayRootBitBufFn(name string, br bitio.ReaderAtSeeker, fn func(d *D)) *Value {
c := &Compound{IsArray: true}
cd := d.fieldDecoder(name, br, c)
cd.Value.IsRoot = true
d.AddChild(cd.Value)
fn(cd)
cd.Value.postProcess()
return cd.Value
}
func (d *D) FieldStructRootBitBufFn(name string, br bitio.ReaderAtSeeker, fn func(d *D)) *Value {
c := &Compound{IsArray: false}
cd := d.fieldDecoder(name, br, c)
cd.Value.IsRoot = true
d.AddChild(cd.Value)
fn(cd)
cd.Value.postProcess()
return cd.Value
}
// TODO: range?
func (d *D) FieldFormatReaderLen(name string, nBits int64, fn func(r io.Reader) (io.ReadCloser, error), group *Group) (*Value, any) {
br, err := d.TryBitBufLen(nBits)
if err != nil {
d.IOPanic(err, "FieldFormatReaderLen: BitBufLen")
}
bbBR := bitio.NewIOReader(br)
r, err := fn(bbBR)
if err != nil {
d.IOPanic(err, "FieldFormatReaderLen: fn")
}
rBuf, err := io.ReadAll(r)
if err != nil {
d.IOPanic(err, "FieldFormatReaderLen: ReadAll")
}
rBR := bitio.NewBitReader(rBuf, -1)
return d.FieldFormatBitBuf(name, rBR, group, nil)
}
// TODO: too mant return values
func (d *D) TryFieldReaderRangeFormat(name string, startBit int64, nBits int64, fn func(r io.Reader) io.Reader, group *Group, inArg any) (int64, bitio.ReaderAtSeeker, *Value, any, error) {
bitLen := nBits
if bitLen == -1 {
bitLen = d.BitsLeft()
}
br, err := d.TryBitBufRange(startBit, bitLen)
if err != nil {
return 0, nil, nil, nil, err
}
r := bitio.NewIOReadSeeker(br)
rb, err := io.ReadAll(fn(r))
if err != nil {
return 0, nil, nil, nil, err
}
cz, err := r.Seek(0, io.SeekCurrent)
rbr := bitio.NewBitReader(rb, -1)
if err != nil {
return 0, nil, nil, nil, err
}
dv, v, err := d.TryFieldFormatBitBuf(name, rbr, group, inArg)
return cz * 8, rbr, dv, v, err
}
func (d *D) FieldReaderRangeFormat(name string, startBit int64, nBits int64, fn func(r io.Reader) io.Reader, group *Group, inArg any) (int64, bitio.ReaderAtSeeker, *Value, any) {
cz, rBR, dv, v, err := d.TryFieldReaderRangeFormat(name, startBit, nBits, fn, group, inArg)
if err != nil {
d.IOPanic(err, "TryFieldReaderRangeFormat")
}
return cz, rBR, dv, v
}
func (d *D) TryFieldValue(name string, fn func() (*Value, error)) (*Value, error) {
start := d.Pos()
v, err := fn()
stop := d.Pos()
v.Name = name
v.RootReader = d.bitBuf
v.Range = ranges.Range{Start: start, Len: stop - start}
if err != nil {
return nil, err
}
d.AddChild(v)
return v, err
}
func (d *D) FieldValue(name string, fn func() *Value) *Value {
v, err := d.TryFieldValue(name, func() (*Value, error) { return fn(), nil })
if err != nil {
d.IOPanic(err, "FieldValue: TryFieldValue")
}
return v
}
func (d *D) RE(re *regexp.Regexp) []ranges.Range {
startPos := d.Pos()
rr := ioex.ByteRuneReader{RS: bitio.NewIOReadSeeker(d.bitBuf)}
locs := re.FindReaderSubmatchIndex(rr)
if locs == nil {
return nil
}
d.SeekAbs(startPos)
var rs []ranges.Range
l := len(locs) / 2
for i := 0; i < l; i++ {
loc := locs[i*2 : i*2+2]
if loc[0] == -1 {
rs = append(rs, ranges.Range{Start: -1})
} else {
rs = append(rs, ranges.Range{
Start: startPos + int64(loc[0]*8),
Len: int64((loc[1] - loc[0]) * 8)},
)
}
}
return rs
}
func (d *D) FieldRE(re *regexp.Regexp, mRef *map[string]string, sms ...scalar.StrMapper) {
subexpNames := re.SubexpNames()
rs := d.RE(re)
for i, r := range rs {
if i == 0 || r.Start == -1 {
continue
}
d.SeekAbs(r.Start)
name := subexpNames[i]
value := d.FieldUTF8(name, int(r.Len/8), sms...)
if mRef != nil {
(*mRef)[name] = value
}
}
d.SeekAbs(rs[0].Stop())
}