Merge pull request #1 from Microsoft/dev/lemejia/FirstCommit

Initial Commit
This commit is contained in:
lemejia 2018-04-19 16:27:52 -07:00 коммит произвёл GitHub
Родитель 007597e57e bd3aecda52
Коммит 7c4addce2c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 703 добавлений и 0 удалений

31
src/CorrelationVector.sln Normal file
Просмотреть файл

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CorrelationVector", "Microsoft.CorrelationVector\Microsoft.CorrelationVector.csproj", "{6F211D1F-0E72-4C05-9B9B-4DEC3AF01B38}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CorrelationVector.UnitTests", "Microsoft.CorrelationVector.UnitTests\Microsoft.CorrelationVector.UnitTests.csproj", "{EBE497F1-56EA-4BEA-B834-AEEE261BA6B8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6F211D1F-0E72-4C05-9B9B-4DEC3AF01B38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6F211D1F-0E72-4C05-9B9B-4DEC3AF01B38}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F211D1F-0E72-4C05-9B9B-4DEC3AF01B38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F211D1F-0E72-4C05-9B9B-4DEC3AF01B38}.Release|Any CPU.Build.0 = Release|Any CPU
{EBE497F1-56EA-4BEA-B834-AEEE261BA6B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBE497F1-56EA-4BEA-B834-AEEE261BA6B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBE497F1-56EA-4BEA-B834-AEEE261BA6B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBE497F1-56EA-4BEA-B834-AEEE261BA6B8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0421635B-CD1D-4001-8D21-FA1A33F7B83E}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,115 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.CorrelationVector;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CorrelationVector.UnitTests
{
[TestClass]
public class CorrelationVectorTests
{
[TestMethod]
public void SimpleCreateCorrelationVectorTest()
{
var correlationVector = new CorrelationVector();
var splitVector = correlationVector.Value.Split('.');
Assert.AreEqual(2, splitVector.Length, "Correlation Vector should be created with two components separated by a '.'");
Assert.AreEqual(16, splitVector[0].Length, "Correlation Vector base should be 16 character long");
Assert.AreEqual("0", splitVector[1], "Correlation Vector extension should start with zero");
}
[TestMethod]
public void CreateV1CorrelationVectorTest()
{
var correlationVector = new CorrelationVector(CorrelationVectorVersion.V1);
var splitVector = correlationVector.Value.Split('.');
Assert.AreEqual(2, splitVector.Length, "Correlation Vector should be created with two components separated by a '.'");
Assert.AreEqual(16, splitVector[0].Length, "Correlation Vector base should be 16 character long");
Assert.AreEqual("0", splitVector[1], "Correlation Vector extension should start with zero");
}
[TestMethod]
public void CreateV2CorrelationVectorTest()
{
var correlationVector = new CorrelationVector(CorrelationVectorVersion.V2);
var splitVector = correlationVector.Value.Split('.');
Assert.AreEqual(2, splitVector.Length, "Correlation Vector should be created with two components separated by a '.'");
Assert.AreEqual(22, splitVector[0].Length, "Correlation Vector base should be 22 character long");
Assert.AreEqual("0", splitVector[1], "Correlation Vector extension should start with zero");
}
[TestMethod]
public void CreateCorrelationVectorFromGuidTest()
{
var guid = System.Guid.NewGuid();
var correlationVector = new CorrelationVector(guid);
var splitVector = correlationVector.Value.Split('.');
Assert.AreEqual(2, splitVector.Length, "Correlation Vector should be created with two components separated by a '.'");
Assert.AreEqual(22, splitVector[0].Length, "Correlation Vector base should be 22 character long");
Assert.AreEqual("0", splitVector[1], "Correlation Vector extension should start with zero");
}
[TestMethod]
public void ParseCorrelationVectorV1Test()
{
var correlationVector = CorrelationVector.Parse("ifCuqpnwiUimg7Pk.1");
var splitVector = correlationVector.Value.Split('.');
Assert.AreEqual("ifCuqpnwiUimg7Pk", splitVector[0], "Correlation Vector base was not parsed properly");
Assert.AreEqual("1", splitVector[1], "Correlation Vector extension was not parsed properly");
}
[TestMethod]
public void ParseCorrelationVectorV2Test()
{
var correlationVector = CorrelationVector.Parse("Y58xO9ov0kmpPvkiuzMUVA.3.4.5");
var splitVector = correlationVector.Value.Split('.');
Assert.AreEqual(4, splitVector.Length, "Correlation Vector was not parsed properly");
Assert.AreEqual("Y58xO9ov0kmpPvkiuzMUVA", splitVector[0], "Correlation Vector base was not parsed properly");
Assert.AreEqual("3", splitVector[1], "Correlation Vector extension was not parsed properly");
Assert.AreEqual("4", splitVector[2], "Correlation Vector extension was not parsed properly");
Assert.AreEqual("5", splitVector[3], "Correlation Vector extension was not parsed properly");
}
[TestMethod]
public void SimpleIncrementCorrelationVectorTest()
{
var correlationVector = new CorrelationVector();
correlationVector.Increment();
var splitVector = correlationVector.Value.Split('.');
Assert.AreEqual("1", splitVector[1], "Correlation Vector extension should have been incremented by one");
}
[TestMethod]
public void SimpleExtendCorrelationVectorTest()
{
var correlationVector = new CorrelationVector();
var splitVector = correlationVector.Value.Split('.');
var vectorBase = splitVector[0];
var extension = splitVector[1];
correlationVector = CorrelationVector.Extend(correlationVector.Value);
splitVector = correlationVector.Value.Split('.');
Assert.AreEqual(3, splitVector.Length, "Correlation Vector should contain 3 components separated by a '.' after extension");
Assert.AreEqual(vectorBase, splitVector[0], "Correlation Vector base should contain the same base after extension");
Assert.AreEqual(extension, splitVector[1], "Correlation Vector should preserve original ");
Assert.AreEqual("0", splitVector[2], "Correlation Vector new extension should start with zero");
}
public void ValidateCreationTest()
{
CorrelationVector.ValidateCorrelationVectorDuringCreation = true;
var correlationVector = new CorrelationVector();
correlationVector.Increment();
var splitVector = correlationVector.Value.Split('.');
Assert.AreEqual("1", splitVector[1], "Correlation Vector extension should have been incremented by one");
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.CorrelationVector\Microsoft.CorrelationVector.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,350 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Threading;
using System.Globalization;
namespace Microsoft.CorrelationVector
{
/// <summary>
/// This class represents a lightweight vector for identifying and measuring
/// causality.
/// </summary>
public sealed partial class CorrelationVector : MarshalByRefObject
{
private const byte MaxVectorLength = 63;
private const byte MaxVectorLengthV2 = 127;
private const byte BaseLength = 16;
private const byte BaseLengthV2 = 22;
private readonly string baseVector = null;
private int extension = 0;
private static Random rng = new Random();
/// <summary>
/// This is the header that should be used between services to pass the correlation
/// vector.
/// </summary>
public const string HeaderName = "MS-CV";
/// <summary>
/// Gets or sets a value indicating whether or not to validate the correlation
/// vector on creation.
/// </summary>
public static bool ValidateCorrelationVectorDuringCreation { get; set; }
/// <summary>
/// Creates a new correlation vector by extending an existing value. This should be
/// done at the entry point of an operation.
/// </summary>
/// <param name="correlationVector">
/// Taken from the message header indicated by <see cref="HeaderName"/>.
/// </param>
/// <returns>A new correlation vector extended from the current vector.</returns>
public static CorrelationVector Extend(string correlationVector)
{
CorrelationVectorVersion version = CorrelationVector.InferVersion(
correlationVector, CorrelationVector.ValidateCorrelationVectorDuringCreation);
if (CorrelationVector.ValidateCorrelationVectorDuringCreation)
{
CorrelationVector.Validate(correlationVector, version);
}
return new CorrelationVector(correlationVector, 0, version);
}
/// <summary>
/// Creates a new correlation vector by applying the Spin operator to an existing value.
/// This should be done at the entry point of an operation.
/// </summary>
/// <param name="correlationVector">
/// Taken from the message header indicated by <see cref="HeaderName"/>.
/// </param>
/// <returns>A new correlation vector extended from the current vector.</returns>
public static CorrelationVector Spin(string correlationVector)
{
SpinParameters defaultParameters = new SpinParameters
{
Interval = SpinCounterInterval.Coarse,
Periodicity = SpinCounterPeriodicity.Short,
Entropy = SpinEntropy.Two
};
return CorrelationVector.Spin(correlationVector, defaultParameters);
}
/// <summary>
/// Creates a new correlation vector by applying the Spin operator to an existing value.
/// This should be done at the entry point of an operation.
/// </summary>
/// <param name="correlationVector">
/// Taken from the message header indicated by <see cref="HeaderName"/>.
/// </param>
/// <param name="parameters">
/// The parameters to use when applying the Spin operator.
/// </param>
/// <returns>A new correlation vector extended from the current vector.</returns>
public static CorrelationVector Spin(string correlationVector, SpinParameters parameters)
{
CorrelationVectorVersion version = CorrelationVector.InferVersion(
correlationVector, CorrelationVector.ValidateCorrelationVectorDuringCreation);
if (CorrelationVector.ValidateCorrelationVectorDuringCreation)
{
CorrelationVector.Validate(correlationVector, version);
}
byte[] entropy = new byte[parameters.EntropyBytes];
rng.NextBytes(entropy);
ulong value = (ulong)(DateTime.UtcNow.Ticks >> parameters.TicksBitsToDrop);
for (int i = 0; i < parameters.EntropyBytes; i++)
{
value = (value << 8) | Convert.ToUInt64(entropy[i]);
}
// Generate a bitmask and mask the lower TotalBits in the value.
// The mask is generated by (1 << TotalBits) - 1. We need to handle the edge case
// when shifting 64 bits, as it wraps around.
value &= (parameters.TotalBits == 64 ? 0 : (ulong)1 << parameters.TotalBits) - 1;
string s = unchecked((uint)value).ToString();
if (parameters.TotalBits > 32)
{
s = string.Concat((value >> 32).ToString(), ".", s);
}
return new CorrelationVector(string.Concat(correlationVector, ".", s), 0, version);
}
/// <summary>
/// Creates a new correlation vector by parsing its string representation
/// </summary>
/// <param name="correlationVector">correlationVector</param>
/// <returns>CorrelationVector</returns>
public static CorrelationVector Parse(string correlationVector)
{
if (!string.IsNullOrEmpty(correlationVector))
{
int p = correlationVector.LastIndexOf('.');
if (p > 0)
{
int extension;
if (int.TryParse(correlationVector.Substring(p + 1), out extension) && extension >= 0)
{
return new CorrelationVector(correlationVector.Substring(0, p), extension, CorrelationVector.InferVersion(correlationVector, false));
}
}
}
return new CorrelationVector();
}
/// <summary>
/// Initializes a new instance of the <see cref="CorrelationVector"/> class. This
/// should only be called when no correlation vector was found in the message
/// header.
/// </summary>
public CorrelationVector()
: this(CorrelationVectorVersion.V1)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CorrelationVector"/> class of the
/// given implemenation version. This should only be called when no correlation
/// vector was found in the message header.
/// </summary>
/// <param name="version">The correlation vector implemenation version.</param>
public CorrelationVector(CorrelationVectorVersion version)
: this(CorrelationVector.GetUniqueValue(version), 0, version)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CorrelationVector"/> class of the
/// V2 implemenation using the given <see cref="System.Guid"/> as the vector base.
/// </summary>
/// <param name="vectorBase">The <see cref="System.Guid"/> to use as a correlation
/// vector base.</param>
public CorrelationVector(Guid vectorBase)
: this(CorrelationVector.GetBaseFromGuid(vectorBase), 0, CorrelationVectorVersion.V2)
{
}
/// <summary>
/// Gets the value of the correlation vector as a string.
/// </summary>
public string Value
{
get
{
return string.Concat(this.baseVector, ".", this.extension);
}
}
/// <summary>
/// Increments the current extension by one. Do this before passing the value to an
/// outbound message header.
/// </summary>
/// <returns>
/// The new value as a string that you can add to the outbound message header
/// indicated by <see cref="HeaderName"/>.
/// </returns>
public string Increment()
{
int snapshot = 0;
int next = 0;
do
{
snapshot = this.extension;
if (snapshot == int.MaxValue)
{
return this.Value;
}
next = snapshot + 1;
int size = baseVector.Length + 1 + (int)Math.Log10(next) + 1;
if ((this.Version == CorrelationVectorVersion.V1 &&
size > CorrelationVector.MaxVectorLength) ||
(this.Version == CorrelationVectorVersion.V2 &&
size > CorrelationVector.MaxVectorLengthV2))
{
return this.Value;
}
}
while (snapshot != Interlocked.CompareExchange(ref this.extension, next, snapshot));
return string.Concat(this.baseVector, ".", next);
}
/// <summary>
/// Gets the version of the correlation vector implementation.
/// </summary>
public CorrelationVectorVersion Version
{
get;
private set;
}
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>A string that represents the current object.</returns>
public override string ToString()
{
return this.Value;
}
/// <summary>
/// Determines whether two instances of the <see cref="CorrelationVector"/> class
/// are equal.
/// </summary>
/// <param name="vector">
/// The correlation vector you want to compare with the current correlation vector.
/// </param>
/// <returns>
/// True if the specified correlation vector is equal to the current correlation
/// vector; otherwise, false.
/// </returns>
public bool Equals(CorrelationVector vector)
{
return string.Equals(this.Value, vector.Value, StringComparison.Ordinal);
}
private CorrelationVector(string baseVector, int extension, CorrelationVectorVersion version)
{
this.baseVector = baseVector;
this.extension = extension;
this.Version = version;
}
private static string GetBaseFromGuid(Guid guid)
{
byte[] bytes = guid.ToByteArray();
// Removes the base64 padding
return Convert.ToBase64String(bytes).Substring(0, CorrelationVector.BaseLengthV2);
}
private static string GetUniqueValue(CorrelationVectorVersion version)
{
if (CorrelationVectorVersion.V1 == version)
{
byte[] bytes = Guid.NewGuid().ToByteArray();
return Convert.ToBase64String(bytes, 0, 12);
}
else if (CorrelationVectorVersion.V2 == version)
{
return CorrelationVector.GetBaseFromGuid(Guid.NewGuid());
}
else
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Unsupported correlation vector version: {0}", version));
}
}
private static CorrelationVectorVersion InferVersion(string correlationVector, bool reportErrors)
{
int index = correlationVector == null ? -1 : correlationVector.IndexOf('.');
if (CorrelationVector.BaseLength == index)
{
return CorrelationVectorVersion.V1;
}
else if (CorrelationVector.BaseLengthV2 == index)
{
return CorrelationVectorVersion.V2;
}
else
{
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Invalid correlation vector {0}", correlationVector));
}
}
private static void Validate(string correlationVector, CorrelationVectorVersion version)
{
byte maxVectorLength;
byte baseLength;
if (CorrelationVectorVersion.V1 == version)
{
maxVectorLength = CorrelationVector.MaxVectorLength;
baseLength = CorrelationVector.BaseLength;
}
else if (CorrelationVectorVersion.V2 == version)
{
maxVectorLength = CorrelationVector.MaxVectorLengthV2;
baseLength = CorrelationVector.BaseLengthV2;
}
else
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Unsupported correlation vector version: {0}", version));
}
if (string.IsNullOrWhiteSpace(correlationVector) || correlationVector.Length > maxVectorLength)
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
"The {0} correlation vector can not be null or bigger than {1} characters", version, maxVectorLength));
}
string[] parts = correlationVector.Split('.');
if (parts.Length < 2 || parts[0].Length != baseLength)
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid correlation vector {0}. Invalid base value {1}", correlationVector, parts[0]));
}
for (int i = 1; i < parts.Length; i++)
{
int result;
if (int.TryParse(parts[i], out result) == false || result < 0)
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid correlation vector {0}. Invalid extension value {1}", correlationVector, parts[i]));
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.CorrelationVector
{
public enum CorrelationVectorVersion
{
V1,
V2,
}
}

Просмотреть файл

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
</Project>

Просмотреть файл

@ -0,0 +1,170 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace Microsoft.CorrelationVector
{
public enum SpinCounterInterval
{
/// <summary>
/// The coarse interval drops the 24 least significant bits in DateTime.Ticks
/// resulting in a counter that increments every 1.67 seconds.
/// </summary>
Coarse,
/// <summary>
/// The fine interval drops the 16 least significant bits in DateTime.Ticks
/// resulting in a counter that increments every 6.5 milliseconds.
/// </summary>
Fine
}
public enum SpinCounterPeriodicity
{
/// <summary>
/// Do not store a counter as part of the spin value.
/// </summary>
None,
/// <summary>
/// The short periodicity stores the counter using 16 bits.
/// </summary>
Short,
/// <summary>
/// The medium periodicity stores the counter using 24 bits.
/// </summary>
Medium,
/// <summary>
/// The long periodicity stores the counter using 32 bits.
/// </summary>
Long
}
public enum SpinEntropy
{
/// <summary>
/// Do not generate entropy as part of the spin value.
/// </summary>
None = 0,
/// <summary>
/// Generate entropy using 8 bits.
/// </summary>
One = 1,
/// <summary>
/// Generate entropy using 16 bits.
/// </summary>
Two = 2,
/// <summary>
/// Generate entropy using 24 bits.
/// </summary>
Three = 3,
/// <summary>
/// Generate entropy using 32 bits.
/// </summary>
Four = 4
}
/// <summary>
/// This class stores parameters used by the CorrelationVector Spin operator.
/// </summary>
public class SpinParameters : MarshalByRefObject
{
// Internal value for entropy bytes.
private int entropyBytes;
/// <summary>
/// The interval (proportional to time) by which the counter increments.
/// </summary>
public SpinCounterInterval Interval { get; set; }
/// <summary>
/// How frequently the counter wraps around to zero, as determined by the amount
/// of space to store the counter.
/// </summary>
public SpinCounterPeriodicity Periodicity { get; set; }
/// <summary>
/// The number of bytes to use for entropy. Valid values from a
/// minimum of 0 to a maximum of 4.
/// </summary>
public SpinEntropy Entropy
{
get
{
return (SpinEntropy)this.entropyBytes;
}
set
{
this.entropyBytes = (int)value;
}
}
/// <summary>
/// The number of least significant bits to drop in DateTime.Ticks when
/// computing the counter.
/// </summary>
internal int TicksBitsToDrop
{
get
{
switch (this.Interval)
{
case SpinCounterInterval.Coarse:
return 24;
case SpinCounterInterval.Fine:
return 16;
default:
return 24;
}
}
}
/// <summary>
/// The number of bytes used to store the entropy.
/// </summary>
internal int EntropyBytes
{
get
{
return this.entropyBytes;
}
}
internal int TotalBits
{
get
{
int counterBits;
switch (this.Periodicity)
{
case SpinCounterPeriodicity.None:
counterBits = 0;
break;
case SpinCounterPeriodicity.Short:
counterBits = 16;
break;
case SpinCounterPeriodicity.Medium:
counterBits = 24;
break;
case SpinCounterPeriodicity.Long:
counterBits = 32;
break;
default:
counterBits = 0;
break;
}
return counterBits + this.EntropyBytes * 8;
}
}
}
}