-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathComb.cs
97 lines (88 loc) · 3.36 KB
/
Comb.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
using System;
using System.Threading;
namespace developmentalmadness
{
public class Comb
{
private static int counter;
private static long GetTicks()
{
int i = Interlocked.Increment(ref counter);
// use UTC now to prevent conflicts w/ daylight savings
return DateTime.UtcNow.Ticks + i;
}
/// <summary>
/// Creates a new sequential guid (aka comb) <see cref="http://www.informit.com/articles/article.aspx?p=25862&seqNum=7"/>.
/// </summary>
/// <remarks>A comb provides the benefits of a standard Guid w/o the database performance problems.</remarks>
/// <returns>A new sequential guid (comb).</returns>
public static Guid NewComb()
{
byte[] uid = Guid.NewGuid().ToByteArray();
byte[] binDate = BitConverter.GetBytes(GetTicks());
// create comb in SQL Server sort order
return new Guid(new[]{
// the first 7 bytes are random - if two combs
// are generated at the same point in time
// they are not guaranteed to be sequential.
// But for every DateTime.Tick there are
// 72,057,594,037,927,935 unique possibilities so
// there shouldn't be any collisions
uid[0],
uid[1],
uid[2],
uid[3],
uid[4],
uid[5],
uid[6],
// set the first 'nibble of the 7th byte to '1100' so
// later we can validate it was generated by us
(byte)(0xc0 | (0xf & uid[7])),
// the last 8 bytes are sequential,
// these will reduce index fragmentation
// to a degree as long as there are not a large
// number of Combs generated per millisecond
binDate[1],
binDate[0],
binDate[7],
binDate[6],
binDate[5],
binDate[4],
binDate[3],
binDate[2]
});
}
/// <summary>
/// Validates if comb was generated by this class
/// </summary>
/// <remarks>
/// Guids generated by Guid.NewGuid() have a value of
/// 0100 for the first 4 bits of the 7th byte. Ours will
/// have a value of 1100 for the 6th byte. We're checking that here.
///
/// We could do additional validation by verifying that
/// the value of a new Guid is greater than the
/// one being validated (or that the last 6 bytes
/// resolve to a valid DateTime), but this should
/// be enough for now.
/// </remarks>
public static bool IsComb(Guid value)
{
// get the 7th byte
byte b = value.ToByteArray()[7];
// make sure the first 'nibble' == 1100
return (0xc0 & b) == 0xc0;
}
/// <summary>
/// Validates Guid to determine the supplied
/// value was generated by Comb.NewComb. If
/// invalid an ArgumentException is thrown.
/// </summary>
/// <param name="value"></param>
public static void ValidateComb(Guid value)
{
if (!Comb.IsComb(value))
throw new ArgumentException("The supplied Id value was not generated by Comb.NewComb.");
}
}
}