newt/builder/symbol_tree.go (209 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package builder import ( "fmt" "sort" "strings" "strconv" ) type Symbol struct { Name string Section string Size uint64 } type File struct { Name string Symbols map[string]*Symbol } type Folder struct { Name string Files map[string]*File Folders map[string]*Folder } type outputFormatter interface { Header(nameStr string, sizeStr string, percentStr string) string Container(level int, name string, size uint64, percent float64) string Symbol(level int, name string, size uint64, percent float64) string Separator() string } type outputFormatterDefault struct { indentStr string headerStr string symbolStr string } type outputFormatterDiffable struct { indentStr string headerStr string containerStr string symbolStr string } func newSymbolFormatterDefault() *outputFormatterDefault { return &outputFormatterDefault{ indentStr: " ", headerStr: "%-59s %9s %9s\n", symbolStr: "%-59s %9d %8.2f%%\n", } } func (fmtr *outputFormatterDefault) Header(nameStr string, sizeStr string, percentStr string) string { return fmt.Sprintf(fmtr.headerStr, nameStr, sizeStr, percentStr) } func (fmtr *outputFormatterDefault) Container(level int, name string, size uint64, percent float64) string { return fmtr.Symbol(level, name, size, percent) } func (fmtr *outputFormatterDefault) Symbol(level int, name string, size uint64, percent float64) string { return fmt.Sprintf(fmtr.symbolStr, strings.Repeat(fmtr.indentStr, level) + name, size, percent) } func (fmtr *outputFormatterDefault) Separator() string { // -1 is to cut \n return strings.Repeat("=", len(fmtr.Header("", "", "")) - 1) + "\n" } func newSymbolFormatterDiffable() *outputFormatterDiffable { return &outputFormatterDiffable{ indentStr: " ", headerStr: "%-70s %9s\n", containerStr: "%-70s\n", symbolStr: "%-70s %9d\n", } } func (fmtr *outputFormatterDiffable) Header(nameStr string, sizeStr string, percentStr string) string { return fmt.Sprintf(fmtr.headerStr, nameStr, sizeStr) } func (fmtr *outputFormatterDiffable) Container(level int, name string, size uint64, percent float64) string { return fmt.Sprintf(fmtr.containerStr, strings.Repeat(fmtr.indentStr, level) + name) } func (fmtr *outputFormatterDiffable) Symbol(level int, name string, size uint64, percent float64) string { return fmt.Sprintf(fmtr.symbolStr, strings.Repeat(fmtr.indentStr, level) + name, size) } func (fmtr *outputFormatterDiffable) Separator() string { // -1 is to cut \n return strings.Repeat("=", len(fmtr.Header("", "", "")) - 1) + "\n" } func newFolder(name string) *Folder { return &Folder{name, make(map[string]*File), make(map[string]*Folder)} } func newFile(name string) *File { return &File{name, make(map[string]*Symbol)} } func (f *File) sumSize() uint64 { var sum uint64 for _, symbol := range f.Symbols { sum += symbol.Size } return sum } func (f *Folder) sumSize() uint64 { var sum uint64 for _, folder := range f.Folders { sum += folder.sumSize() } for _, file := range f.Files { sum += file.sumSize() } return sum } func (f *Folder) getFolder(name string) *Folder { if nextF, ok := f.Folders[name]; ok { return nextF } else { f.Folders[name] = newFolder(name) return f.Folders[name] } return &Folder{} // cannot happen } func (f *Folder) getFile(name string) *File { if nextF, ok := f.Files[name]; ok { return nextF } else { f.Files[name] = newFile(name) return f.Files[name] } return &File{} // cannot happen } func (f *File) getSymbol(name string) *Symbol { if nextF, ok := f.Symbols[name]; ok { return nextF } else { f.Symbols[name] = &Symbol{name, "", 0} return f.Symbols[name] } return &Symbol{} // cannot happen } func (f *Folder) addFolder(path []string) *Folder { if len(path) == 1 { // last segment == new folder return f.getFolder(path[0]) } else { return f.getFolder(path[0]).addFolder(path[1:]) } } func (f *Folder) addFile(path []string) *File { if len(path) == 1 { // last segment == file return f.getFile(path[0]) } else { return f.getFolder(path[0]).addFile(path[1:]) } } func (f *Folder) addSymbol(symbol *Symbol, path string) *Symbol { segments := strings.Split(path, "/") file := f.addFile(segments) sym := file.getSymbol(symbol.Name) sym.Section = symbol.Section sym.Size += symbol.Size return sym } func (f *File) toString(fmtr outputFormatter, level int, total uint64) string { var str string if f.sumSize() <= 0 { return "" } size := f.sumSize() percent := 100 * float64(size) / float64(total) str += fmtr.Container(level, f.Name, size, percent) var sorted []string for symName := range f.Symbols { sorted = append(sorted, symName) } sort.Strings(sorted) for _, sym := range sorted { size := f.Symbols[sym].Size percent := 100 * float64(size) / float64(total) if f.Symbols[sym].Size > 0 { str += fmtr.Symbol(level + 1, f.Symbols[sym].Name, size, percent) } } return str } func (f *Folder) toString(fmtr outputFormatter, level int, total uint64) string { var str string var sorted []string for folderName := range f.Folders { sorted = append(sorted, folderName) } for fileName := range f.Files { sorted = append(sorted, fileName) } sort.Strings(sorted) for _, name := range sorted { if folder, ok := f.Folders[name]; ok { size := folder.sumSize() percent := 100 * float64(size) / float64(total) str += fmtr.Container(level, folder.Name, size, percent) str += folder.toString(fmtr, level+1, total) } else { str += f.Files[name].toString(fmtr, level, total) } } return str } func (f *Folder) ToString(total uint64, diffFriendly bool) string { var str string var fmtr outputFormatter if diffFriendly { fmtr = newSymbolFormatterDiffable() } else { fmtr = newSymbolFormatterDefault() } str += fmtr.Header("Path", "Size", "%") str += fmtr.Separator() str += f.toString(fmtr, 0, total) str += fmtr.Separator() str += fmtr.Header("Total symbol size (i.e. excluding padding, etc.)", strconv.FormatUint(f.sumSize(), 10), "") return str }