Skip to content

Commit

Permalink
xor
Browse files Browse the repository at this point in the history
  • Loading branch information
RyuaNerin committed Jan 11, 2024
1 parent f0e3219 commit a155214
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 0 deletions.
3 changes: 3 additions & 0 deletions internal/subtle/xor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build go1.18 || (amd64 && !purego) || (arm64 && !purego)
// +build go1.18 amd64,!purego arm64,!purego

package subtle

// XORBytes sets dst[i] = x[i] ^ y[i] for all i < n = min(len(x), len(y)),
Expand Down
66 changes: 66 additions & 0 deletions internal/subtle/xor_generic_go18.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2013 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.

//go:build go1.18 && ((!amd64 && !arm64) || purego)
// +build go1.18
// +build !amd64,!arm64 purego

package subtle

import (
"runtime"
"unsafe"
)

const wordSize = unsafe.Sizeof(uintptr(0))

const supportsUnaligned = runtime.GOARCH == "386" ||
runtime.GOARCH == "amd64" ||
runtime.GOARCH == "ppc64" ||
runtime.GOARCH == "ppc64le" ||
runtime.GOARCH == "s390x"

func xorBytes(dstb, xb, yb *byte, n int) {
// xorBytes assembly is written using pointers and n. Back to slices.
dst := unsafe.Slice(dstb, n)

Check failure on line 26 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_arm64 (1.18)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 26 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.20)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 26 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.19)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 26 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.18)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)
x := unsafe.Slice(xb, n)

Check failure on line 27 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_arm64 (1.18)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 27 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.20)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 27 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.19)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 27 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.18)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)
y := unsafe.Slice(yb, n)

Check failure on line 28 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_arm64 (1.18)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 28 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.20)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 28 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.19)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 28 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.18)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

if supportsUnaligned || aligned(dstb, xb, yb) {
xorLoop(words(dst), words(x), words(y))

Check failure on line 31 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_arm64 (1.18)

implicit function instantiation requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 31 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.20)

implicit function instantiation requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 31 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.19)

implicit function instantiation requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 31 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.18)

implicit function instantiation requires go1.18 or later (-lang was set to go1.15; check go.mod)
if uintptr(n)%wordSize == 0 {
return
}
done := n &^ int(wordSize-1)
dst = dst[done:]
x = x[done:]
y = y[done:]
}
xorLoop(dst, x, y)
}

// aligned reports whether dst, x, and y are all word-aligned pointers.
func aligned(dst, x, y *byte) bool {
return (uintptr(unsafe.Pointer(dst))|uintptr(unsafe.Pointer(x))|uintptr(unsafe.Pointer(y)))&(wordSize-1) == 0
}

// words returns a []uintptr pointing at the same data as x,
// with any trailing partial word removed.
func words(x []byte) []uintptr {
n := uintptr(len(x)) / wordSize
if n == 0 {
// Avoid creating a *uintptr that refers to data smaller than a uintptr;
// see issue 59334.
return nil
}
return unsafe.Slice((*uintptr)(unsafe.Pointer(&x[0])), n)

Check failure on line 57 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_arm64 (1.18)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 57 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.20)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 57 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.19)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)

Check failure on line 57 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.18)

unsafe.Slice requires go1.17 or later (-lang was set to go1.15; check go.mod)
}

func xorLoop[T byte | uintptr](dst, x, y []T) {

Check failure on line 60 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_arm64 (1.18)

type parameter requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 60 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_arm64 (1.18)

embedding interface element byte|uintptr requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 60 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.20)

type parameter requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 60 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.20)

embedding interface element byte | uintptr requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 60 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.19)

type parameter requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 60 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.19)

embedding interface element byte|uintptr requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 60 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.18)

type parameter requires go1.18 or later (-lang was set to go1.15; check go.mod)

Check failure on line 60 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.18)

embedding interface element byte|uintptr requires go1.18 or later (-lang was set to go1.15; check go.mod)
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]

Check failure on line 64 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_arm64 (1.18)

invalid operation: operator ^ not defined on x[i] (variable of type T constrained by byte|uintptr)

Check failure on line 64 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.20)

invalid operation: operator ^ not defined on x[i] (variable of type T constrained by byte | uintptr)

Check failure on line 64 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.19)

invalid operation: operator ^ not defined on x[i] (variable of type T constrained by byte|uintptr)

Check failure on line 64 in internal/subtle/xor_generic_go18.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.18)

invalid operation: operator ^ not defined on x[i] (variable of type T constrained by byte|uintptr)
}
}
93 changes: 93 additions & 0 deletions internal/subtle/xor_legacy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2013 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.

//go:build !go1.18 && ((!amd64 && !arm64) || purego)
// +build !go1.18
// +build !amd64,!arm64 purego

package subtle

import (
"runtime"
"unsafe"
)

// 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, n int) int {
n := len(a)

Check failure on line 19 in internal/subtle/xor_legacy.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.17)

no new variables on left side of :=

Check failure on line 19 in internal/subtle/xor_legacy.go

View workflow job for this annotation

GitHub Actions / test_amd64 (1.15)

no new variables on left side of :=
if len(b) < n {
n = len(b)
}
if n == 0 {
return 0
}

switch {
case supportsUnaligned:
fastXORBytes(dst, a, b, n)
default:
// 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
}

const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"

// 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) {
// Assert dst has enough space
_ = dst[n-1]

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]
}
}

// n needs to be smaller or equal than the length of a and b.
func safeXORBytes(dst, a, b []byte, n int) {
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
}

// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
// The arguments are assumed to be of equal length.
func fastXORWords(dst, a, b []byte) {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
n := len(b) / wordSize
for i := 0; i < n; i++ {
dw[i] = aw[i] ^ bw[i]
}
}

// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
// The slice arguments a and b are assumed to be of equal length.
func xorWords(dst, a, b []byte) {
if supportsUnaligned {
fastXORWords(dst, a, b)
} else {
safeXORBytes(dst, a, b, len(b))
}
}

0 comments on commit a155214

Please sign in to comment.