github.com/charlievieth/fastwalk@v1.0.3/entry_filter_unix.go (about) 1 //go:build (linux || darwin || freebsd || openbsd || netbsd || !windows) && !appengine 2 // +build linux darwin freebsd openbsd netbsd !windows 3 // +build !appengine 4 5 package fastwalk 6 7 import ( 8 "io/fs" 9 "sync" 10 "syscall" 11 ) 12 13 type fileKey struct { 14 Dev uint64 15 Ino uint64 16 } 17 18 type entryMap struct { 19 mu sync.Mutex 20 keys map[fileKey]struct{} 21 } 22 23 // An EntryFilter keeps track of visited directory entries and can be used to 24 // detect and avoid symlink loops or processing the same file twice. 25 type EntryFilter struct { 26 // Use an array of 8 to reduce lock contention. The entryMap is 27 // picked via the inode number. We don't take the device number 28 // into account because: we don't expect to see many of them and 29 // uniformly spreading the load isn't terribly beneficial here. 30 ents [8]entryMap 31 } 32 33 // NewEntryFilter returns a new EntryFilter 34 func NewEntryFilter() *EntryFilter { 35 return new(EntryFilter) 36 } 37 38 func (e *EntryFilter) seen(dev, ino uint64) (seen bool) { 39 m := &e.ents[ino%uint64(len(e.ents))] 40 m.mu.Lock() 41 if _, seen = m.keys[fileKey{dev, ino}]; !seen { 42 if m.keys == nil { 43 m.keys = make(map[fileKey]struct{}) 44 } 45 m.keys[fileKey{dev, ino}] = struct{}{} 46 } 47 m.mu.Unlock() 48 return seen 49 } 50 51 // TODO: this name is confusing and should be fixed 52 53 // Entry returns if path and fs.DirEntry have been seen before. 54 func (e *EntryFilter) Entry(path string, de fs.DirEntry) (seen bool) { 55 fi, err := StatDirEntry(path, de) 56 if err != nil { 57 return true // treat errors as duplicate files 58 } 59 stat := fi.Sys().(*syscall.Stat_t) 60 return e.seen(uint64(stat.Dev), uint64(stat.Ino)) 61 }