Diff for /parser3/src/main/pa_charset.C between versions 1.10 and 1.85

version 1.10, 2001/12/28 18:12:30 version 1.85, 2010/08/27 02:53:24
Line 1 Line 1
 /** @file  /** @file
         Parser: Charset connection implementation.          Parser: Charset connection implementation.
   
         Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)          Copyright(c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexander Petrosyan<paf@design.ru>(http://paf.design.ru)          Author: Alexander Petrosyan<paf@design.ru>(http://paf.design.ru)
   
         $Id$  
 */  */
   
   static const char * const IDENT_CHARSET_C="$Date$";
   
 #include "pa_charset.h"  #include "pa_charset.h"
   #include "pa_charsets.h"
   
 #ifdef XML  #ifdef XML
 #include "libxml/encoding.h"  #include "libxml/encoding.h"
 #endif  #endif
   
 // globals  //#define PA_PATCHED_LIBXML_BACKWARD
   
   // reduce memory usage by pre-calculation utf-8 string length
   #define PRECALCULATE_DEST_LENGTH
   
   // globals
   
 // consts  Charset::UTF8CaseTable::Rec UTF8CaseToUpperRecords[]={
   #include "utf8-to-upper.inc"
   };
   Charset::UTF8CaseTable UTF8CaseToUpper={
           sizeof(UTF8CaseToUpperRecords)/sizeof(Charset::UTF8CaseTable::Rec),
           UTF8CaseToUpperRecords};
   
 #define MAX_CHARSET_UNI_CODES 500  Charset::UTF8CaseTable::Rec UTF8CaseToLowerRecords[]={
   #include "utf8-to-lower.inc"
   };
   Charset::UTF8CaseTable UTF8CaseToLower={
           sizeof(UTF8CaseToLowerRecords)/sizeof(Charset::UTF8CaseTable::Rec),
           UTF8CaseToLowerRecords};
   
 // helpers  // helpers
   
