Files
go-fuse/fs/inode_parents.go
Jakob UnterwurzacherandHan-Wen Nienhuys a90e1f463c fs: track most recent parent
Introduce the new struct inodeParents that wraps a map
and one special slot for the most recent parent. Unit tests
included.

Because the map is lazily initialized, we should save some
memory on the common single-parent case (= file with no hard links)
compared to before.

Benchmarking with gocryptfs shows no discernible change
in performance. fsstress testing with gocryptfs shows no issues.

TestStaleHardlinks the previous commit passes now.

Change-Id: I8d69093abc906addde751a9e70dbd78a3a61371a
2021-04-23 17:01:55 +00:00

102 lines
2.2 KiB
Go

// Copyright 2021 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
// inodeParents stores zero or more parents of an Inode,
// remembering which one is the most recent.
//
// No internal locking: the caller is responsible for preventing
// concurrent access.
type inodeParents struct {
// newest is the most-recently add()'ed parent.
// nil when we don't have any parents.
newest *parentData
// other are parents in addition to the newest.
// nil or empty when we have <= 1 parents.
other map[parentData]struct{}
}
// add adds a parent to the store.
func (p *inodeParents) add(n parentData) {
// one and only parent
if p.newest == nil {
p.newest = &n
}
// already known as `newest`
if *p.newest == n {
return
}
// old `newest` gets displaced into `other`
if p.other == nil {
p.other = make(map[parentData]struct{})
}
p.other[*p.newest] = struct{}{}
// new parent becomes `newest` (possibly moving up from `other`)
delete(p.other, n)
p.newest = &n
}
// get returns the most recent parent
// or nil if there is no parent at all.
func (p *inodeParents) get() *parentData {
return p.newest
}
// all returns all known parents
// or nil if there is no parent at all.
func (p *inodeParents) all() []parentData {
count := p.count()
if count == 0 {
return nil
}
out := make([]parentData, 0, count)
out = append(out, *p.newest)
for i := range p.other {
out = append(out, i)
}
return out
}
func (p *inodeParents) delete(n parentData) {
// We have zero parents, so we can't delete any.
if p.newest == nil {
return
}
// If it's not the `newest` it must be in `other` (or nowhere).
if *p.newest != n {
delete(p.other, n)
return
}
// We want to delete `newest`, but there is no other to replace it.
if len(p.other) == 0 {
p.newest = nil
return
}
// Move random entry from `other` over `newest`.
var i parentData
for i = range p.other {
p.newest = &i
break
}
delete(p.other, i)
}
func (p *inodeParents) clear() {
p.newest = nil
p.other = nil
}
func (p *inodeParents) count() int {
if p.newest == nil {
return 0
}
return 1 + len(p.other)
}
type parentData struct {
name string
parent *Inode
}