xor_generic.mx raw
1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 //go:build (!amd64 && !arm64 && !loong64 && !mips && !mipsle && !mips64 && !mips64le && !ppc64 && !ppc64le && !riscv64) || purego
6
7 package subtle
8
9 import (
10 "runtime"
11 "unsafe"
12 )
13
14 const wordSize = unsafe.Sizeof(uintptr(0))
15
16 const supportsUnaligned = runtime.GOARCH == "386" ||
17 runtime.GOARCH == "amd64" ||
18 runtime.GOARCH == "ppc64" ||
19 runtime.GOARCH == "ppc64le" ||
20 runtime.GOARCH == "s390x"
21
22 func xorBytes(dstb, xb, yb *byte, n int) {
23 // xorBytes assembly is written using pointers and n. Back to slices.
24 dst := unsafe.Slice(dstb, n)
25 x := unsafe.Slice(xb, n)
26 y := unsafe.Slice(yb, n)
27
28 if supportsUnaligned || aligned(dstb, xb, yb) {
29 xorLoop(words(dst), words(x), words(y))
30 if uintptr(n)%wordSize == 0 {
31 return
32 }
33 done := n &^ int(wordSize-1)
34 dst = dst[done:]
35 x = x[done:]
36 y = y[done:]
37 }
38 xorLoop(dst, x, y)
39 }
40
41 // aligned reports whether dst, x, and y are all word-aligned pointers.
42 func aligned(dst, x, y *byte) bool {
43 return (uintptr(unsafe.Pointer(dst))|uintptr(unsafe.Pointer(x))|uintptr(unsafe.Pointer(y)))&(wordSize-1) == 0
44 }
45
46 // words returns a []uintptr pointing at the same data as x,
47 // with any trailing partial word removed.
48 func words(x []byte) []uintptr {
49 n := uintptr(len(x)) / wordSize
50 if n == 0 {
51 // Avoid creating a *uintptr that refers to data smaller than a uintptr;
52 // see issue 59334.
53 return nil
54 }
55 return unsafe.Slice((*uintptr)(unsafe.Pointer(&x[0])), n)
56 }
57
58 func xorLoop[T byte | uintptr](dst, x, y []T) {
59 x = x[:len(dst)] // remove bounds check in loop
60 y = y[:len(dst)] // remove bounds check in loop
61 for i := range dst {
62 dst[i] = x[i] ^ y[i]
63 }
64 }
65