<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id$
 */


// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#if HAVE_CONFIG_H
#	include <config.h>
#endif

#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/TranscodingException.hpp>
#include <xercesc/util/XMLException.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/XMLUni.hpp>
#include <xercesc/util/RefHashTableOf.hpp>
#include "Win32TransService.hpp"
#include <windows.h>

XERCES_CPP_NAMESPACE_BEGIN


// ---------------------------------------------------------------------------
//  Local, const data
// ---------------------------------------------------------------------------
static const XMLCh gMyServiceId[] =
{
    chLatin_W, chLatin_i, chLatin_n, chDigit_3, chDigit_2, chNull
};


#if !HAVE_WCSUPR
void wcsupr(LPWSTR str)
{
    int nLen=XMLString::stringLen(str);
    ::LCMapStringW( GetThreadLocale(), LCMAP_UPPERCASE, str, nLen, str, nLen);
}
#endif

#if !HAVE_WCSLWR
void wcslwr(LPWSTR str)
{
    int nLen=XMLString::stringLen(str);
    ::LCMapStringW( GetThreadLocale(), LCMAP_LOWERCASE, str, nLen, str, nLen);
}
#endif

#if !HAVE_WCSNICMP
int wcsnicmp(LPCWSTR comp1, LPCWSTR comp2, unsigned int nLen)
{
    unsigned int len = XMLString::stringLen( comp1);
    unsigned int otherLen = XMLString::stringLen( comp2);
    unsigned int countChar = 0;
    unsigned int maxChars;
    int          theResult = 0;

    // Determine at what string index the comparison stops.
    len = ( len > nLen ) ? nLen : len;
    otherLen = ( otherLen > nLen ) ? nLen : otherLen;
    maxChars = ( len > otherLen ) ? otherLen : len;

    // Handle situation when one argument or the other is NULL
    // by returning +/- string length of non-NULL argument (inferred
    // from XMLString::CompareNString).

    // Obs. Definition of stringLen(XMLCh*) implies NULL ptr and ptr
    // to Empty String are equivalent.  It handles NULL args, BTW.

    if ( !comp1 )
    {
        // Negative because null ptr (c1) less than string (c2).
        return ( 0 - otherLen );
    }

    if ( !comp2 )
    {
        // Positive because string (c1) still greater than null ptr (c2).
        return len;
    }

    // Copy const parameter strings (plus terminating nul) into locals.
    XMLCh* firstBuf = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate( (++len) * sizeof(XMLCh) );//new XMLCh[ ++len];
    XMLCh* secondBuf = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate( (++otherLen) * sizeof(XMLCh) );//new XMLCh[ ++otherLen];
    memcpy( firstBuf, comp1, len * sizeof(XMLCh));
    memcpy( secondBuf, comp2, otherLen * sizeof(XMLCh));

    // Then uppercase both strings, losing their case info.
    ::LCMapStringW( GetThreadLocale(), LCMAP_UPPERCASE, (LPWSTR)firstBuf, len, (LPWSTR)firstBuf, len);
    ::LCMapStringW( GetThreadLocale(), LCMAP_UPPERCASE, (LPWSTR)secondBuf, otherLen, (LPWSTR)secondBuf, otherLen);

    // Strings are equal until proven otherwise.
    while ( ( countChar < maxChars ) && ( !theResult ) )
    {
        theResult = (int)(firstBuf[countChar]) - (int)(secondBuf[countChar]);
        ++countChar;
    }

    XMLPlatformUtils::fgMemoryManager->deallocate(firstBuf);//delete [] firstBuf;
    XMLPlatformUtils::fgMemoryManager->deallocate(secondBuf);//delete [] secondBuf;

    return theResult;
}
#endif

#if !HAVE_WCSICMP
int wcsicmp(LPCWSTR comp1, LPCWSTR comp2)
{
    unsigned int len = XMLString::stringLen( comp1);
    unsigned int otherLen = XMLString::stringLen( comp2);
    // Must compare terminating NUL to return difference if one string is shorter than the other.
    unsigned int maxChars = ( len > otherLen ) ? otherLen : len;
    return wcsnicmp(comp1, comp2, maxChars+1);
}
#endif

