util

package
v8.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 30, 2024 License: Apache-2.0, BSD-2-Clause, BSD-3-Clause, + 1 more Imports: 22 Imported by: 0

Documentation

Overview

Package util contains miscellaneous utilities helpful for ATC components.

One of the most popular class of utility offered by this package is the set of functions that yield references to their arguments. This allows for one-line assignment of pointers to most primitive types.

myInt := new(int)
*myInt = 5

... can be written on one line using IntPtr like so:

myInt := IntPtr(5)

Package util contains various miscellaneous utilities that are helpful throughout components and aren't necessarily related to ATC data structures.

Index

Examples

Constants

View Source
const BitsPerByte = 8

BitsPerByte is the number of bits in a byte.

View Source
const ConstantBackoffDuration = 30 * time.Second

ConstantBackoffDuration is a fallback duration that may be used by an application along with NewConstantBackoff().

View Source
const DefaultFactor = 2.0

DefaultFactor may be used by applications for the factor argument to NewBackoff.

View Source
const MSPerNS = int64(1000000)

MSPerNS is the number of *nanoseconds* in a *millisecond* - not the other way around, as the name might imply.

Variables

This section is empty.

Functions

func AESDecrypt

func AESDecrypt(bytesToDecrypt []byte, aesKey []byte) ([]byte, error)

AESDecrypt takes in a 16, 24, or 32 byte AES key (128, 192, 256 bit encryption respectively) and encrypted text. It returns the resulting decrypted text. In case of error, the text returned is an empty string. AES requires input text to be greater than 12 bytes in length.

func AESEncrypt

func AESEncrypt(bytesToEncrypt []byte, aesKey []byte) ([]byte, error)

AESEncrypt takes in a 16, 24, or 32 byte AES key (128, 192, 256 bit encryption respectively) and plain text. It returns the encrypted text. In case of error, the text returned is an empty string. AES requires input text to be greater than 12 bytes in length.

func BoolPtr

func BoolPtr(b bool) *bool

BoolPtr returns a pointer to the given boolean.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := BoolPtr(true)
fmt.Println(*ptr)
Output:

true

func BytesLenSplit

func BytesLenSplit(s []byte, n int) [][]byte

BytesLenSplit splits the given byte array into an n-length arrays. If n > len(s), returns a slice with a single []byte containing all of s. If n <= 0, returns an empty slice.

func CIDRIsSubset

func CIDRIsSubset(na *net.IPNet, nb *net.IPNet) bool

CIDRIsSubset returns whether na is a subset (possibly improper) of nb.

func CamelToSnakeCase

func CamelToSnakeCase(s string) string

CamelToSnakeCase returns a case transformation of the input string from assumed "camelCase" (or "PascalCase") to "snake_case".

Note that the transformation applied to strings that contain non-"word" characters is undefined. Also, this doesn't handle names that contain abbreviations, initialisms, and/or acronyms very well in general, like e.g. "IPAddress".

Example
camel := "camelCase"
fmt.Println(CamelToSnakeCase(camel))
camel = "PascalCase"
fmt.Println(CamelToSnakeCase(camel))
camel = "IPIsAnInitialismForInternetProtocol"
fmt.Println(CamelToSnakeCase(camel))
Output:

camel_case
pascal_case
ipis_an_initialism_for_internet_protocol

func Coalesce

func Coalesce[T any](p *T, def T) T

Coalesce coalesces the given pointer to a concrete value. This is basically the inverse operation of Ptr - it safely dereferences its input. If the pointer is nil, def is returned as a default value.

Example
var i *int
fmt.Println(Coalesce(i, 9000))

s := Ptr("testquest")
fmt.Println(Coalesce(s, "9001"))
Output:

9000
testquest

func CoalesceCIDRs

func CoalesceCIDRs(cidrs []*net.IPNet, coalesceNumber int, coalesceMaskLen int) []*net.IPNet

CoalesceCIDRs coalesces cidrs into a smaller set of CIDRs, by combining overlapping networks into networks of size coalesceMaskLen, if there are at least coalesceNumber cidrs in the larger mask.

func CoalesceIPs

func CoalesceIPs(ips []net.IP, coalesceNumber int, coalesceMaskLen int) []*net.IPNet

CoalesceIPs combines ips into CIDRs, by combining overlapping networks into networks of size coalesceMaskLen, if there are at least coalesceNumber IPs in the larger mask.

func CoalesceToDefault

func CoalesceToDefault[T any](p *T) T

