File: UniqueId.cs

package info (click to toggle)
mono 4.6.2.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 778,148 kB
  • ctags: 914,052
  • sloc: cs: 5,779,509; xml: 2,773,713; ansic: 432,645; sh: 14,749; makefile: 12,361; perl: 2,488; python: 1,434; cpp: 849; asm: 531; sql: 95; sed: 16; php: 1
file content (138 lines) | stat: -rw-r--r-- 5,210 bytes parent folder | download | duplicates (7)
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//-----------------------------------------------------------------------
// <copyright file="UniqueId.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace System.IdentityModel
{
    using System;
    using System.Globalization;
    using System.IdentityModel;
    using System.Security.Cryptography;
    using System.Text;

    /// <summary>
    /// Generates unique IDs.
    /// </summary>
    internal static class UniqueId
    {
        private const int RandomSaltSize = 16;

        // We use UUIDs as the basis for our unique identifiers. UUIDs
        // cannot be used in xml:id-typed fields, because xml:id 
        // is made from the NCNAME production in XML Schema.
        //
        // An NCNAME looks like this: (simlified out complex unicode)
        //     [A-Za-z_][A-Za-z0-9.-_]*
        //
        // A UUID looks like this:
        //     [0-9A-Fa-f]{8}-(?:[0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}
        //
        // The problem arises when the UUID begins with [0-9], which 
        // violates the NCNAME production.
        //
        // This is fixed trivially by prepending an underscore.
        private const string NcNamePrefix = "_";

        // In some cases we need UniqueId to be a valid URI. In this
        // case we use the urn:uuid: namespace established by 
        // RFC4122. Note that in this case it is not appropriate to 
        // use the auto-incrementing optimization, as the resulting
        // value is no longer properly a UUID. 
        private const string UuidUriPrefix = "urn:uuid:";

        // For non-random identifiers, we optimize the generation of 
        // unique ids by calculating only one UUID per program invocation
        // and adding a 64-bit auto-incrementing value for each id
        // that is needed. 
        private static readonly string reusableUuid = GetRandomUuid();

        // The format of the optimized NCNAMEs produced is:
        //     _[0-9A-Fa-f]{8}-(?:[0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}-[A-Za-z0-9]{8}
        //
        // In other words: underscore + UUID + hyphen + 64-bit auto-incrementing id
        private static readonly string optimizedNcNamePrefix = NcNamePrefix + reusableUuid + "-";

        /// <summary>
        /// Creates a unique ID suitable for use in an xml:id field. The value is 
        /// not hard to guess but is unique.
        /// </summary>
        /// <returns>The unique ID.</returns>
        public static string CreateUniqueId()
        {
            return optimizedNcNamePrefix + GetNextId();
        }

        /// <summary>
        /// Creates a unique ID similar to that created by CreateNonRandomId, 
        /// but instead of an underscore, the supplied prefix is used.
        /// </summary>
        /// <param name="prefix">The prefix to use.</param>
        /// <returns>The unique ID, with the given prefix.</returns>
        public static string CreateUniqueId(string prefix)
        {
            if (string.IsNullOrEmpty(prefix))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("prefix");
            }

            return prefix + reusableUuid + "-" + GetNextId();
        }

        /// <summary>
        /// Creates a unique, random ID suitable for use in an xml:id field. The 
        /// value is hard to guess and unique.
        /// </summary>
        /// <returns>The unique ID.</returns>
        public static string CreateRandomId()
        {
            return NcNamePrefix + GetRandomUuid();
        }

        /// <summary>
        /// Creates a unique, random ID similar to that created by CreateRandomId,
        /// but instead of an underscore, the supplied prefix is used.
        /// </summary>
        /// <param name="prefix">The prefix to use.</param>
        /// <returns>The random URI.</returns>
        public static string CreateRandomId(string prefix)
        {
            if (string.IsNullOrEmpty(prefix))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("prefix");
            }

            return prefix + GetRandomUuid();
        }

        /// <summary>
        /// Creates a unique, random ID suitable for use as a URI. The value is 
        /// hard to guess and unique. The URI is in the urn:uuid: namespace.
        /// </summary>
        /// <returns>The random URI.</returns>
        public static Uri CreateRandomUri()
        {
            return new Uri(UuidUriPrefix + GetRandomUuid());
        }

        private static string GetNextId()
        {
            RandomNumberGenerator rng = RandomNumberGenerator.Create();
            byte[] id = new byte[RandomSaltSize];
            rng.GetBytes(id);
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < id.Length; i++)
            {
                builder.AppendFormat("{0:X2}", id[i]);
            }

            return builder.ToString();
        }

        private static string GetRandomUuid()
        {
            return Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture);
        }
    }
}