Skip to content

Commit cfdbe68

Browse files
committed
Super fast XOR
Closes #76
1 parent 5cdcc5e commit cfdbe68

File tree

3 files changed

+85
-2
lines changed

3 files changed

+85
-2
lines changed

websocket_test.go

+1
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

+83-2
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,87 @@ 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+
66+
// Then we xor until b is less than 64 bytes.
67+
for len(b) >= 64 {
68+
v := binary.LittleEndian.Uint64(b)
69+
binary.LittleEndian.PutUint64(b, v^k)
70+
v = binary.LittleEndian.Uint64(b[8:])
71+
binary.LittleEndian.PutUint64(b[8:], v^k)
72+
v = binary.LittleEndian.Uint64(b[16:])
73+
binary.LittleEndian.PutUint64(b[16:], v^k)
74+
v = binary.LittleEndian.Uint64(b[24:])
75+
binary.LittleEndian.PutUint64(b[24:], v^k)
76+
v = binary.LittleEndian.Uint64(b[32:])
77+
binary.LittleEndian.PutUint64(b[32:], v^k)
78+
v = binary.LittleEndian.Uint64(b[40:])
79+
binary.LittleEndian.PutUint64(b[40:], v^k)
80+
v = binary.LittleEndian.Uint64(b[48:])
81+
binary.LittleEndian.PutUint64(b[48:], v^k)
82+
v = binary.LittleEndian.Uint64(b[56:])
83+
binary.LittleEndian.PutUint64(b[56:], v^k)
84+
b = b[64:]
85+
}
86+
87+
// Then we xor until b is less than 32 bytes.
88+
for len(b) >= 32 {
89+
v := binary.LittleEndian.Uint64(b)
90+
binary.LittleEndian.PutUint64(b, v^k)
91+
v = binary.LittleEndian.Uint64(b[8:])
92+
binary.LittleEndian.PutUint64(b[8:], v^k)
93+
v = binary.LittleEndian.Uint64(b[16:])
94+
binary.LittleEndian.PutUint64(b[16:], v^k)
95+
v = binary.LittleEndian.Uint64(b[24:])
96+
binary.LittleEndian.PutUint64(b[24:], v^k)
97+
b = b[32:]
98+
}
99+
100+
// Then we xor until b is less than 16 bytes.
101+
for len(b) >= 16 {
102+
v := binary.LittleEndian.Uint64(b)
103+
binary.LittleEndian.PutUint64(b, v^k)
104+
v = binary.LittleEndian.Uint64(b[8:])
105+
binary.LittleEndian.PutUint64(b[8:], v^k)
106+
b = b[16:]
107+
}
108+
28109
// Then we xor until b is less than 8 bytes.
29110
for len(b) >= 8 {
30111
v := binary.LittleEndian.Uint64(b)

xor_test.go

+1
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)