Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .build/Version.targets
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<AssemblyVersion>1.0.0.*</AssemblyVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please leave the star at the end?

<MSBuildCommunityTasksPath>.</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project="MSBuild.Community.Tasks.targets" />
Expand Down
3 changes: 2 additions & 1 deletion NCuid.Tests/NCuid.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NCuid.Tests</RootNamespace>
<AssemblyName>NCuid.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was the target framework change an obligation ?

<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down
11 changes: 7 additions & 4 deletions NCuid.sln
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
196 changes: 127 additions & 69 deletions NCuid/Cuid.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -12,13 +15,125 @@ namespace NCuid
/// </summary>
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<Random> RandomGenerator =
new ThreadLocal<Random>(
() =>
{
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<RandomSource, RandomFragmentProvider> ProviderLookup;

static RandomFragmentProviderFactory()
{
ProviderLookup =
new Dictionary<RandomSource, RandomFragmentProvider>
{
{ 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
Expand Down Expand Up @@ -48,31 +163,10 @@ private static string Hostname
/// <returns>A 25 characters string</returns>
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);

Expand All @@ -86,27 +180,11 @@ public static string Generate(RandomSource rs = RandomSource.Simple)
/// <returns>A 7 to 10 characters string (depending of the internal counter value)</returns>
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();
}
Expand All @@ -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);
Expand All @@ -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);
}
}
}
89 changes: 11 additions & 78 deletions NCuid/NCuid.csproj
Original file line number Diff line number Diff line change
@@ -1,79 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C9033C45-5859-4420-8D44-15A43BBC53EA}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NCuid</RootNamespace>
<AssemblyName>NCuid</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\NCuid.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release.Public|AnyCPU'">
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release.Public|AnyCPU'">
<AssemblyOriginatorKeyFile>C:\Users\moonpyk\Documents\Clés\moonpyk_at_gmail_com.pfx</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release.Public|AnyCPU'">
<OutputPath>..\nuget\lib\net40\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>..\nuget\lib\net40\NCuid.XML</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Base36Converter.cs" />
<Compile Include="Cuid.cs" />
<Compile Include="DateTimeExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\AssemblyInfo.Generated.cs" />
<Compile Include="RandomSource.cs" />
<Compile Include="StringExtensions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\.build\version.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>NCuid.Core</PackageId>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/dementeddevil/ncuid</PackageProjectUrl>
</PropertyGroup>
<Import Project="..\.build\Version.targets" />
</Project>