1 //------------------------------------------------------------------------------
2 // <copyright file="HashCodeCombiner.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
8 * HashCodeCombiner class
10 * Copyright (c) 1999 Microsoft Corporation
13 namespace System.Web.Util {
16 using System.Globalization;
17 using System.Diagnostics;
18 using System.Diagnostics.CodeAnalysis;
19 using System.Web.Security.Cryptography;
22 * Class used to combine several hashcodes into a single hashcode
24 internal class HashCodeCombiner {
26 private long _combinedHash;
28 internal HashCodeCombiner() {
29 // Start with a seed (obtained from String.GetHashCode implementation)
33 internal HashCodeCombiner(long initialCombinedHash) {
34 _combinedHash = initialCombinedHash;
37 internal static int CombineHashCodes(int h1, int h2) {
38 return ((h1 << 5) + h1) ^ h2;
41 internal static int CombineHashCodes(int h1, int h2, int h3) {
42 return CombineHashCodes(CombineHashCodes(h1, h2), h3);
45 internal static int CombineHashCodes(int h1, int h2, int h3, int h4) {
46 return CombineHashCodes(CombineHashCodes(h1, h2), CombineHashCodes(h3, h4));
49 internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) {
50 return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), h5);
53 internal static string GetDirectoryHash(VirtualPath virtualDir) {
54 HashCodeCombiner hashCodeCombiner = new HashCodeCombiner();
55 hashCodeCombiner.AddDirectory(virtualDir.MapPathInternal());
56 return hashCodeCombiner.CombinedHashString;
59 internal void AddArray(string[] a) {
62 for (int i = 0; i < n; i++) {
68 internal void AddInt(int n) {
69 _combinedHash = ((_combinedHash << 5) + _combinedHash) ^ n;
70 Debug.Trace("HashCodeCombiner", "Adding " + n.ToString("x") + " --> " + _combinedHash.ToString("x"));
73 internal void AddObject(int n) {
77 internal void AddObject(byte b) {
78 AddInt(b.GetHashCode());
81 internal void AddObject(long l) {
82 AddInt(l.GetHashCode());
85 internal void AddObject(bool b) {
86 AddInt(b.GetHashCode());
89 internal void AddObject(string s) {
91 AddInt(StringUtil.GetStringHashCode(s));
94 internal void AddObject(Type t) {
96 AddObject(System.Web.UI.Util.GetAssemblyQualifiedTypeName(t));
99 internal void AddObject(object o) {
101 AddInt(o.GetHashCode());
104 internal void AddCaseInsensitiveString(string s) {
106 AddInt(StringUtil.GetNonRandomizedHashCode(s, ignoreCase:true));
109 internal void AddDateTime(DateTime dt) {
110 Debug.Trace("HashCodeCombiner", "Ticks: " + dt.Ticks.ToString("x", CultureInfo.InvariantCulture));
111 Debug.Trace("HashCodeCombiner", "Hashcode: " + dt.GetHashCode().ToString("x", CultureInfo.InvariantCulture));
112 AddInt(dt.GetHashCode());
115 private void AddFileSize(long fileSize) {
116 Debug.Trace("HashCodeCombiner", "file size: " + fileSize.ToString("x", CultureInfo.InvariantCulture));
117 Debug.Trace("HashCodeCombiner", "Hashcode: " + fileSize.GetHashCode().ToString("x", CultureInfo.InvariantCulture));
118 AddInt(fileSize.GetHashCode());
121 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This call site is trusted.")]
122 private void AddFileVersionInfo(FileVersionInfo fileVersionInfo) {
123 Debug.Trace("HashCodeCombiner", "FileMajorPart: " + fileVersionInfo.FileMajorPart.GetHashCode().ToString("x", CultureInfo.InvariantCulture));
124 Debug.Trace("HashCodeCombiner", "FileMinorPart: " + fileVersionInfo.FileMinorPart.GetHashCode().ToString("x", CultureInfo.InvariantCulture));
125 Debug.Trace("HashCodeCombiner", "FileBuildPart: " + fileVersionInfo.FileBuildPart.GetHashCode().ToString("x", CultureInfo.InvariantCulture));
126 Debug.Trace("HashCodeCombiner", "FilePrivatePart: " + fileVersionInfo.FilePrivatePart.GetHashCode().ToString("x", CultureInfo.InvariantCulture));
127 AddInt(fileVersionInfo.FileMajorPart.GetHashCode());
128 AddInt(fileVersionInfo.FileMinorPart.GetHashCode());
129 AddInt(fileVersionInfo.FileBuildPart.GetHashCode());
130 AddInt(fileVersionInfo.FilePrivatePart.GetHashCode());
133 private void AddFileContentHashKey(string fileContentHashKey) {
134 AddInt(StringUtil.GetNonRandomizedHashCode(fileContentHashKey));
137 internal void AddFileContentHash(string fileName) {
138 // Convert file content to hash bytes
139 byte[] fileContentBytes = File.ReadAllBytes(fileName);
140 byte[] fileContentHashBytes = CryptoUtil.ComputeSHA256Hash(fileContentBytes);
142 // Convert byte[] to hex string representation
143 StringBuilder sbFileContentHashBytes = new StringBuilder();
144 for (int index = 0; index < fileContentHashBytes.Length; index++) {
145 sbFileContentHashBytes.Append(fileContentHashBytes[index].ToString("X2", CultureInfo.InvariantCulture));
148 AddFileContentHashKey(sbFileContentHashBytes.ToString());
151 internal void AddFile(string fileName) {
152 Debug.Trace("HashCodeCombiner", "AddFile: " + fileName);
153 if (!FileUtil.FileExists(fileName)) {
155 // Review: Should we change the dependency model to take directory into account?
156 if (FileUtil.DirectoryExists(fileName)) {
157 // Add as a directory dependency if it's a directory.
158 AddDirectory(fileName);
162 Debug.Trace("HashCodeCombiner", "Could not find target " + fileName);
166 AddExistingFile(fileName);
169 // Same as AddFile, but only called for a file which is known to exist
170 private void AddExistingFile(string fileName) {
171 Debug.Trace("HashCodeCombiner", "AddExistingFile: " + fileName);
173 AddInt(StringUtil.GetStringHashCode(fileName));
174 FileInfo file = new FileInfo(fileName);
175 if (!AppSettings.PortableCompilationOutput) {
176 AddDateTime(file.CreationTimeUtc);
178 AddDateTime(file.LastWriteTimeUtc);
179 AddFileSize(file.Length);
182 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This call site is trusted.")]
183 internal void AddExistingFileVersion(string fileName) {
184 Debug.Trace("HashCodeCombiner", "AddExistingFileVersion: " + fileName);
186 AddInt(StringUtil.GetStringHashCode(fileName));
187 FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(fileName);
189 AddFileVersionInfo(fileVersionInfo);
192 internal void AddDirectory(string directoryName) {
194 DirectoryInfo directory = new DirectoryInfo(directoryName);
195 if (!directory.Exists) {
199 AddObject(directoryName);
201 // Go through all the files in the directory
202 foreach (FileData fileData in FileEnumerator.Create(directoryName)) {
204 if (fileData.IsDirectory)
205 AddDirectory(fileData.FullName);
207 AddExistingFile(fileData.FullName);
210 if (!AppSettings.PortableCompilationOutput) {
211 AddDateTime(directory.CreationTimeUtc);
212 AddDateTime(directory.LastWriteTimeUtc);
216 // Same as AddDirectory, but only look at files that don't have a culture
217 internal void AddResourcesDirectory(string directoryName) {
219 DirectoryInfo directory = new DirectoryInfo(directoryName);
220 if (!directory.Exists) {
224 AddObject(directoryName);
226 // Go through all the files in the directory
227 foreach (FileData fileData in FileEnumerator.Create(directoryName)) {
229 if (fileData.IsDirectory)
230 AddResourcesDirectory(fileData.FullName);
232 // Ignore the file if it has a culture, since only neutral files
233 // need to re-trigger compilation (VSWhidbey 359029)
234 string fullPath = fileData.FullName;
235 if (System.Web.UI.Util.GetCultureName(fullPath) == null) {
236 AddExistingFile(fullPath);
241 if (!AppSettings.PortableCompilationOutput) {
242 AddDateTime(directory.CreationTimeUtc);
246 internal long CombinedHash { get { return _combinedHash; } }
247 internal int CombinedHash32 { get { return _combinedHash.GetHashCode(); } }
249 internal string CombinedHashString {
251 return _combinedHash.ToString("x", CultureInfo.InvariantCulture);