//-----------------------------------------------------------------------
// 
//     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);
        }
    }
}