//----------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. // //----------------------------------------------------------------------- namespace Microsoft.Isam.Esent.Interop { using System; /// /// Class that helps cache strings. /// internal static class StringCache { /// /// Don't cache strings whose length is longer than this. /// private const int MaxLengthToCache = 128; /// /// Number of converted strings to hash. /// private const int NumCachedBoxedValues = 1031; /// /// Cached string values. /// private static readonly string[] CachedStrings = new string[NumCachedBoxedValues]; /// /// Return the interned version of a string, or the original /// string if it isn't interned. /// /// The string to try to intern. /// An interned copy of the string or the original string. public static string TryToIntern(string s) { #if MANAGEDESENT_ON_WSA // new Windows UI Core CLR does not support string interning. return s; #else return string.IsInterned(s) ?? s; #endif } /// /// Convert a byte array to a string. /// /// The bytes to convert. /// The starting index of the data to convert. /// The number of bytes to convert. /// A string converted from the data. public static string GetString(byte[] value, int startIndex, int count) { unsafe { fixed (byte* data = value) { char* chars = (char*)(data + startIndex); return GetString(chars, 0, count / sizeof(char)); } } } /// /// Convert a char array to a string, using a cached value if possible. /// /// The characters to convert. /// The starting index of the data to convert. /// The number of characters to convert. /// A string converted from the data. private static unsafe string GetString(char* value, int startIndex, int count) { string s; if (0 == count) { s = string.Empty; } else if (count < MaxLengthToCache) { uint hash = CalculateHash(value, startIndex, count); int index = unchecked((int)(hash % NumCachedBoxedValues)); s = CachedStrings[index]; if (null == s || !AreEqual(s, value, startIndex, count)) { s = CreateNewString(value, startIndex, count); CachedStrings[index] = s; } } else { s = CreateNewString(value, startIndex, count); } return s; } /// /// Calculate the hash of a string. /// /// The characters to hash. /// The starting index of the data to hash. /// The number of characters to hash. /// The hash value of the data. private static unsafe uint CalculateHash(char* value, int startIndex, int count) { uint hash = 0; unchecked { for (int i = 0; i < count; ++i) { hash ^= value[startIndex + i]; hash *= 33; } } return hash; } /// /// Determine if a string matches a char array.. /// /// The string to compare against. /// The characters. /// The starting index of the data. /// The number of characters. /// True if the string matches the char array. private static unsafe bool AreEqual(string s, char* value, int startIndex, int count) { if (s.Length != count) { return false; } unchecked { for (int i = 0; i < s.Length; ++i) { if (s[i] != value[startIndex + i]) { return false; } } } return true; } /// /// Convert a char array to a string. /// /// The characters to convert. /// The starting index of the data to convert. /// The number of characters to convert. /// A string converted from the data. private static unsafe string CreateNewString(char* value, int startIndex, int count) { // Encoding.Unicode.GetString copies the data to an array of chars and then // makes a string from it, copying the data twice. Use the more efficient // char* constructor. return new string(value, startIndex, count); } } }