ncdu: refactor accumulated attributes into a struct

This commit is contained in:
albertony 2022-04-11 17:06:01 +02:00
parent a15885dd74
commit 97606bbdef
2 changed files with 68 additions and 47 deletions

View file

@ -299,9 +299,9 @@ func (u *UI) biggestEntry() (biggest int64) {
return return
} }
for i := range u.entries { for i := range u.entries {
size, _, _, _, _, _, _ := u.d.AttrI(u.sortPerm[i]) attrs, _ := u.d.AttrI(u.sortPerm[i])
if size > biggest { if attrs.Size > biggest {
biggest = size biggest = attrs.Size
} }
} }
return return
@ -313,8 +313,8 @@ func (u *UI) hasEmptyDir() bool {
return false return false
} }
for i := range u.entries { for i := range u.entries {
_, count, _, isDir, _, _, _ := u.d.AttrI(u.sortPerm[i]) attrs, _ := u.d.AttrI(u.sortPerm[i])
if isDir && count == 0 { if attrs.IsDir && attrs.Count == 0 {
return true return true
} }
} }
@ -359,9 +359,9 @@ func (u *UI) Draw() error {
if y >= h-1 { if y >= h-1 {
break break
} }
size, count, countUnknownSize, isDir, readable, entriesHaveErrors, err := u.d.AttrI(u.sortPerm[n]) attrs, err := u.d.AttrI(u.sortPerm[n])
fg := termbox.ColorWhite fg := termbox.ColorWhite
if entriesHaveErrors { if attrs.EntriesHaveErrors {
fg = termbox.ColorYellow fg = termbox.ColorYellow
} }
if err != nil { if err != nil {
@ -372,19 +372,19 @@ func (u *UI) Draw() error {
fg, bg = bg, fg fg, bg = bg, fg
} }
mark := ' ' mark := ' '
if isDir { if attrs.IsDir {
mark = '/' mark = '/'
} }
fileFlag := ' ' fileFlag := ' '
message := "" message := ""
if !readable { if !attrs.Readable {
message = " [not read yet]" message = " [not read yet]"
} }
if countUnknownSize > 0 { if attrs.CountUnknownSize > 0 {
message = fmt.Sprintf(" [%d of %d files have unknown size, size may be underestimated]", countUnknownSize, count) message = fmt.Sprintf(" [%d of %d files have unknown size, size may be underestimated]", attrs.CountUnknownSize, attrs.Count)
fileFlag = '~' fileFlag = '~'
} }
if entriesHaveErrors { if attrs.EntriesHaveErrors {
message = " [some subdirectories could not be read, size may be underestimated]" message = " [some subdirectories could not be read, size may be underestimated]"
fileFlag = '.' fileFlag = '.'
} }
@ -394,35 +394,29 @@ func (u *UI) Draw() error {
} }
extras := "" extras := ""
if u.showCounts { if u.showCounts {
ss := operations.CountStringField(count, u.humanReadable, 9) + " " ss := operations.CountStringField(attrs.Count, u.humanReadable, 9) + " "
if count > 0 { if attrs.Count > 0 {
extras += ss extras += ss
} else { } else {
extras += strings.Repeat(" ", len(ss)) extras += strings.Repeat(" ", len(ss))
} }
} }
var averageSize float64
if count > 0 {
countForAverage := count - countUnknownSize
if countForAverage > 0 {
averageSize = float64(size) / float64(countForAverage)
}
}
if u.showDirAverageSize { if u.showDirAverageSize {
ss := operations.SizeStringField(int64(averageSize), u.humanReadable, 9) + " " avg := attrs.AverageSize()
if averageSize > 0 { ss := operations.SizeStringField(int64(avg), u.humanReadable, 9) + " "
if avg > 0 {
extras += ss extras += ss
} else { } else {
extras += strings.Repeat(" ", len(ss)) extras += strings.Repeat(" ", len(ss))
} }
} }
if showEmptyDir { if showEmptyDir {
if isDir && count == 0 && fileFlag == ' ' { if attrs.IsDir && attrs.Count == 0 && fileFlag == ' ' {
fileFlag = 'e' fileFlag = 'e'
} }
} }
if u.showGraph { if u.showGraph {
bars := (size + perBar/2 - 1) / perBar bars := (attrs.Size + perBar/2 - 1) / perBar
// clip if necessary - only happens during startup // clip if necessary - only happens during startup
if bars > 10 { if bars > 10 {
bars = 10 bars = 10
@ -431,7 +425,7 @@ func (u *UI) Draw() error {
} }
extras += "[" + graph[graphBars-bars:2*graphBars-bars] + "] " extras += "[" + graph[graphBars-bars:2*graphBars-bars] + "] "
} }
Linef(0, y, w, fg, bg, ' ', "%c %s %s%c%s%s", fileFlag, operations.SizeStringField(size, u.humanReadable, 12), extras, mark, path.Base(entry.Remote()), message) Linef(0, y, w, fg, bg, ' ', "%c %s %s%c%s%s", fileFlag, operations.SizeStringField(attrs.Size, u.humanReadable, 12), extras, mark, path.Base(entry.Remote()), message)
y++ y++
} }
} }
@ -582,14 +576,14 @@ type ncduSort struct {
// Less is part of sort.Interface. // Less is part of sort.Interface.
func (ds *ncduSort) Less(i, j int) bool { func (ds *ncduSort) Less(i, j int) bool {
var iAvgSize, jAvgSize float64 var iAvgSize, jAvgSize float64
isize, icount, _, _, _, _, _ := ds.d.AttrI(ds.sortPerm[i]) iattrs, _ := ds.d.AttrI(ds.sortPerm[i])
jsize, jcount, _, _, _, _, _ := ds.d.AttrI(ds.sortPerm[j]) jattrs, _ := ds.d.AttrI(ds.sortPerm[j])
iname, jname := ds.entries[ds.sortPerm[i]].Remote(), ds.entries[ds.sortPerm[j]].Remote() iname, jname := ds.entries[ds.sortPerm[i]].Remote(), ds.entries[ds.sortPerm[j]].Remote()
if icount > 0 { if iattrs.Count > 0 {
iAvgSize = float64(isize / icount) iAvgSize = iattrs.AverageSize()
} }
if jcount > 0 { if jattrs.Count > 0 {
jAvgSize = float64(jsize / jcount) jAvgSize = jattrs.AverageSize()
} }
switch { switch {
@ -598,33 +592,33 @@ func (ds *ncduSort) Less(i, j int) bool {
case ds.u.sortByName > 0: case ds.u.sortByName > 0:
break break
case ds.u.sortBySize < 0: case ds.u.sortBySize < 0:
if isize != jsize { if iattrs.Size != jattrs.Size {
return isize < jsize return iattrs.Size < jattrs.Size
} }
case ds.u.sortBySize > 0: case ds.u.sortBySize > 0:
if isize != jsize { if iattrs.Size != jattrs.Size {
return isize > jsize return iattrs.Size > jattrs.Size
} }
case ds.u.sortByCount < 0: case ds.u.sortByCount < 0:
if icount != jcount { if iattrs.Count != jattrs.Count {
return icount < jcount return iattrs.Count < jattrs.Count
} }
case ds.u.sortByCount > 0: case ds.u.sortByCount > 0:
if icount != jcount { if iattrs.Count != jattrs.Count {
return icount > jcount return iattrs.Count > jattrs.Count
} }
case ds.u.sortByAverageSize < 0: case ds.u.sortByAverageSize < 0:
if iAvgSize != jAvgSize { if iAvgSize != jAvgSize {
return iAvgSize < jAvgSize return iAvgSize < jAvgSize
} }
// if avgSize is equal, sort by size // if avgSize is equal, sort by size
return isize < jsize return iattrs.Size < jattrs.Size
case ds.u.sortByAverageSize > 0: case ds.u.sortByAverageSize > 0:
if iAvgSize != jAvgSize { if iAvgSize != jAvgSize {
return iAvgSize > jAvgSize return iAvgSize > jAvgSize
} }
// if avgSize is equal, sort by size // if avgSize is equal, sort by size
return isize > jsize return iattrs.Size > jattrs.Size
} }
// if everything equal, sort by name // if everything equal, sort by name
return iname < jname return iname < jname

View file

@ -25,6 +25,33 @@ type Dir struct {
entriesHaveErrors bool entriesHaveErrors bool
} }
// Attrs contains accumulated properties for a directory entry
//
// Files with unknown size are counted separately but also included
// in the total count. They are not included in the size, i.e. treated
// as empty files, which means the size may be underestimated.
type Attrs struct {
Size int64
Count int64
CountUnknownSize int64
IsDir bool
Readable bool
EntriesHaveErrors bool
}
// AverageSize calculates average size of files in directory
//
// If there are files with unknown size, this returns the average over
// files with known sizes, which means it may be under- or
// overestimated.
func (a *Attrs) AverageSize() float64 {
countKnownSize := a.Count - a.CountUnknownSize
if countKnownSize > 0 {
return float64(a.Size) / float64(countKnownSize)
}
return 0
}
// Parent returns the directory above this one // Parent returns the directory above this one
func (d *Dir) Parent() *Dir { func (d *Dir) Parent() *Dir {
// no locking needed since these are write once in newDir() // no locking needed since these are write once in newDir()
@ -167,19 +194,19 @@ func (d *Dir) Attr() (size int64, count int64) {
} }
// AttrI returns the size, count and flags for the i-th directory entry // AttrI returns the size, count and flags for the i-th directory entry
func (d *Dir) AttrI(i int) (size int64, count int64, countUnknownSize int64, isDir bool, readable bool, entriesHaveErrors bool, err error) { func (d *Dir) AttrI(i int) (attrs Attrs, err error) {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
subDir, isDir := d.getDir(i) subDir, isDir := d.getDir(i)
if !isDir { if !isDir {
return d.entries[i].Size(), 0, 0, false, true, d.entriesHaveErrors, d.readError return Attrs{d.entries[i].Size(), 0, 0, false, true, d.entriesHaveErrors}, d.readError
} }
if subDir == nil { if subDir == nil {
return 0, 0, 0, true, false, false, nil return Attrs{0, 0, 0, true, false, false}, nil
} }
size, count = subDir.Attr() size, count := subDir.Attr()
return size, count, subDir.countUnknownSize, true, true, subDir.entriesHaveErrors, subDir.readError return Attrs{size, count, subDir.countUnknownSize, true, true, subDir.entriesHaveErrors}, subDir.readError
} }
// Scan the Fs passed in, returning a root directory channel and an // Scan the Fs passed in, returning a root directory channel and an