From a1dd1e88845bede0e5f278ada419972d3b2adbbc Mon Sep 17 00:00:00 2001 From: Taco de Wolff Date: Fri, 10 May 2024 20:53:08 -0400 Subject: [PATCH] Update binary reader --- binary.go | 213 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 193 insertions(+), 20 deletions(-) diff --git a/binary.go b/binary.go index ebce520..83c08d7 100644 --- a/binary.go +++ b/binary.go @@ -2,33 +2,44 @@ package parse import ( "encoding/binary" + "fmt" "io" "math" + "os" ) // BinaryReader is a binary big endian file format reader. type BinaryReader struct { - buf []byte - pos uint32 - eof bool + Endianness binary.ByteOrder + buf []byte + pos uint32 + eof bool } // NewBinaryReader returns a big endian binary file format reader. func NewBinaryReader(buf []byte) *BinaryReader { if math.MaxUint32 < uint(len(buf)) { - return &BinaryReader{nil, 0, true} + return &BinaryReader{binary.BigEndian, nil, 0, true} } - return &BinaryReader{buf, 0, false} + return &BinaryReader{binary.BigEndian, buf, 0, false} +} + +// NewBinaryReaderLE returns a little endian binary file format reader. +func NewBinaryReaderLE(buf []byte) *BinaryReader { + r := NewBinaryReader(buf) + r.Endianness = binary.LittleEndian + return r } // Seek set the reader position in the buffer. -func (r *BinaryReader) Seek(pos uint32) { +func (r *BinaryReader) Seek(pos uint32) error { if uint32(len(r.buf)) < pos { r.eof = true - return + return io.EOF } r.pos = pos r.eof = false + return nil } // Pos returns the reader's position. @@ -93,7 +104,7 @@ func (r *BinaryReader) ReadUint16() uint16 { if b == nil { return 0 } - return binary.BigEndian.Uint16(b) + return r.Endianness.Uint16(b) } // ReadUint32 reads a uint32. @@ -102,7 +113,7 @@ func (r *BinaryReader) ReadUint32() uint32 { if b == nil { return 0 } - return binary.BigEndian.Uint32(b) + return r.Endianness.Uint32(b) } // ReadUint64 reads a uint64. @@ -111,7 +122,7 @@ func (r *BinaryReader) ReadUint64() uint64 { if b == nil { return 0 } - return binary.BigEndian.Uint64(b) + return r.Endianness.Uint64(b) } // ReadInt8 reads a int8. @@ -134,27 +145,189 @@ func (r *BinaryReader) ReadInt64() int64 { return int64(r.ReadUint64()) } -// ReadUint16LE reads a uint16 little endian. -func (r *BinaryReader) ReadUint16LE() uint16 { +type BinaryFileReader struct { + f *os.File + size uint64 + offset uint64 + + Endianness binary.ByteOrder + buf []byte + pos int +} + +func NewBinaryFileReader(f *os.File, chunk int) (*BinaryFileReader, error) { + var buf []byte + var size uint64 + if chunk == 0 { + var err error + if buf, err = io.ReadAll(f); err != nil { + return nil, err + } + } else { + buf = make([]byte, 0, chunk) + } + if info, err := f.Stat(); err != nil { + return nil, err + } else { + size = uint64(info.Size()) + } + return &BinaryFileReader{ + f: f, + size: size, + Endianness: binary.BigEndian, + buf: buf, + }, nil +} + +func (r *BinaryFileReader) buffer(pos, length uint64) error { + if pos < r.offset || r.offset+uint64(len(r.buf)) < pos+length { + if math.MaxInt64 < pos { + return fmt.Errorf("seek position too large") + } else if _, err := r.f.Seek(int64(pos), 0); err != nil { + return err + } else if n, err := r.f.Read(r.buf[:cap(r.buf)]); err != nil { + return err + } else { + r.offset = pos + r.buf = r.buf[:n] + r.pos = 0 + } + } + return nil +} + +// Seek set the reader position in the buffer. +func (r *BinaryFileReader) Seek(pos uint64) error { + if r.size <= pos { + return io.EOF + } else if err := r.buffer(pos, 0); err != nil { + return err + } + r.pos = int(pos - r.offset) + return nil +} + +// Pos returns the reader's position. +func (r *BinaryFileReader) Pos() uint64 { + return r.offset + uint64(r.pos) +} + +// Len returns the remaining length of the buffer. +func (r *BinaryFileReader) Len() uint64 { + return r.size - r.Pos() +} + +// Offset returns the offset of the buffer. +func (r *BinaryFileReader) Offset() uint64 { + return r.offset +} + +// BufferLen returns the length of the buffer. +func (r *BinaryFileReader) BufferLen() int { + return len(r.buf) +} + +// Read complies with io.Reader. +func (r *BinaryFileReader) Read(b []byte) (int, error) { + if len(b) <= cap(r.buf) { + if err := r.buffer(r.offset+uint64(r.pos), uint64(len(b))); err != nil { + return 0, err + } + n := copy(b, r.buf[r.pos:]) + r.pos += n + return n, nil + } + + // read directly from file + if _, err := r.f.Seek(int64(r.offset)+int64(r.pos), 0); err != nil { + return 0, err + } + n, err := r.f.Read(b) + r.offset += uint64(r.pos + n) + r.pos = 0 + r.buf = r.buf[:0] + return n, err +} + +// ReadBytes reads n bytes. +func (r *BinaryFileReader) ReadBytes(n int) []byte { + if n < len(r.buf)-r.pos { + b := r.buf[r.pos : r.pos+n] + r.pos += n + return b + } + + b := make([]byte, n) + if _, err := r.Read(b); err != nil { + return nil + } + return b +} + +// ReadString reads a string of length n. +func (r *BinaryFileReader) ReadString(n int) string { + return string(r.ReadBytes(n)) +} + +// ReadByte reads a single byte. +func (r *BinaryFileReader) ReadByte() byte { + b := r.ReadBytes(1) + if b == nil { + return 0 + } + return b[0] +} + +// ReadUint8 reads a uint8. +func (r *BinaryFileReader) ReadUint8() uint8 { + return r.ReadByte() +} + +// ReadUint16 reads a uint16. +func (r *BinaryFileReader) ReadUint16() uint16 { b := r.ReadBytes(2) if b == nil { return 0 } - return binary.LittleEndian.Uint16(b) + return r.Endianness.Uint16(b) } -// ReadUint32LE reads a uint32 little endian. -func (r *BinaryReader) ReadUint32LE() uint32 { +// ReadUint32 reads a uint32. +func (r *BinaryFileReader) ReadUint32() uint32 { b := r.ReadBytes(4) if b == nil { return 0 } - return binary.LittleEndian.Uint32(b) + return r.Endianness.Uint32(b) +} + +// ReadUint64 reads a uint64. +func (r *BinaryFileReader) ReadUint64() uint64 { + b := r.ReadBytes(8) + if b == nil { + return 0 + } + return r.Endianness.Uint64(b) +} + +// ReadInt8 reads a int8. +func (r *BinaryFileReader) ReadInt8() int8 { + return int8(r.ReadByte()) +} + +// ReadInt16 reads a int16. +func (r *BinaryFileReader) ReadInt16() int16 { + return int16(r.ReadUint16()) +} + +// ReadInt32 reads a int32. +func (r *BinaryFileReader) ReadInt32() int32 { + return int32(r.ReadUint32()) } -// ReadInt16LE reads a int16 little endian. -func (r *BinaryReader) ReadInt16LE() int16 { - return int16(r.ReadUint16LE()) +// ReadInt64 reads a int64. +func (r *BinaryFileReader) ReadInt64() int64 { + return int64(r.ReadUint64()) } // BinaryWriter is a big endian binary file format writer. @@ -247,7 +420,7 @@ func (w *BinaryWriter) WriteInt64(v int64) { // BitmapReader is a binary bitmap reader. type BitmapReader struct { buf []byte - pos uint32 + pos uint32 // TODO: to uint64 eof bool }