You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
learn_mqtt_go/clock/clock.go

152 lines
4.8 KiB
Go

package clock
import (
"errors"
"fmt"
)
type Uint interface {
uint32 | uint64
}
type VectorClock[T Uint] struct {
clock []T
}
func max[T Uint](x, y T) T {
if x >= y {
return x
} else {
return y
}
}
// GetClock returns a copy of the internal vector clock.
//
// This method provides a snapshot of the current state of the vector clock
// without exposing the internal slice directly. By returning a copy, it ensures
// that the original vector clock remains immutable and prevents unintended
// modifications to the internal state.
//
// Returns:
//
// []T: A copy of the internal vector clock slice, where each element
// represents the logical time for a corresponding process. The type T
// is constrained by the Uint interface and can be either uint32 or uint64.
func (vc *VectorClock[T]) GetClock() []T {
clock := make([]T, len(vc.clock))
copy(clock, vc.clock)
return clock
}
// Sync synchronizes the current VectorClock with another VectorClock of the same type.
//
// This method takes another VectorClock instance, compares the logical times for each
// process, and updates the current VectorClock to hold the maximum logical time for
// each process. The synchronization ensures that the resulting vector clock reflects
// the latest logical times for both clocks.
//
// If the lengths of the two VectorClocks differ, an error is returned.
//
// Parameters:
//
// v VectorClock[T]: The other VectorClock instance to synchronize with. It must be
// of the same type T (either uint32 or uint64), as constrained by the Uint interface.
//
// Returns:
//
// []T: A copy of the synchronized vector clock, where each element represents the
// updated logical time for the corresponding process.
//
// error: An error is returned if the two VectorClocks have different lengths.
func (vc *VectorClock[T]) Sync(v VectorClock[T]) ([]T, error) {
compClock := v.GetClock()
if len(vc.clock) != len(compClock) {
return nil, errors.New("VectorClocks are of different lengths.")
}
for i := range vc.clock {
vc.clock[i] = max(vc.clock[i], compClock[i])
}
return vc.GetClock(), nil
}
// Increment increments the logical time at the specified index of the vector clock.
//
// This method updates the logical time for a given process (specified by the index) by
// incrementing the corresponding value in the vector clock. It ensures that the index
// is within the bounds of the vector clock. If the index is out of bounds or the vector
// clock is uninitialized, an error is returned.
//
// Parameters:
//
// index (int): The index of the process whose logical time is to be incremented.
// It must be within the range of the vector clock's length.
//
// Returns:
//
// []T: A copy of the updated vector clock after the logical time at the given index
// has been incremented.
//
// error: An error is returned if the index is out of bounds or the vector clock is uninitialized.
//
// Note: Handling of potential overflow for the underlying type T (uint32 or uint64) is currently
// not implemented and should be handled accordingly if required.
func (vc *VectorClock[T]) Increment(index int) ([]T, error) {
if index > len(vc.clock) || vc.clock == nil {
return nil, errors.New(fmt.Sprintf("Cannot access index: %d, clock is of length %d", index, len(vc.clock)))
}
// TODO handle Uint overflow?
vc.clock[index] = vc.clock[index] + 1
return vc.GetClock(), nil
}
// NewVectorClock creates a new generic VectorClock initialized to zero.
//
// This function creates a new instance of VectorClock for the specified type T,
// which can be either uint32 or uint64, depending on the needs of the application.
//
// Parameters:
//
// size (T): The number of entries in the vector clock, typically corresponding
// to the number of processes or nodes. The size is of type T, which can be uint32
// or uint64, as defined by the Uint constraint.
//
// Returns:
//
// *VectorClock[T]: A pointer to the newly created VectorClock instance, where all
// elements of the clock are initialized to zero.
func NewVectorClock[T Uint](size int) *VectorClock[T] {
return &VectorClock[T]{
clock: make([]T, size),
}
}
// ArrayToVectorClock converts an array of type T into a VectorClock instance.
//
// This function creates a new VectorClock where the internal clock is initialized
// by copying the values from the provided array. The input array's values are
// treated as the logical times for each process in the vector clock.
//
// Parameters:
//
// a []T: An array of type T (either uint32 or uint64) representing logical
// times for each process. The type T is constrained by the Uint interface, which
// ensures it is either uint32 or uint64.
//
// Returns:
//
// *VectorClock[T]: A pointer to a newly created VectorClock instance where the
// internal clock is a copy of the provided array.
func ArrayToVectorClock[T Uint](a []T) *VectorClock[T] {
vc := &VectorClock[T]{
clock: make([]T, len(a)),
}
copy(vc.clock, a)
return vc
}