From f3b61f09a838e181c24ac1e963fbd74e6785a20f Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Tue, 2 May 2023 17:47:47 +1000 Subject: [PATCH] Add internal panicif, check and nestedmaps packages Fuckit I'm sick of reinventing the wheel. --- client.go | 5 +- internal/check/check.go | 5 ++ internal/check/check_testing.go | 11 ++++ internal/nestedmaps/nestedmaps.go | 73 ++++++++++++++++++++++++++ internal/nestedmaps/nestedmaps_test.go | 42 +++++++++++++++ internal/panicif/panicif.go | 9 ++++ torrent.go | 3 +- 7 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 internal/check/check.go create mode 100644 internal/check/check_testing.go create mode 100644 internal/nestedmaps/nestedmaps.go create mode 100644 internal/nestedmaps/nestedmaps_test.go create mode 100644 internal/panicif/panicif.go diff --git a/client.go b/client.go index 342025ac..eaf3d677 100644 --- a/client.go +++ b/client.go @@ -37,6 +37,7 @@ import ( "golang.org/x/time/rate" "github.com/anacrolix/torrent/bencode" + "github.com/anacrolix/torrent/internal/check" "github.com/anacrolix/torrent/internal/limiter" "github.com/anacrolix/torrent/iplist" "github.com/anacrolix/torrent/metainfo" @@ -1075,10 +1076,8 @@ func (cl *Client) runHandshookConn(c *PeerConn, t *Torrent) error { return nil } -const check = false - func (p *Peer) initUpdateRequestsTimer() { - if check { + if check.Enabled { if p.updateRequestsTimer != nil { panic(p.updateRequestsTimer) } diff --git a/internal/check/check.go b/internal/check/check.go new file mode 100644 index 00000000..aa75e598 --- /dev/null +++ b/internal/check/check.go @@ -0,0 +1,5 @@ +package check + +// A flag for doing extra checks at runtime that are potentially expensive. Should be enabled for +// testing and debugging. +var Enabled = false diff --git a/internal/check/check_testing.go b/internal/check/check_testing.go new file mode 100644 index 00000000..3ec404e3 --- /dev/null +++ b/internal/check/check_testing.go @@ -0,0 +1,11 @@ +//go:build go1.21 + +package check + +import "testing" + +func init() { + if testing.Testing() { + Enabled = true + } +} diff --git a/internal/nestedmaps/nestedmaps.go b/internal/nestedmaps/nestedmaps.go new file mode 100644 index 00000000..62ebdcc1 --- /dev/null +++ b/internal/nestedmaps/nestedmaps.go @@ -0,0 +1,73 @@ +package nestedmaps + +type next[NK comparable, M ~map[NK]NV, NV any] struct { + last Path[M] + key NK +} + +func (me next[NK, CV, NV]) Exists() bool { + _, ok := me.last.Get()[me.key] + return ok +} + +func (me next[NK, CV, NV]) Get() NV { + return me.last.Get()[me.key] +} + +func (me next[NK, CV, NV]) Set(value NV) { + if me.last.Get() == nil { + me.last.Set(make(CV)) + } + me.last.Get()[me.key] = value +} + +func (me next[NK, CV, NV]) Delete() { + m := me.last.Get() + delete(m, me.key) + if len(m) == 0 { + me.last.Delete() + } +} + +func Next[K comparable, M ~map[K]V, V any]( + last Path[M], + key K, +) Path[V] { + ret := next[K, M, V]{} + ret.last = last + ret.key = key + return ret +} + +type root[K comparable, V any, M ~map[K]V] struct { + m *M +} + +func (me root[K, V, M]) Exists() bool { + return *me.m != nil +} + +func (me root[K, V, M]) Get() M { + return *me.m +} + +func (me root[K, V, M]) Set(value M) { + *me.m = value +} + +func (me root[K, V, M]) Delete() { + *me.m = nil +} + +func Begin[K comparable, M ~map[K]V, V any](m *M) Path[M] { + ret := root[K, V, M]{} + ret.m = m + return ret +} + +type Path[V any] interface { + Set(V) + Get() V + Exists() bool + Delete() +} diff --git a/internal/nestedmaps/nestedmaps_test.go b/internal/nestedmaps/nestedmaps_test.go new file mode 100644 index 00000000..af808eb9 --- /dev/null +++ b/internal/nestedmaps/nestedmaps_test.go @@ -0,0 +1,42 @@ +package nestedmaps + +import ( + "testing" + + qt "github.com/frankban/quicktest" + + g "github.com/anacrolix/generics" +) + +func TestNestedMaps(t *testing.T) { + c := qt.New(t) + var nest map[string]map[*int]map[byte][]int64 + intKey := g.PtrTo(420) + var root = Begin(&nest) + var first = Next(root, "answer") + var second = Next(first, intKey) + var last = Next(second, 69) + c.Assert(root.Exists(), qt.IsFalse) + c.Assert(first.Exists(), qt.IsFalse) + c.Assert(second.Exists(), qt.IsFalse) + c.Assert(last.Exists(), qt.IsFalse) + last.Set([]int64{4, 8, 15, 16, 23, 42}) + c.Assert(root.Exists(), qt.IsTrue) + c.Assert(first.Exists(), qt.IsTrue) + c.Assert(second.Exists(), qt.IsTrue) + c.Assert(last.Exists(), qt.IsTrue) + c.Assert(Next(second, 70).Exists(), qt.IsFalse) + secondIntKey := g.PtrTo(1337) + secondPath := Next(Next(Next(Begin(&nest), "answer"), secondIntKey), 42) + secondPath.Set(nil) + c.Assert(secondPath.Exists(), qt.IsTrue) + last.Delete() + c.Assert(last.Exists(), qt.IsFalse) + c.Assert(second.Exists(), qt.IsFalse) + c.Assert(root.Exists(), qt.IsTrue) + c.Assert(first.Exists(), qt.IsTrue) + // See if we get panics deleting an already deleted item. + last.Delete() + secondPath.Delete() + c.Assert(root.Exists(), qt.IsFalse) +} diff --git a/internal/panicif/panicif.go b/internal/panicif/panicif.go new file mode 100644 index 00000000..8cfb0ef9 --- /dev/null +++ b/internal/panicif/panicif.go @@ -0,0 +1,9 @@ +package panicif + +import "fmt" + +func NotEqual[T comparable](a, b T) { + if a != b { + panic(fmt.Sprintf("%v != %v", a, b)) + } +} diff --git a/torrent.go b/torrent.go index c7ca2549..3dd47cd5 100644 --- a/torrent.go +++ b/torrent.go @@ -37,6 +37,7 @@ import ( "github.com/anacrolix/torrent/bencode" "github.com/anacrolix/torrent/common" + "github.com/anacrolix/torrent/internal/check" "github.com/anacrolix/torrent/metainfo" pp "github.com/anacrolix/torrent/peer_protocol" utHolepunch "github.com/anacrolix/torrent/peer_protocol/ut-holepunch" @@ -1557,7 +1558,7 @@ func (t *Torrent) decPeerPieceAvailability(p *Peer) { } func (t *Torrent) assertPendingRequests() { - if !check { + if !check.Enabled { return } // var actual pendingRequests