diff --git a/common/minmax.go b/common/minmax.go new file mode 100644 index 00000000..90955922 --- /dev/null +++ b/common/minmax.go @@ -0,0 +1,15 @@ +//go:build go1.21 + +package common + +import ( + "cmp" +) + +func Min[T cmp.Ordered](x, y T) T { + return min(x, y) +} + +func Max[T cmp.Ordered](x, y T) T { + return max(x, y) +} diff --git a/common/minmax_compat.go b/common/minmax_compat.go new file mode 100644 index 00000000..41490242 --- /dev/null +++ b/common/minmax_compat.go @@ -0,0 +1,19 @@ +//go:build go1.20 && !go1.21 + +package common + +import "github.com/sagernet/sing/common/x/constraints" + +func Min[T constraints.Ordered](x, y T) T { + if x < y { + return x + } + return y +} + +func Max[T constraints.Ordered](x, y T) T { + if x < y { + return y + } + return x +} diff --git a/common/oncefunc.go b/common/oncefunc.go new file mode 100644 index 00000000..d24ba6bb --- /dev/null +++ b/common/oncefunc.go @@ -0,0 +1,20 @@ +//go:build go1.21 + +package common + +import "sync" + +// OnceFunc is a wrapper around sync.OnceFunc. +func OnceFunc(f func()) func() { + return sync.OnceFunc(f) +} + +// OnceValue is a wrapper around sync.OnceValue. +func OnceValue[T any](f func() T) func() T { + return sync.OnceValue(f) +} + +// OnceValues is a wrapper around sync.OnceValues. +func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { + return sync.OnceValues(f) +} diff --git a/common/oncefunc_compat.go b/common/oncefunc_compat.go new file mode 100644 index 00000000..aadb9c3c --- /dev/null +++ b/common/oncefunc_compat.go @@ -0,0 +1,104 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.20 && !go1.21 + +package common + +import "sync" + +// OnceFunc returns a function that invokes f only once. The returned function +// may be called concurrently. +// +// If f panics, the returned function will panic with the same value on every call. +func OnceFunc(f func()) func() { + var ( + once sync.Once + valid bool + p any + ) + // Construct the inner closure just once to reduce costs on the fast path. + g := func() { + defer func() { + p = recover() + if !valid { + // Re-panic immediately so on the first call the user gets a + // complete stack trace into f. + panic(p) + } + }() + f() + f = nil // Do not keep f alive after invoking it. + valid = true // Set only if f does not panic. + } + return func() { + once.Do(g) + if !valid { + panic(p) + } + } +} + +// OnceValue returns a function that invokes f only once and returns the value +// returned by f. The returned function may be called concurrently. +// +// If f panics, the returned function will panic with the same value on every call. +func OnceValue[T any](f func() T) func() T { + var ( + once sync.Once + valid bool + p any + result T + ) + g := func() { + defer func() { + p = recover() + if !valid { + panic(p) + } + }() + result = f() + f = nil + valid = true + } + return func() T { + once.Do(g) + if !valid { + panic(p) + } + return result + } +} + +// OnceValues returns a function that invokes f only once and returns the values +// returned by f. The returned function may be called concurrently. +// +// If f panics, the returned function will panic with the same value on every call. +func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { + var ( + once sync.Once + valid bool + p any + r1 T1 + r2 T2 + ) + g := func() { + defer func() { + p = recover() + if !valid { + panic(p) + } + }() + r1, r2 = f() + f = nil + valid = true + } + return func() (T1, T2) { + once.Do(g) + if !valid { + panic(p) + } + return r1, r2 + } +}