Skip to content

Commit b857073

Browse files
committed
reduce allocations in Bytes() and manet methods
1 parent ef34820 commit b857073

File tree

4 files changed

+62
-30
lines changed

4 files changed

+62
-30
lines changed

multiaddr.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,18 @@ func (m Multiaddr) Bytes() []byte {
9191
size += len(c.bytes)
9292
}
9393

94-
out := make([]byte, 0, size)
94+
// This method is inlined in the caller. Using a fixed sized array
95+
// avoids an allocation.
96+
var out []byte
97+
if size < 128 {
98+
var o [128]byte
99+
out = o[:0]
100+
} else {
101+
out = make([]byte, 0, size)
102+
}
95103
for _, c := range m {
96104
out = append(out, c.bytes...)
97105
}
98-
99106
return out
100107
}
101108

multiaddr_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,3 +1255,16 @@ func FuzzComponents(f *testing.F) {
12551255
}
12561256
})
12571257
}
1258+
1259+
func BenchmarkBytes(b *testing.B) {
1260+
addr := StringCast("/ip4/127.0.0.1/tcp/1234")
1261+
b.ReportAllocs()
1262+
b.ResetTimer()
1263+
m := make(map[string]Multiaddr)
1264+
for i := 0; i < b.N; i++ {
1265+
_ = addr.Bytes()
1266+
if _, ok := m[string(addr.Bytes())]; !ok {
1267+
m["a"] = addr
1268+
}
1269+
}
1270+
}

net/ip.go

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ func IsIPLoopback(m ma.Multiaddr) bool {
6363
if m == nil {
6464
return false
6565
}
66-
c, _ := ma.SplitFirst(m)
67-
if c == nil {
66+
head, _ := splitFirstSlice(m)
67+
if len(head) == 0 {
6868
return false
6969
}
70-
switch c.Protocol().Code {
70+
switch head[0].Code() {
7171
case ma.P_IP4, ma.P_IP6:
72-
return net.IP(c.RawValue()).IsLoopback()
72+
return net.IP(head[0].RawValue()).IsLoopback()
7373
}
7474
return false
7575
}
@@ -82,39 +82,52 @@ func IsIP6LinkLocal(m ma.Multiaddr) bool {
8282
if m == nil {
8383
return false
8484
}
85-
c, _ := ma.SplitFirst(m)
86-
if c == nil || c.Protocol().Code != ma.P_IP6 {
85+
head, _ := splitFirstSlice(m)
86+
if len(head) == 0 || head[0].Code() != ma.P_IP6 {
8787
return false
8888
}
89-
ip := net.IP(c.RawValue())
89+
ip := net.IP(head[0].RawValue())
9090
return ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()
9191
}
9292

9393
// IsIPUnspecified returns whether a Multiaddr starts with an Unspecified IP address
9494
// This means either /ip4/0.0.0.0/* or /ip6/::/*
9595
func IsIPUnspecified(m ma.Multiaddr) bool {
9696
m = zoneless(m)
97-
if m == nil {
97+
if len(m) == 0 {
98+
return false
99+
}
100+
head, _ := splitFirstSlice(m)
101+
if len(head) == 0 {
98102
return false
99103
}
100-
c, _ := ma.SplitFirst(m)
101-
return net.IP(c.RawValue()).IsUnspecified()
104+
return net.IP(head[0].RawValue()).IsUnspecified()
105+
}
106+
107+
func splitFirstSlice(m ma.Multiaddr) (ma.Multiaddr, ma.Multiaddr) {
108+
switch len(m) {
109+
case 0:
110+
return nil, nil
111+
case 1:
112+
return m, nil
113+
default:
114+
return m[:1], m[1:]
115+
}
102116
}
103117

104118
// If m matches [zone,ip6,...], return [ip6,...]
105119
// else if m matches [], [zone], or [zone,...], return nil
106120
// else return m
107121
func zoneless(m ma.Multiaddr) ma.Multiaddr {
108-
head, tail := ma.SplitFirst(m)
109-
if head == nil {
122+
head, tail := splitFirstSlice(m)
123+
if len(head) == 0 {
110124
return nil
111125
}
112-
if head.Protocol().Code == ma.P_IP6ZONE {
113-
if tail == nil {
126+
if head[0].Code() == ma.P_IP6ZONE {
127+
if len(tail) == 0 {
114128
return nil
115129
}
116-
tailhead, _ := ma.SplitFirst(tail)
117-
if tailhead.Protocol().Code != ma.P_IP6 {
130+
if tail[0].Code() != ma.P_IP6 {
118131
return nil
119132
}
120133
return tail
@@ -126,7 +139,7 @@ func zoneless(m ma.Multiaddr) ma.Multiaddr {
126139
// IsNAT64IPv4ConvertedIPv6Addr returns whether addr is a well-known prefix "64:ff9b::/96" addr
127140
// used for NAT64 Translation. See RFC 6052
128141
func IsNAT64IPv4ConvertedIPv6Addr(addr ma.Multiaddr) bool {
129-
c, _ := ma.SplitFirst(addr)
130-
return c != nil && c.Protocol().Code == ma.P_IP6 &&
131-
inAddrRange(c.RawValue(), nat64)
142+
head, _ := splitFirstSlice(addr)
143+
return len(head) == 1 && head[0].Code() == ma.P_IP6 &&
144+
inAddrRange(head[0].RawValue(), nat64)
132145
}

net/resolve.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,28 @@ import (
77
)
88

99
// ResolveUnspecifiedAddress expands an unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
10-
// use the known local interfaces. If ifaceAddr is nil, we request interface addresses
11-
// from the network stack. (this is so you can provide a cached value if resolving many addrs)
10+
// use the known local interfaces.
1211
func ResolveUnspecifiedAddress(resolve ma.Multiaddr, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
1312
// split address into its components
14-
first, rest := ma.SplitFirst(resolve)
13+
first, rest := splitFirstSlice(resolve)
1514

1615
// if first component (ip) is not unspecified, use it as is.
17-
if !IsIPUnspecified(first.Multiaddr()) {
16+
if !IsIPUnspecified(first) {
1817
return []ma.Multiaddr{resolve}, nil
1918
}
2019

21-
resolveProto := resolve.Protocols()[0].Code
20+
resolveProto := first[0].Code()
2221
out := make([]ma.Multiaddr, 0, len(ifaceAddrs))
2322
for _, ia := range ifaceAddrs {
24-
iafirst, _ := ma.SplitFirst(ia)
23+
iafirst, _ := splitFirstSlice(ia)
2524
// must match the first protocol to be resolve.
26-
if iafirst.Protocol().Code != resolveProto {
25+
if len(iafirst) == 0 || iafirst[0].Code() != resolveProto {
2726
continue
2827
}
2928

3029
joined := ia
31-
if rest != nil {
32-
joined = ma.Join(ia, rest)
30+
if len(rest) > 0 {
31+
joined = ma.Join(iafirst, rest)
3332
}
3433
out = append(out, joined)
3534
}

0 commit comments

Comments
 (0)