types/logid: add Add method (#12478)

The Add method derives a new ID by adding a signed integer
to the ID, treating it as an unsigned 256-bit big-endian integer.

We also add Less and Compare methods to PrivateID to provide
feature parity with existing methods on PublicID.

Updates tailscale/corp#11038

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
This commit is contained in:
Joe Tsai
2024-06-17 16:03:44 -07:00
committed by GitHub
parent 315f3d5df1
commit 2db2d04a37
2 changed files with 128 additions and 0 deletions

View File

@@ -11,6 +11,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"math/bits"
"slices"
"unicode/utf8"
)
@@ -38,6 +39,12 @@ func ParsePrivateID(in string) (out PrivateID, err error) {
return out, err
}
// Add adds i to the id, treating it as an unsigned 256-bit big-endian integer,
// and returns the resulting ID.
func (id PrivateID) Add(i int64) PrivateID {
return add(id, i)
}
func (id PrivateID) AppendText(b []byte) ([]byte, error) {
return hex.AppendEncode(b, id[:]), nil
}
@@ -54,6 +61,14 @@ func (id PrivateID) String() string {
return string(hex.AppendEncode(nil, id[:]))
}
func (id1 PrivateID) Less(id2 PrivateID) bool {
return id1.Compare(id2) < 0
}
func (id1 PrivateID) Compare(id2 PrivateID) int {
return slices.Compare(id1[:], id2[:])
}
func (id PrivateID) IsZero() bool {
return id == PrivateID{}
}
@@ -74,6 +89,12 @@ func ParsePublicID(in string) (out PublicID, err error) {
return out, err
}
// Add adds i to the id, treating it as an unsigned 256-bit big-endian integer,
// and returns the resulting ID.
func (id PublicID) Add(i int64) PublicID {
return add(id, i)
}
func (id PublicID) AppendText(b []byte) ([]byte, error) {
return hex.AppendEncode(b, id[:]), nil
}
@@ -118,3 +139,22 @@ func parseID[Bytes []byte | string](funcName string, out *[32]byte, in Bytes) (e
}
return nil
}
func add(id [32]byte, i int64) [32]byte {
var out uint64
switch {
case i < 0:
borrow := ^uint64(i) + 1 // twos-complement inversion
for i := 0; i < 4 && borrow > 0; i++ {
out, borrow = bits.Sub64(binary.BigEndian.Uint64(id[8*(3-i):]), borrow, 0)
binary.BigEndian.PutUint64(id[8*(3-i):], out)
}
case i > 0:
carry := uint64(i)
for i := 0; i < 4 && carry > 0; i++ {
out, carry = bits.Add64(binary.BigEndian.Uint64(id[8*(3-i):]), carry, 0)
binary.BigEndian.PutUint64(id[8*(3-i):], out)
}
}
return id
}