CoalesceToDefault coalesces a pointer to the type to which it points. It returns the "zero value" of its input's pointed-to type when the input is nil. This is equivalent to:

var x T
result := Coalesce(p, x)

... but can be done on one line without knowing the type of `p`.

Example
var i *int
fmt.Println(CoalesceToDefault(i))

s := Ptr("testquest")
fmt.Println(CoalesceToDefault(s))
Output:

0
testquest

func ContainsStr

func ContainsStr(a []string, x string) bool

ContainsStr returns whether x is one of the elements of a.

Example
arr := []string{"test", "quest"}
fmt.Println(ContainsStr(arr, "test"))
fmt.Println(ContainsStr(arr, "foo"))
Output:

true
false

func ConvertTimeFormat

func ConvertTimeFormat(t time.Time, format string) (*time.Time, error)

ConvertTimeFormat converts the input time to the supplied format.

func CopyIfNotNil

func CopyIfNotNil[T any](p *T) *T

CopyIfNotNil makes a deep copy of p - unless it's nil, in which case it just returns nil.

Example
var i *int
fmt.Println(CopyIfNotNil(i))

s := Ptr("9000")
fmt.Println(*CopyIfNotNil(s))
Output:

<nil>
9000

func CopyMap

func CopyMap[T comparable, U any](original map[T]U) map[T]U

CopyMap makes a "deep-ish" copy of the map passed to it. This will only deeply copy the map itself; this means that if the map values are references or structures containing references, those are only being shallowly copied!

Example
original := map[string]string{
	"foo": "bar",
}

copied := CopyMap(original)
fmt.Println(copied["foo"])

original["foo"] = "foo"
fmt.Println(copied["foo"])

original["test"] = "quest"
fmt.Println(len(copied))
Output:

bar
bar
1

func ErrsToStrs

func ErrsToStrs(errs []error) []string

ErrsToStrs converts a slice of errors to a slice of their string representations.

Example
errs := []error{
	errors.New("test"),
	errors.New("quest"),
}
strs := ErrsToStrs(errs)
fmt.Println(strs[0])
fmt.Println(strs[1])
Output:

test
quest

func FirstIP

func FirstIP(ipn *net.IPNet) net.IP

FirstIP returns the first IP in the CIDR. For example, The CIDR 192.0.2.0/24 returns 192.0.2.0.

func FloatPtr

func FloatPtr(f float64) *float64

FloatPtr returns a pointer to the given 64-bit floating-point number.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := FloatPtr(5.0)
fmt.Println(*ptr)
Output:

5

func HashInts

func HashInts(ints []int, sortIntsBeforeHashing bool) []byte

HashInts returns a SHA512 hash of ints. If sortIntsBeforeHashing, the ints are sorted before before hashing. Sorting is done in a copy, the input ints slice is not modified.

func IP4InRange

func IP4InRange(ip, ipRange string) (bool, error)

IP4InRange checks if the given string IP address falls within the specified hyphen-delimited range.

The range should be of the form "start-end" e.g. "192.0.2.0-192.0.2.255". If either the input IP address or either end of this range fail to parse as IP addresses - or if the range is malformed - an error is returned.

The behavior of this utility is undefined if the start of the IP range does not encode via IP4ToNum to a lower number than the end of the range.

func IP4ToNum

func IP4ToNum(ip string) (uint32, error)

IP4ToNum converts the passed string to a 32-bit unsigned integer where each byte that makes up the number is one of the bytes of the IPv4 address.

The address is encoded with each byte left-to-right making up the most-to-least significant bytes in the resulting number. If the passed string cannot be parsed as an IPv4 address in standard notation, an error is returned.

func IPToCIDR

func IPToCIDR(ip net.IP) *net.IPNet

IPToCIDR returns the CIDR containing just the given IP. For IPv6, this means /128, for IPv4, /32.

func Int64Ptr

func Int64Ptr(i int64) *int64

Int64Ptr returns a pointer to the given 64-bit integer.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := Int64Ptr(5)
fmt.Println(*ptr)
Output:

5

func IntPtr

func IntPtr(i int) *int

IntPtr returns a pointer to the given integer.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := IntPtr(5)
fmt.Println(*ptr)
Output:

5

func IntSliceToMap

func IntSliceToMap(s []int) map[int]struct{}

IntSliceToMap creates an int set from an array.

Example
ints := []int{1, 2, 3}
fmt.Printf("%+v", IntSliceToMap(ints))
Output:

