aboutsummaryrefslogtreecommitdiff
path: root/dedupe/dedupe.go
blob: 44f02c5bbb66b7db19d0269bf713387727441180 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package dedupe

import (
	"encoding/hex"
	"fmt"
	"hash"
	"sync"
	"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            // 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),
		hasher:       hasher,
		expiresAfter: expiresAfter,
	}
	go func() {
		for {
			time.Sleep(expiresAfter)
			pd.Lock()
			for packet, timestamp := range pd.seen {
				if time.Since(timestamp) > pd.expiresAfter {
					delete(pd.seen, packet)
				}
			}
			pd.Unlock()
		}
	}()

	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()
	if _, exists := p.seen[asString]; !exists {
		p.RUnlock()
		p.Lock()
		defer p.Unlock()
		p.seen[asString] = time.Now()
		return false
	}
	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)
	p.RLock()
	if _, exists := p.seen[asHex]; !exists {
		p.RUnlock()
		p.Lock()
		defer p.Unlock()
		p.seen[asHex] = time.Now()
		return false
	}
	p.RUnlock()

	return true
}