1package fastwalk
2
3import (
4 "io/fs"
5 "os"
6 "sync"
7 "sync/atomic"
8 "syscall"
9 "unsafe"
10)
11
12type fileInfo struct {
13 once sync.Once
14 fs.FileInfo
15 err error
16}
17
18func loadFileInfo(pinfo **fileInfo) *fileInfo {
19 ptr := (*unsafe.Pointer)(unsafe.Pointer(pinfo))
20 fi := (*fileInfo)(atomic.LoadPointer(ptr))
21 if fi == nil {
22 fi = &fileInfo{}
23 if !atomic.CompareAndSwapPointer(
24 (*unsafe.Pointer)(unsafe.Pointer(pinfo)), // adrr
25 nil, // old
26 unsafe.Pointer(fi), // new
27 ) {
28 fi = (*fileInfo)(atomic.LoadPointer(ptr))
29 }
30 }
31 return fi
32}
33
34// StatDirEntry returns a [fs.FileInfo] describing the named file ([os.Stat]).
35// If de is a [fastwalk.DirEntry] its Stat method is used and the returned
36// FileInfo may be cached from a prior call to Stat. If a cached result is not
37// desired, users should just call [os.Stat] directly.
38//
39// This is a helper function for calling Stat on the DirEntry passed to the
40// walkFn argument to [Walk].
41//
42// The path argument is only used if de is not of type [fastwalk.DirEntry].
43// Therefore, de should be the DirEntry describing path.
44func StatDirEntry(path string, de fs.DirEntry) (fs.FileInfo, error) {
45 if de == nil {
46 return nil, &os.PathError{Op: "stat", Path: path, Err: syscall.EINVAL}
47 }
48 if de.Type()&os.ModeSymlink == 0 {
49 return de.Info()
50 }
51 if d, ok := de.(DirEntry); ok {
52 return d.Stat()
53 }
54 return os.Stat(path)
55}
56
57// DirEntryDepth returns the depth at which entry de was generated relative
58// to the root being walked or -1 if de does not have type [fastwalk.DirEntry].
59//
60// This is a helper function that saves the user from having to cast the
61// [fs.DirEntry] argument to their walk function to a [fastwalk.DirEntry]
62// and is equivalent to the below code:
63//
64// if d, _ := de.(DirEntry); d != nil {
65// return d.Depth()
66// }
67// return -1
68func DirEntryDepth(de fs.DirEntry) int {
69 if d, _ := de.(DirEntry); d != nil {
70 return d.Depth()
71 }
72 return -1
73}