More efficient Table reading

This commit is contained in:
Caleb Gardner
2025-06-07 02:43:32 -05:00
parent de1b18fd1c
commit 9fd87fe38a
4 changed files with 53 additions and 14 deletions
+3 -1
View File
@@ -7,7 +7,9 @@ import (
"github.com/CalebQ42/squashfs/low/inode" "github.com/CalebQ42/squashfs/low/inode"
) )
func (r Reader) InodeFromRef(ref uint64) (inode.Inode, error) { type InodeRef = uint64
func (r Reader) InodeFromRef(ref InodeRef) (inode.Inode, error) {
offset, meta := (ref>>16)+r.Superblock.InodeTableStart, ref&0xFFFF offset, meta := (ref>>16)+r.Superblock.InodeTableStart, ref&0xFFFF
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d) rdr := metadata.NewReader(toreader.NewReader(r.r, int64(offset)), r.d)
defer rdr.Close() defer rdr.Close()
+35 -5
View File
@@ -34,7 +34,7 @@ type Reader struct {
d decompress.Decompressor d decompress.Decompressor
fragTable *Table[fragEntry] fragTable *Table[fragEntry]
idTable *Table[uint32] idTable *Table[uint32]
exportTable *Table[uint64] exportTable *Table[InodeRef]
} }
func NewReader(r io.ReaderAt) (rdr Reader, err error) { func NewReader(r io.ReaderAt) (rdr Reader, err error) {
@@ -78,12 +78,42 @@ func NewReader(r io.ReaderAt) (rdr Reader, err error) {
if err != nil { if err != nil {
return rdr, errors.Join(errors.New("failed to read root directory"), err) return rdr, errors.Join(errors.New("failed to read root directory"), err)
} }
rdr.fragTable = NewTable[fragEntry](&rdr, rdr.Superblock.FragTableStart, rdr.Superblock.FragCount) rdr.fragTable = NewTable(&rdr, rdr.Superblock.FragTableStart, rdr.Superblock.FragCount, readFrag)
rdr.idTable = NewTable[uint32](&rdr, rdr.Superblock.IdTableStart, uint32(rdr.Superblock.IdCount)) rdr.idTable = NewTable(&rdr, rdr.Superblock.IdTableStart, uint32(rdr.Superblock.IdCount), readId)
rdr.exportTable = NewTable[uint64](&rdr, rdr.Superblock.ExportTableStart, rdr.Superblock.InodeCount) rdr.exportTable = NewTable(&rdr, rdr.Superblock.ExportTableStart, rdr.Superblock.InodeCount, readRef)
return return
} }
func readFrag(r io.Reader) (fragEntry, error) {
dat := make([]byte, 16)
_, err := r.Read(dat)
if err != nil {
return fragEntry{}, err
}
return fragEntry{
Start: binary.LittleEndian.Uint64(dat[0:8]),
Size: binary.LittleEndian.Uint32(dat[8:12]),
}, nil
}
func readId(r io.Reader) (uint32, error) {
dat := make([]byte, 4)
_, err := r.Read(dat)
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint32(dat), nil
}
func readRef(r io.Reader) (InodeRef, error) {
dat := make([]byte, 8)
_, err := r.Read(dat)
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint64(dat), nil
}
// Get a uid/gid at the given index. Lazily populates the reader's Id table as necessary. // Get a uid/gid at the given index. Lazily populates the reader's Id table as necessary.
func (r *Reader) Id(i uint16) (uint32, error) { func (r *Reader) Id(i uint16) (uint32, error) {
return r.idTable.Get(uint32(i)) return r.idTable.Get(uint32(i))
@@ -95,7 +125,7 @@ func (r *Reader) fragEntry(i uint32) (fragEntry, error) {
} }
// Get an inode reference at the given index. Lazily populates the reader's export table as necessary. // Get an inode reference at the given index. Lazily populates the reader's export table as necessary.
func (r *Reader) inodeRef(i uint32) (uint64, error) { func (r *Reader) inodeRef(i uint32) (InodeRef, error) {
return r.exportTable.Get(i) return r.exportTable.Get(i)
} }
+1 -1
View File
@@ -14,7 +14,7 @@ type superblock struct {
IdCount uint16 IdCount uint16
VerMaj uint16 VerMaj uint16
VerMin uint16 VerMin uint16
RootInodeRef uint64 RootInodeRef InodeRef
Size uint64 Size uint64
IdTableStart uint64 IdTableStart uint64
XattrTableStart uint64 XattrTableStart uint64
+14 -7
View File
@@ -3,6 +3,7 @@ package squashfslow
import ( import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"io"
"sync" "sync"
"github.com/CalebQ42/squashfs/internal/metadata" "github.com/CalebQ42/squashfs/internal/metadata"
@@ -13,6 +14,8 @@ var errOutOfBounds = errors.New("out of bounds")
var errUnexpectedOutOfBounds = errors.New("unexpected out of bounds") var errUnexpectedOutOfBounds = errors.New("unexpected out of bounds")
var errNilCollection = errors.New("nil collection") var errNilCollection = errors.New("nil collection")
type CreateFunction[T any] = func(io.Reader) (T, error)
type Table[T any] struct { type Table[T any] struct {
totalItems uint32 totalItems uint32
itemsPerBlock uint32 itemsPerBlock uint32
@@ -20,9 +23,10 @@ type Table[T any] struct {
mut sync.RWMutex mut sync.RWMutex
currentItems []T currentItems []T
rdr *Reader rdr *Reader
createFunc CreateFunction[T]
} }
func NewTable[T any](rdr *Reader, start uint64, totalItems uint32) *Table[T] { func NewTable[T any](rdr *Reader, start uint64, totalItems uint32, createFunc CreateFunction[T]) *Table[T] {
var zero T var zero T
return &Table[T]{ return &Table[T]{
totalItems: totalItems, totalItems: totalItems,
@@ -30,6 +34,7 @@ func NewTable[T any](rdr *Reader, start uint64, totalItems uint32) *Table[T] {
offset: start, offset: start,
mut: sync.RWMutex{}, mut: sync.RWMutex{},
rdr: rdr, rdr: rdr,
createFunc: createFunc,
} }
} }
@@ -65,14 +70,16 @@ func (t *Table[T]) fillAndGet(requestedItemIndex uint32) (T, error) {
} }
t.offset += 8 t.offset += 8
toRead = min(t.itemsPerBlock, t.totalItems-uint32(len(t.currentItems))) toRead = min(t.itemsPerBlock, t.totalItems-uint32(len(t.currentItems)))
new := make([]T, toRead) oldLen := uint32(len(t.currentItems))
t.currentItems = append(t.currentItems, make([]T, toRead)...)
metaRdr = metadata.NewReader(toreader.NewReader(t.rdr.r, int64(offset)), t.rdr.d) metaRdr = metadata.NewReader(toreader.NewReader(t.rdr.r, int64(offset)), t.rdr.d)
err = binary.Read(&metaRdr, binary.LittleEndian, new) for i := range toRead {
if err != nil { t.currentItems[oldLen+i], err = t.createFunc(&metaRdr)
var zero T if err != nil {
return zero, err var zero T
return zero, err
}
} }
t.currentItems = append(t.currentItems, new...)
} }
return t.currentItems[requestedItemIndex], nil return t.currentItems[requestedItemIndex], nil
} }