Skip to content
This repository was archived by the owner on May 25, 2023. It is now read-only.

Commit bf0e22f

Browse files
bradfitzdanderson
authored andcommitted
Change uint128 from [2]uint64 to a struct of hi/lo to appease compiler
If golang/go#24416 gets fixed we could revert this. name old time/op new time/op delta StdIPv4-4 361ns ± 9% 386ns ± 8% ~ (p=0.167 n=5+5) IPv4-4 273ns ±12% 301ns ±14% ~ (p=0.238 n=5+5) IPv4_inline-4 251ns ± 5% 262ns ±10% ~ (p=0.730 n=5+5) StdIPv6-4 507ns ±18% 490ns ±13% ~ (p=0.651 n=5+5) IPv6-4 414ns ± 8% 416ns ± 6% ~ (p=1.000 n=5+5) IPv4Contains-4 30.1ns ± 4% 13.1ns ±12% -56.41% (p=0.008 n=5+5) ParseIP/v4-4 55.9ns ± 5% 49.5ns ±10% -11.35% (p=0.032 n=5+5) ParseIP/v6-4 244ns ± 9% 217ns ± 8% -10.76% (p=0.040 n=5+5) ParseIP/v6_ellipsis-4 157ns ±12% 157ns ± 7% ~ (p=0.730 n=5+5) ParseIP/v6_v4-4 191ns ±12% 168ns ±19% ~ (p=0.310 n=5+5) ParseIP/v6_zone-4 464ns ±21% 448ns ±12% ~ (p=0.841 n=5+5) StdParseIP/v4-4 186ns ± 5% 186ns ±13% ~ (p=1.000 n=5+5) StdParseIP/v6-4 362ns ± 7% 330ns ± 5% -8.68% (p=0.016 n=5+5) StdParseIP/v6_ellipsis-4 258ns ±17% 280ns ± 6% ~ (p=0.254 n=5+5) StdParseIP/v6_v4-4 427ns ±28% 457ns ±15% ~ (p=0.548 n=5+5) StdParseIP/v6_zone-4 285ns ±17% 305ns ±13% ~ (p=0.421 n=5+5) IPString/v4-4 153ns ±21% 122ns ±17% -19.79% (p=0.048 n=5+5) IPString/v6-4 356ns ± 3% 345ns ±22% ~ (p=0.151 n=5+5) IPString/v6_ellipsis-4 346ns ±13% 329ns ±20% ~ (p=0.548 n=5+5) IPString/v6_v4-4 309ns ±15% 266ns ±11% -13.90% (p=0.032 n=5+5) IPString/v6_zone-4 362ns ±18% 330ns ±21% ~ (p=0.421 n=5+5) IPPrefixMasking/IPv4_/32-4 54.8ns ± 4% 15.1ns ± 4% -72.44% (p=0.008 n=5+5) IPPrefixMasking/IPv4_/17-4 54.9ns ± 4% 14.6ns ± 5% -73.38% (p=0.008 n=5+5) IPPrefixMasking/IPv4_/0-4 55.0ns ± 4% 13.5ns ± 6% -75.50% (p=0.008 n=5+5) IPPrefixMasking/IPv6_/128-4 55.0ns ± 1% 15.5ns ± 7% -71.79% (p=0.008 n=5+5) IPPrefixMasking/IPv6_/65-4 54.2ns ± 2% 14.6ns ± 5% -73.04% (p=0.008 n=5+5) IPPrefixMasking/IPv6_/0-4 55.4ns ± 2% 14.9ns ± 9% -73.06% (p=0.008 n=5+5) IPPrefixMasking/IPv6_zone_/128-4 54.4ns ± 1% 15.0ns ±13% -72.44% (p=0.008 n=5+5) IPPrefixMasking/IPv6_zone_/65-4 55.5ns ± 2% 14.8ns ±16% -73.31% (p=0.008 n=5+5) IPPrefixMasking/IPv6_zone_/0-4 54.8ns ± 4% 14.7ns ± 8% -73.23% (p=0.008 n=5+5) ParseIPPort/v4-4 118ns ± 8% 103ns ± 9% -12.88% (p=0.008 n=5+5) ParseIPPort/v6-4 318ns ±12% 262ns ± 6% -17.38% (p=0.008 n=5+5) ParseIPPort/v6_ellipsis-4 243ns ±10% 240ns ±19% ~ (p=0.841 n=5+5) ParseIPPort/v6_v4-4 279ns ±14% 237ns ±16% -14.91% (p=0.040 n=5+5) ParseIPPort/v6_zone-4 578ns ± 9% 546ns ± 7% ~ (p=0.222 n=5+5) IPRangePrefix-4 142ns ± 5% 61ns ±11% -56.74% (p=0.008 n=5+5) IPSetFuzz-4 41.0µs ±16% 33.3µs ±15% -18.87% (p=0.016 n=5+5) Fixes #102 Signed-off-by: Brad Fitzpatrick <[email protected]>
1 parent 9a8898b commit bf0e22f