// ---------------------------------------------------------------------------
//  This is the simple CPMapEntry class. It just contains an encoding name
//  and a code page for that encoding.
// ---------------------------------------------------------------------------
class CPMapEntry : public XMemory
{
public :
    // -----------------------------------------------------------------------
    //  Constructors and Destructor
    // -----------------------------------------------------------------------
    CPMapEntry
    (
        const   XMLCh* const    encodingName
        , const unsigned int    ieId
    );

    CPMapEntry
    (
        const   char* const     encodingName
        , const unsigned int    ieId
    );

    ~CPMapEntry();


    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------
    const XMLCh* getEncodingName() const;
    const XMLCh* getKey() const;
    unsigned int getIEEncoding() const;


private :
    // -----------------------------------------------------------------------
    //  Unimplemented constructors and operators
    // -----------------------------------------------------------------------
    CPMapEntry();
    CPMapEntry(const CPMapEntry&);
    CPMapEntry& operator=(const CPMapEntry&);


    // -----------------------------------------------------------------------
    //  Private data members
    //
    //  fEncodingName
    //      This is the encoding name for the code page that this instance
    //      represents.
    //
    //  fIEId
    //      This is the code page id.
    // -----------------------------------------------------------------------
    XMLCh*          fEncodingName;
    unsigned int    fIEId;
};

// ---------------------------------------------------------------------------
//  CPMapEntry: Constructors and Destructor
// ---------------------------------------------------------------------------
CPMapEntry::CPMapEntry( const   char* const     encodingName
                        , const unsigned int    ieId) :
    fEncodingName(0)
    , fIEId(ieId)
{
    // Transcode the name to Unicode and store that copy
    const XMLSize_t srcLen = strlen(encodingName);
	int targetLen=::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, encodingName, srcLen, NULL, 0);
    if(targetLen!=0)
    {
        fEncodingName = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate
        (
            (targetLen + 1) * sizeof(XMLCh)
        );//new XMLCh[targetLen + 1];
		::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, encodingName, srcLen, (LPWSTR)fEncodingName, targetLen);
        fEncodingName[targetLen] = 0;

        //
        //  Upper case it because we are using a hash table and need to be
        //  sure that we find all case combinations.
        //
        wcsupr(fEncodingName);
  }
}

CPMapEntry::CPMapEntry( const   XMLCh* const    encodingName
                        , const unsigned int    ieId) :

    fEncodingName(0)
    , fIEId(ieId)
{
    fEncodingName = XMLString::replicate(encodingName, XMLPlatformUtils::fgMemoryManager);

    //
    //  Upper case it because we are using a hash table and need to be
    //  sure that we find all case combinations.
    //
    wcsupr(fEncodingName);
}

CPMapEntry::~CPMapEntry()
{
    XMLPlatformUtils::fgMemoryManager->deallocate(fEncodingName);//delete [] fEncodingName;
}


// ---------------------------------------------------------------------------
//  CPMapEntry: Getter methods
// ---------------------------------------------------------------------------
const XMLCh* CPMapEntry::getEncodingName() const
{
    return fEncodingName;
}

unsigned int CPMapEntry::getIEEncoding() const
{
    return fIEId;
}



//---------------------------------------------------------------------------
//
//  class Win32TransService Implementation ...
//
//---------------------------------------------------------------------------


