Skip to content

Commit 2fcbfef

Browse files
committed
Avoid loop bounds check.
1 parent a5e5c92 commit 2fcbfef

File tree

3 files changed

+32
-13
lines changed

3 files changed

+32
-13
lines changed

flate/matchlen_generic.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,32 @@
77
package flate
88

99
import (
10-
"encoding/binary"
1110
"math/bits"
11+
12+
"github.com/klauspost/compress/internal/le"
1213
)
1314

1415
// matchLen returns the maximum common prefix length of a and b.
1516
// a must be the shortest of the two.
1617
func matchLen(a, b []byte) (n int) {
17-
for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] {
18-
diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b)
19-
if diff != 0 {
20-
return n + bits.TrailingZeros64(diff)>>3
18+
if len(a) >= 8 && len(b) >= 8 {
19+
left := len(a) - 8
20+
for left >= 0 {
21+
diff := le.Load64(a, n) ^ le.Load64(b, n)
22+
if diff != 0 {
23+
return n + bits.TrailingZeros64(diff)>>3
24+
}
25+
n += 8
26+
left -= 8
2127
}
22-
n += 8
2328
}
24-
29+
a = a[n:]
30+
b = b[n:]
2531
for i := range a {
2632
if a[i] != b[i] {
2733
break
2834
}
2935
n++
3036
}
3137
return n
32-
3338
}

internal/le/unsafe_enabled.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,32 @@ import (
88
"unsafe"
99
)
1010

11+
// Load16 will load from b at index i.
12+
// If the compiler can prove that b is at least 1 byte this will be without bounds check.
1113
func Load16[I Indexer](b []byte, i I) uint16 {
1214
//return binary.LittleEndian.Uint16(b[i:])
1315
//return *(*uint16)(unsafe.Pointer(&b[i]))
1416
return *(*uint16)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i)*unsafe.Sizeof(b[0])))
1517
}
1618

19+
// Load32 will load from b at index i.
20+
// If the compiler can prove that b is at least 1 byte this will be without bounds check.
1721
func Load32[I Indexer](b []byte, i I) uint32 {
1822
//return binary.LittleEndian.Uint32(b[i:])
1923
//return *(*uint32)(unsafe.Pointer(&b[i]))
2024
return *(*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i)*unsafe.Sizeof(b[0])))
2125
}
2226

27+
// Load64 will load from b at index i.
28+
// If the compiler can prove that b is at least 1 byte this will be without bounds check.
2329
func Load64[I Indexer](b []byte, i I) uint64 {
2430
//return binary.LittleEndian.Uint64(b[i:])
2531
//return *(*uint64)(unsafe.Pointer(&b[i]))
2632
return *(*uint64)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i)*unsafe.Sizeof(b[0])))
2733
}
2834

35+
// Store16 will store v at b.
36+
// If the compiler can prove
2937
func Store16(b []byte, v uint16) {
3038
//binary.LittleEndian.PutUint16(b, v)
3139
*(*uint16)(unsafe.Pointer(&b[0])) = v

zstd/matchlen_generic.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@ import (
1515
// matchLen returns the maximum common prefix length of a and b.
1616
// a must be the shortest of the two.
1717
func matchLen(a, b []byte) (n int) {
18-
for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] {
19-
diff := le.Load64(a, 0) ^ le.Load64(b, 0)
20-
if diff != 0 {
21-
return n + bits.TrailingZeros64(diff)>>3
18+
if len(a) >= 8 && len(b) >= 8 {
19+
left := len(a) - 8
20+
for left >= 0 {
21+
diff := le.Load64(a, n) ^ le.Load64(b, n)
22+
if diff != 0 {
23+
return n + bits.TrailingZeros64(diff)>>3
24+
}
25+
n += 8
26+
left -= 8
2227
}
23-
n += 8
2428
}
29+
a = a[n:]
30+
b = b[n:]
2531

2632
for i := range a {
2733
if a[i] != b[i] {

0 commit comments

Comments
 (0)