//#define TEST
using System;
using System.Diagnostics;
using System.Management;
using System.Runtime.CompilerServices;
using System.Threading;
[Serializable]
public class JitterEx
{
private const int DesaturationLoopLimit = 500;
private const int UpperCpuLoadLimit = 90;
private const int LowerCpuLoadLimit = 10;
private static bool _scaleSet;
private readonly int _bufferSize;
private readonly object _lock = new object();
private readonly Random _prng;
private byte[] _rngCache;
private int _tscLoopLimitCpu = 10;
private int _tscSampleSizeRam = 18;
public JitterEx(int buffersize, bool randomize = false)
{
_bufferSize = buffersize;
_rngCache = new byte[_bufferSize];
_prng = new Random();
Randomize = randomize;
SetScale();
}
public bool Randomize
{
get;
set;
}
#if TEST
public void TestListF()
{
var rlst = new List<List<(byte[], int)>>();
for (var i = 0; i < TestList.Count; i++)
{
var f = TestList[i];
for (var j = i + 1; j < TestList.Count; j++)
{
var s = TestList[j];
rlst.Add(new BMPartialPatternSearch(3).SearchPartialAll(f, s));
}
}
}
#endif
[MethodImpl(MethodImplOptions.NoOptimization)]
private float GetCpuSpeed()
{
void Loop()
{
var i = 0;
while (true)
i = i + 1 - 1;
}
var cpuCounter = new PerformanceCounter("Processor Information", "% Processor Performance", "_Total");
var cpuValue = cpuCounter.NextValue();
var loop = new Thread(() => Loop()) {Priority = ThreadPriority.Highest};
var Speed = 0f;
lock (_lock)
{
loop.Start();
Thread.Sleep(60);
cpuValue = cpuCounter.NextValue();
loop.Abort();
}
foreach (ManagementObject obj in new ManagementObjectSearcher("SELECT *, Name FROM Win32_Processor").Get())
{
var v = Convert.ToSingle(obj["MaxClockSpeed"]);
Speed = v / 1000 * cpuValue / 100;
}
return Speed;
}
/// <summary>
/// This is a fairly arbitrary value which is based on the CPU speed. 4.7 is the speed on which the tests are based.
/// </summary>
private void SetScale()
{
if (_scaleSet)
return;
_scaleSet = true;
const float baseFreq = 4.7f;
var thisFreq = GetCpuSpeed();
var rat = baseFreq / thisFreq;
_tscLoopLimitCpu = (int) Math.Ceiling(_tscLoopLimitCpu * rat);
_tscSampleSizeRam = (int) Math.Ceiling(_tscSampleSizeRam * rat);
}
private void LoadBlock()
{
var DesaturationLoops = 0;
var dump = CpuTotalPc.CPULoad;
do
{
Thread.Sleep(0);
DesaturationLoops++;
} while (DesaturationLoops < DesaturationLoopLimit && ((int) CpuTotalPc.CPULoad > UpperCpuLoadLimit || (int) CpuTotalPc.CPULoad < LowerCpuLoadLimit));
}
[MethodImpl(MethodImplOptions.NoInlining)]
private byte[] GetBufferCpu()
{
void Loop()
{
var x = 0;
for (var i = 0; i < _tscLoopLimitCpu; i++)
x = x + 1 - 1;
}
var jitterBuffer = new byte[_bufferSize];
var ptr = 0;
LoadBlock();
lock (_lock)
{
var start = Rdtsc.TimestampP();
do
{
var loop = new Thread(() => Loop()) {Priority = ThreadPriority.Highest};
loop.Start();
loop.Join();
var stop = Rdtsc.TimestampP();
Buffer.BlockCopy((100000.0 / ((stop - start) * 100000.0)).GetBytes(), 0, jitterBuffer, ptr, 4);
start = stop;
ptr += 4;
} while (ptr < _bufferSize);
return jitterBuffer;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private unsafe byte[] GetBufferRam()
{
void Loop()
{
byte tempByte;
fixed (byte* p1 = _rngCache)
{
var x1 = p1;
for (var j = 0; j < _tscSampleSizeRam; ++j, ++x1)
tempByte = *x1;
}
}
LoadBlock();
var jitterBuffer = new byte[_bufferSize];
lock (_lock)
{
if (_rngCache.Length != _bufferSize)
_rngCache = new byte[_bufferSize];
_prng.NextBytes(_rngCache);
var ptr = 0;
var start = Rdtsc.TimestampP();
while (true)
{
do
{
var loop = new Thread(() => Loop()) {Priority = ThreadPriority.Highest};
loop.Start();
loop.Join();
var stop = Rdtsc.TimestampP();
Buffer.BlockCopy((100000.0 / ((stop - start) * 100000.0)).GetBytes(), 0, jitterBuffer, ptr, 4);
start = stop;
ptr += 4;
if (ptr < _bufferSize)
{
if (_rngCache.Length != _bufferSize)
_rngCache = new byte[_bufferSize];
_prng.NextBytes(_rngCache);
}
} while (ptr < _bufferSize);
return jitterBuffer;
}
}
}
public byte[] GetBuffer()
{
var firstBuffer = GetBufferCpu();
#if TEST
///Weighting add 15%, perfection is not what we are looking for here.
var lim = _bufferSize <= 256 ? _bufferSize / 256.0 * 85.0 : 6.8;
if (ent.Entropy(firstBuffer) < lim)
throw new Exception("Error CPU Jitter Buffer Contains only zeros.");
#endif
var secondBuffer = GetBufferRam();
#if TEST
if (ent.Entropy(secondBuffer) < lim)
throw new Exception("Error CPU Jitter Buffer Contains only zeros.");
#endif
var finalBuffer = new byte[_bufferSize];
for (var j = 0; j < _bufferSize; ++j)
finalBuffer[j] = (byte) (firstBuffer[j] ^ secondBuffer[j]);
#if TEST
if (ent.Entropy(finalBuffer) < lim)
throw new Exception("Error CPU Jitter Buffer Contains only zeros.");
#endif
if (Randomize)
{
var ula = CreateNoiseArrays(finalBuffer);
var rBlock = new byte[ula.Length * 8];
Buffer.BlockCopy(ula, 0, rBlock, 0, ula.Length * 8);
#if TEST
if (_bufferSize >= 256)
{
var loop = new Thread(() =>
{
if (!rBlock.ChiSquaredTest().isRandom)
MessageBoxEx.Show("Warning", "Block not random.", MessageBoxExButtons.OK, MessageBoxExIcon.Warning);
}) {Priority = ThreadPriority.Highest};
loop.Start();
}
TestList.Add(rBlock);
#endif
return rBlock;
}
#if TEST
if (_bufferSize >= 256)
{
var loop = new Thread(() =>
{
if (!finalBuffer.ChiSquaredTest().isRandom)
MessageBoxEx.Show("Warning", "Block not random.", MessageBoxExButtons.OK, MessageBoxExIcon.Warning);
}) {Priority = ThreadPriority.Highest};
loop.Start();
}
TestList.Add(finalBuffer);
#endif
return finalBuffer;
}
private ulong[] ByteArrayToULongArray(byte[] ba, int finalSize)
{
var minSize = ba.Length / 8;
if (finalSize < minSize)
finalSize = minSize;
ba = PadULong(ba);
var result = new ulong[finalSize];
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 Extrude(ulong[] x)
{
var size = x.Length;
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);
}
}
private ulong[] CreateNoiseArrays(byte[] ba)
{
var ula = ByteArrayToULongArray(ba, ba.Length / 8);
Extrude(ula);
return ula;
}
internal class EntropyI
{
private const double NaturalLogOfTwo = 0.69314718055994530941723212145818; //Math.Log(2);
/// <summary>
/// Get the Entropy from 1 to 100% 1 being very ordered data,
/// 100% being very disordered data.
/// </summary>
public double Entropy(byte[] a)
{
var h = new int[256];
var l = a.Length;
for (var i = 0; i < l; ++i)
h[a[i]]++;
var e = 0.0;
for (var i = 0; i < 256; ++i)
{
var v = h[i];
if (v <= 0)
continue;
var r = v / (double) l;
e -= r * (Math.Log(r) / NaturalLogOfTwo);
}
return e / 8.0 * 100.0;
}
}
#if TEST
private EntropyI ent = new EntropyI();
private List<byte[]> TestList = new List<byte[]>();
#endif
}