// ---------------------------------------------------------------------------
//  Win32TransService: Constructors and Destructor
// ---------------------------------------------------------------------------
Win32TransService::Win32TransService()
{
    fCPMap = new RefHashTableOf<CPMapEntry>(109);

    //
    //  Open up the registry key that contains the info we want. Note that,
    //  if this key does not exist, then we just return. It will just mean
    //  that we don't have any support except for intrinsic encodings supported
    //  by the parser itself (and the LCP support of course.
    //
    HKEY charsetKey;
    if (::RegOpenKeyExA
    (
        HKEY_CLASSES_ROOT
        , "MIME\\Database\\Charset"
        , 0
        , KEY_READ
        , &charsetKey))
    {
        return;
    }

    //
    //  Read in the registry keys that hold the code page ids. Skip for now
    //  those entries which indicate that they are aliases for some other
    //  encodings. We'll come back and do a second round for those and look
    //  up the original name and get the code page id.
    //
    //  Note that we have to use A versions here so that this will run on
    //  98, and transcode the strings to Unicode.
    //
    const unsigned int nameBufSz = 1024;
    char nameBuf[nameBufSz + 1];
    unsigned int subIndex;
    unsigned long theSize;
    for (subIndex = 0;;++subIndex)
    {
        // Get the name of the next key
        theSize = nameBufSz;
        if (::RegEnumKeyExA
        (
            charsetKey
            , subIndex
            , nameBuf
            , &theSize
            , 0, 0, 0, 0) == ERROR_NO_MORE_ITEMS)
        {
            break;
        }

        // Open this subkey
        HKEY encodingKey;
        if (::RegOpenKeyExA
        (
            charsetKey
            , nameBuf
            , 0
            , KEY_READ
            , &encodingKey))
        {
            continue;
        }

        //
        //  Lts see if its an alias. If so, then ignore it in this first
        //  loop. Else, we'll add a new entry for this one.
        //
        if (!isAlias(encodingKey))
        {
            //
            //  Lets get the two values out of this key that we are
            //  interested in. There should be a code page entry and an
            //  IE entry.
            //
            //  The Codepage entry is the default code page for a computer using that charset
            //  while the InternetEncoding holds the code page that represents that charset
            //
            unsigned long theType;
            unsigned int CPId;
            unsigned int IEId;

            theSize = sizeof(unsigned int);
            if (::RegQueryValueExA
            (
                encodingKey
                , "Codepage"
                , 0
                , &theType
                , (unsigned char*)&CPId
                , &theSize) != ERROR_SUCCESS)
            {
                ::RegCloseKey(encodingKey);
                continue;
            }

            //
            //  If this is not a valid Id, and it might not be because its
            //  not loaded on this system, then don't take it.
            //
            if (::IsValidCodePage(CPId))
            {
                theSize = sizeof(unsigned int);
                if (::RegQueryValueExA
                (
                    encodingKey
                    , "InternetEncoding"
                    , 0
                    , &theType
                    , (unsigned char*)&IEId
                    , &theSize) != ERROR_SUCCESS)
                {
                    ::RegCloseKey(encodingKey);
                    continue;
                }

                CPMapEntry* newEntry = new CPMapEntry(nameBuf, IEId);
                fCPMap->put((void*)newEntry->getEncodingName(), newEntry);
            }
        }

        // And close the subkey handle
        ::RegCloseKey(encodingKey);
    }

    //
    //  Now loop one more time and this time we do just the aliases. For
    //  each one we find, we look up that name in the map we've already
    //  built and add a new entry with this new name and the same id
    //  values we stored for the original.
    //
    char aliasBuf[nameBufSz + 1];
    for (subIndex = 0;;++subIndex)
    {
        // Get the name of the next key
        theSize = nameBufSz;
        if (::RegEnumKeyExA
        (
            charsetKey
            , subIndex
            , nameBuf
            , &theSize
            , 0, 0, 0, 0) == ERROR_NO_MORE_ITEMS)
        {
            break;
        }

        // Open this subkey
        HKEY encodingKey;
        if (::RegOpenKeyExA
        (
            charsetKey
            , nameBuf
            , 0
            , KEY_READ
            , &encodingKey))
        {
            continue;
        }

        //
        //  If its an alias, look up the name in the map. If we find it,
        //  then construct a new one with the new name and the aliased
        //  ids.
        //
        if (isAlias(encodingKey, aliasBuf, nameBufSz))
        {
            const XMLSize_t srcLen = strlen(aliasBuf);
            int targetLen = ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, aliasBuf, srcLen, NULL, 0);
            if(targetLen!=0)
            {
                XMLCh* uniAlias = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate
                (
                    (targetLen + 1) * sizeof(XMLCh)
                );//new XMLCh[targetLen + 1];
	            ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, aliasBuf, srcLen, (LPWSTR)uniAlias, targetLen);
                uniAlias[targetLen] = 0;
                wcsupr(uniAlias);

                // Look up the alias name
                CPMapEntry* aliasedEntry = fCPMap->get(uniAlias);
                if (aliasedEntry)
                {
                    const XMLSize_t srcLen = strlen(nameBuf);
		            int targetLen = ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, nameBuf, srcLen, NULL, 0);
                    if(targetLen!=0)
                    {
                        XMLCh* uniName = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate
                        (
                            (targetLen + 1) * sizeof(XMLCh)
                        );//new XMLCh[targetLen + 1];
			            ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, nameBuf, srcLen, (LPWSTR)uniName, targetLen);
                        uniName[targetLen] = 0;
                        wcsupr(uniName);

                        //
                        //  If the name is actually different, then take it.
                        //  Otherwise, don't take it. They map aliases that are
                        //  just different case.
                        //
                        if (::wcscmp(uniName, aliasedEntry->getEncodingName()))
                        {
                            CPMapEntry* newEntry = new CPMapEntry(uniName, aliasedEntry->getIEEncoding());
                            fCPMap->put((void*)newEntry->getEncodingName(), newEntry);
                        }

                        XMLPlatformUtils::fgMemoryManager->deallocate(uniName);//delete [] uniName;
                    }
                }
                XMLPlatformUtils::fgMemoryManager->deallocate(uniAlias);//delete [] uniAlias;
            }
        }

        // And close the subkey handle
        ::RegCloseKey(encodingKey);
    }

    // And close the main key handle
    ::RegCloseKey(charsetKey);

}