Line 26  inline void prepare_case_tables(unsigned Line 41  inline void prepare_case_tables(unsigned
         unsigned char *lcc_table=tables+lcc_offset;          unsigned char *lcc_table=tables+lcc_offset;
         unsigned char *fcc_table=tables+fcc_offset;          unsigned char *fcc_table=tables+fcc_offset;
         for(int i=0; i<0x100; i++)          for(int i=0; i<0x100; i++)
                 lcc_table[i]=fcc_table[i]=i;                  lcc_table[i]=fcc_table[i]=(unsigned char)i;
 }  }
 inline void cstr2ctypes(unsigned char *tables, const unsigned char *cstr,   inline void cstr2ctypes(unsigned char *tables, const unsigned char *cstr, 
                                                 unsigned char bit) {                                                  unsigned char bit) {
Line 37  inline void cstr2ctypes(unsigned char *t Line 52  inline void cstr2ctypes(unsigned char *t
                 ctypes_table[c]|=bit;                  ctypes_table[c]|=bit;
         }          }
 }  }
 inline unsigned int to_wchar_code(const char *cstr) {  inline unsigned int to_wchar_code(const char* cstr) {
         if(!cstr || !*cstr)          if(!cstr || !*cstr)
                 return 0;                  return 0;
         if(cstr[1]==0)          if(cstr[1]==0)
Line 46  inline unsigned int to_wchar_code(const Line 61  inline unsigned int to_wchar_code(const
         char *error_pos;          char *error_pos;
         return(unsigned int)strtol(cstr, &error_pos, 0);          return(unsigned int)strtol(cstr, &error_pos, 0);
 }  }
 inline bool to_bool(const char *cstr) {  inline bool to_bool(const char* cstr) {
         return cstr && *cstr!=0;          return cstr && *cstr!=0;
 }  }
 static void element2ctypes(unsigned char c, bool belongs,  static void element2ctypes(unsigned char c, bool belongs,
Line 73  static void element2case(unsigned char f Line 88  static void element2case(unsigned char f
   
 // methods  // methods
   
 extern "C" unsigned char pcre_default_tables[]; // pcre/chartables.c  Charset::Charset(Request_charsets* charsets, const String::Body ANAME, const String* afile_spec): 
 Charset::Charset(Pool& apool, const String& aname, const String *request_file_spec): Pooled(apool),          FNAME(ANAME),
         fname(aname) {          FNAME_CSTR(ANAME.cstrm()) {
   
         char *name_cstr=fname.cstr();  
     for(char *c=name_cstr; *c; c++)  
         *c = toupper(*c);  
   
         if(request_file_spec) {          if(afile_spec) {
                 fisUTF8=false;                  fisUTF8=false;
                 loadDefinition(*request_file_spec);                  load_definition(*charsets, *afile_spec);
 #ifdef XML  #ifdef XML
                 addEncoding(name_cstr);                  addEncoding(FNAME_CSTR);
 #endif  #endif
         } else {          } else {
                 fisUTF8=true;                  fisUTF8=true;
                 // grab default onces [for UTF-8 so to be able to make a-z =>A-Z                  // grab default onces [for UTF-8 so to be able to make a-z =>A-Z
                 memcpy(pcre_tables, pcre_default_tables, sizeof(pcre_tables));                  memcpy(pcre_tables, _pcre_default_tables, sizeof(pcre_tables));
         }          }
   
 #ifdef XML  #ifdef XML
         initTranscoder(&aname, name_cstr);          initTranscoder(FNAME, FNAME_CSTR);
 #endif  
 }  
   
 Charset::~Charset() {  
 #ifdef XML  
         // not deleting transcoder, that's not our business  
 #endif  #endif
 }  }
   
 void Charset::loadDefinition(const String& request_file_spec) {  void Charset::load_definition(Request_charsets& charsets, const String& afile_spec) {
         // pcre_tables          // pcre_tables
         // lowcase, flipcase, bits digit+word+whitespace, masks          // lowcase, flipcase, bits digit+word+whitespace, masks
   
Line 115  void Charset::loadDefinition(const Strin Line 120  void Charset::loadDefinition(const Strin
         cstr2ctypes(pcre_tables,(const unsigned char *)"*+?{^.$|()[", ctype_meta);          cstr2ctypes(pcre_tables,(const unsigned char *)"*+?{^.$|()[", ctype_meta);
   
         // charset          // charset
         memset(tables.fromTable, 0, sizeof(tables.fromTable));          memset(&tables, 0, sizeof(tables));
         tables.toTable=(Charset_TransRec *)calloc(sizeof(Charset_TransRec)*MAX_CHARSET_UNI_CODES);  
         tables.toTableSize=0;  
         // strangly vital  
         tables.toTable[tables.toTableSize].intCh=0;  
         tables.toTable[tables.toTableSize].extCh=(XMLByte)0;  
         tables.toTableSize++;  
   
         // loading text          // loading text
         char *data=file_read_text(pool(), request_file_spec);          char *data=file_read_text(charsets, afile_spec);
   
         // ignore header          // ignore header
         getrow(&data);          getrow(&data);
   
         // parse cells          // parse cells
         char *row;          char *row;
         while(row=getrow(&data)) {          while((row=getrow(&data))) {
                 // remove empty&comment lines                  // remove empty&comment lines
                 if(!*row || *row=='#')                  if(!*row || *row=='#')
                         continue;                          continue;
   
                 // char white-space     digit   hex-digit       letter  word    lowercase       unicode1        unicode2                          // char white-space     digit   hex-digit       letter  word    lowercase       unicode1        unicode2        
                 unsigned int c=0;                  unsigned char c=0;
                 char *cell;                  char *cell;
                 for(int column=0; cell=lsplit(&row, '\t'); column++) {                  for(int column=0; (cell=lsplit(&row, '\t')); column++) {
                         switch(column) {                          switch(column) {
                         case 0: c=to_wchar_code(cell); break;                          case 0: c=(unsigned char)to_wchar_code(cell); break;
                         // pcre_tables                          // pcre_tables
                         case 1: element2ctypes(c, to_bool(cell), pcre_tables, ctype_space, cbit_space); break;                          case 1: element2ctypes(c, to_bool(cell), pcre_tables, ctype_space, cbit_space); break;
                         case 2: element2ctypes(c, to_bool(cell), pcre_tables, ctype_digit, cbit_digit); break;                          case 2: element2ctypes(c, to_bool(cell), pcre_tables, ctype_digit, cbit_digit); break;
                         case 3: element2ctypes(c, to_bool(cell), pcre_tables, ctype_xdigit); break;                          case 3: element2ctypes(c, to_bool(cell), pcre_tables, ctype_xdigit); break;
                         case 4: element2ctypes(c, to_bool(cell), pcre_tables, ctype_letter); break;                          case 4: element2ctypes(c, to_bool(cell), pcre_tables, ctype_letter); break;
                         case 5: element2ctypes(c, to_bool(cell), pcre_tables, ctype_word, cbit_word); break;                          case 5: element2ctypes(c, to_bool(cell), pcre_tables, ctype_word, cbit_word); break;
                         case 6: element2case(c, to_wchar_code(cell), pcre_tables); break;                          case 6: element2case(c, (unsigned char)to_wchar_code(cell), pcre_tables); break;
                         case 7:                          case 7:
                         case 8:                          case 8:
                                 // charset                                  // charset
                                 if(tables.toTableSize>MAX_CHARSET_UNI_CODES)                                  if(tables.toTableSize>MAX_CHARSET_UNI_CODES)
                                         throw Exception(0, 0,                                          throw Exception(PARSER_RUNTIME,
                                                 &request_file_spec,                                                  &afile_spec,
                                                 "charset must contain not more then %d unicode values", MAX_CHARSET_UNI_CODES);                                                  "charset must contain not more then %d unicode values", MAX_CHARSET_UNI_CODES);
   
                                 XMLCh unicode=(XMLCh)to_wchar_code(cell);                                  XMLCh unicode=(XMLCh)to_wchar_code(cell);
Line 178  void Charset::loadDefinition(const Strin Line 177  void Charset::loadDefinition(const Strin
   
 static int sort_cmp_Trans_rec_intCh(const void *a, const void *b) {  static int sort_cmp_Trans_rec_intCh(const void *a, const void *b) {
         return           return 
                 static_cast<const Charset_TransRec *>(a)->intCh-                  static_cast<const Charset::Tables::Rec *>(a)->intCh-
                 static_cast<const Charset_TransRec *>(b)->intCh;                  static_cast<const Charset::Tables::Rec *>(b)->intCh;
 }  }
   
 void Charset::sort_ToTable() {  void Charset::sort_ToTable() {
Line 190  void Charset::sort_ToTable() { Line 189  void Charset::sort_ToTable() {
         //fclose(f);          //fclose(f);
 }  }
   
   // @todo: precache for spedup searching
 static XMLByte xlatOneTo(const XMLCh toXlat,  static XMLByte xlatOneTo(const XMLCh toXlat,
                                                  const Charset::Tables& tables) {                           const Charset::Tables& tables,
     unsigned int    lowOfs = 0;                           XMLByte not_found) {
     unsigned int    hiOfs = tables.toTableSize - 1;          int lo = 0;
     XMLByte         curByte = 0;          int hi = tables.toTableSize - 1;
     do {          while(lo<=hi) {
         // Calc the mid point of the low and high offset.                  // Calc the mid point of the low and high offset.
         const unsigned int midOfs =((hiOfs - lowOfs) / 2)+lowOfs;                  const unsigned int i = (lo + hi) / 2;
   
         //  If our test char is greater than the mid point char, then                  XMLCh cur=tables.toTable[i].intCh;
         //  we move up to the upper half. Else we move to the lower                  if(toXlat==cur)
         //  half. If its equal, then its our guy.                          return tables.toTable[i].extCh;
         if(toXlat>tables.toTable[midOfs].intCh)                  if(toXlat>cur)
             lowOfs = midOfs;                          lo = i+1;
                 else if(toXlat<tables.toTable[midOfs].intCh)  
                         hiOfs = midOfs;  
                 else                  else
                         return tables.toTable[midOfs].extCh;                          hi = i-1;
         } while(lowOfs+1<hiOfs);          }
           
     return '?';          return not_found;
 }  }
   
 void Charset::transcode(Pool& pool,  String::C Charset::transcode(const String::C src,
         const Charset& source_charset, const void *source_body, size_t source_content_length,          const Charset& source_charset, 
         const Charset& dest_charset, const void *& dest_body, size_t& dest_content_length          const Charset& dest_charset) {
         ) {          if(!src.length)
         if(!source_content_length) {                  return String::C("", 0);
                 dest_body=0;  
                 dest_content_length=0;  
                 return;  
         }  
   
         switch((source_charset.isUTF8()?0x10:0x00)|(dest_charset.isUTF8()?0x01:0x00)) {          switch((source_charset.isUTF8()?0x10:0x00)|(dest_charset.isUTF8()?0x01:0x00)) {
                 default: // 0x00                  default: // 0x00
                         source_charset.transcodeToCharset(pool, dest_charset,                          return source_charset.transcodeToCharset(src, dest_charset);
                                 source_body, source_content_length,  
                                 dest_body, dest_content_length);  
                         break;  
                 case 0x01:                  case 0x01:
                         source_charset.transcodeToUTF8(pool,                          return source_charset.transcodeToUTF8(src);
                                 source_body, source_content_length,  
                                 dest_body, dest_content_length);  
                         break;  
                 case 0x10:                  case 0x10:
                         dest_charset.transcodeFromUTF8(pool,                          return dest_charset.transcodeFromUTF8(src);
                                 source_body, source_content_length,  
                                 dest_body, dest_content_length);  
                         break;  
                 case 0x11:                  case 0x11:
                         dest_body=source_body;                          return src;
                         dest_content_length=source_content_length;  
                         break;  
         }          }
 }  }
   
Line 280  static const XMLByte gUTFBytes[0x100] = Line 263  static const XMLByte gUTFBytes[0x100] =
 };  };
   
 static const uint gUTFOffsets[6] = {  static const uint gUTFOffsets[6] = {
     0, 0x3080, 0xE2080, 0x3C82080, 0xFA082080, 0x82082080          0, 0x3080, 0xE2080, 0x3C82080, 0xFA082080, 0x82082080
 };  };
   
 static const XMLByte gFirstByteMark[7] = {  static const XMLByte gFirstByteMark[7] = {
     0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC          0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
 };  };
   
 static size_t transcodeToUTF8(  static int transcodeToUTF8(const XMLByte* srcData, int& srcLen,
                                                         const void *source_body, size_t source_content_length,                                  XMLByte *toFill, int& toFillLen,
                                                         XMLByte *dest_body, size_t dest_buf_length,                                  const Charset::Tables& tables) {
                                                         const Charset::Tables& tables) {          const XMLByte* srcPtr=srcData;
         const XMLByte* srcPtr=(const XMLByte*)source_body;          const XMLByte* srcEnd=srcData+srcLen;
         const XMLByte* srcEnd=(const XMLByte*)source_body+source_content_length;          XMLByte* outPtr=toFill;
         XMLByte* outPtr=dest_body;          XMLByte* outEnd=toFill+toFillLen;
         XMLByte* outEnd=dest_body+dest_buf_length;  
   
     while(srcPtr<srcEnd) {          while(srcPtr<srcEnd) {
         uint curVal = tables.fromTable[*srcPtr];                  uint curVal = tables.fromTable[*srcPtr];
                 if(!curVal) {                  if(!curVal) {
             // use the replacement character                          // use the replacement character
             *outPtr++= '?';                          *outPtr++= '?';
             srcPtr++;                          srcPtr++;
             continue;                          continue;
         }                  }
   
         // Figure out how many bytes we need  
         unsigned int encodedBytes;  
         if(curVal<0x80)  
             encodedBytes = 1;  
         else if(curVal<0x800)  
             encodedBytes = 2;  
         else if(curVal<0x10000)  
             encodedBytes = 3;  
         else if(curVal<0x200000)  
             encodedBytes = 4;  
         else if(curVal<0x4000000)  
             encodedBytes = 5;  
         else if(curVal<= 0x7FFFFFFF)  
             encodedBytes = 6;  
         else {  
             // use the replacement character  
             *outPtr++= '?';  
             srcPtr++;  
             continue;  
         }  
   
         //  If we cannot fully get this char into the output buffer  
         if (outPtr + encodedBytes > outEnd)  
             break;  
   
         // We can do it, so update the source index  
         srcPtr++;  
   
         //  And spit out the bytes. We spit them out in reverse order  
         //  here, so bump up the output pointer and work down as we go.  
         outPtr+= encodedBytes;  
         switch(encodedBytes) {  
             case 6: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);  
                      curVal>>= 6;  
             case 5: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);  
                      curVal>>= 6;  
             case 4: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);  
                      curVal>>= 6;  
             case 3: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);  
                      curVal>>= 6;  
             case 2: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);  
                      curVal>>= 6;  
             case 1: *--outPtr = XMLByte(curVal | gFirstByteMark[encodedBytes]);  
         }  
   
         // Add the encoded bytes back in again to indicate we've eaten them  
         outPtr+= encodedBytes;  
     }  
   
         // eaten  
         return outPtr-dest_body;  
 }  
 static size_t transcodeFromUTF8(  
                                                                    const void *source_body, size_t source_content_length,  
                                                                    XMLByte* dest_body, size_t dest_buf_length,  
                                                                         const Charset::Tables& tables) {  
         const XMLByte* srcPtr=(const XMLByte*)source_body;  
         const XMLByte* srcEnd=(const XMLByte*)source_body+source_content_length;  
         XMLByte* outPtr=dest_body;  
         XMLByte* outEnd=dest_body+dest_buf_length;  
   
     //  We now loop until we either run out of input data, or room to store  
     while ((srcPtr < srcEnd) && (outPtr < outEnd)) {  
         // Get the next leading byte out  
         const XMLByte firstByte = *srcPtr;  
   
         // Special-case ASCII, which is a leading byte value of<= 127  
         if(firstByte<= 127) {  
             *outPtr++= firstByte;  
             srcPtr++;  
             continue;  
         }  
   
         // See how many trailing src bytes this sequence is going to require  
         const unsigned int trailingBytes = gUTFBytes[firstByte];  
   
         //  If there are not enough source bytes to do this one, then we  
         //  are done. Note that we done>= here because we are implicitly  
         //  counting the 1 byte we get no matter what.  
         if(srcPtr+trailingBytes>= srcEnd)  
             break;  
   
         // Looks ok, so lets build up the value  
         uint tmpVal=0;  
         switch(trailingBytes) {  
             case 5: tmpVal+=*srcPtr++; tmpVal<<=6;  
             case 4: tmpVal+=*srcPtr++; tmpVal<<=6;  
             case 3: tmpVal+=*srcPtr++; tmpVal<<=6;  
             case 2: tmpVal+=*srcPtr++; tmpVal<<=6;  
             case 1: tmpVal+=*srcPtr++; tmpVal<<=6;  
             case 0: tmpVal+=*srcPtr++;  
                      break;  
   
             default:                  // Figure out how many bytes we need
                 throw Exception(0, 0,                  unsigned int encodedBytes;
                                         0,                  if(curVal<0x80)
                                         "transcodeFromUTF8 error: wrong trailingBytes value(%d)", trailingBytes);                          encodedBytes = 1;
         }                  else if(curVal<0x800)
         tmpVal-=gUTFOffsets[trailingBytes];                          encodedBytes = 2;
                   else if(curVal<0x10000)
         //  If it will fit into a single char, then put it in. Otherwise                          encodedBytes = 3;
         //  fail [*encode it as a surrogate pair. If its not valid, use the                  else if(curVal<0x200000)
         //  replacement char.*]                          encodedBytes = 4;
         if(!(tmpVal & 0xFFFF0000))                  else if(curVal<0x4000000)
             *outPtr++= xlatOneTo(tmpVal, tables);                          encodedBytes = 5;
                 else                  else if(curVal<= 0x7FFFFFFF)
                         throw Exception(0, 0,                          encodedBytes = 6;
                   else {
                           // use the replacement character
                           *outPtr++= '?';
                           srcPtr++;
                           continue;
                   }
   
                   //  If we cannot fully get this char into the output buffer
                   if (outPtr + encodedBytes > outEnd)
                           break;
                   
                   // We can do it, so update the source index
                   srcPtr++;
                   
                   //  And spit out the bytes. We spit them out in reverse order
                   //  here, so bump up the output pointer and work down as we go.
                   outPtr+= encodedBytes;
                   switch(encodedBytes) {
                           case 6: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                                   curVal>>= 6;
                           case 5: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                                   curVal>>= 6;
                           case 4: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                                   curVal>>= 6;
                           case 3: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                                   curVal>>= 6;
                           case 2: *--outPtr = XMLByte((curVal | 0x80UL) & 0xBFUL);
                                   curVal>>= 6;
                           case 1: *--outPtr = XMLByte(curVal | gFirstByteMark[encodedBytes]);
                   }
                   
                   // Add the encoded bytes back in again to indicate we've eaten them
                   outPtr+= encodedBytes;
           }
           
           // Update the bytes eaten
           srcLen = srcPtr - srcData;
           
           // Return the characters read
           toFillLen = outPtr - toFill;
           
           //return srcPtr==srcEnd?(int)toFillLen:-1;
   /*
   xmlCharEncodingInputFunc
   Returns :
   the number of byte written, or -1 by lack of space, or -2 if the transcoding failed. The value of inlen after return is the
   number of octets consumed as the return value is positive, else unpredictiable. The value of outlen after return is the number
   of ocetes consumed.
   */
           return 0;
   }
   /// @todo digital entites only when xml/html output [at output in html/xml mode, in html part of a letter]
   static int transcodeFromUTF8(const XMLByte* srcData, int& srcLen,
                                   XMLByte* toFill, int& toFillLen,
                                   const Charset::Tables& tables) {
           const XMLByte* srcPtr=srcData;
           const XMLByte* srcEnd=srcData+srcLen;
           XMLByte* outPtr=toFill;
           XMLByte* outEnd=toFill+toFillLen;
   
           //  We now loop until we either run out of input data, or room to store
           while ((srcPtr < srcEnd) && (outPtr < outEnd)) {
                   // Get the next leading byte out
                   const XMLByte firstByte =* srcPtr;
                   
                   // Special-case ASCII, which is a leading byte value of<= 127
                   if(firstByte<=127) {
                           *outPtr++= firstByte;
                           srcPtr++;
                           continue;
                   }
                   
                   // See how many trailing src bytes this sequence is going to require
                   const unsigned int trailingBytes = gUTFBytes[firstByte];
                   
                   //  If there are not enough source bytes to do this one, then we
                   //  are done. Note that we done>= here because we are implicitly
                   //  counting the 1 byte we get no matter what.
                   if(srcPtr+trailingBytes>= srcEnd)
                           break;
                   
                   // Looks ok, so lets build up the value
                   uint tmpVal=0;
                   switch(trailingBytes) {
                   case 5: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 4: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 3: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 2: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 1: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 0: tmpVal+=*srcPtr++;
                           break;
                           
                   default:
                           throw Exception(0,
                                 0,                                  0,
                                 "transcodeFromUTF8 error: too big tmpVal(0x%08X)", tmpVal);                                  "transcodeFromUTF8 error: wrong trailingBytes value(%d)", trailingBytes); // never
                   }
                   tmpVal-=gUTFOffsets[trailingBytes];
                   
                   //  If it will fit into a single char, then put it in. Otherwise
                   //  fail [*encode it as a surrogate pair. If its not valid, use the
                   //  replacement char.*]
                   if(!(tmpVal & 0xFFFF0000)) {
                           if(XMLByte xlat=xlatOneTo(tmpVal, tables, 0))
                                   *outPtr++=xlat;
                           else {
                                   outPtr+=sprintf((char *)outPtr, "&#%u;", tmpVal); // &#decimal;
                           }
                   } else {
                           const XMLByte* recoverPtr=srcPtr-trailingBytes-1;
                           for(uint i=0; i<=trailingBytes; i++)
                                   outPtr+=sprintf((char*)outPtr, "%%%02X", *recoverPtr++);
                   }
         }          }
           
           // Update the bytes eaten
           srcLen = srcPtr - srcData;
           
           // Return the characters read
           toFillLen = outPtr - toFill;
   
         // eaten          //return srcPtr==srcEnd?(int)toFillLen:-1;
         return outPtr-dest_body;  /*
   xmlCharEncodingOutputFunc
   Returns :
   the number of byte written, or -1 by lack of space, or -2 if the transcoding failed. The value of inlen after return is the
   number of octets consumed as the return value is positive, else unpredictiable. The value of outlen after return is the number
   of ocetes consumed.
   */
           return 0;
 }  }
   
 /// @todo not so memory-hungry with prescan  static bool need_escape(XMLByte c){
 /// @test buf overflow          return
 void Charset::transcodeToUTF8(Pool& pool,                  !(
                                                                  const void *source_body, size_t source_content_length,                          (c<=127)
                                                                  const void *& adest_body, size_t& adest_content_length) const {                          && (
         size_t dest_buf_length=source_content_length*6/*so that surly enough*/;                                  ((c>='0') && (c<='9'))
         XMLByte *dest_body=(XMLByte*)pool.malloc(dest_buf_length);                                  || ((c>='A') && (c<='Z'))
                                   || ((c>='a') && (c<='z'))
         size_t dest_content_length=::transcodeToUTF8(                                  || strchr("*@-_+./", c)!=0
                 source_body, source_content_length,                          )
                 dest_body, dest_buf_length,                  );
                 tables);  }
         /*if(dest_content_length<dest_buf_length)  
                 throw(0, 0,  // read one UTF8 char and return length of this char (in bytes)
                         0,  static unsigned int readUTF8Char(const XMLByte*& srcPtr, const XMLByte* srcEnd, XMLByte& firstByte, XMLCh& UTF8Char){
                         "Charset::transcodeToUTF8 error");*/          if(!srcPtr || !*srcPtr || srcPtr>=srcEnd)
                   return 0;
   
           firstByte=*srcPtr;
   
           if(firstByte<=127){
                   UTF8Char=firstByte;
                   srcPtr++;
                   return 1;
           }
   
           unsigned int trailingBytes=gUTFBytes[firstByte];
   
           if(srcPtr+trailingBytes>=srcEnd){
                   return 0; // not enough bytes in source string for reading
           }
   
           uint tmpVal=0;
           switch(trailingBytes){
                   case 5: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 4: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 3: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 2: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 1: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 0: tmpVal+=*srcPtr++;
           }
   
           tmpVal-=gUTFOffsets[trailingBytes];
           UTF8Char=tmpVal;
   
           return trailingBytes+1;
   }
   
   // skip UTF8 char and return length of this char (in bytes)
   static unsigned int skipUTF8Char(const XMLByte*& srcPtr, const XMLByte* srcEnd){
           if(!srcPtr || !*srcPtr || srcPtr>=srcEnd)
                   return 0;
   
           unsigned int trailingBytes=gUTFBytes[*srcPtr]+1;
           srcPtr+=trailingBytes;
   
           return trailingBytes;
   }
   
   // read non-UTF8 char, and return number of bytes needed for storing this char in UTF8
   static unsigned int readChar(const XMLByte*& srcPtr, const XMLByte* srcEnd, XMLByte& firstByte, XMLCh& UTF8Char, const Charset::Tables& tables){
           if(!srcPtr || !*srcPtr || srcPtr>=srcEnd)
                   return 0;
   
           firstByte=*srcPtr++;
           UTF8Char=tables.fromTable[firstByte];
   
           if(UTF8Char<0x80)
                   return 1;
           else if(UTF8Char<0x800)
                   return 2;
           else if(UTF8Char<0x10000)
                   return 3;
           else if(UTF8Char<0x200000)
                   return 4;
           else if(UTF8Char<0x4000000)
                   return 5;
           else if(UTF8Char<= 0x7FFFFFFF)
                   return 6;
   
           // will use the replacement character '?'
           firstByte=0;
           return 1;
   }
   
   size_t Charset::calc_escaped_length_UTF8(XMLByte* src, size_t src_length){
           size_t dest_length=0;
   
           for(UTF8_string_iterator i(src, src_length); i.has_next(); ){
                   if(i.getCharSize()==1)
                           dest_length+=!need_escape(i.getFirstByte())?1/*as-is*/:3/*%XX*/;
                   else
                           dest_length+=6; // %uXXXX
           }
   
           return dest_length;
   }
   
   size_t Charset::calc_escaped_length(XMLByte* src, size_t src_length, const Charset::Tables& tables){
           size_t dest_length=0;
   
           const XMLByte* src_ptr=src;
           const XMLByte* src_end=src_ptr+src_length;
           XMLByte first_byte;
           XMLCh UTF8_char;
           while(uint char_size=readChar(src_ptr, src_end, first_byte, UTF8_char, tables)){
                   if(char_size==1)
                           dest_length+=(!first_byte/*replacement char '?'*/ || !need_escape(first_byte))?1:3/*'%XX'*/;
                   else
                           dest_length+=6; // %uXXXX
           }
   
           return dest_length;
   }
   
   size_t Charset::calc_escaped_length(const String::C src, const Charset& source_charset){
           size_t src_length=src.length;
           if(!src_length)
                   return 0;
   
   #ifdef PRECALCULATE_DEST_LENGTH
           if(source_charset.isUTF8())
                   return calc_escaped_length_UTF8((XMLByte *)src.str, src_length);
           else
                   return calc_escaped_length((XMLByte *)src.str, src_length, source_charset.tables);
   #else
           return src_length*6; // enough for %uXXXX but too memory-hungry
   #endif
   }
   
   #define escape_char(dest_ptr, char_size, first_byte, UTF8_char) \
           if(char_size==1) \
                   if(first_byte){ \
                           if(need_escape(first_byte)) \
                                   dest_ptr+=sprintf((char*)dest_ptr, "%%%02X", first_byte); /* %XX */ \
                           else \
                                   *dest_ptr++=first_byte; /*as is*/ \
                   } else \
                           *dest_ptr++='?'; /* replacement char '?' */ \
           else \
                   dest_ptr+=sprintf((char*)dest_ptr, "%%u%04X", UTF8_char); // %uXXXX
   
   
   size_t Charset::escape_UTF8(const XMLByte* src, size_t src_length, XMLByte* dest) {
           XMLByte* dest_ptr=dest;
   
           // loop until we either run out of input data
           for(UTF8_string_iterator i((XMLByte *)src, src_length); i.has_next(); )
                   escape_char(dest_ptr, i.getCharSize(), i.getFirstByte(), i.next())
           
           return dest_ptr - dest;
   }
   
   size_t Charset::escape(const XMLByte* src, size_t src_length, XMLByte* dest, const Charset::Tables& tables) {
           const XMLByte* src_ptr=src;
           const XMLByte* src_end=src+src_length;
           XMLByte* dest_ptr=dest;
   
           XMLByte first_byte;
           XMLCh UTF8_char;
           uint char_size;
   
           while(char_size=readChar(src_ptr, src_end, first_byte, UTF8_char, tables))
                   escape_char(dest_ptr, char_size, first_byte, UTF8_char)
   
           return dest_ptr - dest;
   }
   
   String::C Charset::escape(const String::C src, const Charset& source_charset){
           size_t src_length=src.length;
           if(!src_length)
                   return String::C("", 0);
   
           size_t dest_calculated_length=calc_escaped_length(src, source_charset);
           XMLByte *dest_body=new(PointerFreeGC) XMLByte[dest_calculated_length+1/*terminator*/];
   
           size_t dest_length;
           if(source_charset.isUTF8())
                   dest_length=escape_UTF8((XMLByte *)src.str, src_length, dest_body);
           else
                   dest_length=escape((XMLByte *)src.str, src_length, dest_body, source_charset.tables);
   
           if(dest_length>dest_calculated_length)
                   throw Exception(0, 0, "Charset::escape buffer overflow");
   
           dest_body[dest_length]=0; // terminator
           return String::C((char*)dest_body, dest_length);
   }
   
   String::Body Charset::escape(const String::Body src, const Charset& source_charset) {
           const char *src_ptr=src.cstr();
           size_t src_size=src.length();
   
           String::C dest=Charset::escape(String::C(src_ptr, src_size), source_charset);
   
           return String::Body(dest.length ? dest.str:0);
   }
   
   String& Charset::escape(const String& src, const Charset& source_charset) {
           if(src.is_empty())
                   return *new String();
   
           return *new String(escape((String::Body)src, source_charset), String::L_CLEAN);
   }
   
   inline bool need_json_escape(unsigned char c){
           return strchr("\n\"\\/\t\r\b\f", c)!=0;
   }
   
   size_t Charset::calc_JSON_escaped_length_UTF8(XMLByte* src, size_t src_length){
           size_t dest_length=0;
   
           for(UTF8_string_iterator i(src, src_length); i.has_next(); ){
                   if(i.getCharSize()==1)
                           dest_length+=need_json_escape(i.getFirstByte()) ? 2 : 1;
                   else
                           dest_length+=6; // \uXXXX
           }
   
           return dest_length;
   }
   
   size_t Charset::calc_JSON_escaped_length(XMLByte* src, size_t src_length, const Charset::Tables& tables){
           const XMLByte* src_ptr=src;
           const XMLByte* src_end=src_ptr+src_length;
           XMLByte first_byte;
           XMLCh UTF8_char;
           size_t dest_length=0;
   
           while(uint char_size=readChar(src_ptr, src_end, first_byte, UTF8_char, tables)){
                   if(char_size==1)
                           dest_length+=(!first_byte/*replacement char '?'*/ || !need_json_escape(first_byte))? 1 : 2;
                   else
                           dest_length+=6; // \uXXXX
           }
   
           return dest_length;
   }
   
   size_t Charset::calc_JSON_escaped_length(const String::C src, const Charset& source_charset){
           size_t src_length=src.length;
           if(!src_length)
                   return 0;
   
   #ifdef PRECALCULATE_DEST_LENGTH
           if(source_charset.isUTF8())
                   return calc_JSON_escaped_length_UTF8((XMLByte *)src.str, src_length);
           else
                   return calc_JSON_escaped_length((XMLByte *)src.str, src_length, source_charset.tables);
   #else
           return src_length*6; // enough for \uXXXX but too memory-hungry
   #endif
   }
   
   #define escape_char_JSON(dest_ptr, char_size, first_byte, UTF8_char) \
           if(char_size==1) \
                   switch(first_byte){ \
                           case '\n': *dest_ptr++='\\'; *dest_ptr++='n';  break; \
                           case '"' : *dest_ptr++='\\'; *dest_ptr++='"';  break; \
                           case '\\': *dest_ptr++='\\'; *dest_ptr++='\\'; break; \
                           case '/' : *dest_ptr++='\\'; *dest_ptr++='/';  break; \
                           case '\t': *dest_ptr++='\\'; *dest_ptr++='t';  break; \
                           case '\r': *dest_ptr++='\\'; *dest_ptr++='r';  break; \
                           case '\b': *dest_ptr++='\\'; *dest_ptr++='b';  break; \
                           case '\f': *dest_ptr++='\\'; *dest_ptr++='f';  break; \
                           case   0 : *dest_ptr++='?'; break; /*replacement char*/ \
                           default  : *dest_ptr++=first_byte; \
                   } \
           else \
                   dest_ptr+=sprintf((char*)dest_ptr, "\\u%04X", UTF8_char); // \uXXXX
   
   
   size_t Charset::escape_JSON_UTF8(const XMLByte* src, size_t src_length, XMLByte* dest) {
           XMLByte* dest_ptr=dest;
   
           // loop until we either run out of input data
           for(UTF8_string_iterator i((XMLByte *)src, src_length); i.has_next(); )
                   escape_char_JSON(dest_ptr, i.getCharSize(), i.getFirstByte(), i.next())
   
         // return          return dest_ptr - dest;
         adest_body=dest_body;  }
         adest_content_length=dest_content_length;  
 }  size_t Charset::escape_JSON(const XMLByte* src, size_t src_length, XMLByte* dest, const Charset::Tables& tables) {
 /// @test buf overflow          const XMLByte* src_ptr=src;
 void Charset::transcodeFromUTF8(Pool& pool,          const XMLByte* src_end=src+src_length;
                                                                    const void *source_body, size_t source_content_length,          XMLByte* dest_ptr=dest;
                                                                    const void *& adest_body, size_t& adest_content_length) const {  
         size_t dest_buf_length=source_content_length/*surly enough*/;          XMLByte first_byte;
         XMLByte *dest_body=(XMLByte*)pool.malloc(dest_buf_length);          XMLCh UTF8_char;
           uint char_size;
         size_t dest_content_length=::transcodeFromUTF8(  
                 source_body, source_content_length,          while(char_size=readChar(src_ptr, src_end, first_byte, UTF8_char, tables))
                 dest_body, dest_buf_length,                  escape_char_JSON(dest_ptr, char_size, first_byte, UTF8_char)
                 tables);  
 /*      if(dest_content_length!=dest_buf_length)          return dest_ptr - dest;
                 throw(0, 0,  }
   
   String::C Charset::escape_JSON(const String::C src, const Charset& source_charset){
           size_t src_length=src.length;
           if(!src_length)
                   return String::C("", 0);
   
   
           size_t dest_calculated_length=calc_JSON_escaped_length(src, source_charset);
           XMLByte *dest_body=new(PointerFreeGC) XMLByte[dest_calculated_length+1/*terminator*/];
   
           size_t dest_length;
           if(source_charset.isUTF8())
                   dest_length=escape_JSON_UTF8((XMLByte *)src.str, src_length, dest_body);
           else
                   dest_length=escape_JSON((XMLByte *)src.str, src_length, dest_body, source_charset.tables);
   
           if(dest_length>dest_calculated_length)
                   throw Exception(0, 0, "Charset::escape_JSON buffer overflow");
   
           dest_body[dest_length]=0; // terminator
           return String::C((char*)dest_body, dest_length);
   }
   
   String::Body Charset::escape_JSON(const String::Body src, const Charset& source_charset) {
           const char *src_ptr=src.cstr();
           size_t src_size=src.length();
   
           String::C dest=Charset::escape_JSON(String::C(src_ptr, src_size), source_charset);
   
           return String::Body(dest.length ? dest.str:0);
   }
   
   String& Charset::escape_JSON(const String& src, const Charset& source_charset) {
           if(src.is_empty())
                   return *new String();
   
           return *new String(escape_JSON((String::Body)src, source_charset), String::L_CLEAN);
   }
   
   const String::C Charset::transcodeToUTF8(const String::C src) const {
           int src_length=src.length;
   
   #ifdef PRECALCULATE_DEST_LENGTH
           int dest_length=0;
           const XMLByte* srcPtr=(XMLByte*)src.str;
           const XMLByte* srcEnd=srcPtr+src_length;
           XMLByte firstByte;
           XMLCh UTF8Char;
           while(uint charSize=readChar(srcPtr, srcEnd, firstByte, UTF8Char, tables))
                   dest_length+=charSize;
   #else
           int dest_length=src_length*6; // so that surly enough (max utf8 seq len=6) but too memory-hungry
   #endif
   
   #ifndef NDEBUG
           int saved_dest_length=dest_length;
   #endif
           XMLByte *dest_body=new(PointerFreeGC) XMLByte[dest_length+1/*for terminator*/];
   
           if(::transcodeToUTF8(
                   (XMLByte *)src.str, src_length,
                   dest_body, dest_length,
                   tables)<0)
                   throw Exception(0,
                         0,                          0,
                         "Charset::transcodeToUTF8 error");*/                          "Charset::transcodeToUTF8 buffer overflow");
   
           assert(dest_length<=saved_dest_length);
           dest_body[dest_length]=0; // terminator
           return String::C((char*)dest_body, dest_length);
   }
   
   static XMLCh change_case_UTF8(const XMLCh src, const Charset::UTF8CaseTable& table) {
           int lo = 0;
           int hi = table.size - 1;
           while(lo<=hi) {
                   // Calc the mid point of the low and high offset.
                   const unsigned int i = (lo + hi) / 2;
   
                   XMLCh cur=table.records[i].from;
                   if(src==cur)
                           return table.records[i].to;
                   if(src>cur)
                           lo = i+1;
                   else
                           hi = i-1;
           }
   
         // return          // not found
         adest_body=dest_body;          return src;
         adest_content_length=dest_content_length;  
 }  }
   
 /// transcode using both charsets  static void store_UTF8(XMLCh src, XMLByte*& outPtr){
 void Charset::transcodeToCharset(Pool& pool,          if(!src) {
                                                                            const Charset& dest_charset,                  // use the replacement character
                                                                            const void *source_body, size_t source_content_length,                  *outPtr++= '?';
                                                                            const void *& adest_body, size_t& adest_content_length) const {                  return;
         if(&dest_charset==this) {          }
                 adest_body=source_body;  
                 adest_content_length=source_content_length;  
         } else {  
                 size_t dest_content_length=source_content_length;  
                 unsigned char *dest_body=(unsigned char *)pool.malloc(dest_content_length);  
   
                 const XMLByte* srcPtr=(const XMLByte*)source_body;          // Figure out how many bytes we need
                 const XMLByte* srcEnd=(const XMLByte*)source_body+source_content_length;          unsigned int encodedBytes;
           if(src<0x80)
                   encodedBytes = 1;
           else if(src<0x800)
                   encodedBytes = 2;
           else if(src<0x10000)
                   encodedBytes = 3;
           else if(src<0x200000)
                   encodedBytes = 4;
           else if(src<0x4000000)
                   encodedBytes = 5;
           else if(src<= 0x7FFFFFFF)
                   encodedBytes = 6;
           else {
                   // use the replacement character
                   *outPtr++= '?';
                   return;
           }
   
                 for(XMLByte* outPtr=dest_body; srcPtr<srcEnd; srcPtr++) {          //  And spit out the bytes. We spit them out in reverse order
                         XMLCh curVal = tables.fromTable[*srcPtr];          //  here, so bump up the output pointer and work down as we go.
                         if(curVal)           outPtr+= encodedBytes;
                                 *outPtr++=xlatOneTo(curVal, dest_charset.tables);          switch(encodedBytes) {
                         else {          case 6: *--outPtr = XMLByte((src | 0x80UL) & 0xBFUL);
                                 // use the replacement character                  src>>= 6;
                                 *outPtr++= '?';          case 5: *--outPtr = XMLByte((src | 0x80UL) & 0xBFUL);
                         }                         src>>= 6;
           case 4: *--outPtr = XMLByte((src | 0x80UL) & 0xBFUL);
                   src>>= 6;
           case 3: *--outPtr = XMLByte((src | 0x80UL) & 0xBFUL);
                   src>>= 6;
           case 2: *--outPtr = XMLByte((src | 0x80UL) & 0xBFUL);
                   src>>= 6;
           case 1: *--outPtr = XMLByte(src | gFirstByteMark[encodedBytes]);
           }
           
           // Add the encoded bytes back in again to indicate we've eaten them
           outPtr+= encodedBytes;
   }
   
   static void change_case_UTF8(XMLCh src, XMLByte*& outPtr, 
                                                   const Charset::UTF8CaseTable& table) {
           store_UTF8(change_case_UTF8(src, table), outPtr);
   };
   void change_case_UTF8(const XMLByte* srcData, size_t srcLen,
                                             XMLByte* toFill, size_t toFillLen,
                                             const Charset::UTF8CaseTable& table) {
           const XMLByte* srcPtr=srcData;
           const XMLByte* srcEnd=srcData+srcLen;
           XMLByte* outPtr=toFill;
           XMLByte* outEnd=toFill+toFillLen;
   
           //  We now loop until we either run out of input data, or room to store
           while ((srcPtr < srcEnd) && (outPtr < outEnd)) {
                   // Get the next leading byte out
                   const XMLByte firstByte =* srcPtr;
   
                   if(firstByte<=127) {
                           change_case_UTF8(firstByte, outPtr, table);
                           srcPtr++;
                           continue;
                   }
                   
                   // See how many trailing src bytes this sequence is going to require
                   const unsigned int trailingBytes = gUTFBytes[firstByte];
                   
                   // Looks ok, so lets build up the value
                   uint tmpVal=0;
                   switch(trailingBytes) {
                   case 5: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 4: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 3: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 2: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 1: tmpVal+=*srcPtr++; tmpVal<<=6;
                   case 0: tmpVal+=*srcPtr++;
                           break;
                           
                   default:
                           throw Exception(0,
                                   0,
                                   "change_case_UTF8 error: wrong trailingBytes value(%d)", trailingBytes);
                   }
                   tmpVal-=gUTFOffsets[trailingBytes];
                   
                   //  If it will fit into a single char, then put it in. Otherwise
                   //  fail [*encode it as a surrogate pair. If its not valid, use the
                   //  replacement char.*]
                   if(!(tmpVal & 0xFFFF0000))
                           change_case_UTF8(tmpVal, outPtr, table);
                   else
                           throw Exception(0,
                                   0,
                                   "change_case_UTF8 error: too big tmpVal(0x%08X)", tmpVal);
           }
           
           if(srcPtr!=outPtr)
                   throw Exception(0,  
                           0,
                           "change_case_UTF8 error: end pointers do not match");
   }
   
   static size_t getDecNumLength(XMLCh UTF8Char){
           return
                   (UTF8Char < 100)
                           ?2
                           :(UTF8Char < 1000)
                                   ?3
                                   :(UTF8Char < 10000)
                                           ?4
                                           :5;
   }
   
   const String::C Charset::transcodeFromUTF8(const String::C src) const {
           int src_length=src.length;
   #ifdef PRECALCULATE_DEST_LENGTH
           int dest_length=0;
           for(UTF8_string_iterator i((XMLByte *)src.str, src_length); i.has_next(); ){
                   if(i.getCharSize()==1)
                           dest_length++;
                   else
                           dest_length+=(i.next() & 0xFFFF0000)
                                                           ?i.getCharSize()*3      // '%XX' for each byte
                                                           :(xlatOneTo(i.next(), tables, 0)!=0)
                                                                   ?1              // can convert it to single char
                                                                   :getDecNumLength(i.next())+3;           // &#XX; - &#XXXXX;
           }
   #else
           // so that surly enough, "&#XXX;" has max ratio (huh? 8 bytes needed for '&#XXXXX;')
           int dest_length=src_length*6;
   #endif
   
   #ifndef NDEBUG
           int saved_dest_length=dest_length;
   #endif
           XMLByte *dest_body=new(PointerFreeGC) XMLByte[dest_length+1/*for terminator*/];
   
           if(::transcodeFromUTF8(
                   (XMLByte *)src.str, src_length,
                   dest_body, dest_length,
                   tables)<0)
                   throw Exception(0, 
                           0,
                           "Charset::transcodeFromUTF8 buffer overflow");
   
           assert(dest_length<=saved_dest_length);
           dest_body[dest_length]=0; // terminator
           return String::C((char*)dest_body, dest_length);
   }
   
   /// transcode using both charsets
   const String::C Charset::transcodeToCharset(const String::C src, 
                                                   const Charset& dest_charset) const {
           if(&dest_charset==this) 
                   return src;
           else {
                   size_t dest_length=src.length;
                   XMLByte* dest_body=new(PointerFreeGC) XMLByte[dest_length+1/*for terminator*/];
   
                   XMLByte* output=dest_body;
                   const XMLByte* input=(XMLByte *)src.str;
                   while(XMLCh c=*input++) {
                           XMLCh curVal = tables.fromTable[c];
                           *output++=curVal?
                                   xlatOneTo(curVal, dest_charset.tables, '?') // OK
                                   :'?'; // use the replacement character
                 }                  }
   
                 adest_body=dest_body;                  dest_body[dest_length]=0; // terminator
                 adest_content_length=dest_content_length;                  return String::C((char*)dest_body, dest_length);
         }          }
 }                         }                       
   
 #ifdef XML  void Charset::store_Char(XMLByte*& outPtr, XMLCh src, XMLByte not_found){
 static int         xml256CharEncodingInputFunc     (          if(isUTF8())
                                                                                                         unsigned char *out,                  store_UTF8(src, outPtr);
                                                                                                         int *outlen,          else if(char ch=xlatOneTo(src, tables, not_found))
                                                                                                         const unsigned char *in,                          *outPtr++=ch;
                                                                                                         int *inlen,   
                                                                                                         void *info) {  
         return transcodeToUTF8(  
                                                         in, *inlen,  
                                                         out, *(unsigned int*)outlen,  
                                                         *(const Charset::Tables *)info);  
 }  
   
 static int         xml256CharEncodingOutputFunc    (  
                                                                                                         unsigned char *out,  
                                                                                                         int *outlen,  
                                                                                                         const unsigned char *in,  
                                                                                                         int *inlen,   
                                                                                                         void *info) {  
         return transcodeFromUTF8(  
                                                         in, *inlen,  
                                                         out, *outlen,  
                                                         *(const Charset::Tables *)info);  
 }  }
   
   #ifdef XML
   
   static const Charset::Tables* tables[MAX_CHARSETS];
   
   #ifdef PA_PATCHED_LIBXML_BACKWARD
   
   #define declareXml256ioFuncs(i) \
           static int xml256CharEncodingInputFunc##i( \
                   unsigned char *out, int *outlen, \
                   const unsigned char *in, int *inlen, void*) { \
                   return transcodeToUTF8( \
                           in, *inlen, \
                           out, *outlen, \
                           *tables[i]); \
           } \
           static int xml256CharEncodingOutputFunc##i( \
                   unsigned char *out, int *outlen, \
                   const unsigned char *in, int *inlen, void*) { \
                   return transcodeFromUTF8( \
                           in, *inlen, \
                           out, *outlen, \
                           *tables[i]); \
           }
   
   #else
   
   #define declareXml256ioFuncs(i) \
           static int xml256CharEncodingInputFunc##i( \
                   unsigned char *out, int *outlen, \
                   const unsigned char *in, int *inlen) { \
                   return transcodeToUTF8( \
                           in, *inlen, \
                           out, *outlen, \
                           *tables[i]); \
           } \
           static int xml256CharEncodingOutputFunc##i( \
                   unsigned char *out, int *outlen, \
                   const unsigned char *in, int *inlen) { \
                   return transcodeFromUTF8( \
                           in, *inlen, \
                           out, *outlen, \
                           *tables[i]); \
           }
   
   #endif
   
   
   declareXml256ioFuncs(0) declareXml256ioFuncs(1)
   declareXml256ioFuncs(2) declareXml256ioFuncs(3)
   declareXml256ioFuncs(4) declareXml256ioFuncs(5)
   declareXml256ioFuncs(6) declareXml256ioFuncs(7)
   declareXml256ioFuncs(8) declareXml256ioFuncs(9)
   
   static xmlCharEncodingInputFunc inputFuncs[MAX_CHARSETS]={
           xml256CharEncodingInputFunc0,   xml256CharEncodingInputFunc1,
           xml256CharEncodingInputFunc2,   xml256CharEncodingInputFunc3,
           xml256CharEncodingInputFunc4,   xml256CharEncodingInputFunc5,
           xml256CharEncodingInputFunc6,   xml256CharEncodingInputFunc7,
           xml256CharEncodingInputFunc8,   xml256CharEncodingInputFunc9
   };
   static xmlCharEncodingOutputFunc outputFuncs[MAX_CHARSETS]={
           xml256CharEncodingOutputFunc0,  xml256CharEncodingOutputFunc1,
           xml256CharEncodingOutputFunc2,  xml256CharEncodingOutputFunc3,
           xml256CharEncodingOutputFunc4,  xml256CharEncodingOutputFunc5,
           xml256CharEncodingOutputFunc6,  xml256CharEncodingOutputFunc7,
           xml256CharEncodingOutputFunc8,  xml256CharEncodingOutputFunc9
   };
   static size_t handlers_count=0;
   
 void Charset::addEncoding(char *name_cstr) {  void Charset::addEncoding(char *name_cstr) {
         xmlCharEncodingHandler *handler=          if(handlers_count==MAX_CHARSETS)
                 (xmlCharEncodingHandler *)malloc(sizeof(xmlCharEncodingHandler));                  throw Exception(0,
         handler->name=name_cstr;                          0,
         handler->input=xml256CharEncodingInputFunc; handler->inputInfo=&tables;                          "already allocated %d handlers, no space for new encoding '%s'",
         handler->output=xml256CharEncodingOutputFunc; handler->outputInfo=&tables;                                  MAX_CHARSETS, name_cstr);
   
           xmlCharEncodingHandler* handler=new(UseGC) xmlCharEncodingHandler;
           {
                   handler->name=name_cstr;
                   handler->input=inputFuncs[handlers_count]; 
                   handler->output=outputFuncs[handlers_count]; 
                   ::tables[handlers_count]=&tables;
                   handlers_count++;
           }
                   
         xmlRegisterCharEncodingHandler(handler);          xmlRegisterCharEncodingHandler(handler);
   
   }
   
   void Charset::initTranscoder(const String::Body NAME, const char* name_cstr) {
           ftranscoder=xmlFindCharEncodingHandler(name_cstr);
           transcoder(NAME); // check right way
 }  }
   
 void Charset::initTranscoder(const String *source, const char *name_cstr) {  xmlCharEncodingHandler& Charset::transcoder(const String::Body NAME) {
         transcoder=xmlFindCharEncodingHandler(name_cstr);          if(!ftranscoder)
         if(!transcoder)                  throw Exception(PARSER_RUNTIME,
                 throw Exception(0, 0,                          new String(NAME, String::L_TAINTED),
                         source,  
                         "unsupported encoding");                          "unsupported encoding");
           return *ftranscoder;
 }  }
   
 const char *Charset::transcode_cstr(GdomeDOMString *s) {   String::C Charset::transcode_cstr(const xmlChar* s) {
         if(!transcoder)          if(!s)
                 throw Exception(0, 0,                  return String::C("", 0);
                         0,  
                         "transcode_cstr no transcoder");  
   
         int inlen=gdome_str_length(s);          int inlen=strlen((const char*)s);
         int outlen=inlen+1; // max          int outlen=inlen*6/*strlen("&#255;")*/; // max
         char *out=(char *)malloc(outlen*sizeof(char));  #ifndef NDEBUG
                   int saved_outlen=outlen;
         int size=transcoder->output(  #endif
                 (unsigned char*)out, &outlen,          char *out=new(PointerFreeGC) char[outlen+1];
                 (const unsigned char*)s->str, &inlen,          
                 transcoder->outputInfo);          int error;
         if(size<0)          if(xmlCharEncodingOutputFunc output=transcoder(FNAME).output) {
                 throw Exception(0, 0,                  error=output(
                           (unsigned char*)out, &outlen,
                           (const unsigned char*)s, &inlen
   #ifdef PA_PATCHED_LIBXML_BACKWARD
                           ,0
   #endif
                           );
           } else {
                   memcpy(out, s, outlen=inlen);
                   error=0;
           }
           if(error<0)
                   throw Exception(0,
                         0,                          0,
                         "transcode_cstr failed (%d)", size);                          "transcode_cstr failed (%d)", error);
   
         out[size]=0;          assert(outlen<=saved_outlen); out[outlen]=0;
         return out;          return String::C(out, outlen);
 }  }
 String& Charset::transcode(GdomeDOMString *s) {   const String& Charset::transcode(const xmlChar* s) { 
         return *NEW String(pool(), transcode_cstr(s));           String::C cstr=transcode_cstr(s);
           return *new String(cstr.str, String::L_TAINTED);
 }  }
   
 /// @test less memory using -maybe- xmlParserInputBufferCreateMem  /// @test less memory using -maybe- xmlParserInputBufferCreateMem
 GdomeDOMString *Charset::transcode_buf(const char *buf, size_t buf_size) {   xmlChar* Charset::transcode_buf2xchar(const char* buf, size_t buf_size) {
         if(!transcoder)          xmlChar* out;
                 throw Exception(0, 0,          int outlen;
           int error;
   #ifndef NDEBUG
           int saved_outlen;
   #endif
           if(xmlCharEncodingInputFunc input=transcoder(FNAME).input) {
                   outlen=buf_size*6/*max UTF8 bytes per char*/;
   #ifndef NDEBUG
                   saved_outlen=outlen;
   #endif
                   out=(xmlChar*)xmlMalloc(outlen+1);
                   error=input(
                           out, &outlen,
                           (const unsigned char*)buf, (int*)&buf_size
   #ifdef PA_PATCHED_LIBXML_BACKWARD
                           ,0
   #endif
                           );
           } else {
                   outlen=buf_size;
   #ifndef NDEBUG
                   saved_outlen=outlen;
   #endif
                   out=(xmlChar*)xmlMalloc(outlen+1);
                   memcpy(out, buf, outlen);
                   error=0;
           }
           
           if(error<0)
                   throw Exception(0,
                         0,                          0,
                         "transcode_buf no transcoder");                          "transcode_buf failed (%d)", error);
   
         int outlen=buf_size*6/*max*/+1;          assert(outlen<=saved_outlen); out[outlen]=0;
         unsigned char *out=(unsigned char*)malloc(outlen*sizeof(unsigned char));          return out;
         int size=transcoder->input(  }
                 out, &outlen,  
                 (const unsigned char *)buf,  (int *)&buf_size,  
                 transcoder->inputInfo);  
         if(size<0)  
                 throw Exception(0, 0,  
                         0,  
                         "transcode_buf failed (%d)", size);  
   
         out[size]=0;  xmlChar* Charset::transcode(const String& s) {
         return gdome_str_mkref_own((gchar*)out);          String::Body sbody=s.cstr_to_string_body_untaint(String::L_AS_IS);
           return transcode_buf2xchar(sbody.cstr(), sbody.length()); 
 }  }
 GdomeDOMString *Charset::transcode(const String& s) {   
         const char *cstr=s.cstr(String::UL_UNSPECIFIED);  
   
         return transcode_buf(cstr, strlen(cstr));   xmlChar* Charset::transcode(const String::Body s) {
           return transcode_buf2xchar(s.cstr(), s.length()); 
 }  }
 #endif  #endif
   
   String::Body Charset::transcode(const String::Body src, 
           const Charset& source_transcoder, 
           const Charset& dest_transcoder) {
   
           const char *src_ptr=src.cstr();
           size_t src_size=src.length();
   
           String::C dest=Charset::transcode(String::C(src_ptr, src_size), source_transcoder, dest_transcoder);
   
           return String::Body(dest.length ? dest.str:0);
   }
   
   String& Charset::transcode(const String& src, 
           const Charset& source_transcoder, 
           const Charset& dest_transcoder) {
           if(src.is_empty())
                   return *new String();
   
           return *new String(transcode((String::Body)src, source_transcoder, dest_transcoder), String::L_CLEAN);
   }
   
   void Charset::transcode(ArrayString& src,
           const Charset& source_transcoder, 
           const Charset& dest_transcoder) {
           for(size_t i=0; i<src.count(); i++)
                   src.put(i, &transcode(*src[i], source_transcoder, dest_transcoder));
   }
   
   #ifndef DOXYGEN
   struct Transcode_pair_info {
           const Charset* source_transcoder;
           const Charset* dest_transcoder;
   };
   #endif
   static void transcode_pair(HashStringValue::key_type /*akey*/, 
                            String::Body& avalue, 
                            Transcode_pair_info* info) {
           avalue=Charset::transcode(avalue,
                   *info->source_transcoder, 
                   *info->dest_transcoder);
   }
   
   void Charset::transcode(HashStringString& src,
           const Charset& source_transcoder, 
           const Charset& dest_transcoder) {
           Transcode_pair_info info={&source_transcoder, &dest_transcoder};
           src.for_each_ref<Transcode_pair_info*>(transcode_pair, &info);
   }
   
   size_t getUTF8BytePos(const XMLByte* srcBegin, const XMLByte* srcEnd, size_t charPos){
           const XMLByte* ptr=srcBegin;
           while(charPos-- && skipUTF8Char(ptr, srcEnd));
   
           return ptr-srcBegin;
   }
   
   size_t getUTF8CharPos(const XMLByte* srcBegin, const XMLByte* srcEnd, size_t bytePos){
           size_t charPos=0;
           const XMLByte* ptr=srcBegin;
           const XMLByte* ptrEnd=srcBegin+bytePos;
           while(skipUTF8Char(ptr, srcEnd)){
                   if(ptr>ptrEnd)
                           return charPos;
                   charPos++;
           }
   
           // scan till end but position in bytes still too low
           throw Exception(0,
                                           0,
                                           "Error convertion byte pos to char pos");
   }
   
   size_t lengthUTF8(const XMLByte* srcBegin, const XMLByte* srcEnd){
           size_t size=0;
           while(skipUTF8Char(srcBegin, srcEnd))
                   size++;
   
           return size;
   }
   
   unsigned int lengthUTF8Char(const XMLByte c){
           return gUTFBytes[c]+1;
   }
   
   bool UTF8_string_iterator::has_next(){
           fcharSize=readUTF8Char(fsrcPtr, fsrcEnd, ffirstByte, fUTF8Char);
           return fcharSize!=0;
   }

Removed from v.1.10  
changed lines
  Added in v.1.85


E-mail: