Skip to content

Commit

Permalink
Avoid loop bounds check.
Browse files Browse the repository at this point in the history
  • Loading branch information
klauspost committed Jan 14, 2025
1 parent a5e5c92 commit d5a26ee
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 14 deletions.
20 changes: 12 additions & 8 deletions flate/matchlen_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,31 @@
package flate

import (
"encoding/binary"
"math/bits"

"github.com/klauspost/compress/internal/le"
)

// matchLen returns the maximum common prefix length of a and b.
// a must be the shortest of the two.
func matchLen(a, b []byte) (n int) {
for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] {
diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b)
if diff != 0 {
return n + bits.TrailingZeros64(diff)>>3
if len(a) >= 8 && len(b) >= 8 {
left := len(a) - 8
for left >= 0 {
diff := le.Load64(a, n) ^ le.Load64(b, n)
if diff != 0 {
return n + bits.TrailingZeros64(diff)>>3
}
n += 8
left -= 8
}
n += 8
}

for i := range a {
for i := range a[n:] {
if a[i] != b[i] {
break
}
n++
}
return n

}
8 changes: 8 additions & 0 deletions internal/le/unsafe_enabled.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,32 @@ import (
"unsafe"
)

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

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

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

// Store16 will store v at b.
// If the compiler can prove
func Store16(b []byte, v uint16) {
//binary.LittleEndian.PutUint16(b, v)
*(*uint16)(unsafe.Pointer(&b[0])) = v
Expand Down
16 changes: 10 additions & 6 deletions zstd/matchlen_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ import (
// matchLen returns the maximum common prefix length of a and b.
// a must be the shortest of the two.
func matchLen(a, b []byte) (n int) {
for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] {
diff := le.Load64(a, 0) ^ le.Load64(b, 0)
if diff != 0 {
return n + bits.TrailingZeros64(diff)>>3
if len(a) >= 8 && len(b) >= 8 {
left := len(a) - 8
for left >= 0 {
diff := le.Load64(a, n) ^ le.Load64(b, n)
if diff != 0 {
return n + bits.TrailingZeros64(diff)>>3
}
n += 8
left -= 8
}
n += 8
}

for i := range a {
for i := range a[n:] {
if a[i] != b[i] {
break
}
Expand Down

0 comments on commit d5a26ee

Please sign in to comment.