Win32TransService::~Win32TransService()
{
    delete fCPMap;
}


// ---------------------------------------------------------------------------
//  Win32TransService: The virtual transcoding service API
// ---------------------------------------------------------------------------
int Win32TransService::compareIString(  const   XMLCh* const    comp1
                                        , const XMLCh* const    comp2)
{
    return wcsicmp(comp1, comp2);
}


int Win32TransService::compareNIString( const   XMLCh* const    comp1
                                        , const XMLCh* const    comp2
                                        , const XMLSize_t       maxChars)
{
    return wcsnicmp(comp1, comp2, maxChars);
}


const XMLCh* Win32TransService::getId() const
{
    return gMyServiceId;
}

XMLLCPTranscoder* Win32TransService::makeNewLCPTranscoder()
{
    // Just allocate a new LCP transcoder of our type
    return new Win32LCPTranscoder;
}


bool Win32TransService::supportsSrcOfs() const
{
    //
    //  Since the only mechanism we have to translate XML text in this
    //  transcoder basically require us to do work that allows us to support
    //  source offsets, we might as well do it.
    //
    return true;
}


void Win32TransService::upperCase(XMLCh* const toUpperCase) const
{
    wcsupr(toUpperCase);
}

void Win32TransService::lowerCase(XMLCh* const toLowerCase) const
{
    wcslwr(toLowerCase);
}

bool Win32TransService::isAlias(const   HKEY            encodingKey
                    ,       char* const     aliasBuf
                    , const unsigned int    nameBufSz )
{
    unsigned long theType;
    unsigned long theSize = nameBufSz;
    return (::RegQueryValueExA
    (
        encodingKey
        , "AliasForCharset"
        , 0
        , &theType
        , (unsigned char*)aliasBuf
        , &theSize
    ) == ERROR_SUCCESS);
}


