diff --git a/format/bits/bits.go b/format/bits/bits.go index e252da44..252e107b 100644 --- a/format/bits/bits.go +++ b/format/bits/bits.go @@ -2,14 +2,21 @@ package bits import ( "embed" + "encoding/binary" + "fmt" + "math" "github.com/wader/fq/format" + "github.com/wader/fq/internal/gojqex" + "github.com/wader/fq/internal/mathex" + "github.com/wader/fq/pkg/bitio" "github.com/wader/fq/pkg/decode" "github.com/wader/fq/pkg/interp" "github.com/wader/fq/pkg/scalar" ) //go:embed bits.md +//go:embed bits.jq //go:embed bytes.md var bitsFS embed.FS @@ -39,5 +46,74 @@ func init() { DecodeFn: decodeBits(8), SkipDecodeFunction: true, // skip add bytes and frombytes function }) + interp.RegisterFunc2("_from_float", func(_ *interp.Interp, c any, nBits int, isLE bool) any { + switch nBits { + case 16, 32, 64: + default: + return fmt.Errorf("unsupported bit size %d, must be 16, 32 or 64", nBits) + } + + br, err := interp.ToBitReader(c) + if err != nil { + return err + } + var b [8]byte + bs := b[:][0 : nBits/8] + _, err = br.ReadBits(bs[:], int64(nBits)) + if err != nil { + return err + } + if isLE { + decode.ReverseBytes(bs[:]) + } + + switch nBits { + case 64: + return math.Float64frombits(binary.BigEndian.Uint64(bs[:])) + case 32: + return float64(math.Float32frombits(binary.BigEndian.Uint32(bs[:]))) + case 16: + return float64(mathex.Float16(binary.BigEndian.Uint16(bs[:])).Float32()) + default: + panic("unreachable") + } + }) + interp.RegisterFunc2("_to_float", func(_ *interp.Interp, c any, nBits int, isLE bool) any { + switch nBits { + case 16, 32, 64: + default: + return fmt.Errorf("unsupported bit size %d, must be 16, 32 or 64", nBits) + } + + v, ok := gojqex.Cast[float64](c) + if !ok { + return gojqex.FuncTypeError{Name: "_to_float", V: v} + } + + var b [8]byte + bs := b[:][0 : nBits/8] + switch nBits { + case 64: + binary.BigEndian.PutUint64(bs, math.Float64bits(v)) + case 32: + binary.BigEndian.PutUint32(bs, math.Float32bits(float32(v))) + case 16: + binary.BigEndian.PutUint16(bs, uint16(mathex.NewFloat16(float32(v)))) + default: + panic("unreachable") + } + if isLE { + decode.ReverseBytes(bs[:]) + } + + br, err := interp.NewBinaryFromBitReader(bitio.NewBitReader(bs, -1), 8, 0) + if err != nil { + return err + } + + return br + + }) + interp.RegisterFS(bitsFS) } diff --git a/format/bits/bits.jq b/format/bits/bits.jq new file mode 100644 index 00000000..5091a786 --- /dev/null +++ b/format/bits/bits.jq @@ -0,0 +1,12 @@ +def from_f16be: _from_float(16; false); +def from_f32be: _from_float(32; false); +def from_f64be: _from_float(64; false); +def from_f16le: _from_float(16; true); +def from_f32le: _from_float(32; true); +def from_f64le: _from_float(64; true); +def to_f16le: _to_float(16; true); +def to_f32le: _to_float(32; true); +def to_f64le: _to_float(64; true); +def to_f16be: _to_float(16; false); +def to_f32be: _to_float(32; false); +def to_f64be: _to_float(64; false);