github.com/searKing/golang/go@v1.2.117/exp/math/serial_number_arithmetic.go (about) 1 // Copyright 2023 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package math 6 7 import ( 8 "github.com/searKing/golang/go/exp/types" 9 "golang.org/x/exp/constraints" 10 ) 11 12 // SerialNumber is a constraint that permits any unsigned integer type. 13 // If future releases of Go add new predeclared unsigned integer types, 14 // this constraint will be modified to include them. 15 type SerialNumber = constraints.Unsigned 16 17 // IsNewer implements RFC 1982: Serial Number Arithmetic 18 // See also: https://datatracker.ietf.org/doc/html/rfc1982#section-2 19 // s1 < s2 and (s1 + 1) > (s2 + 1) 20 // See also: https://chromium.googlesource.com/external/webrtc/trunk/webrtc/+/f54860e9ef0b68e182a01edc994626d21961bc4b/modules/include/module_common_types.h 21 func IsNewer[T SerialNumber](t1, t2 T) bool { 22 // kBreakpoint is the half-way mark for the type U. For instance, for a 23 // uint16_t it will be 0x8000, and for a uint32_t, it will be 0x8000000. 24 kBreakpoint := (MaxInt[T]() >> 1) + 1 25 if t1 == t2 { 26 return false 27 } 28 // Distinguish between elements that are exactly kBreakpoint apart. 29 // If t1>t2 and |t1-t2| = kBreakpoint: IsNewer(t1,t2)=true, 30 // IsNewer(t2,t1)=false 31 // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false. 32 if t1 > t2 { 33 return t1-t2 <= kBreakpoint 34 } 35 36 return t2-t1 > kBreakpoint 37 } 38 39 // Latest return newer of s1, s2 40 func Latest[T SerialNumber](s1, s2 T) T { 41 if IsNewer(s1, s2) { 42 return s1 43 } 44 return s2 45 } 46 47 // Unwrap unwrap a number to a larger type. 48 // The numbers will never be unwrapped to a negative value. 49 func Unwrap[T SerialNumber](lastValue int64, value T) int64 { 50 kMaxPlusOne := int64(MaxInt[T]()) + 1 51 croppedLast := T(lastValue) 52 delta := int64(value - croppedLast) 53 if IsNewer(value, croppedLast) { 54 if delta < 0 { 55 delta += kMaxPlusOne // Wrap forwards. 56 } 57 } else if delta > 0 && (lastValue+delta-kMaxPlusOne) >= 0 { 58 // If value is older but delta is positive, this is a backwards 59 // wrap-around. However, don't wrap backwards past 0 (unwrapped). 60 delta -= kMaxPlusOne 61 } 62 return lastValue + delta 63 } 64 65 // Unwrapper is an utility class to unwrap a number to a larger type. 66 // The numbers will never be unwrapped to a negative value. 67 type Unwrapper[T SerialNumber] struct { 68 lastValue *int64 69 } 70 71 // UnwrapWithoutUpdate returns the unwrapped value, but don't update the internal state. 72 func (u *Unwrapper[T]) UnwrapWithoutUpdate(value T) int64 { 73 if u.lastValue == nil { 74 return int64(value) 75 } 76 kMaxPlusOne := int64(MaxInt[T]()) + 1 77 croppedLast := T(*u.lastValue) 78 delta := int64(value - croppedLast) 79 if IsNewer(value, croppedLast) { 80 if delta < 0 { 81 delta += kMaxPlusOne // Wrap forwards. 82 } 83 } else if delta > 0 && (*u.lastValue+delta-kMaxPlusOne) >= 0 { 84 // If value is older but delta is positive, this is a backwards 85 // wrap-around. However, don't wrap backwards past 0 (unwrapped). 86 delta -= kMaxPlusOne 87 } 88 return *u.lastValue + delta 89 } 90 91 // UpdateLast only update the internal state to the specified last (unwrapped) value. 92 func (u *Unwrapper[T]) UpdateLast(lastValue int64) { 93 u.lastValue = types.Pointer(lastValue) 94 } 95 96 // Unwrap the value and update the internal state. 97 func (u *Unwrapper[T]) Unwrap(value T) int64 { 98 unwrapped := u.UnwrapWithoutUpdate(value) 99 u.UpdateLast(unwrapped) 100 return unwrapped 101 }