map[1:{} 2:{} 3:{}]

func InterfacePtr

func InterfacePtr(i any) *any

InterfacePtr returns a pointer to the given empty interface.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := InterfacePtr(1 + 2i)
fmt.Println(*ptr)
Output:

(1+2i)

func JoinErrs

func JoinErrs(errs []error) error

JoinErrs joins the passed errors into a single error with a message that is a combination of their error messages.

This is equivalent to calling JoinErrsSep(errs, "").

func JoinErrsSep

func JoinErrsSep(errs []error, separator string) error

JoinErrsSep joins the passed errors into a single error with a message that is a combination of their error messages, joined by the given separator.

If the given separator is an empty string, the default (", ") is used.

Note that this DOES NOT preserve error identity. For example:

err := JoinErrsSep([]error{sql.ErrNoRows, errors.New("foo")})
fmt.Println(errors.Is(err, sql.ErrNoRows))
// Output: false
Example
errs := []error{
	errors.New("test"),
	errors.New("quest"),
}

fmt.Println(JoinErrsSep(errs, "\n"))
Output:

test
quest

func JoinErrsStr

func JoinErrsStr(errs []error) string

JoinErrsStr returns a string representation of all of the passed errors.

This is equivalent to calling JoinErrs(errs).Error(), but in the case that JoinErrs returns nil that would panic. This checks for that case and instead returns an empty string.

Example
errs := []error{
	errors.New("test"),
	errors.New("quest"),
}

fmt.Println(JoinErrsStr(errs))
fmt.Println(JoinErrsStr(nil))
Output:

test, quest

func LastIP

func LastIP(ipn *net.IPNet) net.IP

LastIP returns the last IP in the CIDR. For example, The CIDR 192.0.2.0/24 returns 192.0.2.255.

func Ptr

func Ptr[T any](v T) *T

Ptr returns a pointer to the given value.

Example
ptr := Ptr("testquest")
fmt.Println(*ptr)
Output:

testquest

func RangeStr

func RangeStr(ipn *net.IPNet) string

RangeStr returns the hyphenated range of IPs. For example, The CIDR 192.0.2.0/24 returns "192.0.2.0-192.0.2.255".

func RemoveStrDuplicates

func RemoveStrDuplicates(inputStrings []string, seenStrings map[string]struct{}) ([]string, map[string]struct{})

RemoveStrDuplicates removes duplicates from strings, considering a map of already-seen duplicates. Returns the strings which are unique, and not already present in seen; and a map of the unique strings in inputStrings and seenStrings.

This can be used, for example, to remove duplicates from multiple lists of strings, in order, using a shared map of seen strings.

Example
strs := []string{
	"test",
	"quest",
	"foo",
	"test",
	"foo",
	"bar",
}
unDuped, _ := RemoveStrDuplicates(strs, nil)
for _, str := range unDuped {
	fmt.Println(str)
}
Output:

test
quest
foo
bar

func RemoveStrFromArray

func RemoveStrFromArray(strs []string, s string) []string

RemoveStrFromArray removes a specific string from a string slice.

func SliceToSet

func SliceToSet[T comparable](sl []T) map[T]struct{}

SliceToSet converts a slice to a map whose keys are the slice members, that is, a set. Note duplicates will be lost, as is the nature of a set.

func Stacktrace

func Stacktrace() []byte

Stacktrace is a helper function which returns the current stacktrace. It wraps runtime.Stack, which requires a sufficiently long buffer.

func StrInArray deprecated

func StrInArray(strs []string, s string) bool

StrInArray returns whether s is one of the strings in strs.

Deprecated: This function is totally identical to ContainsStr, but this one is not used in any known ATC code, while ContainsStr is. New code should use ContainsStr so that this duplicate can be removed.

Example
arr := []string{"test", "quest"}
fmt.Println(StrInArray(arr, "test"))
fmt.Println(StrInArray(arr, "foo"))
Output:

true
false

func StrPtr

func StrPtr(str string) *string

StrPtr returns a pointer to the given string.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := StrPtr("testquest")
fmt.Println(*ptr)
Output:

testquest

func StripAllWhitespace

func StripAllWhitespace(s string) string

StripAllWhitespace returns s with all whitespace removed, as defined by unicode.IsSpace.

Example
input := "\n\t \vtest\t\v\r\n quest\v\n \t"
fmt.Println(StripAllWhitespace(input))
Output:

testquest

func TimePtr

func TimePtr(t time.Time) *time.Time

