Uses Sha3 to Shuffle Primitive Arrays
using System;
public class ArrayMixer
{
private readonly SHA3ModInt _alg;
private readonly int _moveSize;
public ArrayMixer() : this(256, 24)
{
}
public ArrayMixer(int hashSize) : this(hashSize, 24)
{
}
public ArrayMixer(int hashSize, int rounds)
{
_alg = new SHA3ModInt(hashSize, rounds);
_moveSize = _alg.ComputeHash(2.GetBytes()).Length;
}
public byte[] Mix(byte[] buf, int iterations = 1000)
{
var bufferSize = buf.Length;
var lBuffer = new byte[_moveSize];
var oBuffer = new byte[bufferSize];
for (var i = 0; i < iterations; ++i)
{
var bytesSuffled = 0;
var moveSize = _moveSize;
var p = 0;
while (true)
{
var rBytesShuffle = bufferSize - bytesSuffled;
if (rBytesShuffle < moveSize)
moveSize = rBytesShuffle;
if (rBytesShuffle <= 0)
break;
Buffer.BlockCopy(buf, p, lBuffer, 0, moveSize);
lBuffer = _alg.ComputeHash(lBuffer);
Buffer.BlockCopy(lBuffer, 0, oBuffer, p, moveSize);
p += moveSize;
bytesSuffled += moveSize;
}
Buffer.BlockCopy(oBuffer, 0, buf, 0, bufferSize);
}
lBuffer.Fill(0);
oBuffer.Fill(0);
return buf;
}
public ushort[] Mix(ushort[] buf, int iterations = 1000)
{
var bb = buf.GetBytes();
return Mix(bb, iterations).ToUShortArray();
}
public uint[] Mix(uint[] buf, int iterations = 1000)
{
var bb = buf.GetBytes();
return Mix(bb, iterations).ToUIntArray();
}
public ulong[] Mix(ulong[] buf, int iterations = 1000)
{
var bb = buf.GetBytes();
return Mix(bb, iterations).ToULongArray();
}
/// <summary>
/// Will round up finalSize to the nearest 8 byte boundary.
/// </summary>
/// <param name="ba"></param>
/// <param name="finalSize"></param>
/// <returns></returns>
private static ulong[] ByteArrayToULongArray(byte[] ba, int finalSize)
{
var minSize = ba.Length / 8;
if (finalSize < minSize)
finalSize = minSize;
ba = PadULong(ba);
var os = finalSize / 8;
if (os * 8 < finalSize)
os++;
var result = new ulong[os];
for (var i = 0; i < ba.Length; i += 8)
Buffer.BlockCopy(ba, i, result, i, 8);
return result;
}
private static byte[] PadULong(byte[] ba)
{
var s = ba.Length % 8;
switch (s)
{
case 0:
break;
case 1:
Array.Resize(ref ba, ba.Length + 7);
ba[ba.Length - 1] = 0x80;
ba[ba.Length - 2] = 0x80;
ba[ba.Length - 3] = 0x80;
ba[ba.Length - 4] = 0x80;
ba[ba.Length - 5] = 0x80;
ba[ba.Length - 6] = 0x80;
ba[ba.Length - 7] = 0x80;
break;
case 2:
Array.Resize(ref ba, ba.Length + 6);
ba[ba.Length - 1] = 0x80;
ba[ba.Length - 2] = 0x80;
ba[ba.Length - 3] = 0x80;
ba[ba.Length - 4] = 0x80;
ba[ba.Length - 5] = 0x80;
ba[ba.Length - 6] = 0x80;
break;
case 3:
Array.Resize(ref ba, ba.Length + 5);
ba[ba.Length - 1] = 0x80;
ba[ba.Length - 2] = 0x80;
ba[ba.Length - 3] = 0x80;
ba[ba.Length - 4] = 0x80;
ba[ba.Length - 5] = 0x80;
break;
case 4:
Array.Resize(ref ba, ba.Length + 4);
ba[ba.Length - 1] = 0x80;
ba[ba.Length - 2] = 0x80;
ba[ba.Length - 3] = 0x80;
ba[ba.Length - 4] = 0x80;
break;
case 5:
Array.Resize(ref ba, ba.Length + 3);
ba[ba.Length - 1] = 0x80;
ba[ba.Length - 2] = 0x80;
ba[ba.Length - 3] = 0x80;
break;
case 6:
Array.Resize(ref ba, ba.Length + 2);
ba[ba.Length - 1] = 0x80;
ba[ba.Length - 2] = 0x80;
break;
case 7:
Array.Resize(ref ba, ba.Length + 1);
ba[ba.Length - 1] = 0x80;
break;
}
return ba;
}
private static void Expand(ulong[] x, int iterations = 1)
{
var size = x.Length;
for (var k = 0; k < iterations; ++k)
for (var i = 0; i < size; ++i)
{
ulong n = 0;
var j = 0;
while (j < size)
{
n ^= x[j];
++j;
}
x[i] = (n << 1) | (n >> 56);
}
}
/// <summary>
/// ExpandAndMixArray resizes the array by extrusion then mixes the array using a Sha3 one way hash
/// </summary>
/// <param name="ba">The buffer</param>
/// <param name="size">The final desired size</param>
/// <param name="iterations">The number of iterations to mix the final array</param>
public byte[] ExpandAndMixArray(byte[] ba, int size, int iterations = 1000)
{
var ula = ByteArrayToULongArray(ba, size);
Expand(ula, 1);
var array = ula.GetBytes();
return Mix(array, iterations);
}
public ushort[] ExpandAndMixArray(ushort[] ba, int size, int iterations = 1000)
{
var bb = ba.GetBytes();
return ExpandAndMixArray(bb, size, iterations).ToUShortArray();
}
public uint[] ExpandAndMixArray(uint[] ba, int size, int iterations = 1000)
{
var bb = ba.GetBytes();
return ExpandAndMixArray(bb, size, iterations).ToUIntArray();
}
public ulong[] ExpandAndMixArray(ulong[] ba, int size, int iterations = 1000)
{
var bb = ba.GetBytes();
return ExpandAndMixArray(bb, size, iterations).ToULongArray();
}
}