Aggressive rewrite of core random number generation.
Add Random.cs to VS project.
This commit is contained in:
parent
f0086865b4
commit
ec3b7c8758
3 changed files with 343 additions and 38 deletions
332
Server/Random.cs
Normal file
332
Server/Random.cs
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
/***************************************************************************
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
|
||||
namespace Server {
|
||||
/// <summary>
|
||||
/// Handles random number generation.
|
||||
/// </summary>
|
||||
public static class RandomImpl {
|
||||
private static readonly IRandomImpl _Random;
|
||||
|
||||
static RandomImpl() {
|
||||
if ( Core.Unix ) {
|
||||
_Random = new SimpleRandom();
|
||||
} else if (Core.Is64Bit && File.Exists("rdrand64.dll")) {
|
||||
_Random = new RDRand64();
|
||||
} else if ( !Core.Is64Bit && File.Exists("rdrand32.dll") ) {
|
||||
_Random = new RDRand32();
|
||||
} else {
|
||||
_Random = new CSPRandom();
|
||||
}
|
||||
|
||||
if (_Random is IHardwareRNG) {
|
||||
if (!((IHardwareRNG)_Random).IsSupported()) {
|
||||
_Random = new CSPRandom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static double NextDouble() {
|
||||
return _Random.NextDouble();
|
||||
}
|
||||
|
||||
public static int Next(int c) {
|
||||
return _Random.Next(c);
|
||||
}
|
||||
|
||||
public static void NextBytes(byte[] b) {
|
||||
_Random.NextBytes(b);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRandomImpl {
|
||||
double NextDouble();
|
||||
int Next(int c);
|
||||
void NextBytes(byte[] b);
|
||||
}
|
||||
|
||||
public interface IHardwareRNG {
|
||||
bool IsSupported();
|
||||
}
|
||||
|
||||
public sealed class SimpleRandom : IRandomImpl {
|
||||
private Random m_Random = new Random();
|
||||
|
||||
public SimpleRandom() {
|
||||
}
|
||||
|
||||
public double NextDouble() {
|
||||
double r;
|
||||
lock (m_Random)
|
||||
r = m_Random.NextDouble();
|
||||
return r;
|
||||
}
|
||||
|
||||
public int Next(int c) {
|
||||
int r;
|
||||
lock (m_Random)
|
||||
r = m_Random.Next(c);
|
||||
return r;
|
||||
}
|
||||
|
||||
public void NextBytes(byte[] b) {
|
||||
lock (m_Random)
|
||||
m_Random.NextBytes(b);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CSPRandom : IRandomImpl {
|
||||
private RNGCryptoServiceProvider _CSP = new RNGCryptoServiceProvider();
|
||||
|
||||
private static int BUFFER_SIZE = 0x4000;
|
||||
private static int LARGE_REQUEST = BUFFER_SIZE / 0x100;
|
||||
|
||||
private byte[] _Working = new byte[BUFFER_SIZE];
|
||||
private byte[] _Buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
private int _Index = 0;
|
||||
|
||||
private object _sync = new object();
|
||||
|
||||
public CSPRandom() {
|
||||
_CSP.GetBytes(_Working);
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
|
||||
}
|
||||
|
||||
private void CheckSwap(int c) {
|
||||
lock (_sync) {
|
||||
if (_Index + c < BUFFER_SIZE)
|
||||
return;
|
||||
|
||||
lock (_Buffer) {
|
||||
byte[] b = _Working;
|
||||
_Working = _Buffer;
|
||||
_Buffer = b;
|
||||
_Index = 0;
|
||||
}
|
||||
}
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
|
||||
}
|
||||
|
||||
private void Fill(object o) {
|
||||
lock (_Buffer)
|
||||
lock (_CSP)
|
||||
_CSP.GetBytes(_Buffer);
|
||||
}
|
||||
|
||||
private void _GetBytes(byte[] b) {
|
||||
int c = b.Length;
|
||||
|
||||
CheckSwap(c);
|
||||
|
||||
lock (_sync) {
|
||||
Buffer.BlockCopy(_Working, _Index, b, 0, c);
|
||||
_Index += c;
|
||||
}
|
||||
}
|
||||
|
||||
public double NextDouble() {
|
||||
byte[] b = new byte[8];
|
||||
|
||||
_GetBytes(b);
|
||||
|
||||
return (double)BitConverter.ToUInt64(b, 0) / ulong.MaxValue;
|
||||
}
|
||||
|
||||
public int Next(int c) {
|
||||
return (int)(c * NextDouble());
|
||||
}
|
||||
|
||||
public void NextBytes(byte[] b) {
|
||||
int c = b.Length;
|
||||
|
||||
if (c >= LARGE_REQUEST) {
|
||||
lock (_CSP)
|
||||
_CSP.GetBytes(b);
|
||||
return;
|
||||
}
|
||||
_GetBytes(b);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RDRand32 : IRandomImpl, IHardwareRNG {
|
||||
[DllImport("rdrand32")]
|
||||
private static extern RDRandError rdrand_32(ref uint rand, bool retry);
|
||||
|
||||
[DllImport("rdrand32")]
|
||||
private static extern RDRandError rdrand_get_bytes(int n, byte[] buffer);
|
||||
|
||||
private static int BUFFER_SIZE = 0x4000;
|
||||
private static int LARGE_REQUEST = BUFFER_SIZE / 0x100;
|
||||
|
||||
private byte[] _Working = new byte[BUFFER_SIZE];
|
||||
private byte[] _Buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
private int _Index = 0;
|
||||
|
||||
private object _sync = new object();
|
||||
|
||||
public RDRand32() {
|
||||
rdrand_get_bytes(BUFFER_SIZE, _Working);
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
|
||||
}
|
||||
|
||||
public bool IsSupported() {
|
||||
uint r = 0;
|
||||
return rdrand_32(ref r, true) == RDRandError.Success;
|
||||
}
|
||||
|
||||
private void CheckSwap(int c) {
|
||||
lock (_sync) {
|
||||
if (_Index + c < BUFFER_SIZE)
|
||||
return;
|
||||
|
||||
lock (_Buffer) {
|
||||
byte[] b = _Working;
|
||||
_Working = _Buffer;
|
||||
_Buffer = b;
|
||||
_Index = 0;
|
||||
}
|
||||
}
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
|
||||
}
|
||||
|
||||
private void Fill(object o) {
|
||||
lock (_Buffer)
|
||||
rdrand_get_bytes(BUFFER_SIZE, _Buffer);
|
||||
}
|
||||
|
||||
private void _GetBytes(byte[] b) {
|
||||
int c = b.Length;
|
||||
|
||||
CheckSwap(c);
|
||||
|
||||
lock (_sync) {
|
||||
Buffer.BlockCopy(_Working, _Index, b, 0, c);
|
||||
_Index += c;
|
||||
}
|
||||
}
|
||||
|
||||
public double NextDouble() {
|
||||
byte[] b = new byte[8];
|
||||
_GetBytes(b);
|
||||
return (double)BitConverter.ToUInt64(b, 0) / ulong.MaxValue;
|
||||
}
|
||||
|
||||
public int Next(int c) {
|
||||
return (int)(c * NextDouble());
|
||||
}
|
||||
|
||||
public void NextBytes(byte[] b) {
|
||||
int c = b.Length;
|
||||
|
||||
if (c >= LARGE_REQUEST) {
|
||||
rdrand_get_bytes(c, b);
|
||||
return;
|
||||
}
|
||||
_GetBytes(b);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RDRand64 : IRandomImpl, IHardwareRNG {
|
||||
[DllImport("rdrand64")]
|
||||
private static extern RDRandError rdrand_64(ref ulong rand, bool retry);
|
||||
|
||||
[DllImport("rdrand64")]
|
||||
private static extern RDRandError rdrand_get_bytes(int n, byte[] buffer);
|
||||
|
||||
private static int BUFFER_SIZE = 0x4000;
|
||||
private static int LARGE_REQUEST = BUFFER_SIZE / 0x100;
|
||||
|
||||
private byte[] _Working = new byte[BUFFER_SIZE];
|
||||
private byte[] _Buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
private int _Index = 0;
|
||||
|
||||
private object _sync = new object();
|
||||
|
||||
public RDRand64() {
|
||||
rdrand_get_bytes(BUFFER_SIZE, _Working);
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
|
||||
}
|
||||
|
||||
public bool IsSupported() {
|
||||
ulong r = 0;
|
||||
return rdrand_64(ref r, true) == RDRandError.Success;
|
||||
}
|
||||
|
||||
private void CheckSwap(int c) {
|
||||
lock (_sync) {
|
||||
if (_Index + c < BUFFER_SIZE)
|
||||
return;
|
||||
|
||||
lock (_Buffer) {
|
||||
byte[] b = _Working;
|
||||
_Working = _Buffer;
|
||||
_Buffer = b;
|
||||
_Index = 0;
|
||||
}
|
||||
}
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
|
||||
}
|
||||
|
||||
private void Fill(object o) {
|
||||
lock (_Buffer)
|
||||
rdrand_get_bytes(BUFFER_SIZE, _Buffer);
|
||||
}
|
||||
|
||||
private void _GetBytes(byte[] b) {
|
||||
int c = b.Length;
|
||||
|
||||
CheckSwap(c);
|
||||
|
||||
lock (_sync) {
|
||||
Buffer.BlockCopy(_Working, _Index, b, 0, c);
|
||||
_Index += c;
|
||||
}
|
||||
}
|
||||
|
||||
public double NextDouble() {
|
||||
byte[] b = new byte[8];
|
||||
_GetBytes(b);
|
||||
return (double)BitConverter.ToUInt64(b, 0) / ulong.MaxValue;
|
||||
}
|
||||
|
||||
public int Next(int c) {
|
||||
return (int)(c * NextDouble());
|
||||
}
|
||||
|
||||
public void NextBytes(byte[] b) {
|
||||
int c = b.Length;
|
||||
|
||||
if (c >= LARGE_REQUEST) {
|
||||
rdrand_get_bytes(c, b);
|
||||
return;
|
||||
}
|
||||
_GetBytes(b);
|
||||
}
|
||||
}
|
||||
|
||||
public enum RDRandError : int {
|
||||
Unknown = -4,
|
||||
Unsupported = -3,
|
||||
Supported = -2,
|
||||
NotReady = -1,
|
||||
|
||||
Failure = 0,
|
||||
|
||||
Success = 1,
|
||||
}
|
||||
}
|
||||
|
|
@ -150,6 +150,7 @@
|
|||
<Compile Include="Prompt.cs" />
|
||||
<Compile Include="QuestArrow.cs" />
|
||||
<Compile Include="Race.cs" />
|
||||
<Compile Include="Random.cs" />
|
||||
<Compile Include="Region.cs" />
|
||||
<Compile Include="ScriptCompiler.cs" />
|
||||
<Compile Include="Sector.cs" />
|
||||
|
|
|
|||
|
|
@ -35,8 +35,6 @@ namespace Server
|
|||
{
|
||||
public static class Utility
|
||||
{
|
||||
// TODO: ThreadLocal<T> for .NET 4.0?
|
||||
private static Random m_Random = new Random();
|
||||
private static Encoding m_UTF8, m_UTF8WithEncoding;
|
||||
|
||||
public static Encoding UTF8
|
||||
|
|
@ -777,11 +775,8 @@ namespace Server
|
|||
{
|
||||
int total = 0;
|
||||
|
||||
lock (m_Random)
|
||||
{
|
||||
for (int i = 0; i < numDice; ++i)
|
||||
total += m_Random.Next(numSides) + 1;
|
||||
}
|
||||
for (int i = 0; i < numDice; ++i)
|
||||
total += RandomImpl.Next(numSides) + 1;
|
||||
|
||||
total += bonus;
|
||||
return total;
|
||||
|
|
@ -789,18 +784,12 @@ namespace Server
|
|||
|
||||
public static int RandomList( params int[] list )
|
||||
{
|
||||
int r;
|
||||
lock (m_Random)
|
||||
r = m_Random.Next( list.Length );
|
||||
return list[r];
|
||||
return list[RandomImpl.Next(list.Length)];
|
||||
}
|
||||
|
||||
public static bool RandomBool()
|
||||
{
|
||||
bool r;
|
||||
lock (m_Random)
|
||||
r = ( m_Random.Next( 2 ) == 0 );
|
||||
return r;
|
||||
return (RandomImpl.Next(2) == 0);
|
||||
}
|
||||
|
||||
public static int RandomMinMax( int min, int max )
|
||||
|
|
@ -816,11 +805,7 @@ namespace Server
|
|||
return min;
|
||||
}
|
||||
|
||||
int r;
|
||||
lock (m_Random)
|
||||
r = m_Random.Next( (max - min) + 1 );
|
||||
|
||||
return min + r;
|
||||
return min + RandomImpl.Next((max - min) + 1);
|
||||
}
|
||||
|
||||
public static int Random( int from, int count )
|
||||
|
|
@ -831,40 +816,27 @@ namespace Server
|
|||
}
|
||||
else if ( count > 0 )
|
||||
{
|
||||
int r;
|
||||
lock (m_Random)
|
||||
r = m_Random.Next( count );
|
||||
return from + r;
|
||||
return from + RandomImpl.Next(count);
|
||||
}
|
||||
else
|
||||
{
|
||||
int r;
|
||||
lock (m_Random)
|
||||
r = m_Random.Next( -count );
|
||||
return from - r;
|
||||
return from - RandomImpl.Next(-count);
|
||||
}
|
||||
}
|
||||
|
||||
public static int Random( int count )
|
||||
{
|
||||
int r;
|
||||
lock (m_Random)
|
||||
r = m_Random.Next( count );
|
||||
return r;
|
||||
return RandomImpl.Next(count);
|
||||
}
|
||||
|
||||
public static void RandomBytes( byte[] buffer )
|
||||
{
|
||||
lock (m_Random)
|
||||
m_Random.NextBytes(buffer);
|
||||
RandomImpl.NextBytes(buffer);
|
||||
}
|
||||
|
||||
public static double RandomDouble()
|
||||
{
|
||||
double r;
|
||||
lock (m_Random)
|
||||
r = m_Random.NextDouble();
|
||||
return r;
|
||||
return RandomImpl.NextDouble();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue