diff options
Diffstat (limited to 'object/store/packed/packed.go')
| -rw-r--r-- | object/store/packed/packed.go | 125 |
1 files changed, 95 insertions, 30 deletions
diff --git a/object/store/packed/packed.go b/object/store/packed/packed.go index c0961508..897b3b98 100644 --- a/object/store/packed/packed.go +++ b/object/store/packed/packed.go @@ -1,36 +1,101 @@ package packed -// import ( -// "os" -// -// "lindenii.org/go/furgit/object/id" -// "lindenii.org/go/furgit/object/store" -// ) +import ( + "errors" + "os" + "sync" + + "lindenii.org/go/furgit/internal/cache/clock" + "lindenii.org/go/furgit/internal/mru" + "lindenii.org/go/furgit/object/id" + "lindenii.org/go/furgit/object/store" +) + +// ErrMalformedPackedStore reports that +// a pack or pack index in the store is +// truncated, inconsistent, or otherwise corrupt. +var ErrMalformedPackedStore = errors.New("object/store/packed: malformed packed store") + +// Packed reads Git objects from pack/index files +// under an objects/pack root. // -// // Packed reads Git objects from pack/index files under an objects/pack root, -// // and ingests incoming pack streams into it. -// // -// // Labels: Close-Caller. -// type Packed struct { -// // root is the objects/pack directory capability -// // used for all pack and index file access. -// // Packed borrows this root. -// root *os.Root -// // objectFormat is the expected object format for lookups. -// objectFormat id.ObjectFormat -// } +// Packs appearing after construction are only visible +// after an explicit [Packed.Refresh]. // -// var ( -// _ store.ObjectReader = (*Packed)(nil) -// _ store.PackWriter = (*Packed)(nil) -// ) +// Labels: Close-Caller. +type Packed struct { + // root is the objects/pack directory + // used for all pack and index file access. + root *os.Root + + // objectFormat is the expected object format for lookups. + objectFormat id.ObjectFormat + + // order contains the packs to probe, MRU-first. + order *mru.Order[*pack] + + // baseCache caches delta bases consumed during resolution. + baseCache *clock.Clock[baseKey, cachedBase] + + // refreshMu serializes Refresh. + // Readers uses none of these. + refreshMu sync.Mutex + + // byName supports reusing surviving packs across Refresh, + // and retired holds dropped packs until Close, + // since concurrent readers may still use them. + byName map[string]*pack + + retired []*pack +} + +var _ store.ObjectReader = (*Packed)(nil) + +// New creates a packed-object store rooted at an objects/pack directory, +// performing an initial Refresh. // -// // New creates a packed-object store rooted at an objects/pack directory. -// // -// // Labels: Deps-Borrowed, Life-Parent. -// func New(root *os.Root, objectFormat id.ObjectFormat) (*Packed, error) +// Labels: Deps-Borrowed, Life-Parent. +func New(root *os.Root, objectFormat id.ObjectFormat) (*Packed, error) { + if objectFormat.Size() == 0 { + return nil, id.ErrInvalidObjectFormat + } + + packed := &Packed{ + root: root, + objectFormat: objectFormat, + order: mru.New[*pack](mru.Options{Interval: 48}), + baseCache: newBaseCache(), + refreshMu: sync.Mutex{}, + byName: nil, + retired: nil, + } + + err := packed.Refresh() + if err != nil { + return nil, err + } + + return packed, nil +} + +// Close releases mapped pack/index resources associated with the store. // -// // Close releases mapped pack/index resources associated with the store. -// // -// // Labels: MT-Unsafe. -// func (packed *Packed) Close() error +// Labels: MT-Unsafe. +func (packed *Packed) Close() error { + errs := make([]error, 0, len(packed.byName)+len(packed.retired)) + + for _, p := range packed.byName { + errs = append(errs, p.close()) + } + + for _, p := range packed.retired { + errs = append(errs, p.close()) + } + + packed.byName = nil + packed.retired = nil + + packed.baseCache.Clear() + + return errors.Join(errs...) +} |
