diff options
| author | Smoke <[email protected]> | 2024-01-31 14:59:09 -1000 |
|---|---|---|
| committer | Smoke <[email protected]> | 2024-02-02 09:52:08 -1000 |
| commit | 8d835a1816b857aa1048c3d66408c1e60c53cc79 (patch) | |
| tree | 493a8450263cb5a746c2d4d746e8db859310cd63 | |
| parent | 67c102385e5a4e3b29c29bbe95071eabd45b4fb9 (diff) | |
Add dedupe and tests (#4)
* document dedupe.go
* add dedupe test
| -rw-r--r-- | dedupe.go | 18 | ||||
| -rw-r--r-- | dedupe_test.go | 61 |
2 files changed, 75 insertions, 4 deletions
@@ -8,13 +8,17 @@ import ( "time" ) +// PacketDeduplicator is a structure that prevents processing of duplicate packets. +// It keeps a record of seen packets and the time they were last seen. type PacketDeduplicator struct { - hasher hash.Hash - expiresAfter time.Duration - sync.RWMutex - seen map[string]time.Time + hasher hash.Hash // hasher is used for generating packet identifiers. + expiresAfter time.Duration // expiresAfter defines the duration after which a seen packet record expires. + sync.RWMutex // RWMutex is used to protect the seen map from concurrent access. + seen map[string]time.Time // seen maps a packet identifier to the last time it was seen. } +// NewDeduplicator creates a new PacketDeduplicator with a given hasher and expiration duration for packet records. +// It starts a background goroutine to periodically clean up expired packet records. func NewDeduplicator(hasher hash.Hash, expiresAfter time.Duration) *PacketDeduplicator { pd := PacketDeduplicator{ seen: make(map[string]time.Time), @@ -36,6 +40,9 @@ func NewDeduplicator(hasher hash.Hash, expiresAfter time.Duration) *PacketDedupl return &pd } + +// Seen checks whether a packet with the given sender and packetID has been seen before. +// If not, it records the packet as seen and returns false. Otherwise, it returns true. func (p *PacketDeduplicator) Seen(sender, packetID uint32) bool { asString := fmt.Sprintf("%d-%d", sender, packetID) p.RLock() @@ -49,6 +56,9 @@ func (p *PacketDeduplicator) Seen(sender, packetID uint32) bool { p.RUnlock() return true } + +// SeenData checks whether the data has been seen before based on its hashed value. +// If not, it records the data as seen and returns false. Otherwise, it returns true. func (p *PacketDeduplicator) SeenData(data []byte) bool { hashed := p.hasher.Sum(data) asHex := hex.EncodeToString(hashed) diff --git a/dedupe_test.go b/dedupe_test.go new file mode 100644 index 0000000..6388360 --- /dev/null +++ b/dedupe_test.go @@ -0,0 +1,61 @@ +package meshtastic_test + +import ( + "crypto/md5" + "github.com/crypto-smoke/meshtastic-go" + "testing" + "time" +) + +func TestPacketDeduplicatorSeen(t *testing.T) { + hasher := md5.New() + expiresAfter := 100 * time.Millisecond + dedup := meshtastic.NewDeduplicator(hasher, expiresAfter) + + sender := uint32(1) + packetID := uint32(1) + + // Test Seen with new packetID + if dedup.Seen(sender, packetID) { + t.Error("Expected the packet to not have been seen") + } + + // Test Seen with same packetID again + if !dedup.Seen(sender, packetID) { + t.Error("Expected the packet to have been seen") + } + + // Wait for expiration + time.Sleep(expiresAfter + 100*time.Millisecond) + + // Test Seen with same packetID after expiration + if dedup.Seen(sender, packetID) { + t.Error("Expected the packet to not have been seen after expiration") + } +} + +func TestPacketDeduplicatorSeenData(t *testing.T) { + hasher := md5.New() + expiresAfter := 100 * time.Millisecond + dedup := meshtastic.NewDeduplicator(hasher, expiresAfter) + + data := []byte("test data") + + // Test SeenData with new data + if dedup.SeenData(data) { + t.Error("Expected the data to not have been seen") + } + + // Test SeenData with same data again + if !dedup.SeenData(data) { + t.Error("Expected the data to have been seen") + } + + // Wait for expiration + time.Sleep(expiresAfter + 100*time.Millisecond) + + // Test SeenData with same data after expiration + if dedup.SeenData(data) { + t.Error("Expected the data to not have been seen after expiration") + } +} |
