1
1
mirror of https://github.com/wader/fq.git synced 2024-12-25 22:34:14 +03:00
fq/pkg/decode/decode_readers.go

322 lines
7.5 KiB
Go

package decode
import (
"io"
"strings"
"github.com/wader/fq/pkg/bitio"
"golang.org/x/text/encoding/unicode"
)
// TODO: FP64,unsigned/BE/LE? rename SFP32?
func (d *D) TryUTF8Fn(nBytes int, fn func(s string) string) (string, error) {
s, err := d.bitBuf.BytesLen(nBytes)
if err != nil {
return "", err
}
return fn(string(s)), nil
}
func (d *D) TryUTF8(nBytes int) (string, error) {
return d.TryUTF8Fn(nBytes, func(s string) string { return s })
}
func (d *D) TryUTF16BE(nBytes int) (string, error) {
b, err := d.bitBuf.BytesLen(nBytes)
if err != nil {
return "", err
}
return unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewDecoder().String(string(b))
}
func (d *D) TryUTF16LE(nBytes int) (string, error) {
b, err := d.bitBuf.BytesLen(nBytes)
// TODO: len check
if err != nil {
return "", err
}
return unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder().String(string(b))
}
// TryUTF8ShortString read pascal short string, max nBytes
func (d *D) TryUTF8ShortString(nBytes int) (string, error) {
l, err := d.TryU8()
if err != nil {
return "", err
}
n := int(l)
if nBytes != -1 {
n = nBytes - 1
}
s, err := d.bitBuf.BytesLen(n)
if err != nil {
return "", err
}
return string(s[0:l]), nil
}
// 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.bits(nBits)
if _, err := d.bitBuf.SeekBits(start, io.SeekStart); err != nil {
return 0, err
}
return n, err
}
// Bits reads nBits bits from buffer
func (d *D) bits(nBits int) (uint64, error) {
// 64 bits max, 9 byte worse case if not byte aligned
buf := d.SharedReadBuf(9)
_, err := bitio.ReadFull(d.bitBuf, buf, nBits)
if err != nil {
return 0, err
}
return bitio.Read64(buf[:], 0, nBits), nil
}
// Bits reads nBits bits from buffer
func (d *D) Bits(nBits int) (uint64, error) {
n, err := d.bits(nBits)
if err != nil {
return 0, err
}
return n, nil
}
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
}
found := false
var count int64
var v uint64
for {
if 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) ZeroPadding(nBits int) bool {
isZero := true
left := nBits
for {
// TODO: smart skip?
rBits := left
if rBits == 0 {
break
}
if rBits > 64 {
rBits = 64
}
n, err := d.Bits(rBits)
if err != nil {
panic(IOError{Err: err, Op: "ZeroPadding", Size: int64(rBits), Pos: d.Pos()})
}
isZero = isZero && n == 0
left -= rBits
}
return isZero
}
func (d *D) FieldOptionalFillFn(name string, fn func(d *D)) int64 {
start := d.Pos()
fn(d)
fillLen := d.Pos() - start
if fillLen > 0 {
d.FieldBitBufRange(name, start, fillLen)
}
return fillLen
}
func (d *D) FieldOptionalZeroBytes(name string) int64 {
return d.FieldOptionalFillFn(name, func(d *D) {
for d.BitsLeft() >= 8 && d.PeekBits(8) == 0 {
d.SeekRel(8)
}
})
}
func (d *D) fieldZeroPadding(name string, nBits int, panicOnNonZero bool) {
pos := d.Pos()
var isZero bool
d.FieldFn(name, func() *Value {
isZero = d.ZeroPadding(nBits)
s := "Correct"
if !isZero {
s = "Incorrect"
}
// TODO: proper warnings
return &Value{Symbol: s, Description: "zero padding"}
})
if panicOnNonZero && !isZero {
panic(ValidateError{Reason: "expected zero padding", Pos: pos})
}
}
func (d *D) FieldValidateZeroPadding(name string, nBits int) {
d.fieldZeroPadding(name, nBits, true)
}
func (d *D) FieldZeroPadding(name string, nBits int) {
d.fieldZeroPadding(name, nBits, false)
}
// Bool reads one bit as a boolean
func (d *D) TryBool() (bool, error) {
n, err := d.TryU(1)
if err != nil {
return false, err
}
return n == 1, nil
}
func (d *D) Bool() bool {
b, err := d.TryBool()
if err != nil {
panic(IOError{Err: err, Op: "Bool", Size: 1, Pos: d.Pos()})
}
return b
}
func (d *D) FieldBool(name string) bool {
return d.FieldBoolFn(name, func() (bool, string) {
b, err := d.TryBool()
if err != nil {
panic(IOError{Err: err, Name: name, Op: "FieldBool", Size: 1, Pos: d.Pos()})
}
return b, ""
})
}
func (d *D) FieldBytesLen(name string, nBytes int) []byte {
return d.FieldBytesFn(name, func() ([]byte, string) {
bs, err := d.bitBuf.BytesLen(nBytes)
if err != nil {
panic(IOError{Err: err, Name: name, Op: "FieldBytesLen", Size: int64(nBytes) * 8, Pos: d.Pos()})
}
return bs, ""
})
}
// UTF8Fn read nBytes utf8 string and transform it using fn
func (d *D) UTF8Fn(nBytes int, fn func(s string) string) string {
s, err := d.TryUTF8Fn(nBytes, fn)
if err != nil {
panic(IOError{Err: err, Op: "UTF8", Size: int64(nBytes) * 8, Pos: d.Pos()})
}
return s
}
func (d *D) UTF8(nBytes int) string {
return d.UTF8Fn(nBytes, func(s string) string { return s })
}
// UTF16BE read nBytes utf16be string
func (d *D) UTF16BE(nBytes int) string {
s, err := d.TryUTF16BE(nBytes)
if err != nil {
panic(IOError{Err: err, Op: "UTF16BE", Size: int64(nBytes) * 8, Pos: d.Pos()})
}
return s
}
// UTF16LE read nBytes utf16le string
func (d *D) UTF16LE(nBytes int) string {
s, err := d.TryUTF16LE(nBytes)
if err != nil {
panic(IOError{Err: err, Op: "UTF16LE", Size: int64(nBytes) * 8, Pos: d.Pos()})
}
return s
}
// FieldUTF8 read nBytes utf8 string and add a field
func (d *D) FieldUTF8Fn(name string, nBytes int, fn func(s string) string) string {
return d.FieldStrFn(name, func() (string, string) {
str, err := d.TryUTF8Fn(nBytes, fn)
if err != nil {
panic(IOError{Err: err, Name: name, Op: "FieldUTF8", Size: int64(nBytes) * 8, Pos: d.Pos()})
}
return str, ""
})
}
func (d *D) FieldUTF8Null(name string, nBytes int) string {
return d.FieldUTF8Fn(name, nBytes, func(s string) string { return strings.TrimRight(s, "\x00") })
}
func (d *D) FieldUTF8(name string, nBytes int) string {
return d.FieldUTF8Fn(name, nBytes, func(s string) string { return s })
}
// FieldUTF16BE read nBytes utf16be string and add a field
func (d *D) FieldUTF16BE(name string, nBytes int) string {
return d.FieldStrFn(name, func() (string, string) {
str, err := d.TryUTF16BE(nBytes)
if err != nil {
panic(IOError{Err: err, Name: name, Op: "FieldUTF16BE", Size: int64(nBytes) * 8, Pos: d.Pos()})
}
return str, ""
})
}
// FieldUTF16LE read nBytes utf16le string and add a field
func (d *D) FieldUTF16LE(name string, nBytes int) string {
return d.FieldStrFn(name, func() (string, string) {
str, err := d.TryUTF16LE(nBytes)
if err != nil {
panic(IOError{Err: err, Name: name, Op: "FieldUTF16LE", Size: int64(nBytes) * 8, Pos: d.Pos()})
}
return str, ""
})
}
// FieldUTF8ShortString read nBytes utf8 pascal short string and add a field
func (d *D) FieldUTF8ShortString(name string, nBytes int) string {
return d.FieldStrFn(name, func() (string, string) {
str, err := d.TryUTF8ShortString(nBytes)
if err != nil {
panic(IOError{Err: err, Name: name, Op: "FieldUTF8ShortString", Size: int64(nBytes) * 8, Pos: d.Pos()})
}
return str, ""
})
}