github.com/network-quality/goresponsiveness@v0.0.0-20240129151524-343954285090/utilities/utilities.go (about) 1 /* 2 * This file is part of Go Responsiveness. 3 * 4 * Go Responsiveness is free software: you can redistribute it and/or modify it under 5 * the terms of the GNU General Public License as published by the Free Software Foundation, 6 * either version 2 of the License, or (at your option) any later version. 7 * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY 8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 9 * PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 * 11 * You should have received a copy of the GNU General Public License along 12 * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>. 13 */ 14 15 package utilities 16 17 import ( 18 "context" 19 "fmt" 20 "math" 21 "math/rand" 22 "os" 23 "reflect" 24 "strings" 25 "sync" 26 "sync/atomic" 27 "time" 28 ) 29 30 // GitVersion is the Git revision hash 31 var GitVersion = "dev" 32 33 func Iota(low int, high int) (made []int) { 34 made = make([]int, high-low) 35 for counter := low; counter < high; counter++ { 36 made[counter-low] = counter 37 } 38 return 39 } 40 41 func IsInterfaceNil(ifc interface{}) bool { 42 return ifc == nil || 43 (reflect.ValueOf(ifc).Kind() == reflect.Ptr && reflect.ValueOf(ifc).IsNil()) 44 } 45 46 func Conditional[T any](condition bool, t T, f T) T { 47 if condition { 48 return t 49 } 50 return f 51 } 52 53 func ToMbps(bytes float64) float64 { 54 return ToMBps(bytes) * float64(8) 55 } 56 57 func ToMBps(bytes float64) float64 { 58 return float64(bytes) / float64(1024*1024) 59 } 60 61 type MeasurementResult struct { 62 Delay time.Duration 63 MeasurementCount uint16 64 Err error 65 } 66 67 func SeekForAppend(file *os.File) (err error) { 68 _, err = file.Seek(0, 2) 69 return 70 } 71 72 var GenerateUniqueId func() uint64 = func() func() uint64 { 73 var nextConnectionId uint64 = 0 74 return func() uint64 { 75 return atomic.AddUint64(&nextConnectionId, 1) 76 } 77 }() 78 79 type Optional[S any] struct { 80 value S 81 some bool 82 } 83 84 func Some[S any](value S) Optional[S] { 85 return Optional[S]{value: value, some: true} 86 } 87 88 func None[S any]() Optional[S] { 89 return Optional[S]{some: false} 90 } 91 92 func IsNone[S any](optional Optional[S]) bool { 93 return !optional.some 94 } 95 96 func IsSome[S any](optional Optional[S]) bool { 97 return optional.some 98 } 99 100 func GetSome[S any](optional Optional[S]) S { 101 if !optional.some { 102 panic("Attempting to access Some of a None.") 103 } 104 return optional.value 105 } 106 107 func (optional Optional[S]) String() string { 108 if IsSome(optional) { 109 return fmt.Sprintf("Some: %v", optional.some) 110 } else { 111 return "None" 112 } 113 } 114 115 func RandBetween(max int) int { 116 return rand.New(rand.NewSource(int64(time.Now().Nanosecond()))).Int() % max 117 } 118 119 func ChannelToSlice[S any](channel <-chan S) (slice []S) { 120 slice = make([]S, 0) 121 for element := range channel { 122 slice = append(slice, element) 123 } 124 return 125 } 126 127 func Reverse[T any](elements []T) []T { 128 result := make([]T, len(elements)) 129 iterator := len(elements) - 1 130 for _, v := range elements { 131 result[iterator] = v 132 iterator-- 133 } 134 return result 135 } 136 137 func Filter[S any](elements []S, filterer func(S) bool) []S { 138 result := make([]S, 0) 139 for _, s := range elements { 140 if filterer(s) { 141 result = append(result, s) 142 } 143 } 144 return result 145 } 146 147 func Fmap[S any, F any](elements []S, mapper func(S) F) []F { 148 result := make([]F, 0) 149 for _, s := range elements { 150 result = append(result, mapper(s)) 151 } 152 return result 153 } 154 155 func OrTimeout(f func(), timeout time.Duration) { 156 completeChannel := func() chan interface{} { 157 completed := make(chan interface{}) 158 go func() { 159 // This idea taken from https://stackoverflow.com/a/32843750. 160 // Closing the channel will let the read of it proceed immediately. 161 // Making that operation a defer ensures that it will happen even if 162 // the function f panics during its execution. 163 defer close(completed) 164 f() 165 }() 166 return completed 167 }() 168 select { 169 case <-completeChannel: 170 break 171 case <-time.After(timeout): 172 break 173 } 174 } 175 176 func FilenameAppend(filename, appendage string) string { 177 pieces := strings.SplitN(filename, ".", 2) 178 result := pieces[0] + appendage 179 if len(pieces) > 1 { 180 result = result + "." + strings.Join(pieces[1:], ".") 181 } 182 return result 183 } 184 185 func ApproximatelyEqual[T float32 | float64](truth T, maybe T, fudge T) bool { 186 bTruth := float64(truth) 187 bMaybe := float64(maybe) 188 bFudge := float64(fudge) 189 diff := math.Abs((bTruth - bMaybe)) 190 return diff < bFudge 191 } 192 193 func UserAgent() string { 194 return fmt.Sprintf("goresponsiveness/%s", GitVersion) 195 } 196 197 func WaitWithContext(ctxt context.Context, condition *func() bool, mu *sync.Mutex, c *sync.Cond) bool { 198 mu.Lock() 199 for !(*condition)() && ctxt.Err() == nil { 200 c.Wait() 201 } 202 return ctxt.Err() == nil 203 } 204 205 func ContextSignaler(ctxt context.Context, st time.Duration, condition *func() bool, c *sync.Cond) { 206 for !(*condition)() && ctxt.Err() == nil { 207 time.Sleep(st) 208 } 209 if ctxt.Err() != nil { 210 c.Broadcast() 211 return 212 } 213 } 214 215 type Pair[T1, T2 any] struct { 216 First T1 217 Second T2 218 } 219 220 func PerSecondToInterval(rate int64) time.Duration { 221 return time.Duration(time.Second.Nanoseconds() / rate) 222 } 223 224 func IndentOutput(output string, depth uint, character string) string { 225 finalNewline := false 226 if strings.LastIndex(output, "\n") == len(output)-1 { 227 finalNewline = true 228 output = strings.TrimSuffix(output, "\n") 229 } 230 indentedOutput := strings.Join(Fmap[string](strings.SplitAfter(output, "\n"), func(line string) string { 231 return strings.Repeat(character, int(depth)) + line 232 }), "") 233 if finalNewline { 234 indentedOutput += "\n" 235 } 236 return indentedOutput 237 }