diff --git a/.build/Version.targets b/.build/Version.targets index f0f022c..f1303bc 100644 --- a/.build/Version.targets +++ b/.build/Version.targets @@ -1,7 +1,7 @@  - 1.0.0.* + 1.0.0.0 . diff --git a/NCuid.Tests/NCuid.Tests.csproj b/NCuid.Tests/NCuid.Tests.csproj index 7775cda..4c90c02 100644 --- a/NCuid.Tests/NCuid.Tests.csproj +++ b/NCuid.Tests/NCuid.Tests.csproj @@ -8,7 +8,7 @@ Properties NCuid.Tests NCuid.Tests - v4.5 + v4.6 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 @@ -16,6 +16,7 @@ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages False UnitTest + true diff --git a/NCuid.sln b/NCuid.sln index 88fd4b1..e7e9828 100644 --- a/NCuid.sln +++ b/NCuid.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30110.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.421 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NCuid.Tests", "NCuid.Tests\NCuid.Tests.csproj", "{1CD3A779-B529-497C-A186-F0637E316D2A}" EndProject @@ -27,12 +27,15 @@ Global {1CD3A779-B529-497C-A186-F0637E316D2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9033C45-5859-4420-8D44-15A43BBC53EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C9033C45-5859-4420-8D44-15A43BBC53EA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9033C45-5859-4420-8D44-15A43BBC53EA}.Release.Public|Any CPU.ActiveCfg = Release.Public|Any CPU - {C9033C45-5859-4420-8D44-15A43BBC53EA}.Release.Public|Any CPU.Build.0 = Release.Public|Any CPU + {C9033C45-5859-4420-8D44-15A43BBC53EA}.Release.Public|Any CPU.ActiveCfg = Release|Any CPU + {C9033C45-5859-4420-8D44-15A43BBC53EA}.Release.Public|Any CPU.Build.0 = Release|Any CPU {C9033C45-5859-4420-8D44-15A43BBC53EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9033C45-5859-4420-8D44-15A43BBC53EA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C6CFFDFB-0411-46A4-B4A7-E309D553DA34} + EndGlobalSection EndGlobal diff --git a/NCuid/Cuid.cs b/NCuid/Cuid.cs index 5d04d1a..9befe67 100644 --- a/NCuid/Cuid.cs +++ b/NCuid/Cuid.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Security; using System.Security.Cryptography; +using System.Text; +using System.Threading; namespace NCuid { @@ -12,13 +15,125 @@ namespace NCuid /// public static class Cuid { - private const int BlockSize = 4; - private const int Base = 36; + private abstract class RandomFragmentProvider + { + public abstract string GetBlock(int repeatCount); + + public abstract string GetFragment(int sliceLength); + } + + private class SimpleRandomFragmentProvider : RandomFragmentProvider + { + private static readonly ThreadLocal RandomGenerator = + new ThreadLocal( + () => + { + using (var rng = new RNGCryptoServiceProvider()) + { + var block = new byte[4]; + rng.GetNonZeroBytes(block); + return new Random(BitConverter.ToInt32(block, 0)); + } + }); + + public override string GetBlock(int repeatCount) + { + var sb = new StringBuilder(); + + for (var index = 0; index < repeatCount; ++index) + { + sb.Append(SimpleRandomBlock()); + } + + return sb.ToString(); + } + + public override string GetFragment(int sliceLength) + { + return SimpleRandomBlock().Slice(sliceLength); + } + + private static string SimpleRandomBlock() + { + var number = (long)(RandomGenerator.Value.NextDouble() * DiscreteValues); + + var r = number.ToBase36().Pad(BlockSize); + + return r; + } + } + + private class SecureRandomFragmentProvider : RandomFragmentProvider + { + public override string GetBlock(int repeatCount) + { + using (var gen = new RNGCryptoServiceProvider()) + { + var sb = new StringBuilder(); + + for (var index = 0; index < repeatCount; ++index) + { + sb.Append(SecureRandomBlock(gen)); + } + + return sb.ToString(); + } + } + + public override string GetFragment(int sliceLength) + { + using (var gen = new RNGCryptoServiceProvider()) + { + return SecureRandomBlock(gen).Slice(sliceLength); + } + } + + private static string SecureRandomBlock(RandomNumberGenerator gen) + { + var data = new byte[8]; + gen.GetNonZeroBytes(data); + + var baseNum = ((double)BitConverter.ToUInt64(data, 0) / ulong.MaxValue); + var number = (long)(baseNum * DiscreteValues); + + return number.ToBase36().Pad(BlockSize); + } + } + + private static class RandomFragmentProviderFactory + { + private static readonly SimpleRandomFragmentProvider SimpleRandomFragmentProvider = new SimpleRandomFragmentProvider(); + private static readonly SecureRandomFragmentProvider SecureRandomFragmentProvider = new SecureRandomFragmentProvider(); + private static readonly IDictionary ProviderLookup; + + static RandomFragmentProviderFactory() + { + ProviderLookup = + new Dictionary + { + { RandomSource.Simple, SimpleRandomFragmentProvider }, + { RandomSource.Secure, SecureRandomFragmentProvider } + }; + } + + public static RandomFragmentProvider Get(RandomSource source) + { + if (!ProviderLookup.TryGetValue(source, out var provider)) + { + throw new IndexOutOfRangeException("Invalid RandomSource specified"); + } + + return provider; + } + } + + private const int BlockSize = 4; + private const int Base = 36; private static readonly ulong DiscreteValues = (ulong)Math.Pow(Base, BlockSize); private static ulong _globalCounter; private static string _hostname; - + private static string Hostname { get @@ -48,31 +163,10 @@ private static string Hostname /// A 25 characters string public static string Generate(RandomSource rs = RandomSource.Simple) { - var ts = DateTime.Now.ToUnixMilliTime().ToBase36(); + var ts = DateTime.UtcNow.ToUnixMilliTime().ToBase36(); var fingerprint = FingerPrint(); - string rnd; - - switch (rs) - { - case RandomSource.Secure: - { - using (var gen = new RNGCryptoServiceProvider()) - { - rnd = SecureRandomBlock(gen) + SecureRandomBlock(gen); - } - } - break; - case RandomSource.Simple: - { - var gen = new Random(); - rnd = SimpleRandomBlock(gen) + SimpleRandomBlock(gen); - } - break; - - default: - throw new IndexOutOfRangeException("Invalid RandomSource specified"); - } + var rnd = RandomFragmentProviderFactory.Get(rs).GetBlock(2); var counter = SafeCounter().ToBase36().Pad(BlockSize); @@ -86,27 +180,11 @@ public static string Generate(RandomSource rs = RandomSource.Simple) /// A 7 to 10 characters string (depending of the internal counter value) public static string Slug(RandomSource rs = RandomSource.Simple) { - var print = FingerPrint().Slice(0, 1) + FingerPrint().Slice(-1); + var print = FingerPrint().Slice(0, 1) + FingerPrint().Slice(-1); var counter = SafeCounter().ToBase36().Slice(-4); - var dt = DateTime.Now.ToUnixMilliTime().ToBase36(); - - string rnd; + var dt = DateTime.UtcNow.ToUnixMilliTime().ToBase36(); - switch (rs) - { - case RandomSource.Secure: - using (var gen = new RNGCryptoServiceProvider()) - { - rnd = SecureRandomBlock(gen).Slice(-2); - } - break; - case RandomSource.Simple: - rnd = SimpleRandomBlock(new Random()).Slice(-2); - break; - - default: - throw new IndexOutOfRangeException("Invalid RandomSource specified"); - } + var rnd = RandomFragmentProviderFactory.Get(rs).GetFragment(-2); return (dt.Slice(-2) + counter + print + rnd).ToLowerInvariant(); } @@ -121,9 +199,9 @@ public static string FingerPrint() { const int padding = 2; - var pid = Base36Converter.ToBase36((Process.GetCurrentProcess().Id)).Pad(padding); - var hostname = Hostname; - var length = hostname.Length; + var pid = Base36Converter.ToBase36((Process.GetCurrentProcess().Id)).Pad(padding); + var hostname = Hostname; + var length = hostname.Length; var inputNumber = hostname.Split().Aggregate(length + 36, (prev, c) => prev + c[0]); var hostId = Base36Converter.ToBase36(inputNumber).Pad(padding); @@ -135,30 +213,10 @@ private static ulong SafeCounter() _globalCounter = (_globalCounter < DiscreteValues) ? _globalCounter : 0; - + _globalCounter++; return _globalCounter - 1; } - - private static string SimpleRandomBlock(Random rnd) - { - var number = (long)(rnd.NextDouble() * DiscreteValues); - - var r = number.ToBase36().Pad(BlockSize); - - return r; - } - - private static string SecureRandomBlock(RandomNumberGenerator gen) - { - var data = new byte[8]; - gen.GetNonZeroBytes(data); - - var baseNum = ((double)BitConverter.ToUInt64(data, 0) / ulong.MaxValue); - var number = (long)(baseNum * DiscreteValues); - - return number.ToBase36().Pad(BlockSize); - } } } diff --git a/NCuid/NCuid.csproj b/NCuid/NCuid.csproj index b42b15b..6aef70e 100644 --- a/NCuid/NCuid.csproj +++ b/NCuid/NCuid.csproj @@ -1,79 +1,12 @@ - - - + + - Debug - AnyCPU - {C9033C45-5859-4420-8D44-15A43BBC53EA} - Library - Properties - NCuid - NCuid - v4.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - bin\Debug\NCuid.XML - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true - - - C:\Users\moonpyk\Documents\Clés\moonpyk_at_gmail_com.pfx - - - ..\nuget\lib\net40\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - ..\nuget\lib\net40\NCuid.XML - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + netstandard2.0;net46 + false + true + NCuid.Core + MIT + https://github.com/dementeddevil/ncuid + + +