XMLTranscoder*
Win32TransService::makeNewXMLTranscoder(const   XMLCh* const            encodingName
                                        ,       XMLTransService::Codes& resValue
                                        , const XMLSize_t               blockSize
                                        ,       MemoryManager* const    manager)
{
    const XMLSize_t upLen = 1024;
    XMLCh upEncoding[upLen + 1];

    //
    //  Get an upper cased copy of the encoding name, since we use a hash
    //  table and we store them all in upper case.
    //
	XMLString::copyNString(upEncoding, encodingName, upLen);
    wcsupr(upEncoding);

    // Now to try to find this guy in the CP map
    CPMapEntry* theEntry = fCPMap->get(upEncoding);

    // If not found, then return a null pointer
    if (!theEntry)
    {
        resValue = XMLTransService::UnsupportedEncoding;
        return 0;
    }

    // We found it, so return a Win32 transcoder for this encoding
    return new (manager) Win32Transcoder
    (
        encodingName
        , theEntry->getIEEncoding()
        , blockSize
        , manager<commit_msg>Avoid invoking strlen<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id$
 */


// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#if HAVE_CONFIG_H
#	include <config.h>
#endif

#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/TranscodingException.hpp>
#include <xercesc/util/XMLException.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/XMLUni.hpp>
#include <xercesc/util/RefHashTableOf.hpp>
#include "Win32TransService.hpp"
#include <windows.h>

XERCES_CPP_NAMESPACE_BEGIN


// ---------------------------------------------------------------------------
//  Local, const data
// ---------------------------------------------------------------------------
static const XMLCh gMyServiceId[] =
{
    chLatin_W, chLatin_i, chLatin_n, chDigit_3, chDigit_2, chNull
};


#if !HAVE_WCSUPR
void wcsupr(LPWSTR str)
{
    int nLen=XMLString::stringLen(str);
    ::LCMapStringW( GetThreadLocale(), LCMAP_UPPERCASE, str, nLen, str, nLen);
}
#endif

#if !HAVE_WCSLWR
void wcslwr(LPWSTR str)
{
    int nLen=XMLString::stringLen(str);
    ::LCMapStringW( GetThreadLocale(), LCMAP_LOWERCASE, str, nLen, str, nLen);
}
#endif

#if !HAVE_WCSNICMP
int wcsnicmp(LPCWSTR comp1, LPCWSTR comp2, unsigned int nLen)
{
    unsigned int len = XMLString::stringLen( comp1);
    unsigned int otherLen = XMLString::stringLen( comp2);
    unsigned int countChar = 0;
    unsigned int maxChars;
    int          theResult = 0;

    // Determine at what string index the comparison stops.
    len = ( len > nLen ) ? nLen : len;
    otherLen = ( otherLen > nLen ) ? nLen : otherLen;
    maxChars = ( len > otherLen ) ? otherLen : len;

    // Handle situation when one argument or the other is NULL
    // by returning +/- string length of non-NULL argument (inferred
    // from XMLString::CompareNString).

    // Obs. Definition of stringLen(XMLCh*) implies NULL ptr and ptr
    // to Empty String are equivalent.  It handles NULL args, BTW.

    if ( !comp1 )
    {
        // Negative because null ptr (c1) less than string (c2).
        return ( 0 - otherLen );
    }

    if ( !comp2 )
    {
        // Positive because string (c1) still greater than null ptr (c2).
        return len;
    }

    // Copy const parameter strings (plus terminating nul) into locals.
    XMLCh* firstBuf = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate( (++len) * sizeof(XMLCh) );//new XMLCh[ ++len];
    XMLCh* secondBuf = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate( (++otherLen) * sizeof(XMLCh) );//new XMLCh[ ++otherLen];
    memcpy( firstBuf, comp1, len * sizeof(XMLCh));
    memcpy( secondBuf, comp2, otherLen * sizeof(XMLCh));

    // Then uppercase both strings, losing their case info.
    ::LCMapStringW( GetThreadLocale(), LCMAP_UPPERCASE, (LPWSTR)firstBuf, len, (LPWSTR)firstBuf, len);
    ::LCMapStringW( GetThreadLocale(), LCMAP_UPPERCASE, (LPWSTR)secondBuf, otherLen, (LPWSTR)secondBuf, otherLen);

    // Strings are equal until proven otherwise.
    while ( ( countChar < maxChars ) && ( !theResult ) )
    {
        theResult = (int)(firstBuf[countChar]) - (int)(secondBuf[countChar]);
        ++countChar;
    }

    XMLPlatformUtils::fgMemoryManager->deallocate(firstBuf);//delete [] firstBuf;
    XMLPlatformUtils::fgMemoryManager->deallocate(secondBuf);//delete [] secondBuf;

    return theResult;
}
#endif

#if !HAVE_WCSICMP
int wcsicmp(LPCWSTR comp1, LPCWSTR comp2)
{
    unsigned int len = XMLString::stringLen( comp1);
    unsigned int otherLen = XMLString::stringLen( comp2);
    // Must compare terminating NUL to return difference if one string is shorter than the other.
    unsigned int maxChars = ( len > otherLen ) ? otherLen : len;
    return wcsnicmp(comp1, comp2, maxChars+1);
}
#endif

// ---------------------------------------------------------------------------
//  This is the simple CPMapEntry class. It just contains an encoding name
//  and a code page for that encoding.
// ---------------------------------------------------------------------------
class CPMapEntry : public XMemory
{
public :
    // -----------------------------------------------------------------------
    //  Constructors and Destructor
    // -----------------------------------------------------------------------
    CPMapEntry
    (
        const   XMLCh* const    encodingName
        , const unsigned int    ieId
    );

    CPMapEntry
    (
        const   char* const     encodingName
        , const unsigned int    ieId
    );

    ~CPMapEntry();


    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------
    const XMLCh* getEncodingName() const;
    const XMLCh* getKey() const;
    unsigned int getIEEncoding() const;


private :
    // -----------------------------------------------------------------------
    //  Unimplemented constructors and operators
    // -----------------------------------------------------------------------
    CPMapEntry();
    CPMapEntry(const CPMapEntry&);
    CPMapEntry& operator=(const CPMapEntry&);


    // -----------------------------------------------------------------------
    //  Private data members
    //
    //  fEncodingName
    //      This is the encoding name for the code page that this instance
    //      represents.
    //
    //  fIEId
    //      This is the code page id.
    // -----------------------------------------------------------------------
    XMLCh*          fEncodingName;
    unsigned int    fIEId;
};

// ---------------------------------------------------------------------------
//  CPMapEntry: Constructors and Destructor
// ---------------------------------------------------------------------------
CPMapEntry::CPMapEntry( const   char* const     encodingName
                        , const unsigned int    ieId) :
    fEncodingName(0)
    , fIEId(ieId)
{
    // Transcode the name to Unicode and store that copy
    int targetLen=::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, encodingName, -1, NULL, 0);
    if(targetLen!=0)
    {
        fEncodingName = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate
        (
            (targetLen + 1) * sizeof(XMLCh)
        );//new XMLCh[targetLen + 1];
        ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, encodingName, -1, (LPWSTR)fEncodingName, targetLen);
        fEncodingName[targetLen] = 0;

        //
        //  Upper case it because we are using a hash table and need to be
        //  sure that we find all case combinations.
        //
        wcsupr(fEncodingName);
  }
}

