github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/columns/sort/sort.go (about) 1 // Copyright 2022-2023 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sort 16 17 import ( 18 "reflect" 19 "sort" 20 21 "golang.org/x/exp/constraints" 22 23 "github.com/inspektor-gadget/inspektor-gadget/pkg/columns" 24 ) 25 26 type columnSorter[T any] struct { 27 column *columns.Column[T] 28 order columns.Order 29 } 30 31 type ColumnSorterCollection[T any] struct { 32 sorters []*columnSorter[T] 33 } 34 35 func (csc *ColumnSorterCollection[T]) Sort(entries []*T) { 36 if len(entries) == 0 { 37 return 38 } 39 40 for _, s := range csc.sorters { 41 var sortFunc func(i, j int) bool 42 order := s.order 43 44 kind := s.column.Kind() 45 if s.column.HasCustomExtractor() { 46 kind = s.column.GetRaw(entries[0]).Kind() 47 } 48 49 switch kind { 50 case reflect.Int: 51 sortFunc = getLessFunc[int, T](entries, s.column, order) 52 case reflect.Int8: 53 sortFunc = getLessFunc[int8, T](entries, s.column, order) 54 case reflect.Int16: 55 sortFunc = getLessFunc[int16, T](entries, s.column, order) 56 case reflect.Int32: 57 sortFunc = getLessFunc[int32, T](entries, s.column, order) 58 case reflect.Int64: 59 sortFunc = getLessFunc[int64, T](entries, s.column, order) 60 case reflect.Uint: 61 sortFunc = getLessFunc[uint, T](entries, s.column, order) 62 case reflect.Uint8: 63 sortFunc = getLessFunc[uint8, T](entries, s.column, order) 64 case reflect.Uint16: 65 sortFunc = getLessFunc[uint16, T](entries, s.column, order) 66 case reflect.Uint32: 67 sortFunc = getLessFunc[uint32, T](entries, s.column, order) 68 case reflect.Uint64: 69 sortFunc = getLessFunc[uint64, T](entries, s.column, order) 70 case reflect.Float32: 71 sortFunc = getLessFunc[float32, T](entries, s.column, order) 72 case reflect.Float64: 73 sortFunc = getLessFunc[float64, T](entries, s.column, order) 74 case reflect.String: 75 sortFunc = getLessFunc[string, T](entries, s.column, order) 76 default: 77 continue 78 } 79 80 sort.SliceStable(entries, sortFunc) 81 } 82 } 83 84 // Prepare prepares a sorter collection that can be re-used for multiple calls to Sort() for efficiency. Filter rules 85 // will be applied from right to left (first rule has the highest priority). 86 func Prepare[T any](cols columns.ColumnMap[T], sortBy []string) *ColumnSorterCollection[T] { 87 valid, _ := FilterSortableColumns(cols, sortBy) 88 89 sorters := make([]*columnSorter[T], 0, len(sortBy)) 90 for i := len(valid) - 1; i >= 0; i-- { 91 sortField := valid[i] 92 // Handle ordering 93 order := columns.OrderAsc 94 if sortField[0] == '-' { 95 sortField = sortField[1:] 96 order = columns.OrderDesc 97 } 98 99 column, _ := cols.GetColumn(sortField) 100 101 sorters = append(sorters, &columnSorter[T]{ 102 column: column, 103 order: order, 104 }) 105 } 106 107 return &ColumnSorterCollection[T]{ 108 sorters: sorters, 109 } 110 } 111 112 // SortEntries sorts entries by applying the sortBy rules from right to left (first rule has the highest 113 // priority). The rules are strings containing the column names, optionally prefixed with "-" to switch to descending 114 // sort order. 115 func SortEntries[T any](cols columns.ColumnMap[T], entries []*T, sortBy []string) { 116 if entries == nil { 117 return 118 } 119 120 coll := Prepare(cols, sortBy) 121 coll.Sort(entries) 122 } 123 124 func getLessFunc[OT constraints.Ordered, T any](array []*T, column columns.ColumnInternals, order columns.Order) func(i, j int) bool { 125 fieldFunc := columns.GetFieldFuncExt[OT, T](column, true) 126 return func(i, j int) bool { 127 if array[i] == nil { 128 return false 129 } 130 if array[j] == nil { 131 return true 132 } 133 return !(fieldFunc(array[i]) < fieldFunc(array[j])) != order 134 } 135 } 136 137 // CanSortBy returns true, if all requested sortBy arguments can be used for sorting 138 // This is not the case for a virtual column, which has no underlying value type 139 func CanSortBy[T any](cols columns.ColumnMap[T], sortBy []string) bool { 140 valid, _ := FilterSortableColumns(cols, sortBy) 141 142 return len(valid) == len(sortBy) 143 } 144 145 // FilterSortableColumns returns two lists, one containing the valid column names 146 // and another containing the invalid column names. 147 func FilterSortableColumns[T any](cols columns.ColumnMap[T], sortBy []string) ([]string, []string) { 148 valid := make([]string, 0, len(sortBy)) 149 invalid := make([]string, 0) 150 151 for _, sortField := range sortBy { 152 if len(sortField) == 0 { 153 invalid = append(invalid, sortField) 154 continue 155 } 156 157 rawSortField := sortField 158 if rawSortField[0] == '-' { 159 rawSortField = rawSortField[1:] 160 } 161 162 column, ok := cols.GetColumn(rawSortField) 163 if !ok { 164 invalid = append(invalid, sortField) 165 continue 166 } 167 168 // Skip virtual columns, they have no underlying value to sort by 169 if column.IsVirtual() { 170 invalid = append(invalid, sortField) 171 continue 172 } 173 174 valid = append(valid, sortField) 175 } 176 177 return valid, invalid 178 }