ArrayMixer.cs

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();
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *