Skip to content

Commit 589abd3

Browse files
add a Unique function (#203)
* add a Deduplicate function * rename Deduplicate to Unique
1 parent c9dc72e commit 589abd3

File tree

4 files changed

+76
-0
lines changed

4 files changed

+76
-0
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/multiformats/go-multihash v0.0.14
99
github.com/multiformats/go-varint v0.0.6
1010
github.com/stretchr/testify v1.7.0
11+
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df
1112
)
1213

1314
require (

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
3232
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
3333
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
3434
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
35+
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
36+
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
3537
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
3638
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
3739
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

multiaddr.go

+23
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"fmt"
77
"log"
88
"strings"
9+
10+
"golang.org/x/exp/slices"
911
)
1012

1113
// multiaddr is the data structure representing a Multiaddr
@@ -210,3 +212,24 @@ func Contains(addrs []Multiaddr, addr Multiaddr) bool {
210212
}
211213
return false
212214
}
215+
216+
// Unique deduplicates addresses in place, leave only unique addresses.
217+
// It doesn't allocate.
218+
func Unique(addrs []Multiaddr) []Multiaddr {
219+
if len(addrs) == 0 {
220+
return addrs
221+
}
222+
// Use the new slices package here, as the sort function doesn't allocate (sort.Slice does).
223+
slices.SortFunc(addrs, func(a, b Multiaddr) bool { return bytes.Compare(a.Bytes(), b.Bytes()) < 0 })
224+
idx := 1
225+
for i := 1; i < len(addrs); i++ {
226+
if !addrs[i-1].Equal(addrs[i]) {
227+
addrs[idx] = addrs[i]
228+
idx++
229+
}
230+
}
231+
for i := idx; i < len(addrs); i++ {
232+
addrs[i] = nil
233+
}
234+
return addrs[:idx]
235+
}

multiaddr_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package multiaddr
33
import (
44
"bytes"
55
"encoding/hex"
6+
"fmt"
7+
"math"
8+
"math/rand"
69
"testing"
710

811
"github.com/ipfs/go-cid"
@@ -785,3 +788,50 @@ func TestContains(t *testing.T) {
785788
require.False(t, Contains(addrs, newMultiaddr(t, "/ip4/4.3.2.1/udp/1234/utp")))
786789
require.False(t, Contains(nil, a1))
787790
}
791+
792+
func TestUniqueAddrs(t *testing.T) {
793+
tcpAddr := StringCast("/ip4/127.0.0.1/tcp/1234")
794+
quicAddr := StringCast("/ip4/127.0.0.1/udp/1234/quic-v1")
795+
wsAddr := StringCast("/ip4/127.0.0.1/tcp/1234/ws")
796+
797+
type testcase struct {
798+
in, out []Multiaddr
799+
}
800+
801+
for i, tc := range []testcase{
802+
{in: nil, out: nil},
803+
{in: []Multiaddr{tcpAddr}, out: []Multiaddr{tcpAddr}},
804+
{in: []Multiaddr{tcpAddr, tcpAddr, tcpAddr}, out: []Multiaddr{tcpAddr}},
805+
{in: []Multiaddr{tcpAddr, quicAddr, tcpAddr}, out: []Multiaddr{tcpAddr, quicAddr}},
806+
{in: []Multiaddr{tcpAddr, quicAddr, wsAddr}, out: []Multiaddr{tcpAddr, quicAddr, wsAddr}},
807+
} {
808+
tc := tc
809+
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
810+
deduped := Unique(tc.in)
811+
for _, a := range tc.out {
812+
require.Contains(t, deduped, a)
813+
}
814+
})
815+
}
816+
}
817+
818+
func BenchmarkUniqueAddrs(b *testing.B) {
819+
b.ReportAllocs()
820+
var addrs []Multiaddr
821+
r := rand.New(rand.NewSource(1234))
822+
for i := 0; i < 100; i++ {
823+
tcpAddr := StringCast(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", r.Intn(math.MaxUint16)))
824+
quicAddr := StringCast(fmt.Sprintf("/ip4/127.0.0.1/udp/%d/quic-v1", r.Intn(math.MaxUint16)))
825+
wsAddr := StringCast(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d/ws", r.Intn(math.MaxUint16)))
826+
addrs = append(addrs, tcpAddr, tcpAddr, quicAddr, quicAddr, wsAddr)
827+
}
828+
for _, sz := range []int{10, 20, 30, 50, 100} {
829+
b.Run(fmt.Sprintf("%d", sz), func(b *testing.B) {
830+
items := make([]Multiaddr, sz)
831+
for i := 0; i < b.N; i++ {
832+
copy(items, addrs[:sz])
833+
Unique(items)
834+
}
835+
})
836+
}
837+
}

0 commit comments

Comments
 (0)