Skip to content

Commit 3db4c88

Browse files
authored
Merge pull request #77 from nhooyr/fastxor
Super fast XOR
2 parents edbaaeb + 148ee0d commit 3db4c88

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

websocket_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ func benchConn(b *testing.B, echo, stream bool, size int) {
776776
func BenchmarkConn(b *testing.B) {
777777
sizes := []int{
778778
2,
779+
16,
779780
32,
780781
512,
781782
4096,

xor.go

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import (
1313
// to be used for masking in the key. This is so that
1414
// unmasking can be performed without the entire frame.
1515
func fastXOR(key [4]byte, keyPos int, b []byte) int {
16-
// If the payload is greater than 16 bytes, then it's worth
16+
// If the payload is greater than or equal to 16 bytes, then it's worth
1717
// masking 8 bytes at a time.
1818
// Optimization from https://github.com/golang/go/issues/31586#issuecomment-485530859
19-
if len(b) > 16 {
19+
if len(b) >= 16 {
2020
// We first create a key that is 8 bytes long
2121
// and is aligned on the position correctly.
2222
var alignedKey [8]byte
@@ -25,6 +25,86 @@ func fastXOR(key [4]byte, keyPos int, b []byte) int {
2525
}
2626
k := binary.LittleEndian.Uint64(alignedKey[:])
2727

28+
// Then we xor until b is less than 128 bytes.
29+
for len(b) >= 128 {
30+
v := binary.LittleEndian.Uint64(b)
31+
binary.LittleEndian.PutUint64(b, v^k)
32+
v = binary.LittleEndian.Uint64(b[8:])
33+
binary.LittleEndian.PutUint64(b[8:], v^k)
34+
v = binary.LittleEndian.Uint64(b[16:])
35+
binary.LittleEndian.PutUint64(b[16:], v^k)
36+
v = binary.LittleEndian.Uint64(b[24:])
37+
binary.LittleEndian.PutUint64(b[24:], v^k)
38+
v = binary.LittleEndian.Uint64(b[32:])
39+
binary.LittleEndian.PutUint64(b[32:], v^k)
40+
v = binary.LittleEndian.Uint64(b[40:])
41+
binary.LittleEndian.PutUint64(b[40:], v^k)
42+
v = binary.LittleEndian.Uint64(b[48:])
43+
binary.LittleEndian.PutUint64(b[48:], v^k)
44+
v = binary.LittleEndian.Uint64(b[56:])
45+
binary.LittleEndian.PutUint64(b[56:], v^k)
46+
v = binary.LittleEndian.Uint64(b[64:])
47+
binary.LittleEndian.PutUint64(b[64:], v^k)
48+
v = binary.LittleEndian.Uint64(b[72:])
49+
binary.LittleEndian.PutUint64(b[72:], v^k)
50+
v = binary.LittleEndian.Uint64(b[80:])
51+
binary.LittleEndian.PutUint64(b[80:], v^k)
52+
v = binary.LittleEndian.Uint64(b[88:])
53+
binary.LittleEndian.PutUint64(b[88:], v^k)
54+
v = binary.LittleEndian.Uint64(b[96:])
55+
binary.LittleEndian.PutUint64(b[96:], v^k)
56+
v = binary.LittleEndian.Uint64(b[104:])
57+
binary.LittleEndian.PutUint64(b[104:], v^k)
58+
v = binary.LittleEndian.Uint64(b[112:])
59+
binary.LittleEndian.PutUint64(b[112:], v^k)
60+
v = binary.LittleEndian.Uint64(b[120:])
61+
binary.LittleEndian.PutUint64(b[120:], v^k)
62+
b = b[128:]
63+
}
64+
65+
// Then we xor until b is less than 64 bytes.
66+
for len(b) >= 64 {
67+
v := binary.LittleEndian.Uint64(b)
68+
binary.LittleEndian.PutUint64(b, v^k)
69+
v = binary.LittleEndian.Uint64(b[8:])
70+
binary.LittleEndian.PutUint64(b[8:], v^k)
71+
v = binary.LittleEndian.Uint64(b[16:])
72+
binary.LittleEndian.PutUint64(b[16:], v^k)
73+
v = binary.LittleEndian.Uint64(b[24:])
74+
binary.LittleEndian.PutUint64(b[24:], v^k)
75+
v = binary.LittleEndian.Uint64(b[32:])
76+
binary.LittleEndian.PutUint64(b[32:], v^k)
77+
v = binary.LittleEndian.Uint64(b[40:])
78+
binary.LittleEndian.PutUint64(b[40:], v^k)
79+
v = binary.LittleEndian.Uint64(b[48:])
80+
binary.LittleEndian.PutUint64(b[48:], v^k)
81+
v = binary.LittleEndian.Uint64(b[56:])
82+
binary.LittleEndian.PutUint64(b[56:], v^k)
83+
b = b[64:]
84+
}
85+
86+
// Then we xor until b is less than 32 bytes.
87+
for len(b) >= 32 {
88+
v := binary.LittleEndian.Uint64(b)
89+
binary.LittleEndian.PutUint64(b, v^k)
90+
v = binary.LittleEndian.Uint64(b[8:])
91+
binary.LittleEndian.PutUint64(b[8:], v^k)
92+
v = binary.LittleEndian.Uint64(b[16:])
93+
binary.LittleEndian.PutUint64(b[16:], v^k)
94+
v = binary.LittleEndian.Uint64(b[24:])
95+
binary.LittleEndian.PutUint64(b[24:], v^k)
96+
b = b[32:]
97+
}
98+
99+
// Then we xor until b is less than 16 bytes.
100+
for len(b) >= 16 {
101+
v := binary.LittleEndian.Uint64(b)
102+
binary.LittleEndian.PutUint64(b, v^k)
103+
v = binary.LittleEndian.Uint64(b[8:])
104+
binary.LittleEndian.PutUint64(b[8:], v^k)
105+
b = b[16:]
106+
}
107+
28108
// Then we xor until b is less than 8 bytes.
29109
for len(b) >= 8 {
30110
v := binary.LittleEndian.Uint64(b)

xor_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func basixXOR(maskKey [4]byte, pos int, b []byte) int {
3636
func BenchmarkXOR(b *testing.B) {
3737
sizes := []int{
3838
2,
39+
16,
3940
32,
4041
512,
4142
4096,

0 commit comments

Comments
 (0)