Skip to content

Commit 012b861

Browse files
committed
feat: vendor xor
0 parents  commit 012b861

14 files changed

+535
-0
lines changed

.editorconfig

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# http://editorconfig.org/
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
insert_final_newline = true
8+
trim_trailing_whitespace = true
9+
end_of_line = lf
10+
11+
[{*.go, go.mod}]
12+
indent_style = tab
13+
indent_size = 4
14+
15+
16+
# Makefiles always use tabs for indentation
17+
[Makefile]
18+
indent_style = tab

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.idea
2+
_bin/*
3+
4+
*-fuzz.zip
5+
/cmd/gotdecho/gotdecho

LICENSE

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2009 The Go Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
6+
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above
10+
copyright notice, this list of conditions and the following disclaimer
11+
in the documentation and/or other materials provided with the
12+
distribution.
13+
* Neither the name of Google Inc. nor the names of its
14+
contributors may be used to endorse or promote products derived from
15+
this software without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# xor
2+
3+
Package xor implements XOR operations on byte slices.
4+
Extracted from [crypto/cipher](https://golang.org/src/crypto/cipher/xor_generic.go).
5+
6+
## Reference
7+
8+
* [issues/30553](https://github.com/golang/go/issues/30553) as rejected proposal to provide XOR in go stdlib
9+
*

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/gotd/xor
2+
3+
go 1.15

xor.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package xor
2+
3+
// Bytes xors the bytes in a and b. The destination should have enough
4+
// space, otherwise Bytes will panic. Returns the number of bytes xor'd.
5+
func Bytes(dst, a, b []byte) int {
6+
return xorBytes(dst, a, b)
7+
}
8+
9+
// Words XORs multiples of 4 or 8 bytes (depending on architecture.)
10+
// The slice arguments a and b are assumed to be of equal length.
11+
func Words(dst, a, b []byte) {
12+
xorWords(dst, a, b)
13+
}

xor_amd64.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2018 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+
package xor
6+
7+
// xorBytes xors the bytes in a and b. The destination should have enough
8+
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
9+
func xorBytes(dst, a, b []byte) int {
10+
n := len(a)
11+
if len(b) < n {
12+
n = len(b)
13+
}
14+
if n == 0 {
15+
return 0
16+
}
17+
_ = dst[n-1]
18+
xorBytesSSE2(&dst[0], &a[0], &b[0], n) // amd64 must have SSE2
19+
return n
20+
}
21+
22+
func xorWords(dst, a, b []byte) {
23+
xorBytes(dst, a, b)
24+
}
25+
26+
//go:noescape
27+
func xorBytesSSE2(dst, a, b *byte, n int)

xor_amd64.s

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2018 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+
#include "textflag.h"
6+
7+
// func xorBytesSSE2(dst, a, b *byte, n int)
8+
TEXT ·xorBytesSSE2(SB), NOSPLIT, $0
9+
MOVQ dst+0(FP), BX
10+
MOVQ a+8(FP), SI
11+
MOVQ b+16(FP), CX
12+
MOVQ n+24(FP), DX
13+
TESTQ $15, DX // AND 15 & len, if not zero jump to not_aligned.
14+
JNZ not_aligned
15+
16+
aligned:
17+
MOVQ $0, AX // position in slices
18+
19+
loop16b:
20+
MOVOU (SI)(AX*1), X0 // XOR 16byte forwards.
21+
MOVOU (CX)(AX*1), X1
22+
PXOR X1, X0
23+
MOVOU X0, (BX)(AX*1)
24+
ADDQ $16, AX
25+
CMPQ DX, AX
26+
JNE loop16b
27+
RET
28+
29+
loop_1b:
30+
SUBQ $1, DX // XOR 1byte backwards.
31+
MOVB (SI)(DX*1), DI
32+
MOVB (CX)(DX*1), AX
33+
XORB AX, DI
34+
MOVB DI, (BX)(DX*1)
35+
TESTQ $7, DX // AND 7 & len, if not zero jump to loop_1b.
36+
JNZ loop_1b
37+
CMPQ DX, $0 // if len is 0, ret.
38+
JE ret
39+
TESTQ $15, DX // AND 15 & len, if zero jump to aligned.
40+
JZ aligned
41+
42+
not_aligned:
43+
TESTQ $7, DX // AND $7 & len, if not zero jump to loop_1b.
44+
JNE loop_1b
45+
SUBQ $8, DX // XOR 8bytes backwards.
46+
MOVQ (SI)(DX*1), DI
47+
MOVQ (CX)(DX*1), AX
48+
XORQ AX, DI
49+
MOVQ DI, (BX)(DX*1)
50+
CMPQ DX, $16 // if len is greater or equal 16 here, it must be aligned.
51+
JGE aligned
52+
53+
ret:
54+
RET

xor_arm64.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2020 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+
package xor
6+
7+
// xorBytes xors the bytes in a and b. The destination should have enough
8+
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
9+
func xorBytes(dst, a, b []byte) int {
10+
n := len(a)
11+
if len(b) < n {
12+
n = len(b)
13+
}
14+
if n == 0 {
15+
return 0
16+
}
17+
// make sure dst has enough space
18+
_ = dst[n-1]
19+
20+
xorBytesARM64(&dst[0], &a[0], &b[0], n)
21+
return n
22+
}
23+
24+
func xorWords(dst, a, b []byte) {
25+
xorBytes(dst, a, b)
26+
}
27+
28+
//go:noescape
29+
func xorBytesARM64(dst, a, b *byte, n int)

xor_arm64.s

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2020 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+
#include "textflag.h"
6+
7+
// func xorBytesARM64(dst, a, b *byte, n int)
8+
TEXT ·xorBytesARM64(SB), NOSPLIT|NOFRAME, $0
9+
MOVD dst+0(FP), R0
10+
MOVD a+8(FP), R1
11+
MOVD b+16(FP), R2
12+
MOVD n+24(FP), R3
13+
CMP $64, R3
14+
BLT tail
15+
loop_64:
16+
VLD1.P 64(R1), [V0.B16, V1.B16, V2.B16, V3.B16]
17+
VLD1.P 64(R2), [V4.B16, V5.B16, V6.B16, V7.B16]
18+
VEOR V0.B16, V4.B16, V4.B16
19+
VEOR V1.B16, V5.B16, V5.B16
20+
VEOR V2.B16, V6.B16, V6.B16
21+
VEOR V3.B16, V7.B16, V7.B16
22+
VST1.P [V4.B16, V5.B16, V6.B16, V7.B16], 64(R0)
23+
SUBS $64, R3
24+
CMP $64, R3
25+
BGE loop_64
26+
tail:
27+
// quick end
28+
CBZ R3, end
29+
TBZ $5, R3, less_than32
30+
VLD1.P 32(R1), [V0.B16, V1.B16]
31+
VLD1.P 32(R2), [V2.B16, V3.B16]
32+
VEOR V0.B16, V2.B16, V2.B16
33+
VEOR V1.B16, V3.B16, V3.B16
34+
VST1.P [V2.B16, V3.B16], 32(R0)
35+
less_than32:
36+
TBZ $4, R3, less_than16
37+
LDP.P 16(R1), (R11, R12)
38+
LDP.P 16(R2), (R13, R14)
39+
EOR R11, R13, R13
40+
EOR R12, R14, R14
41+
STP.P (R13, R14), 16(R0)
42+
less_than16:
43+
TBZ $3, R3, less_than8
44+
MOVD.P 8(R1), R11
45+
MOVD.P 8(R2), R12
46+
EOR R11, R12, R12
47+
MOVD.P R12, 8(R0)
48+
less_than8:
49+
TBZ $2, R3, less_than4
50+
MOVWU.P 4(R1), R13
51+
MOVWU.P 4(R2), R14
52+
EORW R13, R14, R14
53+
MOVWU.P R14, 4(R0)
54+
less_than4:
55+
TBZ $1, R3, less_than2
56+
MOVHU.P 2(R1), R15
57+
MOVHU.P 2(R2), R16
58+
EORW R15, R16, R16
59+
MOVHU.P R16, 2(R0)
60+
less_than2:
61+
TBZ $0, R3, end
62+
MOVBU (R1), R17
63+
MOVBU (R2), R19
64+
EORW R17, R19, R19
65+
MOVBU R19, (R0)
66+
end:
67+
RET

xor_generic.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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+
// +build !amd64,!ppc64,!ppc64le,!arm64
6+
7+
package xor
8+
9+
import (
10+
"runtime"
11+
"unsafe"
12+
)
13+
14+
// xorBytes xors the bytes in a and b. The destination should have enough
15+
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
16+
func xorBytes(dst, a, b []byte) int {
17+
n := len(a)
18+
if len(b) < n {
19+
n = len(b)
20+
}
21+
if n == 0 {
22+
return 0
23+
}
24+
25+
switch {
26+
case supportsUnaligned:
27+
fastXORBytes(dst, a, b, n)
28+
default:
29+
// TODO(hanwen): if (dst, a, b) have common alignment
30+
// we could still try fastXORBytes. It is not clear
31+
// how often this happens, and it's only worth it if
32+
// the block encryption itself is hardware
33+
// accelerated.
34+
safeXORBytes(dst, a, b, n)
35+
}
36+
return n
37+
}
38+
39+
const wordSize = int(unsafe.Sizeof(uintptr(0)))
40+
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
41+
42+
// fastXORBytes xors in bulk. It only works on architectures that
43+
// support unaligned read/writes.
44+
// n needs to be smaller or equal than the length of a and b.
45+
func fastXORBytes(dst, a, b []byte, n int) {
46+
// Assert dst has enough space
47+
_ = dst[n-1]
48+
49+
w := n / wordSize
50+
if w > 0 {
51+
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
52+
aw := *(*[]uintptr)(unsafe.Pointer(&a))
53+
bw := *(*[]uintptr)(unsafe.Pointer(&b))
54+
for i := 0; i < w; i++ {
55+
dw[i] = aw[i] ^ bw[i]
56+
}
57+
}
58+
59+
for i := (n - n%wordSize); i < n; i++ {
60+
dst[i] = a[i] ^ b[i]
61+
}
62+
}
63+
64+
// n needs to be smaller or equal than the length of a and b.
65+
func safeXORBytes(dst, a, b []byte, n int) {
66+
for i := 0; i < n; i++ {
67+
dst[i] = a[i] ^ b[i]
68+
}
69+
}
70+
71+
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
72+
// The arguments are assumed to be of equal length.
73+
func fastXORWords(dst, a, b []byte) {
74+
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
75+
aw := *(*[]uintptr)(unsafe.Pointer(&a))
76+
bw := *(*[]uintptr)(unsafe.Pointer(&b))
77+
n := len(b) / wordSize
78+
for i := 0; i < n; i++ {
79+
dw[i] = aw[i] ^ bw[i]
80+
}
81+
}
82+
83+
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
84+
// The slice arguments a and b are assumed to be of equal length.
85+
func xorWords(dst, a, b []byte) {
86+
if supportsUnaligned {
87+
fastXORWords(dst, a, b)
88+
} else {
89+
safeXORBytes(dst, a, b, len(b))
90+
}
91+
}

xor_ppc64x.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2018 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+
// +build ppc64 ppc64le
6+
7+
package xor
8+
9+
// xorBytes xors the bytes in a and b. The destination should have enough
10+
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
11+
func xorBytes(dst, a, b []byte) int {
12+
n := len(a)
13+
if len(b) < n {
14+
n = len(b)
15+
}
16+
if n == 0 {
17+
return 0
18+
}
19+
_ = dst[n-1]
20+
xorBytesVSX(&dst[0], &a[0], &b[0], n)
21+
return n
22+
}
23+
24+
func xorWords(dst, a, b []byte) {
25+
xorBytes(dst, a, b)
26+
}
27+
28+
//go:noescape
29+
func xorBytesVSX(dst, a, b *byte, n int)

0 commit comments

Comments
 (0)