Commit c84bccff authored by Savorboard's avatar Savorboard

tweak ObjectId

parent 681e3822
...@@ -18,15 +18,13 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -18,15 +18,13 @@ namespace DotNetCore.CAP.Infrastructure
public struct ObjectId : IComparable<ObjectId>, IEquatable<ObjectId> public struct ObjectId : IComparable<ObjectId>, IEquatable<ObjectId>
{ {
// private static fields // private static fields
private static readonly DateTime __unixEpoch; private static readonly DateTime UnixEpoch;
private static readonly long __dateTimeMaxValueMillisecondsSinceEpoch; private static readonly int StaticMachine;
private static readonly long __dateTimeMinValueMillisecondsSinceEpoch; private static readonly short StaticPid;
private static readonly int __staticMachine; private static int _staticIncrement; // high byte will be masked out when generating new ObjectId
private static readonly short __staticPid;
private static int __staticIncrement; // high byte will be masked out when generating new ObjectId
private static readonly uint[] _lookup32 = Enumerable.Range(0, 256).Select(i => private static readonly uint[] Lookup32 = Enumerable.Range(0, 256).Select(i =>
{ {
var s = i.ToString("x2"); var s = i.ToString("x2");
return (uint) s[0] + ((uint) s[1] << 16); return (uint) s[0] + ((uint) s[1] << 16);
...@@ -44,40 +42,13 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -44,40 +42,13 @@ namespace DotNetCore.CAP.Infrastructure
// static constructor // static constructor
static ObjectId() static ObjectId()
{ {
__unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
__dateTimeMaxValueMillisecondsSinceEpoch = (DateTime.MaxValue - __unixEpoch).Ticks / 10000; StaticMachine = GetMachineHash();
__dateTimeMinValueMillisecondsSinceEpoch = (DateTime.MinValue - __unixEpoch).Ticks / 10000; _staticIncrement = new Random().Next();
__staticMachine = GetMachineHash(); StaticPid = (short) GetCurrentProcessId();
__staticIncrement = new Random().Next();
__staticPid = (short) GetCurrentProcessId();
} }
// constructors // constructors
/// <summary>
/// Initializes a new instance of the ObjectId class.
/// </summary>
/// <param name="bytes">The bytes.</param>
public ObjectId(byte[] bytes)
{
if (bytes == null)
{
throw new ArgumentNullException("bytes");
}
Unpack(bytes, out _timestamp, out _machine, out _pid, out _increment);
}
/// <summary>
/// Initializes a new instance of the ObjectId class.
/// </summary>
/// <param name="timestamp">The timestamp (expressed as a DateTime).</param>
/// <param name="machine">The machine hash.</param>
/// <param name="pid">The PID.</param>
/// <param name="increment">The increment.</param>
public ObjectId(DateTime timestamp, int machine, short pid, int increment)
: this(GetTimestampFromDateTime(timestamp), machine, pid, increment)
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the ObjectId class. /// Initializes a new instance of the ObjectId class.
...@@ -90,14 +61,14 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -90,14 +61,14 @@ namespace DotNetCore.CAP.Infrastructure
{ {
if ((machine & 0xff000000) != 0) if ((machine & 0xff000000) != 0)
{ {
throw new ArgumentOutOfRangeException("machine", throw new ArgumentOutOfRangeException(nameof(machine),
"The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); @"The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
} }
if ((increment & 0xff000000) != 0) if ((increment & 0xff000000) != 0)
{ {
throw new ArgumentOutOfRangeException("increment", throw new ArgumentOutOfRangeException(nameof(increment),
"The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); @"The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
} }
_timestamp = timestamp; _timestamp = timestamp;
...@@ -106,75 +77,6 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -106,75 +77,6 @@ namespace DotNetCore.CAP.Infrastructure
_increment = increment; _increment = increment;
} }
/// <summary>
/// Initializes a new instance of the ObjectId class.
/// </summary>
/// <param name="value">The value.</param>
public ObjectId(string value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
Unpack(ParseHexString(value), out _timestamp, out _machine, out _pid, out _increment);
}
// public static properties
/// <summary>
/// Gets an instance of ObjectId where the value is empty.
/// </summary>
public static ObjectId Empty { get; } = default(ObjectId);
// public properties
/// <summary>
/// Gets the timestamp.
/// </summary>
public int Timestamp => _timestamp;
/// <summary>
/// Gets the machine.
/// </summary>
public int Machine => _machine;
/// <summary>
/// Gets the PID.
/// </summary>
public short Pid => _pid;
/// <summary>
/// Gets the increment.
/// </summary>
public int Increment => _increment;
/// <summary>
/// Gets the creation time (derived from the timestamp).
/// </summary>
public DateTime CreationTime => __unixEpoch.AddSeconds(_timestamp);
// public operators
/// <summary>
/// Compares two ObjectIds.
/// </summary>
/// <param name="lhs">The first ObjectId.</param>
/// <param name="rhs">The other ObjectId</param>
/// <returns>True if the first ObjectId is less than the second ObjectId.</returns>
public static bool operator <(ObjectId lhs, ObjectId rhs)
{
return lhs.CompareTo(rhs) < 0;
}
/// <summary>
/// Compares two ObjectIds.
/// </summary>
/// <param name="lhs">The first ObjectId.</param>
/// <param name="rhs">The other ObjectId</param>
/// <returns>True if the first ObjectId is less than or equal to the second ObjectId.</returns>
public static bool operator <=(ObjectId lhs, ObjectId rhs)
{
return lhs.CompareTo(rhs) <= 0;
}
/// <summary> /// <summary>
/// Compares two ObjectIds. /// Compares two ObjectIds.
/// </summary> /// </summary>
...@@ -197,28 +99,6 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -197,28 +99,6 @@ namespace DotNetCore.CAP.Infrastructure
return !(lhs == rhs); return !(lhs == rhs);
} }
/// <summary>
/// Compares two ObjectIds.
/// </summary>
/// <param name="lhs">The first ObjectId.</param>
/// <param name="rhs">The other ObjectId</param>
/// <returns>True if the first ObjectId is greather than or equal to the second ObjectId.</returns>
public static bool operator >=(ObjectId lhs, ObjectId rhs)
{
return lhs.CompareTo(rhs) >= 0;
}
/// <summary>
/// Compares two ObjectIds.
/// </summary>
/// <param name="lhs">The first ObjectId.</param>
/// <param name="rhs">The other ObjectId</param>
/// <returns>True if the first ObjectId is greather than the second ObjectId.</returns>
public static bool operator >(ObjectId lhs, ObjectId rhs)
{
return lhs.CompareTo(rhs) > 0;
}
// public static methods // public static methods
/// <summary> /// <summary>
/// Generates a new ObjectId with a unique value. /// Generates a new ObjectId with a unique value.
...@@ -229,16 +109,6 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -229,16 +109,6 @@ namespace DotNetCore.CAP.Infrastructure
return GenerateNewId(GetTimestampFromDateTime(DateTime.UtcNow)); return GenerateNewId(GetTimestampFromDateTime(DateTime.UtcNow));
} }
/// <summary>
/// Generates a new ObjectId with a unique value (with the timestamp component based on a given DateTime).
/// </summary>
/// <param name="timestamp">The timestamp component (expressed as a DateTime).</param>
/// <returns>An ObjectId.</returns>
public static ObjectId GenerateNewId(DateTime timestamp)
{
return GenerateNewId(GetTimestampFromDateTime(timestamp));
}
/// <summary> /// <summary>
/// Generates a new ObjectId with a unique value (with the given timestamp). /// Generates a new ObjectId with a unique value (with the given timestamp).
/// </summary> /// </summary>
...@@ -246,8 +116,8 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -246,8 +116,8 @@ namespace DotNetCore.CAP.Infrastructure
/// <returns>An ObjectId.</returns> /// <returns>An ObjectId.</returns>
public static ObjectId GenerateNewId(int timestamp) public static ObjectId GenerateNewId(int timestamp)
{ {
var increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff; // only use low order 3 bytes var increment = Interlocked.Increment(ref _staticIncrement) & 0x00ffffff; // only use low order 3 bytes
return new ObjectId(timestamp, __staticMachine, __staticPid, increment); return new ObjectId(timestamp, StaticMachine, StaticPid, increment);
} }
/// <summary> /// <summary>
...@@ -271,14 +141,14 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -271,14 +141,14 @@ namespace DotNetCore.CAP.Infrastructure
{ {
if ((machine & 0xff000000) != 0) if ((machine & 0xff000000) != 0)
{ {
throw new ArgumentOutOfRangeException("machine", throw new ArgumentOutOfRangeException(nameof(machine),
"The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); @"The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
} }
if ((increment & 0xff000000) != 0) if ((increment & 0xff000000) != 0)
{ {
throw new ArgumentOutOfRangeException("increment", throw new ArgumentOutOfRangeException(nameof(increment),
"The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); @"The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
} }
var bytes = new byte[12]; var bytes = new byte[12];
...@@ -297,53 +167,6 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -297,53 +167,6 @@ namespace DotNetCore.CAP.Infrastructure
return bytes; return bytes;
} }
/// <summary>
/// Parses a string and creates a new ObjectId.
/// </summary>
/// <param name="s">The string value.</param>
/// <returns>A ObjectId.</returns>
public static ObjectId Parse(string s)
{
if (s == null)
{
throw new ArgumentNullException("s");
}
if (s.Length != 24)
{
throw new ArgumentOutOfRangeException("s", "ObjectId string value must be 24 characters.");
}
return new ObjectId(ParseHexString(s));
}
/// <summary>
/// Unpacks a byte array into the components of an ObjectId.
/// </summary>
/// <param name="bytes">A byte array.</param>
/// <param name="timestamp">The timestamp.</param>
/// <param name="machine">The machine hash.</param>
/// <param name="pid">The PID.</param>
/// <param name="increment">The increment.</param>
public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment)
{
if (bytes == null)
{
throw new ArgumentNullException("bytes");
}
if (bytes.Length != 12)
{
throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long.");
}
timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6];
pid = (short) ((bytes[7] << 8) + bytes[8]);
increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11];
}
// private static methods
/// <summary> /// <summary>
/// Gets the current process id. This method exists because of how CAS operates on the call stack, checking /// Gets the current process id. This method exists because of how CAS operates on the call stack, checking
/// for permissions before executing the method. Hence, if we inlined this call, the calling method would not execute /// for permissions before executing the method. Hence, if we inlined this call, the calling method would not execute
...@@ -365,7 +188,7 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -365,7 +188,7 @@ namespace DotNetCore.CAP.Infrastructure
private static int GetTimestampFromDateTime(DateTime timestamp) private static int GetTimestampFromDateTime(DateTime timestamp)
{ {
return (int) Math.Floor((ToUniversalTime(timestamp) - __unixEpoch).TotalSeconds); return (int) Math.Floor((ToUniversalTime(timestamp) - UnixEpoch).TotalSeconds);
} }
// public methods // public methods
...@@ -421,9 +244,9 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -421,9 +244,9 @@ namespace DotNetCore.CAP.Infrastructure
/// <returns>True if the other object is an ObjectId and equal to this one.</returns> /// <returns>True if the other object is an ObjectId and equal to this one.</returns>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is ObjectId) if (obj is ObjectId id)
{ {
return Equals((ObjectId) obj); return Equals(id);
} }
return false; return false;
...@@ -461,33 +284,6 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -461,33 +284,6 @@ namespace DotNetCore.CAP.Infrastructure
return ToHexString(ToByteArray()); return ToHexString(ToByteArray());
} }
/// <summary>
/// Parses a hex string into its equivalent byte array.
/// </summary>
/// <param name="s">The hex string to parse.</param>
/// <returns>The byte equivalent of the hex string.</returns>
public static byte[] ParseHexString(string s)
{
if (s == null)
{
throw new ArgumentNullException("s");
}
if (s.Length % 2 == 1)
{
throw new Exception("The binary key cannot have an odd number of digits");
}
var arr = new byte[s.Length >> 1];
for (var i = 0; i < s.Length >> 1; ++i)
{
arr[i] = (byte) ((GetHexVal(s[i << 1]) << 4) + GetHexVal(s[(i << 1) + 1]));
}
return arr;
}
/// <summary> /// <summary>
/// Converts a byte array to a hex string. /// Converts a byte array to a hex string.
/// </summary> /// </summary>
...@@ -497,13 +293,13 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -497,13 +293,13 @@ namespace DotNetCore.CAP.Infrastructure
{ {
if (bytes == null) if (bytes == null)
{ {
throw new ArgumentNullException("bytes"); throw new ArgumentNullException(nameof(bytes));
} }
var result = new char[bytes.Length * 2]; var result = new char[bytes.Length * 2];
for (var i = 0; i < bytes.Length; i++) for (var i = 0; i < bytes.Length; i++)
{ {
var val = _lookup32[bytes[i]]; var val = Lookup32[bytes[i]];
result[2 * i] = (char) val; result[2 * i] = (char) val;
result[2 * i + 1] = (char) (val >> 16); result[2 * i + 1] = (char) (val >> 16);
} }
...@@ -511,17 +307,6 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -511,17 +307,6 @@ namespace DotNetCore.CAP.Infrastructure
return new string(result); return new string(result);
} }
/// <summary>
/// Converts a DateTime to number of milliseconds since Unix epoch.
/// </summary>
/// <param name="dateTime">A DateTime.</param>
/// <returns>Number of seconds since Unix epoch.</returns>
public static long ToMillisecondsSinceEpoch(DateTime dateTime)
{
var utcDateTime = ToUniversalTime(dateTime);
return (utcDateTime - __unixEpoch).Ticks / 10000;
}
/// <summary> /// <summary>
/// Converts a DateTime to UTC (with special handling for MinValue and MaxValue). /// Converts a DateTime to UTC (with special handling for MinValue and MaxValue).
/// </summary> /// </summary>
...@@ -541,16 +326,5 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -541,16 +326,5 @@ namespace DotNetCore.CAP.Infrastructure
return dateTime.ToUniversalTime(); return dateTime.ToUniversalTime();
} }
private static int GetHexVal(char hex)
{
int val = hex;
//For uppercase A-F letters:
//return val - (val < 58 ? 48 : 55);
//For lowercase a-f letters:
//return val - (val < 58 ? 48 : 87);
//Or the two combined, but a bit slower:
return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
}
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment