File: system\globalization\encodingtable.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
namespace System.Globalization
{
    using System;
    using System.Text;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Runtime.Versioning;
    using System.Security;
    using System.Threading;
    using System.Diagnostics.Contracts;
    //
    // Data table for encoding classes.  Used by System.Text.Encoding.
    // This class contains two hashtables to allow System.Text.Encoding
    // to retrieve the data item either by codepage value or by webName.
    //
    
    // Only statics, does not need to be marked with the serializable attribute
    internal static class EncodingTable
    {
 
        //This number is the size of the table in native.  The value is retrieved by
        //calling the native GetNumEncodingItems().
        private static int lastEncodingItem = GetNumEncodingItems() - 1;
 
        //This number is the size of the code page table.  Its generated when we walk the table the first time.
        private static volatile int lastCodePageItem;
        
        //
        // This points to a native data table which maps an encoding name to the correct code page.        
        //
        [SecurityCritical]
        unsafe internal static InternalEncodingDataItem *encodingDataPtr = GetEncodingData();
        //
        // This points to a native data table which stores the properties for the code page, and
        // the table is indexed by code page.
        //
        [SecurityCritical]
        unsafe internal static InternalCodePageDataItem *codePageDataPtr = GetCodePageData();
        //
        // This caches the mapping of an encoding name to a code page.
        //
        private static Hashtable hashByName = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase));
        //
        // THe caches the data item which is indexed by the code page value.
        //
        private static Hashtable hashByCodePage = Hashtable.Synchronized(new Hashtable());
 
        [System.Security.SecuritySafeCritical] // static constructors should be safe to call
        static EncodingTable()
        { 
        }
 
        // Find the data item by binary searching the table that we have in native.
        // nativeCompareOrdinalWC is an internal-only function.
        [System.Security.SecuritySafeCritical]  // auto-generated
        unsafe private static int internalGetCodePageFromName(String name) {
            int left  = 0;
            int right = lastEncodingItem;
            int index;
            int result;
    
            //Binary search the array until we have only a couple of elements left and then
            //just walk those elements.
            while ((right - left)>3) {
                index = ((right - left)/2) + left;
                
                result = String.nativeCompareOrdinalIgnoreCaseWC(name, encodingDataPtr[index].webName);
    
                if (result == 0) {
                    //We found the item, return the associated codepage.
                    return (encodingDataPtr[index].codePage);
                } else if (result<0) {
                    //The name that we're looking for is less than our current index.
                    right = index;
                } else {
                    //The name that we're looking for is greater than our current index
                    left = index;
                }
            }
    
            //Walk the remaining elements (it'll be 3 or fewer).
            for (; left<=right; left++) {
                if (String.nativeCompareOrdinalIgnoreCaseWC(name, encodingDataPtr[left].webName) == 0) {
                    return (encodingDataPtr[left].codePage);
                }
            }
            // The encoding name is not valid.
            throw new ArgumentException(
                String.Format(
                    CultureInfo.CurrentCulture,
                    Environment.GetResourceString("Argument_EncodingNotSupported"), name), "name");
        }
 
        // Return a list of all EncodingInfo objects describing all of our encodings
        [System.Security.SecuritySafeCritical]  // auto-generated
        internal static unsafe EncodingInfo[] GetEncodings()
        {
            if (lastCodePageItem == 0)
            {
                int count;
                for (count = 0; codePageDataPtr[count].codePage != 0; count++)
                {
                    // Count them
                }
                lastCodePageItem = count;
            }
 
            EncodingInfo[] arrayEncodingInfo = new EncodingInfo[lastCodePageItem];
 
            int i;
            for (i = 0; i < lastCodePageItem; i++)
            {
                arrayEncodingInfo[i] = new EncodingInfo(codePageDataPtr[i].codePage, CodePageDataItem.CreateString(codePageDataPtr[i].Names, 0),
                    Environment.GetResourceString("Globalization.cp_" + codePageDataPtr[i].codePage));
            }
            
            return arrayEncodingInfo;
        }        
    
        /*=================================GetCodePageFromName==========================
        **Action: Given a encoding name, return the correct code page number for this encoding.
        **Returns: The code page for the encoding.
        **Arguments:
        **  name    the name of the encoding
        **Exceptions:
        **  ArgumentNullException if name is null.
        **  internalGetCodePageFromName will throw ArgumentException if name is not a valid encoding name.
        ============================================================================*/
        
        internal static int GetCodePageFromName(String name)
        {   
            if (name==null) {
                throw new ArgumentNullException("name");
            }
            Contract.EndContractBlock();
 
            Object codePageObj;
 
            //
            // The name is case-insensitive, but ToLower isn't free.  Check for
            // the code page in the given capitalization first.
            //
            codePageObj = hashByName[name];
 
            if (codePageObj!=null) {
                return ((int)codePageObj);
            }
 
            //Okay, we didn't find it in the hash table, try looking it up in the 
            //unmanaged data.
            int codePage = internalGetCodePageFromName(name);
 
            hashByName[name] = codePage;
 
            return codePage;
        }
    
        [System.Security.SecuritySafeCritical]  // auto-generated
        unsafe internal static CodePageDataItem GetCodePageDataItem(int codepage) {
            CodePageDataItem dataItem;
 
            // We synchronize around dictionary gets/sets. There's still a possibility that two threads
            // will create a CodePageDataItem and the second will clobber the first in the dictionary. 
            // However, that's acceptable because the contents are correct and we make no guarantees
            // other than that. 
 
            //Look up the item in the hashtable.
            dataItem = (CodePageDataItem)hashByCodePage[codepage];
            
            //If we found it, return it.
            if (dataItem!=null) {
                return dataItem;
            }
 
 
            //If we didn't find it, try looking it up now.
            //If we find it, add it to the hashtable.
            //This is a linear search, but we probably won't be doing it very often.
            //
            int i = 0;
            int data;
            while ((data = codePageDataPtr[i].codePage) != 0) {
                if (data == codepage) {
                    dataItem = new CodePageDataItem(i);
                    hashByCodePage[codepage] = dataItem;
                    return (dataItem);
                }
                i++;
            }
        
            //Nope, we didn't find it.
            return null;
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]  // Returns a pointer to a process-wide instance
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private unsafe static extern InternalEncodingDataItem *GetEncodingData();
        
        //
        // Return the number of encoding data items.
        //
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern int GetNumEncodingItems();
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]  // Returns a pointer to a process-wide instance
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private unsafe static extern InternalCodePageDataItem* GetCodePageData();
 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal unsafe static extern byte* nativeCreateOpenFileMapping(
            String inSectionName, int inBytesToAllocate, out IntPtr mappedFileHandle);   
    }
    
    /*=================================InternalEncodingDataItem==========================
    **Action: This is used to map a encoding name to a correct code page number. By doing this,
    ** we can get the properties of this encoding via the InternalCodePageDataItem.
    **
    ** We use this structure to access native data exposed by the native side.
    ============================================================================*/
    
    [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
    internal unsafe struct InternalEncodingDataItem {
        [SecurityCritical]
        internal sbyte  * webName;
        internal UInt16   codePage;
    }
 
    /*=================================InternalCodePageDataItem==========================
    **Action: This is used to access the properties related to a code page.
    ** We use this structure to access native data exposed by the native side.
    ============================================================================*/
 
    [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
    internal unsafe struct InternalCodePageDataItem {
        internal UInt16   codePage;
        internal UInt16   uiFamilyCodePage;
        internal uint     flags;
        [SecurityCritical]
        internal sbyte  * Names;
    }
 
}