TimePtr returns a pointer to the given time.Time value.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := TimePtr(time.Time{})
fmt.Println(*ptr)
Output:

0001-01-01 00:00:00 +0000 UTC

func ToNumeric

func ToNumeric(v interface{}) (float64, bool)

ToNumeric returns a float for any numeric type, and false if the interface does not hold a numeric type. This allows converting unknown numeric types (for example, from JSON) in a single line.

TODO try to parse string stats as numbers? Also, JSON numbers are defined by the JSON spec to be IEEE double-precision floating point numbers and as such the encoding/json package always decodes them as float64s before coercing to requested types, so this may not be needed.

func UInt64Ptr

func UInt64Ptr(u uint64) *uint64

UInt64Ptr returns a pointer to the given 64-bit unsigned integer.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := UInt64Ptr(5)
fmt.Println(*ptr)
Output:

5

func UIntPtr

func UIntPtr(u uint) *uint

UIntPtr returns a pointer to the given unsigned integer.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := UIntPtr(5)
fmt.Println(*ptr)
Output:

5

func Uint64Ptr

func Uint64Ptr(u uint64) *uint64

Uint64Ptr returns a pointer to the given 64-bit unsigned integer.

Deprecated. This is just a common mis-casing of UInt64Ptr. These should not both exist, and this one - being the less proper casing - is subject to removal without warning, as its very existence is likely accidental.

Deprecated. This is exactly equivalent to just using Ptr, so duplicated functionality like this function will likely be removed before too long.

Example
ptr := Uint64Ptr(5)
fmt.Println(*ptr)
Output:

5

func ValidateAESKey

func ValidateAESKey(keyBytes []byte) error

ValidateAESKey takes in a byte slice and tests if it's a valid AES key (16, 24, or 32 bytes), returning an error if it isn't.

func WrapError

func WrapError(context string, e error) error

WrapError wraps an error in a context for that error. This allows comparison of errors using errors.Is. This is much faster than fmt.Errorf, so it should be preferred wherever that performance impact is considered significant.

Example
err := WrapError("querying for cdns", sql.ErrNoRows)
fmt.Println(err)
fmt.Println(err == sql.ErrNoRows, errors.Is(err, sql.ErrNoRows))
Output:

querying for cdns: sql: no rows in result set
false true

Types

type Backoff

type Backoff interface {
	// BackoffDuration returns the time that should be waited before attempting
	// the action again. Normally, this duration will grow exponentially, but
	// in the case of a Backoff constructed using NewConstantBackoff, it will
	// be the same every time.
	BackoffDuration() time.Duration
	// Reset clears any incrementing of the duration returned by
	// BackoffDuration, so that the next call will yield the same duration as
	// the first call.
	Reset()
}

A Backoff is a definition of how long to wait between attempting something, which is normally a network action, to avoid overloading requested resources.

func NewBackoff

func NewBackoff(min time.Duration, max time.Duration, factor float64) (Backoff, error)

NewBackoff constructs and returns a Backoff that starts with a duration at min and increments it exponentially according to the "factor" up to a maximum defined by the passed max.

The rate of increase is defined to be nmfⁿ⁻¹ where n is the number of attempts that have already been made, m is the minimum duration, and f is the factor. The duration for any given attempt n (starting at zero) is defined to be mfⁿ+j where j is a randomly generated "jitter" that is added to each duration. The "jitter" will be a number of nanoseconds between the zero and the magnitude of the difference between the min and the factor. If the factor, treated as a number of nanoseconds, is greater than the minimum duration, this jitter will *subtract* from the resulting duration, and it will be between this difference (exclusive) and zero (inclusive). If the factor is less than the min it will *add* to the resulting duration, and it will be between 0 (inclusive) and the difference (exclusive).

func NewConstantBackoff

func NewConstantBackoff(d time.Duration) Backoff

NewConstantBackoff returns a Backoff that does not change its duration.

