common/bitutil: deprecate XORBytes in favor of stdlib crypto/subtle (#33331)

XORBytes was added to package crypto/subtle in Go 1.20, and it's faster 
than our bitutil.XORBytes. There is only one use of this function
across go-ethereum so we can simply deprecate the custom implementation.

---------

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
cui 2025-12-09 00:40:59 +08:00 committed by GitHub
parent af47d9b472
commit 31f9c9ff75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 26 additions and 43 deletions

View file

@ -8,6 +8,7 @@
package bitutil package bitutil
import ( import (
"crypto/subtle"
"runtime" "runtime"
"unsafe" "unsafe"
) )
@ -17,46 +18,16 @@ const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" |
// XORBytes xors the bytes in a and b. The destination is assumed to have enough // XORBytes xors the bytes in a and b. The destination is assumed to have enough
// space. Returns the number of bytes xor'd. // space. Returns the number of bytes xor'd.
//
// If dst does not have length at least n,
// XORBytes panics without writing anything to dst.
//
// dst and x or y may overlap exactly or not at all,
// otherwise XORBytes may panic.
//
// Deprecated: use crypto/subtle.XORBytes
func XORBytes(dst, a, b []byte) int { func XORBytes(dst, a, b []byte) int {
if supportsUnaligned { return subtle.XORBytes(dst, a, b)
return fastXORBytes(dst, a, b)
}
return safeXORBytes(dst, a, b)
}
// fastXORBytes xors in bulk. It only works on architectures that support
// unaligned read/writes.
func fastXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
w := n / wordSize
if w > 0 {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
for i := 0; i < w; i++ {
dw[i] = aw[i] ^ bw[i]
}
}
for i := n - n%wordSize; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
// safeXORBytes xors one by one. It works on all architectures, independent if
// it supports unaligned read/writes or not.
func safeXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
} }
// ANDBytes ands the bytes in a and b. The destination is assumed to have enough // ANDBytes ands the bytes in a and b. The destination is assumed to have enough

View file

@ -29,7 +29,7 @@ func TestXOR(t *testing.T) {
d2 := make([]byte, 1023+alignD)[alignD:] d2 := make([]byte, 1023+alignD)[alignD:]
XORBytes(d1, p, q) XORBytes(d1, p, q)
safeXORBytes(d2, p, q) naiveXOR(d2, p, q)
if !bytes.Equal(d1, d2) { if !bytes.Equal(d1, d2) {
t.Error("not equal", d1, d2) t.Error("not equal", d1, d2)
} }
@ -38,6 +38,18 @@ func TestXOR(t *testing.T) {
} }
} }
// naiveXOR xors bytes one by one.
func naiveXOR(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
// Tests that bitwise AND works for various alignments. // Tests that bitwise AND works for various alignments.
func TestAND(t *testing.T) { func TestAND(t *testing.T) {
for alignP := 0; alignP < 2; alignP++ { for alignP := 0; alignP < 2; alignP++ {
@ -134,7 +146,7 @@ func benchmarkBaseXOR(b *testing.B, size int) {
p, q := make([]byte, size), make([]byte, size) p, q := make([]byte, size), make([]byte, size)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
safeXORBytes(p, p, q) naiveXOR(p, p, q)
} }
} }

View file

@ -24,6 +24,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/hmac" "crypto/hmac"
"crypto/rand" "crypto/rand"
"crypto/subtle"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@ -33,7 +34,6 @@ import (
"net" "net"
"time" "time"
"github.com/ethereum/go-ethereum/common/bitutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
@ -677,6 +677,6 @@ func exportPubkey(pub *ecies.PublicKey) []byte {
func xor(one, other []byte) (xor []byte) { func xor(one, other []byte) (xor []byte) {
xor = make([]byte, len(one)) xor = make([]byte, len(one))
bitutil.XORBytes(xor, one, other) subtle.XORBytes(xor, one, other)
return xor return xor
} }