14
14
*/
15
15
16
16
using System ;
17
- using System . Diagnostics ;
18
- using System . Runtime . CompilerServices ;
19
- using System . Security ;
20
- using System . Security . Cryptography ;
21
- using System . Text ;
22
17
using System . Threading ;
23
18
24
19
namespace MongoDB . Bson . Serialization . IdGenerators
@@ -30,40 +25,23 @@ namespace MongoDB.Bson.Serialization.IdGenerators
30
25
/// as the storage representation.
31
26
/// Internally the GUID is of the form
32
27
/// 8 bytes: Ticks from DateTime.UtcNow.Ticks
33
- /// 3 bytes: hash of machine name
34
- /// 2 bytes: low order bytes of process Id
28
+ /// 5 bytes: Random value from ObjectId spec
35
29
/// 3 bytes: increment
36
30
/// </summary>
37
31
public class AscendingGuidGenerator : IIdGenerator
38
32
{
39
33
// private static fields
40
34
private static readonly AscendingGuidGenerator __instance = new AscendingGuidGenerator ( ) ;
41
- private static readonly byte [ ] __machineProcessId ;
35
+ private static readonly byte [ ] __random ;
42
36
private static int __increment ;
43
37
44
38
// static constructor
45
39
static AscendingGuidGenerator ( )
46
40
{
47
- var machineHash = GetMachineHash ( ) ;
48
- short processId ;
49
- try
50
- {
51
- // use low order two bytes only
52
- processId = ( short ) GetCurrentProcessId ( ) ;
53
- }
54
- catch ( SecurityException )
55
- {
56
- processId = 0 ;
57
- }
58
-
59
- __machineProcessId = new byte [ 5 ]
60
- {
61
- machineHash [ 0 ] ,
62
- machineHash [ 1 ] ,
63
- machineHash [ 2 ] ,
64
- ( byte ) ( processId >> 8 ) ,
65
- ( byte ) ( processId )
66
- } ;
41
+ var random = ObjectId . CalculateRandomValue ( ) ;
42
+ var random8Bytes = BitConverter . GetBytes ( random ) ;
43
+ __random = new byte [ 5 ] ;
44
+ Array . Copy ( random8Bytes , __random , 5 ) ; // the 5 bytes we need are the first 5 bytes assuming little-endian
67
45
}
68
46
69
47
// public static properties
@@ -89,7 +67,7 @@ public static AscendingGuidGenerator Instance
89
67
public object GenerateId ( object container , object document )
90
68
{
91
69
var increment = Interlocked . Increment ( ref __increment ) & 0x00ffffff ;
92
- return GenerateId ( DateTime . UtcNow . Ticks , __machineProcessId , increment ) ;
70
+ return GenerateId ( DateTime . UtcNow . Ticks , __random , increment ) ;
93
71
}
94
72
95
73
/// <summary>
@@ -109,11 +87,14 @@ public object GenerateId(
109
87
byte [ ] machineProcessId ,
110
88
int increment )
111
89
{
90
+ if ( machineProcessId == null ) { throw new ArgumentNullException ( nameof ( machineProcessId ) ) ; }
91
+ if ( machineProcessId . Length != 5 ) { throw new ArgumentException ( $ "{ nameof ( machineProcessId ) } argument must be exactly 5 bytes", nameof ( machineProcessId ) ) ; }
92
+ var random5Bytes = machineProcessId ; // changing the parameter name could be considered a breaking change
112
93
var a = ( int ) ( tickCount >> 32 ) ;
113
94
var b = ( short ) ( tickCount >> 16 ) ;
114
95
var c = ( short ) ( tickCount ) ;
115
96
var d = new byte [ 8 ] ;
116
- Array . Copy ( machineProcessId , d , 5 ) ;
97
+ Array . Copy ( random5Bytes , d , 5 ) ;
117
98
d [ 5 ] = ( byte ) ( increment >> 16 ) ;
118
99
d [ 6 ] = ( byte ) ( increment >> 8 ) ;
119
100
d [ 7 ] = ( byte ) ( increment ) ;
@@ -129,32 +110,5 @@ public bool IsEmpty(object id)
129
110
{
130
111
return id == null || ( Guid ) id == Guid . Empty ;
131
112
}
132
-
133
- // private static methods
134
- /// <summary>
135
- /// Gets the current process id. This method exists because of how
136
- /// CAS operates on the call stack, checking for permissions before
137
- /// executing the method. Hence, if we inlined this call, the calling
138
- /// method would not execute before throwing an exception requiring the
139
- /// try/catch at an even higher level that we don't necessarily control.
140
- /// </summary>
141
- [ MethodImpl ( MethodImplOptions . NoInlining ) ]
142
- private static int GetCurrentProcessId ( )
143
- {
144
- return Process . GetCurrentProcess ( ) . Id ;
145
- }
146
-
147
- private static byte [ ] GetMachineHash ( )
148
- {
149
- // use instead of Dns.HostName so it will work offline
150
- var machineName = GetMachineName ( ) ;
151
- var sha1 = SHA1 . Create ( ) ;
152
- return sha1 . ComputeHash ( Encoding . UTF8 . GetBytes ( machineName ) ) ;
153
- }
154
-
155
- private static string GetMachineName ( )
156
- {
157
- return Environment . MachineName ;
158
- }
159
113
}
160
114
}
0 commit comments