This is roughly equivalent to calling NewBackoff(d, d+1*time.Second, 1.0) (the max duration doesn't matter when the factor is 1), but is more efficient in terms of both CPU load/time and memory used, and so should be done instead.

type BodyInterceptor

type BodyInterceptor struct {
	W         http.ResponseWriter
	BodyBytes []byte
}

BodyInterceptor fulfills the Writer interface, but records the body and doesn't actually write. This allows performing operations on the entire body written by a handler, for example, compressing or hashing. To actually write, call `RealWrite()`. Note this means `len(b)` and `nil` are always returned by `Write()`, any real write errors will be returned by `RealWrite()`.

func (*BodyInterceptor) Body

func (i *BodyInterceptor) Body() []byte

Body returns the internal bytes stored by calls to Write.

func (*BodyInterceptor) Header

func (i *BodyInterceptor) Header() http.Header

Header implements http.ResponseWriter. It returns BodyInterceptor's internal ResponseWriter.Header, without modification or tracking.

Example
i := BodyInterceptor{W: httptest.NewRecorder()}
i.W.Header().Add("test", "quest")
fmt.Println(i.Header().Get("test"))
Output:

quest

func (*BodyInterceptor) RealWrite

func (i *BodyInterceptor) RealWrite(b []byte) (int, error)

RealWrite writes BodyInterceptor's internal bytes, which were stored by calls to Write.

func (*BodyInterceptor) Write

func (i *BodyInterceptor) Write(b []byte) (int, error)

Write implements http.ResponseWriter. It writes the given bytes to BodyInterceptor's internal tracking bytes, and does not write them to the internal ResponseWriter. To write the internal bytes, call BodyInterceptor.RealWrite.

func (*BodyInterceptor) WriteHeader

func (i *BodyInterceptor) WriteHeader(rc int)

WriteHeader implements http.ResponseWriter. It does the real write to Interceptor's internal ResponseWriter, without modification or tracking.

type Interceptor

type Interceptor struct {
	W         http.ResponseWriter
	Code      int
	ByteCount int
}

Interceptor implements http.ResponseWriter. It intercepts writes to w, and tracks the HTTP code and the count of bytes written, while still writing to w.

func (*Interceptor) Header

func (i *Interceptor) Header() http.Header

Header implements http.ResponseWriter. It returns Interceptor's internal ResponseWriter.Header, without modification or tracking.

Example
i := Interceptor{W: httptest.NewRecorder()}
i.W.Header().Add("test", "quest")
fmt.Println(i.Header().Get("test"))
Output:

quest

func (*Interceptor) Write

func (i *Interceptor) Write(b []byte) (int, error)

Write implements http.ResponseWriter. It does the real write to Interceptor's internal ResponseWriter, while keeping track of the count of bytes written. It also sets Interceptor's tracked code to 200 if WriteHeader wasn't called (which is what the real http.ResponseWriter will write to the client).

func (*Interceptor) WriteHeader

func (i *Interceptor) WriteHeader(rc int)

WriteHeader implements http.ResponseWriter. It does the real write to Interceptor's internal ResponseWriter, while keeping track of the code.

type JSONIntStr

type JSONIntStr int64

JSONIntStr unmarshals JSON strings or numbers into an int. This is designed to handle backwards-compatibility for old Perl endpoints which accept both. Please do not use this for new endpoints or new APIs, APIs should be well-typed.

func (JSONIntStr) String

func (i JSONIntStr) String() string

String implements the fmt.Stringer interface by returning the JSONIntStr encoded in base 10 into a string.

Example
var a JSONIntStr = 5
fmt.Println(a)
Output:

5

func (JSONIntStr) ToInt64

func (i JSONIntStr) ToInt64() int64

ToInt64 returns the int64 value of the JSONIntStr.

Example
var a JSONIntStr = 5
fmt.Printf("%d (%T)\n", a, a)
fmt.Printf("%d (%T)\n", a.ToInt64(), a.ToInt64())
Output:

5 (util.JSONIntStr)
5 (int64)

func (*JSONIntStr) UnmarshalJSON

func (i *JSONIntStr) UnmarshalJSON(d []byte) error

UnmarshalJSON implements the encoding/json.Unmarshaler interface.

type JSONNameOrIDStr

type JSONNameOrIDStr struct {
	Name *string
	ID   *int
}

JSONNameOrIDStr is designed to handle backwards-compatibility for old Perl endpoints which accept both. Please do not use this for new endpoints or new APIs, APIs should be well-typed. NOTE: this differs from JSONIntStr in that this field could be 1 of 3 options:

  1. string representing an integer
  2. string representing a unique name
  3. integer

func (JSONNameOrIDStr) MarshalJSON

func (i JSONNameOrIDStr) MarshalJSON() ([]byte, error)

MarshalJSON implements the encoding/json.Marshaler interface.

func (*JSONNameOrIDStr) UnmarshalJSON

func (i *JSONNameOrIDStr) UnmarshalJSON(d []byte) error

UnmarshalJSON implements the encoding/json.Unmarshaler interface.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL