Skip to content

Instantly share code, notes, and snippets.

@enobufs
Created February 13, 2019 06:11
Show Gist options
  • Save enobufs/3ef71a587ba2b9ece55dd54182b8d48f to your computer and use it in GitHub Desktop.
Save enobufs/3ef71a587ba2b9ece55dd54182b8d48f to your computer and use it in GitHub Desktop.
Sequence Number Arithmetic (RFC 1982) for Go
// sna32 provides 32-bit serial number arithmetic (RFC 1982)
type sna32 uint32
// lessThan comares 32-bit serial numbers
func (sn sna32) lessThan(val sna32) bool {
return (sn < val && val-sn < 1<<31) || (sn > val && sn-val > 1<<31)
}
func (sn sna32) lessThanOrEqualTo(val sna32) bool {
return sn == val || sn.lessThan(val)
}
func (sn sna32) greaterThan(val sna32) bool {
return (sn < val && (val-sn) >= 1<<31) || (sn > val && (sn-val) <= 1<<31)
}
func (sn sna32) greaterThanOrEqualTo(val sna32) bool {
return sn == val || sn.greaterThan(val)
}
// sna16 provides 16-bit serial number arithmetic (RFC 1982)
type sna16 uint16
func (sn sna16) lessThan(val sna16) bool {
return (sn < val && (val-sn) < 1<<15) || (sn > val && (sn-val) > 1<<15)
}
func (sn sna16) lessThanOrEqualTo(val sna16) bool {
return sn == val || sn.lessThan(val)
}
func (sn sna16) greaterThan(val sna16) bool {
return (sn < val && (val-sn) >= 1<<15) || (sn > val && (sn-val) <= 1<<15)
}
func (sn sna16) greaterThanOrEqualTo(val sna16) bool {
return sn == val || sn.greaterThan(val)
}
@enobufs
Copy link
Author

enobufs commented Feb 13, 2019

And a test for it:

func TestSerialNumberArithmetic(t *testing.T) {
	const div int = 16

	t.Run("sna32", func(t *testing.T) {
		const serialBits sna32 = 32
		const interval sna32 = sna32((uint64(1) << uint64(serialBits)) / uint64(div))
		const maxForwardDistance sna32 = 1<<(serialBits-1) - 1
		const maxBackwardDistance sna32 = 1 << (serialBits - 1)

		for i := sna32(0); i < sna32(div); i++ {
			s1 := i * interval
			s2f := s1 + maxForwardDistance
			s2b := s1 + maxBackwardDistance

			assert.True(t, s1.lessThan(s2f), "s1 < s2 should be true: s1=0x%x s2=0x%x", s1, s2f)
			assert.False(t, s1.lessThan(s2b), "s1 < s2 should be false: s1=0x%x s2=0x%x", s1, s2b)

			assert.False(t, s1.greaterThan(s2f), "s1 > s2 should be fales: s1=0x%x s2=0x%x", s1, s2f)
			assert.True(t, s1.greaterThan(s2b), "s1 > s2 should be true: s1=0x%x s2=0x%x", s1, s2b)

			assert.True(t, s1.lessThanOrEqualTo(s2f), "s1 <= s2 should be true: s1=0x%x s2=0x%x", s1, s2f)
			assert.False(t, s1.lessThanOrEqualTo(s2b), "s1 <= s2 should be false: s1=0x%x s2=0x%x", s1, s2b)

			assert.False(t, s1.greaterThanOrEqualTo(s2f), "s1 >= s2 should be fales: s1=0x%x s2=0x%x", s1, s2f)
			assert.True(t, s1.greaterThanOrEqualTo(s2b), "s1 >= s2 should be true: s1=0x%x s2=0x%x", s1, s2b)

			assert.True(t, s1.lessThanOrEqualTo(s1), "s1 <= s1 should be true: s1=0x%x s2=0x%x", s1, s1)
			assert.True(t, s2b.lessThanOrEqualTo(s2b), "s2 <= s2 should be true: s2=0x%x s2=0x%x", s2b, s2b)

			assert.True(t, s1.greaterThanOrEqualTo(s1), "s1 >= s1 should be true: s1=0x%x s2=0x%x", s1, s1)
			assert.True(t, s2b.greaterThanOrEqualTo(s2b), "s2 >= s2 should be true: s2=0x%x s2=0x%x", s2b, s2b)
		}
	})

	t.Run("sna16", func(t *testing.T) {
		const serialBits sna16 = 16
		const interval sna16 = sna16((uint64(1) << uint64(serialBits)) / uint64(div))
		const maxForwardDistance sna16 = 1<<(serialBits-1) - 1
		const maxBackwardDistance sna16 = 1 << (serialBits - 1)

		for i := sna16(0); i < sna16(div); i++ {
			s1 := i * interval
			s2f := s1 + maxForwardDistance
			s2b := s1 + maxBackwardDistance

			assert.True(t, s1.lessThan(s2f), "s1 < s2 should be true: s1=0x%x s2=0x%x", s1, s2f)
			assert.False(t, s1.lessThan(s2b), "s1 < s2 should be false: s1=0x%x s2=0x%x", s1, s2b)

			assert.False(t, s1.greaterThan(s2f), "s1 > s2 should be fales: s1=0x%x s2=0x%x", s1, s2f)
			assert.True(t, s1.greaterThan(s2b), "s1 > s2 should be true: s1=0x%x s2=0x%x", s1, s2b)

			assert.True(t, s1.lessThanOrEqualTo(s2f), "s1 <= s2 should be true: s1=0x%x s2=0x%x", s1, s2f)
			assert.False(t, s1.lessThanOrEqualTo(s2b), "s1 <= s2 should be false: s1=0x%x s2=0x%x", s1, s2b)

			assert.False(t, s1.greaterThanOrEqualTo(s2f), "s1 >= s2 should be fales: s1=0x%x s2=0x%x", s1, s2f)
			assert.True(t, s1.greaterThanOrEqualTo(s2b), "s1 >= s2 should be true: s1=0x%x s2=0x%x", s1, s2b)

			assert.True(t, s1.lessThanOrEqualTo(s1), "s1 <= s1 should be true: s1=0x%x s2=0x%x", s1, s1)
			assert.True(t, s2b.lessThanOrEqualTo(s2b), "s2 <= s2 should be true: s2=0x%x s2=0x%x", s2b, s2b)

			assert.True(t, s1.greaterThanOrEqualTo(s1), "s1 >= s1 should be true: s1=0x%x s2=0x%x", s1, s1)
			assert.True(t, s2b.greaterThanOrEqualTo(s2b), "s2 >= s2 should be true: s2=0x%x s2=0x%x", s2b, s2b)
		}
	})
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment