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  }