/// <summary> /// For internal use; use the parameterless overload for effeciency. /// </summary> /// <param name="host"></param> /// <returns></returns> public static string ComputeHostHash(string host) { long sum = new long?(getBytesSum(computeMd5(host), 36, 2)).Value; string hash = Base36Converter.Encode(sum); return(hash); }
/// <summary> /// Compresses the MD5 of this server's hostname by summing the bytes until the sum /// fits into two Base36 characters (<= 36^2, or 1296): /// </summary> /// <returns>2 character Base36 checksum of MD5 of hostname</returns> public static string ComputeHostHash() { string hash = Base36Converter.Encode( (_hostSum.HasValue ? _hostSum.Value : ( _hostSum = new long?(getBytesSum(computeMd5(getHostBytes()), 36, 2))).Value)); return(hash); }
/// <summary> /// Returns a random Base36 number 3 characters long. /// </summary> /// <returns></returns> private static string getRandomDigits() { Int32 rndInt; lock (_lockObj) { rndInt = _rnd.Next(46655); } return(Base36Converter.Encode(rndInt)); }
/// <summary> /// Returns value with all non base 36 characters removed. /// </summary> /// <param name="value"></param> /// <returns></returns> public static Base36Id Parse(string value) { if (string.IsNullOrEmpty(value)) { throw new ArgumentNullException(paramName: "value"); } string ret = _regex.Replace(value, "").ToUpper(); if (ret.Length != 16) { throw new ArgumentException("Value was not a valid Base36 id", "value"); } Base36Id fields = new Base36Id { Id = ret, HostHash = ret.Substring(10, 2), Reserved = ret.Substring(12, 1), InService = _inService }; int pos = Base36Converter.CharList.IndexOf(fields.Reserved, System.StringComparison.Ordinal); int centuryCycles = Math.Max(0, ((35 - pos) - 1)); // some precision is lost, as chars beyond 10 weren't originally from the timestamp // component of the ID: int probableEncodedLength = Base36Converter.FromInt64(InServiceDate.AddDays(365.25 * 115.89 * centuryCycles).Ticks / 10).Length; if (centuryCycles == 0) { probableEncodedLength = 10; } string encodedMs = ret.Substring(0, probableEncodedLength); long ms = Base36Converter.Decode(encodedMs); fields.Microseconds = ms; fields.CreatedRoughly = _inService.AddTicks(ms * 10); //.AddTicks((3656158440062976 * 10 + 9) * centuryCycles); return(fields); }
/// <summary> /// Generates a unique, sequential, Base36 string, 16 characters long. /// The first 10 characters are the microseconds elapsed since the InService DateTime /// (constant field you hardcode in this file). /// The next 2 characters are a compressed checksum of the MD5 of this host. /// The next 1 character is a reserved constant of 36 ('Z' in Base36). /// The last 3 characters are random number less than 46655 additional for additional uniqueness. /// </summary> /// <param name="delimiter">If provided, formats the ID as four groups of /// 4 characters separated by the delimiter.</param> /// <returns>Returns a unique, sequential, 16-character Base36 string</returns> public static string NewBase36String(string delimiter) { // For 10 chars from timestamp, microseconds since InService, uses Stopwatch... long microseconds = initStaticsReturnMicroseconds(); // For 2 chars from host id MD5... string hostHash = (_hostHash ?? (_hostHash = ComputeHostHash())); // 1 reserved char; static largest base36 digit. // If the same ID system, scheme and sequence is still in use // more than 115.85 years after in-service date, decrements // 'reserved' by 1 for each whole multiple of 115 years // elapsed, up to 35 times max. If the same system, scheme // and sequence is still in service 3,850 years from the // initial go-live, you probably have bigger problems than // ID collisions... int reserved = _reserved; // 3 chars random in Base36 = 46656 units string rndHexStr = getRandomDigits(); StringBuilder sb = new StringBuilder(); sb.Append(Base36Converter.FromInt64(microseconds).PadLeft(10, '0')); sb.Length = 10; sb.Append(hostHash.PadLeft(2, '0')); sb.Length = 12; sb.Append(Base36Converter.FromInt32(reserved)); sb.Length = 13; sb.Append(Base36Converter.FromHex(rndHexStr).PadLeft(3, '0')); sb.Length = 16; if (!string.IsNullOrEmpty(delimiter)) { sb.Insert(12, delimiter); sb.Insert(8, delimiter); sb.Insert(4, delimiter); } return(sb.ToString()); }