Back to index

lightning-sunbird  0.9+nobinonly
nsCaseConversionImp2.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "pratom.h"
00039 #include "nsUUDll.h"
00040 #include "nsCaseConversionImp2.h"
00041 #include "casetable.h"
00042 
00043 
00044 // For gUpperToTitle 
00045 enum {
00046   kUpperIdx =0,
00047   kTitleIdx
00048 };
00049 
00050 // For gUpperToTitle
00051 enum {
00052   kLowIdx =0,
00053   kSizeEveryIdx,
00054   kDiffIdx
00055 };
00056 
00057 #define IS_ASCII(u)       ( 0x0000 == ((u) & 0xFF80))
00058 #define IS_ASCII_UPPER(u) ((0x0041 <= (u)) && ( (u) <= 0x005a))
00059 #define IS_ASCII_LOWER(u) ((0x0061 <= (u)) && ( (u) <= 0x007a))
00060 #define IS_ASCII_ALPHA(u) (IS_ASCII_UPPER(u) || IS_ASCII_LOWER(u))
00061 #define IS_ASCII_SPACE(u) ( 0x0020 == (u) )
00062 
00063 #define IS_NOCASE_CHAR(u)  (0==(1&(gCaseBlocks[(u)>>13]>>(0x001F&((u)>>8)))))
00064   
00065 // Size of Tables
00066 
00067 
00068 #define CASE_MAP_CACHE_SIZE 0x40
00069 #define CASE_MAP_CACHE_MASK 0x3F
00070 
00071 class nsCompressedMap {
00072 public:
00073    nsCompressedMap(const PRUnichar *aTable, PRUint32 aSize);
00074    ~nsCompressedMap();
00075    PRUnichar Map(PRUnichar aChar);
00076 protected:
00077    PRUnichar Lookup(PRUint32 l, PRUint32 m, PRUint32 r, PRUnichar aChar);
00078 
00079 private: 
00080    const PRUnichar *mTable;
00081    PRUint32 mSize;
00082    PRUint32 *mCache;
00083    PRUint32 mLastBase;
00084 };
00085 
00086 nsCompressedMap::nsCompressedMap(const PRUnichar *aTable, PRUint32 aSize)
00087 {
00088    MOZ_COUNT_CTOR(nsCompressedMap);
00089    mTable = aTable;
00090    mSize = aSize;
00091    mLastBase = 0;
00092    mCache = new PRUint32[CASE_MAP_CACHE_SIZE];
00093    for(int i = 0; i < CASE_MAP_CACHE_SIZE; i++)
00094       mCache[i] = 0;
00095 }
00096 
00097 nsCompressedMap::~nsCompressedMap()
00098 {
00099    MOZ_COUNT_DTOR(nsCompressedMap);
00100    delete[] mCache;
00101 }
00102 
00103 PRUnichar nsCompressedMap::Map(PRUnichar aChar)
00104 {
00105    // no need to worry thread since cached value are 
00106    // not object but primitive data type which could be 
00107    // accessed in atomic operation. We need to access
00108    // the whole 32 bit of cachedData at once in order to make it
00109    // thread safe. Never access bits from mCache dirrectly
00110 
00111    PRUint32 cachedData = mCache[aChar & CASE_MAP_CACHE_MASK];
00112    if(aChar == ((cachedData >> 16) & 0x0000FFFF))
00113      return (cachedData & 0x0000FFFF);
00114 
00115    // try the last index first
00116    // store into local variable so we can be thread safe
00117    PRUint32 base = mLastBase; 
00118    PRUnichar res = 0;
00119  
00120    if (( aChar <=  ((mTable[base+kSizeEveryIdx] >> 8) + 
00121                  mTable[base+kLowIdx])) &&
00122        ( mTable[base+kLowIdx]  <= aChar )) 
00123    {
00124       // Hit the last base
00125       if(((mTable[base+kSizeEveryIdx] & 0x00FF) > 0) && 
00126          (0 != ((aChar - mTable[base+kLowIdx]) % 
00127                (mTable[base+kSizeEveryIdx] & 0x00FF))))
00128       {
00129          res = aChar;
00130       } else {
00131          res = aChar + mTable[base+kDiffIdx];
00132       }
00133    } else {
00134       res = this->Lookup(0, (mSize/2), mSize-1, aChar);
00135    }
00136 
00137    mCache[aChar & CASE_MAP_CACHE_MASK] =
00138        (((aChar << 16) & 0xFFFF0000) | (0x0000FFFF & res));
00139    return res;
00140 }
00141 
00142 PRUnichar nsCompressedMap::Lookup(
00143    PRUint32 l, PRUint32 m, PRUint32 r, PRUnichar aChar)
00144 {
00145   PRUint32 base = m*3;
00146   if ( aChar >  ((mTable[base+kSizeEveryIdx] >> 8) + 
00147                  mTable[base+kLowIdx])) 
00148   {
00149     if( l > m )
00150       return aChar;
00151     PRUint32 newm = (m+r+1)/2;
00152     if(newm == m)
00153           newm++;
00154     return this->Lookup(m+1, newm , r, aChar);
00155     
00156   } else if ( mTable[base+kLowIdx]  > aChar ) {
00157     if( r < m )
00158       return aChar;
00159     PRUint32 newm = (l+m-1)/2;
00160     if(newm == m)
00161           newm++;
00162        return this->Lookup(l, newm, m-1, aChar);
00163 
00164   } else  {
00165     if(((mTable[base+kSizeEveryIdx] & 0x00FF) > 0) && 
00166        (0 != ((aChar - mTable[base+kLowIdx]) % 
00167               (mTable[base+kSizeEveryIdx] & 0x00FF))))
00168     {
00169        return aChar;
00170     }
00171     mLastBase = base; // cache the base
00172     return aChar + mTable[base+kDiffIdx];
00173   }
00174 }
00175 
00176 nsrefcnt nsCaseConversionImp2::gInit      = 0;
00177 
00178 NS_IMPL_ISUPPORTS1(nsCaseConversionImp2, nsICaseConversion)
00179 
00180 static nsCompressedMap *gUpperMap = nsnull;
00181 static nsCompressedMap *gLowerMap = nsnull;
00182 
00183 nsresult nsCaseConversionImp2::ToUpper(
00184   PRUnichar aChar, PRUnichar* aReturn
00185 )
00186 {
00187   if( IS_ASCII(aChar)) // optimize for ASCII
00188   {
00189      if(IS_ASCII_LOWER(aChar))
00190         *aReturn = aChar - 0x0020;
00191      else
00192         *aReturn = aChar;
00193   } 
00194   else if( IS_NOCASE_CHAR(aChar)) // optimize for block which have no case
00195   {
00196     *aReturn = aChar;
00197   } 
00198   else 
00199   {
00200     *aReturn = gUpperMap->Map(aChar);
00201   }
00202   return NS_OK;
00203 }
00204 
00205 // a non-virtual version of ToLower
00206 static PRUnichar FastToLower(
00207   PRUnichar aChar
00208 )
00209 {
00210   if( IS_ASCII(aChar)) // optimize for ASCII
00211   {
00212      if(IS_ASCII_UPPER(aChar))
00213         return aChar + 0x0020;
00214      else
00215         return aChar;
00216   } 
00217   else if( IS_NOCASE_CHAR(aChar)) // optimize for block which have no case
00218   {
00219     return aChar;
00220   } 
00221   else
00222   {
00223     return gLowerMap->Map(aChar);
00224   } 
00225   return NS_OK;
00226 }
00227 
00228 nsresult nsCaseConversionImp2::ToLower(
00229   PRUnichar aChar, PRUnichar* aReturn
00230 )
00231 {
00232   *aReturn = FastToLower(aChar);
00233   return NS_OK;
00234 }
00235 nsresult nsCaseConversionImp2::ToTitle(
00236   PRUnichar aChar, PRUnichar* aReturn
00237 )
00238 {
00239   if( IS_ASCII(aChar)) // optimize for ASCII
00240   {
00241     return this->ToUpper(aChar, aReturn);
00242   }
00243   else if( IS_NOCASE_CHAR(aChar)) // optimize for block which have no case
00244   {
00245     *aReturn = aChar;
00246   } 
00247   else
00248   {
00249     // First check for uppercase characters whose titlecase mapping is
00250     //  different, like U+01F1 DZ: they must remain unchanged.
00251     if( 0x01C0 == ( aChar & 0xFFC0)) // 0x01Cx - 0x01Fx
00252     {
00253       for(PRUint32 i = 0 ; i < gUpperToTitleItems; i++) {
00254         if ( aChar == gUpperToTitle[(i*2)+kUpperIdx]) {
00255           *aReturn = aChar;
00256           return NS_OK;
00257         }
00258       }
00259     }
00260 
00261     PRUnichar upper;
00262     upper = gUpperMap->Map(aChar);
00263     
00264     if( 0x01C0 == ( upper & 0xFFC0)) // 0x01Cx - 0x01Fx
00265     {
00266       for(PRUint32 i = 0 ; i < gUpperToTitleItems; i++) {
00267         if ( upper == gUpperToTitle[(i*2)+kUpperIdx]) {
00268            *aReturn = gUpperToTitle[(i*2)+kTitleIdx];
00269            return NS_OK;
00270         }
00271       }
00272     }
00273     *aReturn = upper;
00274   }
00275   return NS_OK;
00276 }
00277 
00278 nsresult nsCaseConversionImp2::ToUpper(
00279   const PRUnichar* anArray, PRUnichar* aReturn, PRUint32 aLen
00280 )
00281 {
00282   PRUint32 i;
00283   for(i=0;i<aLen;i++) 
00284   {
00285     PRUnichar aChar = anArray[i];
00286     if( IS_ASCII(aChar)) // optimize for ASCII
00287     {
00288        if(IS_ASCII_LOWER(aChar))
00289           aReturn[i] = aChar - 0x0020;
00290        else
00291           aReturn[i] = aChar;
00292     }
00293     else if( IS_NOCASE_CHAR(aChar)) // optimize for block which have no case
00294     {
00295           aReturn[i] = aChar;
00296     } 
00297     else 
00298     {
00299       aReturn[i] = gUpperMap->Map(aChar);
00300     }
00301   }
00302   return NS_OK;
00303 }
00304 
00305 nsresult nsCaseConversionImp2::ToLower(
00306   const PRUnichar* anArray, PRUnichar* aReturn, PRUint32 aLen
00307 )
00308 {
00309   PRUint32 i;
00310   for(i=0;i<aLen;i++) 
00311     aReturn[i] = FastToLower(anArray[i]);
00312   return NS_OK;
00313 }
00314 
00315 
00316 
00317 nsresult nsCaseConversionImp2::ToTitle(
00318   const PRUnichar* anArray, PRUnichar* aReturn, PRUint32 aLen,
00319   PRBool aStartInWordBoundary
00320 )
00321 {
00322   if(0 == aLen)
00323     return NS_OK;
00324 
00325   //
00326   // We need to replace this implementation to a real one
00327   // Currently, it only do the right thing for ASCII
00328   // Howerver, we need a word breaker to do the right job
00329   //
00330   // this->ToLower(anArray, aReturn, aLen); 
00331   // CSS define Capitalize as 
00332   //  Uppercases the first character of each word
00333   // 
00334   
00335   PRBool bLastIsSpace =  IS_ASCII_SPACE(anArray[0]);
00336   if(aStartInWordBoundary)
00337   {
00338      this->ToTitle(anArray[0], &aReturn[0]);
00339   }
00340 
00341   PRUint32 i;
00342   for(i=1;i<aLen;i++)
00343   {
00344     if(bLastIsSpace)
00345     {
00346       this->ToTitle(anArray[i], &aReturn[i]);
00347     }
00348     else
00349     {
00350       aReturn[i] = anArray[i];
00351     }
00352 
00353     bLastIsSpace = IS_ASCII_SPACE(aReturn[i]);
00354   }
00355   return NS_OK;
00356 }
00357 
00358 // implementation moved from the old nsCRT routine
00359 NS_IMETHODIMP
00360 nsCaseConversionImp2::CaseInsensitiveCompare(const PRUnichar *aLeft,
00361                                              const PRUnichar *aRight,
00362                                              PRUint32 aCount, PRInt32* aResult)
00363 {
00364   if (!aLeft || !aRight)
00365     return NS_ERROR_INVALID_POINTER;
00366 
00367   // assume equality. We bail early if no equality
00368   *aResult = 0;
00369   
00370   if (aCount) {
00371     do {
00372       PRUnichar c1 = *aLeft++;
00373       PRUnichar c2 = *aRight++;
00374       
00375       if (c1 != c2) {
00376         c1 = FastToLower(c1);
00377         c2 = FastToLower(c2);
00378         if (c1 != c2) {
00379           if (c1 < c2) {
00380             *aResult = -1;
00381             return NS_OK;
00382           }
00383           *aResult = 1;
00384           return NS_OK;
00385         }
00386       }
00387     } while (--aCount != 0);
00388   }
00389   return NS_OK;
00390 }
00391 
00392 nsCaseConversionImp2::nsCaseConversionImp2()
00393 {
00394   if (gInit++ == 0) {
00395     gUpperMap = new nsCompressedMap(NS_REINTERPRET_CAST(const PRUnichar*, &gToUpper[0]), gToUpperItems);
00396     gLowerMap = new nsCompressedMap(NS_REINTERPRET_CAST(const PRUnichar*, &gToLower[0]), gToLowerItems);
00397   }
00398 }
00399 
00400 nsCaseConversionImp2::~nsCaseConversionImp2()
00401 {
00402   if (--gInit == 0) {
00403     delete gUpperMap;
00404     gUpperMap = nsnull;
00405     delete gLowerMap;
00406     gLowerMap = nsnull;
00407   }
00408 }
00409 
00410 nsresult NS_NewCaseConversion(nsISupports** oResult)
00411 {
00412   if(!oResult)
00413     return NS_ERROR_NULL_POINTER;
00414   *oResult = new nsCaseConversionImp2();
00415   if(*oResult)
00416     NS_ADDREF(*oResult);
00417   return (*oResult) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; 
00418 }