1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
//go:build darwin || aix || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris
package fastwalk
import (
"io/fs"
"os"
"sort"
"sync"
"github.com/charlievieth/fastwalk/internal/fmtdirent"
)
type unixDirent struct {
parent string
name string
typ fs.FileMode
depth uint32 // uint32 so that we can pack it next to typ
info *fileInfo
stat *fileInfo
}
func (d *unixDirent) Name() string { return d.name }
func (d *unixDirent) IsDir() bool { return d.typ.IsDir() }
func (d *unixDirent) Type() fs.FileMode { return d.typ }
func (d *unixDirent) Depth() int { return int(d.depth) }
func (d *unixDirent) String() string { return fmtdirent.FormatDirEntry(d) }
func (d *unixDirent) Info() (fs.FileInfo, error) {
info := loadFileInfo(&d.info)
info.once.Do(func() {
info.FileInfo, info.err = os.Lstat(d.parent + "/" + d.name)
})
return info.FileInfo, info.err
}
func (d *unixDirent) Stat() (fs.FileInfo, error) {
if d.typ&os.ModeSymlink == 0 {
return d.Info()
}
stat := loadFileInfo(&d.stat)
stat.once.Do(func() {
stat.FileInfo, stat.err = os.Stat(d.parent + "/" + d.name)
})
return stat.FileInfo, stat.err
}
func newUnixDirent(parent, name string, typ fs.FileMode, depth int) *unixDirent {
return &unixDirent{
parent: parent,
name: name,
typ: typ,
depth: uint32(depth),
}
}
func fileInfoToDirEntry(dirname string, fi fs.FileInfo) DirEntry {
info := &fileInfo{
FileInfo: fi,
}
info.once.Do(func() {})
return &unixDirent{
parent: dirname,
name: fi.Name(),
typ: fi.Mode().Type(),
info: info,
}
}
var direntSlicePool = sync.Pool{
New: func() any {
a := make([]*unixDirent, 0, 32)
return &a
},
}
func putDirentSlice(p *[]*unixDirent) {
if p != nil && cap(*p) <= 32*1024 /* 256Kb */ {
a := *p
for i := range a {
a[i] = nil
}
*p = a[:0]
direntSlicePool.Put(p)
}
}
func sortDirents(mode SortMode, dents []*unixDirent) {
if len(dents) <= 1 {
return
}
switch mode {
case SortLexical:
sort.Slice(dents, func(i, j int) bool {
return dents[i].name < dents[j].name
})
case SortFilesFirst:
sort.Slice(dents, func(i, j int) bool {
d1 := dents[i]
d2 := dents[j]
r1 := d1.typ.IsRegular()
r2 := d2.typ.IsRegular()
switch {
case r1 && !r2:
return true
case !r1 && r2:
return false
case !r1 && !r2:
// Both are not regular files: sort directories last
dd1 := d1.typ.IsDir()
dd2 := d2.typ.IsDir()
switch {
case !dd1 && dd2:
return true
case dd1 && !dd2:
return false
}
}
return d1.name < d2.name
})
case SortDirsFirst:
sort.Slice(dents, func(i, j int) bool {
d1 := dents[i]
d2 := dents[j]
dd1 := d1.typ.IsDir()
dd2 := d2.typ.IsDir()
switch {
case dd1 && !dd2:
return true
case !dd1 && dd2:
return false
case !dd1 && !dd2:
// Both are not directories: sort regular files first
r1 := d1.typ.IsRegular()
r2 := d2.typ.IsRegular()
switch {
case r1 && !r2:
return true
case !r1 && r2:
return false
}
}
return d1.name < d2.name
})
}
}
|