CPMapEntry::CPMapEntry( const   XMLCh* const    encodingName
                        , const unsigned int    ieId) :

    fEncodingName(0)
    , fIEId(ieId)
{
    fEncodingName = XMLString::replicate(encodingName, XMLPlatformUtils::fgMemoryManager);

    //
    //  Upper case it because we are using a hash table and need to be
    //  sure that we find all case combinations.
    //
    wcsupr(fEncodingName);
}

CPMapEntry::~CPMapEntry()
{
    XMLPlatformUtils::fgMemoryManager->deallocate(fEncodingName);//delete [] fEncodingName;
}


// ---------------------------------------------------------------------------
//  CPMapEntry: Getter methods
// ---------------------------------------------------------------------------
const XMLCh* CPMapEntry::getEncodingName() const
{
    return fEncodingName;
}

unsigned int CPMapEntry::getIEEncoding() const
{
    return fIEId;
}



//---------------------------------------------------------------------------
//
//  class Win32TransService Implementation ...
//
//---------------------------------------------------------------------------


// ---------------------------------------------------------------------------
//  Win32TransService: Constructors and Destructor
// ---------------------------------------------------------------------------
Win32TransService::Win32TransService()
{
    fCPMap = new RefHashTableOf<CPMapEntry>(109);

    //
    //  Open up the registry key that contains the info we want. Note that,
    //  if this key does not exist, then we just return. It will just mean
    //  that we don't have any support except for intrinsic encodings supported
    //  by the parser itself (and the LCP support of course.
    //
    HKEY charsetKey;
    if (::RegOpenKeyExA
    (
        HKEY_CLASSES_ROOT
        , "MIME\\Database\\Charset"
        , 0
        , KEY_READ
        , &charsetKey))
    {
        return;
    }

    //
    //  Read in the registry keys that hold the code page ids. Skip for now
    //  those entries which indicate that they are aliases for some other
    //  encodings. We'll come back and do a second round for those and look
    //  up the original name and get the code page id.
    //
    //  Note that we have to use A versions here so that this will run on
    //  98, and transcode the strings to Unicode.
    //
    const unsigned int nameBufSz = 1024;
    char nameBuf[nameBufSz + 1];
    unsigned int subIndex;
    unsigned long theSize;
    for (subIndex = 0;;++subIndex)
    {
        // Get the name of the next key
        theSize = nameBufSz;
        if (::RegEnumKeyExA
        (
            charsetKey
            , subIndex
            , nameBuf
            , &theSize
            , 0, 0, 0, 0) == ERROR_NO_MORE_ITEMS)
        {
            break;
        }

        // Open this subkey
        HKEY encodingKey;
        if (::RegOpenKeyExA
        (
            charsetKey
            , nameBuf
            , 0
            , KEY_READ
            , &encodingKey))
        {
            continue;
        }

        //
        //  Lts see if its an alias. If so, then ignore it in this first
        //  loop. Else, we'll add a new entry for this one.
        //
        if (!isAlias(encodingKey))
        {
            //
            //  Lets get the two values out of this key that we are
            //  interested in. There should be a code page entry and an
            //  IE entry.
            //
            //  The Codepage entry is the default code page for a computer using that charset
            //  while the InternetEncoding holds the code page that represents that charset
            //
            unsigned long theType;
            unsigned int CPId;
            unsigned int IEId;

            theSize = sizeof(unsigned int);
            if (::RegQueryValueExA
            (
                encodingKey
                , "Codepage"
                , 0
                , &theType
                , (unsigned char*)&CPId
                , &theSize) != ERROR_SUCCESS)
            {
                ::RegCloseKey(encodingKey);
                continue;
            }

            //
            //  If this is not a valid Id, and it might not be because its
            //  not loaded on this system, then don't take it.
            //
            if (::IsValidCodePage(CPId))
            {
                theSize = sizeof(unsigned int);
                if (::RegQueryValueExA
                (
                    encodingKey
                    , "InternetEncoding"
                    , 0
                    , &theType
                    , (unsigned char*)&IEId
                    , &theSize) != ERROR_SUCCESS)
                {
                    ::RegCloseKey(encodingKey);
                    continue;
                }

                CPMapEntry* newEntry = new CPMapEntry(nameBuf, IEId);
                fCPMap->put((void*)newEntry->getEncodingName(), newEntry);
            }
        }

        // And close the subkey handle
        ::RegCloseKey(encodingKey);
    }

    //
    //  Now loop one more time and this time we do just the aliases. For
    //  each one we find, we look up that name in the map we've already
    //  built and add a new entry with this new name and the same id
    //  values we stored for the original.
    //
    char aliasBuf[nameBufSz + 1];
    for (subIndex = 0;;++subIndex)
    {
        // Get the name of the next key
        theSize = nameBufSz;
        if (::RegEnumKeyExA
        (
            charsetKey
            , subIndex
            , nameBuf
            , &theSize
            , 0, 0, 0, 0) == ERROR_NO_MORE_ITEMS)
        {
            break;
        }

        // Open this subkey
        HKEY encodingKey;
        if (::RegOpenKeyExA
        (
            charsetKey
            , nameBuf
            , 0
            , KEY_READ
            , &encodingKey))
        {
            continue;
        }

        //
        //  If its an alias, look up the name in the map. If we find it,
        //  then construct a new one with the new name and the aliased
        //  ids.
        //
        if (isAlias(encodingKey, aliasBuf, nameBufSz))
        {
            int targetLen = ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, aliasBuf, -1, NULL, 0);
            if(targetLen!=0)
            {
                XMLCh* uniAlias = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate
                (
                    (targetLen + 1) * sizeof(XMLCh)
                );//new XMLCh[targetLen + 1];
                ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, aliasBuf, -1, (LPWSTR)uniAlias, targetLen);
                uniAlias[targetLen] = 0;
                wcsupr(uniAlias);

                // Look up the alias name
                CPMapEntry* aliasedEntry = fCPMap->get(uniAlias);
                if (aliasedEntry)
                {
                    int targetLen = ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, nameBuf, -1, NULL, 0);
                    if(targetLen!=0)
                    {
                        XMLCh* uniName = (XMLCh*) XMLPlatformUtils::fgMemoryManager->allocate
                        (
                            (targetLen + 1) * sizeof(XMLCh)
                        );//new XMLCh[targetLen + 1];
                        ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, nameBuf, -1, (LPWSTR)uniName, targetLen);
                        uniName[targetLen] = 0;
                        wcsupr(uniName);

                        //
                        //  If the name is actually different, then take it.
                        //  Otherwise, don't take it. They map aliases that are
                        //  just different case.
                        //
                        if (::wcscmp(uniName, aliasedEntry->getEncodingName()))
                        {
                            CPMapEntry* newEntry = new CPMapEntry(uniName, aliasedEntry->getIEEncoding());
                            fCPMap->put((void*)newEntry->getEncodingName(), newEntry);
                        }

                        XMLPlatformUtils::fgMemoryManager->deallocate(uniName);//delete [] uniName;
                    }
                }
                XMLPlatformUtils::fgMemoryManager->deallocate(uniAlias);//delete [] uniAlias;
            }
        }

        // And close the subkey handle
        ::RegCloseKey(encodingKey);
    }

    // And close the main key handle
    ::RegCloseKey(charsetKey);

}

Win32TransService::~Win32TransService()
{
    delete fCPMap;
}


// ---------------------------------------------------------------------------
//  Win32TransService: The virtual transcoding service API
// ---------------------------------------------------------------------------
int Win32TransService::compareIString(  const   XMLCh* const    comp1
                                        , const XMLCh* const    comp2)
{
    return wcsicmp(comp1, comp2);
}


int Win32TransService::compareNIString( const   XMLCh* const    comp1
                                        , const XMLCh* const    comp2
                                        , const XMLSize_t       maxChars)
{
    return wcsnicmp(comp1, comp2, maxChars);
}


const XMLCh* Win32TransService::getId() const
{
    return gMyServiceId;
}

XMLLCPTranscoder* Win32TransService::makeNewLCPTranscoder()
{
    // Just allocate a new LCP transcoder of our type
    return new Win32LCPTranscoder;
}


bool Win32TransService::supportsSrcOfs() const
{
    //
    //  Since the only mechanism we have to translate XML text in this
    //  transcoder basically require us to do work that allows us to support
    //  source offsets, we might as well do it.
    //
    return true;
}


void Win32TransService::upperCase(XMLCh* const toUpperCase) const
{
    wcsupr(toUpperCase);
}

void Win32TransService::lowerCase(XMLCh* const toLowerCase) const
{
    wcslwr(toLowerCase);
}

bool Win32TransService::isAlias(const   HKEY            encodingKey
                    ,       char* const     aliasBuf
                    , const unsigned int    nameBufSz )
{
    unsigned long theType;
    unsigned long theSize = nameBufSz;
    return (::RegQueryValueExA
    (
        encodingKey
        , "AliasForCharset"
        , 0
        , &theType
        , (unsigned char*)aliasBuf
        , &theSize
    ) == ERROR_SUCCESS);
}


XMLTranscoder*
Win32TransService::makeNewXMLTranscoder(const   XMLCh* const            encodingName
                                        ,       XMLTransService::Codes& resValue
                                        , const XMLSize_t               blockSize
                                        ,       MemoryManager* const    manager)
{
    const XMLSize_t upLen = 1024;
    XMLCh upEncoding[upLen + 1];

    //
    //  Get an upper cased copy of the encoding name, since we use a hash
    //  table and we store them all in upper case.
    //
    XMLString::copyNString(upEncoding, encodingName, upLen);
    wcsupr(upEncoding);

    // Now to try to find this guy in the CP map
    CPMapEntry* theEntry = fCPMap->get(upEncoding);

    // If not found, then return a null pointer
    if (!theEntry)
    {
        resValue = XMLTransService::UnsupportedEncoding;
        return 0;
    }

    // We found it, so return a Win32 transcoder for this encoding
    return new (manager) Win32Transcoder
    (
        encodingName
        , theEntry->getIEEncoding()
        , blockSize
        , manager
{'fraction_non_alphanumeric': 0.17701536518627659, 'fraction_numerical': 0.006788044622184803, 'mean_word_length': 1.4274492272320858, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 26, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4820558', 'n_tokens_mistral': 10351, 'n_tokens_neox': 9858, 'n_words': 2929}