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  }