-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
26da0d2
commit a8d81ac
Showing
6 changed files
with
186 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
using BenchmarkDotNet.Running; | ||
|
||
BenchmarkRunner.Run<AtbashBenchmarks>(); | ||
BenchmarkRunner.Run<Rot47Benchmarks>(); | ||
//BenchmarkRunner.Run(typeof(Program).Assembly); |
29 changes: 29 additions & 0 deletions
29
perf/Science.Cryptography.Ciphers.Benchmarks/Rot47Benchmarks.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using BenchmarkDotNet.Attributes; | ||
|
||
using Science.Cryptography.Ciphers.Specialized; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System; | ||
using Science.Cryptography.Ciphers; | ||
|
||
[MemoryDiagnoser] | ||
public class Rot47Benchmarks | ||
{ | ||
private static readonly Rot47Cipher General = new(); | ||
private static readonly AsciiRot47Cipher Optimized = new(); | ||
|
||
private const string Plaintext = "The quick brown fox jumps over the lazy dog."; | ||
private static readonly char[] Output = new char[64]; | ||
|
||
[Benchmark] | ||
public void Atbash() | ||
{ | ||
General.Encrypt(Plaintext, Output, out _); | ||
} | ||
|
||
[Benchmark] | ||
public void SlowXor_I64_K32() | ||
{ | ||
Optimized.Encrypt(Plaintext, Output, out _); | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
src/Science.Cryptography.Ciphers.Specialized/Optimized/AsciiRot47Cipher.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
using System; | ||
using System.Composition; | ||
using System.Numerics; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
using System.Runtime.Intrinsics; | ||
using System.Runtime.Intrinsics.X86; | ||
|
||
using TVector = System.Runtime.Intrinsics.Vector256<short>; | ||
|
||
namespace Science.Cryptography.Ciphers.Specialized; | ||
|
||
/// <summary> | ||
/// Represents the Atbash cipher. | ||
/// </summary> | ||
[Export("ASCII-ROT-47", typeof(ICipher))] | ||
public class AsciiRot47Cipher : ReciprocalCipher | ||
{ | ||
private static readonly TVector VectorOfSpace = Vector256.Create((short)' '); | ||
private static readonly TVector VectorOf47 = Vector256.Create((short)47); | ||
private static readonly TVector VectorOf126 = Vector256.Create((short)126); | ||
private static readonly TVector VectorOf33 = Vector256.Create((short)33); | ||
private static readonly TVector VectorOf94 = Vector256.Create((short)94); | ||
|
||
protected override void Crypt(ReadOnlySpan<char> text, Span<char> result, out int written) | ||
{ | ||
if (result.Length < text.Length) | ||
{ | ||
throw new ArgumentException("Size of output buffer is insufficient.", nameof(result)); | ||
} | ||
|
||
var totalVectorizedLength = 0; | ||
|
||
// process vectorized | ||
if (Avx2.IsSupported && Vector256.IsHardwareAccelerated) | ||
{ | ||
var vectorCount = text.Length / TVector.Count; | ||
totalVectorizedLength = vectorCount * TVector.Count; | ||
var inputAsShort = MemoryMarshal.Cast<char, short>(text); | ||
var outputAsShort = MemoryMarshal.Cast<char, short>(result); | ||
for (int offset = 0; offset < totalVectorizedLength; offset += TVector.Count) | ||
{ | ||
var inputBlock = Vector256.LoadUnsafe(ref MemoryMarshal.GetReference(inputAsShort[offset..])); | ||
var outputBlock = CryptBlockAvx2(inputBlock); | ||
outputBlock.StoreUnsafe(ref MemoryMarshal.GetReference(outputAsShort[offset..])); | ||
} | ||
} | ||
|
||
// process the remaining input | ||
if (totalVectorizedLength < text.Length) | ||
{ | ||
var remainingInput = text[totalVectorizedLength..]; | ||
var remainingOutput = result[totalVectorizedLength..]; | ||
CryptSlow(remainingInput, remainingOutput); | ||
} | ||
|
||
written = text.Length; | ||
} | ||
|
||
internal static void CryptSlow(ReadOnlySpan<char> text, Span<char> result) | ||
{ | ||
for (int i = 0; i < text.Length; i++) | ||
{ | ||
var ch = text[i]; | ||
if (ch == ' ') | ||
{ | ||
result[i] = ' '; | ||
continue; | ||
} | ||
|
||
int value = ch + 47; | ||
|
||
if (value > 126) | ||
{ | ||
value -= 94; | ||
} | ||
else if (value < 33) | ||
{ | ||
value += 94; | ||
} | ||
|
||
result[i] = (char)value; | ||
} | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static TVector CryptBlockAvx2(TVector input) | ||
{ | ||
// whitespace mask | ||
var spaceMask = Avx2.CompareEqual(VectorOfSpace, input); | ||
|
||
// add 47 | ||
var transformed = Avx2.Add(input, VectorOf47); | ||
|
||
// subtract 94 if greater than 126 | ||
var greaterThan126Mask = Avx2.CompareGreaterThan(transformed, VectorOf126); | ||
var subtracted = Avx2.Subtract(transformed, VectorOf94); | ||
transformed = Avx2.BlendVariable(transformed, subtracted, greaterThan126Mask); | ||
|
||
// add 94 if less than 33 | ||
var lessThan33Mask = Avx2.CompareGreaterThan(VectorOf33, transformed); | ||
var added = Avx2.Add(transformed, VectorOf94); | ||
transformed = Avx2.BlendVariable(transformed, added, lessThan33Mask); | ||
|
||
// restore whitespace | ||
transformed = Avx2.BlendVariable(transformed, VectorOfSpace, spaceMask); | ||
|
||
return transformed; | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
tests/Science.Cryptography.Ciphers.Tests/Ciphers/AsciiRot47CipherTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
||
using Science.Cryptography.Ciphers.Specialized; | ||
using System; | ||
|
||
namespace Science.Cryptography.Ciphers.Tests; | ||
|
||
[TestClass] | ||
public class AsciiRot47CipherTests | ||
{ | ||
[TestMethod] | ||
public void Rot47() | ||
{ | ||
var cipher = new Rot47Cipher(); | ||
|
||
const string plaintext = "The quick brown fox jumps over the lazy dog"; | ||
const string ciphertext = "%96 BF:4< 3C@H? 7@I ;F>AD @G6C E96 =2KJ 5@8"; | ||
|
||
Assert.AreEqual(ciphertext, cipher.Encrypt(plaintext)); | ||
Assert.AreEqual(plaintext, cipher.Decrypt(ciphertext)); | ||
} | ||
|
||
[TestMethod] | ||
public void AsciiRot47() | ||
{ | ||
var cipher = new AsciiRot47Cipher(); | ||
|
||
const string plaintext = "The quick brown fox jumps over the lazy dog"; | ||
const string ciphertext = "%96 BF:4< 3C@H? 7@I ;F>AD @G6C E96 =2KJ 5@8"; | ||
|
||
Assert.AreEqual(ciphertext, cipher.Encrypt(plaintext)); | ||
Assert.AreEqual(plaintext, cipher.Decrypt(ciphertext)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters