[go1.17] internal/subtle xor 简化代码,去除重复 #75

This commit is contained in:
Sun Yimin 2023-03-08 10:10:28 +08:00 committed by GitHub
parent 71657b8d34
commit cedbf0c355
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 83 deletions

26
internal/subtle/xor.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package subtle
// XORBytes sets dst[i] = x[i] ^ y[i] for all i < n = min(len(x), len(y)),
// returning n, the number of bytes written to dst.
// If dst does not have length at least n,
// XORBytes panics without writing anything to dst.
//
// will use golang SDK directly later.
func XORBytes(dst, x, y []byte) int {
n := len(x)
if len(y) < n {
n = len(y)
}
if n == 0 {
return 0
}
if n > len(dst) {
panic("subtle.XORBytes: dst too short")
}
xorBytes(&dst[0], &x[0], &y[0], n) // arch-specific
return n
}

View File

@ -7,22 +7,5 @@
package subtle package subtle
// XORBytes xors the bytes in a and b. The destination should have enough
// space, otherwise XORBytes will panic. Returns the number of bytes xor'd.
func XORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
if n == 0 {
return 0
}
if n > len(dst) {
panic("subtle.XORBytes: dst too short")
}
xorBytes(&dst[0], &a[0], &b[0], n) // amd64 must have SSE2
return n
}
//go:noescape //go:noescape
func xorBytes(dst, a, b *byte, n int) func xorBytes(dst, a, b *byte, n int)

View File

@ -7,23 +7,5 @@
package subtle package subtle
// XORBytes xors the bytes in a and b. The destination should have enough
// space, otherwise XORBytes will panic. Returns the number of bytes xor'd.
func XORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
if n == 0 {
return 0
}
if n > len(dst) {
panic("subtle.XORBytes: dst too short")
}
xorBytes(&dst[0], &a[0], &b[0], n)
return n
}
//go:noescape //go:noescape
func xorBytes(dst, a, b *byte, n int) func xorBytes(dst, a, b *byte, n int)

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// //
//go:build !amd64 && !arm64 || purego //go:build (!amd64 && !arm64) || purego
// +build !amd64,!arm64 purego // +build !amd64,!arm64 purego
package subtle package subtle
@ -12,63 +12,48 @@ import (
"unsafe" "unsafe"
) )
const wordSize = int(unsafe.Sizeof(uintptr(0))) const wordSize = unsafe.Sizeof(uintptr(0))
const supportsUnaligned = runtime.GOARCH == "386" || const supportsUnaligned = runtime.GOARCH == "386" ||
runtime.GOARCH == "amd64" ||
runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64" ||
runtime.GOARCH == "ppc64le" || runtime.GOARCH == "ppc64le" ||
runtime.GOARCH == "s390x" runtime.GOARCH == "s390x"
// XORBytes xors the bytes in a and b. The destination should have enough func xorBytes(dstb, xb, yb *byte, n int) {
// space, otherwise XORBytes will panic. Returns the number of bytes xor'd. // xorBytes assembly is written using pointers and n. Back to slices.
func XORBytes(dst, a, b []byte) int { dst := unsafe.Slice(dstb, n)
n := len(a) x := unsafe.Slice(xb, n)
if len(b) < n { y := unsafe.Slice(yb, n)
n = len(b)
}
if n == 0 {
return 0
}
if n > len(dst) {
panic("subtle.XORBytes: dst too short")
}
switch { if supportsUnaligned || aligned(dstb, xb, yb) {
case supportsUnaligned: xorLoop(words(dst), words(x), words(y))
fastXORBytes(dst, a, b, n) if uintptr(n)%wordSize == 0 {
default: return
// TODO(hanwen): if (dst, a, b) have common alignment
// we could still try fastXORBytes. It is not clear
// how often this happens, and it's only worth it if
// the block encryption itself is hardware
// accelerated.
safeXORBytes(dst, a, b, n)
}
return n
}
// fastXORBytes xors in bulk. It only works on architectures that
// support unaligned read/writes.
// n needs to be smaller or equal than the length of a and b.
func fastXORBytes(dst, a, b []byte, n int) {
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]
} }
done := n &^ int(wordSize-1)
dst = dst[done:]
x = x[done:]
y = y[done:]
} }
xorLoop(dst, x, y)
for i := (n - n%wordSize); i < n; i++ {
dst[i] = a[i] ^ b[i]
}
} }
// n needs to be smaller or equal than the length of a and b. // aligned reports whether dst, x, and y are all word-aligned pointers.
func safeXORBytes(dst, a, b []byte, n int) { func aligned(dst, x, y *byte) bool {
for i := 0; i < n; i++ { return (uintptr(unsafe.Pointer(dst))|uintptr(unsafe.Pointer(x))|uintptr(unsafe.Pointer(y)))&(wordSize-1) == 0
dst[i] = a[i] ^ b[i] }
// words returns a []uintptr pointing at the same data as x,
// with any trailing partial word removed.
func words(x []byte) []uintptr {
return unsafe.Slice((*uintptr)(unsafe.Pointer(&x[0])), uintptr(len(x))/wordSize)
}
func xorLoop[T byte | uintptr](dst, x, y []T) {
x = x[:len(dst)] // remove bounds check in loop
y = y[:len(dst)] // remove bounds check in loop
for i := range dst {
dst[i] = x[i] ^ y[i]
} }
} }