File tree

3 files changed

+35
-34
lines changed

3 files changed

+35
-34
lines changed

inlining_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func TestInlining(t *testing.T) {
4646
"(*IPSet).RemoveRange",
4747
"(*uint128).clear",
4848
"(*uint128).set",
49+
"(*uint128).halves",
4950
"IP.BitLen",
5051
"IP.IPAddr",
5152
"IP.Is4",
@@ -58,8 +59,6 @@ func TestInlining(t *testing.T) {
5859
"IP.MarshalText",
5960
"IP.Unmap",
6061
"IP.Zone",
61-
"IP.hi",
62-
"IP.lo",
6362
"IP.v4",
6463
"IP.v6",
6564
"IP.v6u16",

netaddr.go

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,16 @@ const maxUint16 = 1<<16 - 1
4040
// Its memory representation is 24 bytes on 64-bit machines (the same
4141
// size as a Go slice header) for both IPv4 and IPv6 address.
4242
type IP struct {
43-
// addr are the hi (the first uint64) and lo bits (second
44-
// uint64) of an IPv6 address. If z==z4, hi and lo contain the
45-
// IPv4-mapped IPv6 address.
43+
// addr are the hi and lo bits of an IPv6 address. If z==z4,
44+
// hi and lo contain the IPv4-mapped IPv6 address.
4645
//
4746
// hi and lo are constructed by interpreting a 16-byte IPv6
4847
// address as a big-endian 128-bit number. The most significant
4948
// bits of that number go into hi, the rest into lo.
5049
//
5150
// For example, 0011:2233:4455:6677:8899:aabb:ccdd:eeff is stored as:
52-
// addr[0] (hi) = 0x0011223344556677
53-
// addr[1] (lo) = 0x8899aabbccddeeff
51+
// addr.hi = 0x0011223344556677
52+
// addr.lo = 0x8899aabbccddeeff
5453
//
5554
// We store IPs like this, rather than as [16]byte, because it
5655
// turns most operations on IPs into arithmetic and bit-twiddling
@@ -68,9 +67,6 @@ type IP struct {
6867
z *intern.Value
6968
}
7069

71-
func (ip IP) hi() uint64 { return ip.addr[0] }
72-
func (ip IP) lo() uint64 { return ip.addr[1] }
73-
7470
// z0, z4, and z6noz are sentinel IP.z values.
7571
// See the IP type's field docs.
7672
var (
@@ -379,17 +375,17 @@ func FromStdIPRaw(std net.IP) (ip IP, ok bool) {
379375
// v4 returns the i'th byte of ip. If ip is not an IPv4, v4 returns
380376
// unspecified garbage.
381377
func (ip IP) v4(i uint8) uint8 {
382-
return uint8(ip.lo() >> ((3 - i) * 8))
378+
return uint8(ip.addr.lo >> ((3 - i) * 8))
383379
}
384380

385381
// v6 returns the i'th byte of ip. If ip is an IPv4 address, this
386382
// accesses the IPv4-mapped IPv6 address form of the IP.
387383
func (ip IP) v6(i uint8) uint8 {
388-
return uint8(ip.addr[(i/8)%2] >> ((7 - i%8) * 8))
384+
return uint8(*(ip.addr.halves()[(i/8)%2]) >> ((7 - i%8) * 8))
389385
}
390386

391387
func (ip IP) v6u16(i uint8) uint16 {
392-
return uint16(ip.addr[(i/4)%2] >> ((3 - i%4) * 16))
388+
return uint16(*(ip.addr.halves()[(i/4)%2]) >> ((3 - i%4) * 16))
393389
}
394390

395391
// IsZero reports whether ip is the zero value of the IP type.
@@ -436,12 +432,12 @@ func (ip IP) Compare(ip2 IP) int {
436432
if f1 > f2 {
437433
return 1
438434
}
439-
if hi1, hi2 := ip.hi(), ip2.hi(); hi1 < hi2 {
435+
if hi1, hi2 := ip.addr.hi, ip2.addr.hi; hi1 < hi2 {
440436
return -1
441437
} else if hi1 > hi2 {
442438
return 1
443439
}
444-
if lo1, lo2 := ip.lo(), ip2.lo(); lo1 < lo2 {
440+
if lo1, lo2 := ip.addr.lo, ip2.addr.lo; lo1 < lo2 {
445441
return -1
446442
} else if lo1 > lo2 {
447443
return 1
@@ -496,7 +492,7 @@ func (ip IP) Is4() bool {
496492

497493
// Is4in6 reports whether ip is an IPv4-mapped IPv6 address.
498494
func (ip IP) Is4in6() bool {
499-
return ip.Is6() && ip.hi() == 0 && ip.lo()>>32 == 0xffff
495+
return ip.Is6() && ip.addr.hi == 0 && ip.addr.lo>>32 == 0xffff
500496
}
501497

502498
// Is6 reports whether ip is an IPv6 address, including IPv4-mapped
@@ -551,7 +547,7 @@ func (ip IP) IsLoopback() bool {
551547
return ip.v4(0) == 127
552548
}
553549
if ip.Is6() {
554-
return ip.hi() == 0 && ip.lo() == 1
550+
return ip.addr.hi == 0 && ip.addr.lo == 1
555551
}
556552
return false // zero value
557553
}
@@ -563,7 +559,7 @@ func (ip IP) IsMulticast() bool {
563559
return ip.v4(0)&0xf0 == 0xe0
564560
}
565561
if ip.Is6() {
566-
return ip.v6(0) == 0xff
562+
return ip.addr.hi>>(64-8) == 0xff // ip.v6(0) == 0xff
567563
}
568564
return false // zero value
569565
}
@@ -598,8 +594,8 @@ func (ip IP) Prefix(bits uint8) (IPPrefix, error) {
598594
// The ip zero value returns all zeroes.
599595
func (ip IP) As16() [16]byte {
600596
var ret [16]byte
601-
binary.BigEndian.PutUint64(ret[:8], ip.hi())
602-
binary.BigEndian.PutUint64(ret[8:], ip.lo())
597+
binary.BigEndian.PutUint64(ret[:8], ip.addr.hi)
598+
binary.BigEndian.PutUint64(ret[8:], ip.addr.lo)
603599
return ret
604600
}
605601

@@ -609,7 +605,7 @@ func (ip IP) As16() [16]byte {
609605
func (ip IP) As4() [4]byte {
610606
if ip.z == z4 || ip.Is4in6() {
611607
var ret [4]byte
612-
binary.BigEndian.PutUint32(ret[:], uint32(ip.lo()))
608+
binary.BigEndian.PutUint32(ret[:], uint32(ip.addr.lo))
613609
return ret
614610
}
615611
if ip.z == z0 {
@@ -1074,12 +1070,12 @@ func (p IPPrefix) Contains(ip IP) bool {
10741070
}
10751071
if ip.Is4() {
10761072
m := mask4(p.Bits)
1077-
return uint32(ip.lo())&m == uint32(p.IP.lo())&m
1073+
return uint32(ip.addr.lo)&m == uint32(p.IP.addr.lo)&m
10781074
} else {
10791075
m := mask6(p.Bits)
10801076
// TODO: benchmark whether the short circuit below is faster or slower
10811077
// than the higher level alternative: 'ip.addr.and(m) == p.IP.addr.and(m)'.
1082-
return ip.hi()&m[0] == p.IP.hi()&m[0] && ip.lo()&m[1] == p.IP.lo()&m[1]
1078+
return ip.addr.hi&m.hi == p.IP.addr.hi&m.hi && ip.addr.lo&m.lo == p.IP.addr.lo&m.lo
10831079
}
10841080
}
10851081

@@ -1273,22 +1269,24 @@ func (r IPRange) Overlaps(o IPRange) bool {
12731269
}
12741270

12751271
// uint128 represents a uint128 using two uint64s.
1276-
// Index 0 contains the high bits, index 1 contains the low.
1277-
type uint128 [2]uint64
1272+
type uint128 struct {
1273+
hi uint64
1274+
lo uint64
1275+
}
12781276

12791277
// and returns the bitwise AND of u and m (u&m).
12801278
func (u uint128) and(m uint128) uint128 {
1281-
return uint128{u[0] & m[0], u[1] & m[1]}
1279+
return uint128{u.hi & m.hi, u.lo & m.lo}
12821280
}
12831281

12841282
// xor returns the bitwise XOR of u and m (u^m).
12851283
func (u uint128) xor(m uint128) uint128 {
1286-
return uint128{u[0] ^ m[0], u[1] ^ m[1]}
1284+
return uint128{u.hi ^ m.hi, u.lo ^ m.lo}
12871285
}
12881286

12891287
// or returns the bitwise OR of u and m (u|m).
12901288
func (u uint128) or(m uint128) uint128 {
1291-
return uint128{u[0] | m[0], u[1] | m[1]}
1289+
return uint128{u.hi | m.hi, u.lo | m.lo}
12921290
}
12931291

12941292
func u64CommonPrefixLen(a, b uint64) uint8 {
@@ -1303,22 +1301,26 @@ func u64CommonPrefixLen(a, b uint64) uint8 {
13031301
}
13041302

13051303
func (a uint128) commonPrefixLen(b uint128) (n uint8) {
1306-
if n = u64CommonPrefixLen(a[0], b[0]); n == 64 {
1307-
n += u64CommonPrefixLen(a[1], b[1])
1304+
if n = u64CommonPrefixLen(a.hi, b.hi); n == 64 {
1305+
n += u64CommonPrefixLen(a.lo, b.lo)
13081306
}
13091307
return
13101308
}
13111309

1310+
func (u *uint128) halves() [2]*uint64 {
1311+
return [2]*uint64{&u.hi, &u.lo}
1312+
}
1313+
13121314
func (u *uint128) set(bit uint8) {
13131315
hli := (bit / 64) % 2 // hi/lo index: 0 or 1, respectively
13141316
s := 63 - (bit % 64)
1315-
u[hli] |= 1 << s
1317+
*(u.halves()[hli]) |= 1 << s
13161318
}
13171319

13181320
func (u *uint128) clear(bit uint8) {
13191321
hli := (bit / 64) % 2 // hi/lo index: 0 or 1, respectively
13201322
s := 63 - (bit % 64)
1321-
u[hli] &^= 1 << s
1323+
*(u.halves()[hli]) &^= 1 << s
13221324
}
13231325

13241326
// lastWithBitZero returns a copy of u with the given bit

netaddr_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,15 +2716,15 @@ func TestPointLess(t *testing.T) {
27162716
func (u uint128) bitSet(bit uint8) bool {
27172717
hli := (bit / 64) % 2 // hi/lo index: 0 or 1, respectively
27182718
s := 63 - (bit % 64)
2719-
return u[hli]&(1<<s) != 0
2719+
return *(u.halves()[hli])&(1<<s) != 0
27202720
}
27212721

27222722
func TestUint128(t *testing.T) {
27232723
randU128 := func() (uint128, string) {
27242724
var a [16]byte
27252725
rand.Read(a[:])
27262726
u := ipv6Slice(a[:]).addr
2727-
return u, fmt.Sprintf("%064b%064b", u[0], u[1])
2727+
return u, fmt.Sprintf("%064b%064b", u.hi, u.lo)
27282728
}
27292729

27302730
u128, bitStr := randU128()

0 commit comments

Comments
 (0)