Ответить на комментарий

Юникодная елка

Февраль 4, 2012 — Шарахов А.П.

Лишь годные дятлы собираются в стаи,
Юникодом пугая мозги января.
Их песни не стихнут, они не устанут.
А елка как кактус беспокоит меня.

Как испортить кашу

Юникодные преобразования в Delphi почти невозможно ускорить. Но можно выбросить из головы все плоскости, кроме нулевой, транжирить память на таблицы, напрягать ее пересылками, наплевать на особые случаи и ошибки. Можно быть проще, короче.

AnsiToUnicode и UnicodeToAnsi

Функции ShaAnsiToUnicode и ShaUnicodeToAnsi довольно незатейливы, они быстрее аналогов за счет развернутого цикла и таблиц. Здесь и далее типы NInt и UInt — знаковое и беззнаковое нативное целое.

function ShaAnsiToUnicode(Dest: PWideChar; Source: PAnsiChar; SourceChars: NInt;
                          pTable: PConvTable): PWideChar; overload;
var
  Tail: PAnsiChar;
begin;
  if NInt(SourceChars)>0 then begin;
    Tail:=Source + SourceChars + (-4);
    if Source<=Tail then repeat;
      Dest[0]:=WideChar(pTable.AnsiToWide[byte(Source[0])]);
      Dest[1]:=WideChar(pTable.AnsiToWide[byte(Source[1])]);
      Dest[2]:=WideChar(pTable.AnsiToWide[byte(Source[2])]);
      Dest[3]:=WideChar(pTable.AnsiToWide[byte(Source[3])]);
      inc(Source, 4);
      inc(Dest, 4);
      until Source>Tail;
    inc(Tail, 4);
    if Source<Tail then repeat;
      Dest[0]:=WideChar(pTable.AnsiToWide[byte(Source[0])]);
      inc(Source);
      inc(Dest);
      until Source>=Tail;
    end;
  Result:=Dest;
  end;

function ShaUnicodeToAnsi(Dest: PAnsiChar; Source: PWideChar; SourceChars: NInt;
                          pTable: PConvTable): PAnsiChar; overload;
var
  Tail: PWideChar;
begin;
  if NInt(SourceChars)>0 then begin;
    NInt(Tail):=NInt(Source) + SourceChars * SizeOf(WideChar) + (-4 * SizeOf(WideChar));
    if Source<=Tail then repeat;                 
      Dest[0]:=AnsiChar(pTable.WideToAnsi[word(Source[0])]);
      Dest[1]:=AnsiChar(pTable.WideToAnsi[word(Source[1])]);
      Dest[2]:=AnsiChar(pTable.WideToAnsi[word(Source[2])]);
      Dest[3]:=AnsiChar(pTable.WideToAnsi[word(Source[3])]);
      inc(Source, 4);
      inc(Dest, 4);
      until Source>Tail;
    inc(Tail, 4);
    if Source<Tail then repeat;
      Dest[0]:=AnsiChar(pTable.WideToAnsi[word(Source[0])]);
      inc(Source);
      inc(Dest);
      until Source>=Tail;
    end;
  Result:=Dest;
  end;

UnicodeToUTF8 и AnsiToUTF8

Функции ShaUnicodeToUTF8 и ShaAnsiToUTF8 содержат только одну хитрость: начальные ASCII-символы строки обрабатываются группами. Педаль для моих любимых буковок в них отсутствует, хотя эксперименты показывают, что при сильном желании можно увеличить скорость еще примерно на 10%. В общем, трудно человеку.

function ShaUnicodeToUTF8(Dest: PUTF8Char; Source: PWideChar; SourceChars: NInt): PUTF8Char; overload;
var
  Ch: UInt;
  Tail: PWideChar;
begin;
  if NInt(SourceChars)>0 then begin;
 
    //by 2 WideChars
    NInt(Tail):=NInt(Source) + SourceChars * SizeOf(WideChar) + (-4);
    if Source<=Tail then repeat;
      Ch:=pCardinal(Source)^;
      if Ch and $FF80FF80<>0 then break;         //break if not ASCII pair
      Ch:=Ch shr 8 or Ch;
      pWord(Dest)^:=Ch;
      inc(Source, 2);
      inc(Dest, 2);
      until Source>Tail;
 
    //by 1 WideChar
    NInt(Tail):=NInt(Tail) + 4;                  //Tail = terminator
    if Source<Tail then while true do begin;
      Ch:=word(Source[0]);
      inc(Source);
      if Ch<=127 then begin;
        Dest[0]:=AnsiChar(Ch);
        inc(Dest);
        if Source<Tail then continue else break;
        end;
 
      if Ch<=$7FF then begin;
        Dest[0]:=AnsiChar(Ch shr 6   or $C0);
        Dest[1]:=AnsiChar(Ch and $3F or $80);
        inc(Dest, 2);
        if Source<Tail then continue else break;
        end
      else begin;
        Dest[0]:=AnsiChar(Ch shr 12        or $E0);
        Dest[1]:=AnsiChar(Ch shr 6 and $3F or $80);
        Dest[2]:=AnsiChar(Ch       and $3F or $80);
        inc(Dest, 3);
        if Source<Tail then continue else break;
        end;
      end;
    end;
  Result:=Dest;
  end;

function ShaAnsiToUTF8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: NInt;
                       pTable: PConvTable): PUTF8Char; overload;
var
  Ch: UInt;
  Tail: PAnsiChar;
begin;
  if NInt(SourceChars)>0 then begin;
 
    //by 4 AnsiChars
    Tail:=Source + SourceChars + (-4);
    if Source<=Tail then repeat;
      Ch:=pCardinal(Source)^;
      if Ch and $80808080<>0 then break;         //break if not ASCII dword
      pCardinal(Dest)^:=Ch;
      inc(Source, 4);
      inc(Dest, 4);
      until Source>Tail;
 
    //by 1 AnsiChar
    Tail:=Tail + 4;                              //Tail = terminator
    if Source<Tail then while true do begin;
      Ch:=byte(Source[0]);
      inc(Source);
      if Ch<=127 then begin;
        Dest[0]:=AnsiChar(Ch);
        inc(Dest);
        if Source<Tail then continue else break;
        end;
 
      Ch:=pTable.AnsiToWide[Ch];
      if Ch<=$7ff then begin;
        //Ch:=Ch and $003F shl 8 or Ch shr 6 or $80C0;
        //pWord(Dest)^:=Ch;
        Dest[0]:=AnsiChar(Ch shr 6   or $C0);
        Dest[1]:=AnsiChar(Ch and $3F or $80);
        inc(Dest, 2);
        if Source<Tail then continue else break;
        end
      else begin;
        Dest[0]:=AnsiChar(Ch shr 12        or $E0);
        Dest[1]:=AnsiChar(Ch shr 6 and $3F or $80);
        Dest[2]:=AnsiChar(Ch       and $3F or $80);
        inc(Dest, 3);
        if Source<Tail then continue else break;
        end;
      end;
    end;
  Result:=Dest;
  end;

UTF8ToUnicode и UTF8ToAnsi

Здесь применяется тот же трюк, что и в двух предыдущих функциях. Остальные нагромождения кода в ShaUTF8ToUnicode и ShaUTF8ToAnsi рождены в попытках оптимизировать их для обработки реального текста.

Любите ли вы таблицы? Из-за них скорость ShaUTF8ToAnsi меньше, чем ShaUTF8ToUnicode.

function ShaUTF8ToUnicode(Dest: PWideChar; Source: PUTF8Char; SourceBytes: NInt): PWideChar; overload;
var
  Tail: PUTF8Char;
  Ch: UInt;
begin;
  if (Source<>nil) and (NInt(SourceBytes)>0) then begin;
 
    //1-byte sequences by 4 AnsiChars
    Tail:=Source + SourceBytes + (-4);           //Tail = terminator-4
    if Source<=Tail then repeat;
      Ch:=pCardinal(Source)^;
      if (Ch + Ch) and Ch and $80808080<>0 then break; //break if multibyte
      pCardinal(Dest)^:=(Ch shl 8 or Ch and $FF) and $00FF00FF;
      Ch:=Ch shr 16;
      inc(Dest, 2);
      pCardinal(Dest)^:=(Ch shl 8 or Ch) and $00FF00FF;
      inc(Dest, 2);
      inc(Source, 4);
      until Source>Tail;
 
    if Source<Tail+4 then begin;                 //if Source < terminator
      inc(Tail, 2);                              //Tail = terminator-2
 
      Ch:=byte(Source[0]);
      inc(Source);
      if Ch<$C0 then repeat;
        Dest[0]:=WideChar(Ch);
        inc(Dest);
        Ch:=byte(Source[0]);
        inc(Source);
        until (Ch>=$C0) or (Source>=Tail);
 
      //multibyte sequences
      while true do begin;
        if Source>=Tail then break;              //Source<=Tail-1
 
        //2-byte sequences
        if Ch and $20=0 then begin;              //here Ch>=$C0
          Ch:=Ch shl 6 + byte(Source[0]) - $00003080; //Tail-1 max
          inc(Source, 2);                        //Source<=Tail+1
          Dest[0]:=WideChar(Ch);
          Ch:=byte(Source[-1]);                  //Tail max
          inc(Dest);
          if Ch>=$C0 then continue;
 
          //1-byte sequences after 2-byte sequences
          inc(Dest);
          inc(Source);                           //Source<=Tail+2
          while true do begin;
            Dest[-1]:=WideChar(Ch);
            Ch:=byte(Source[-1]);                //Tail+1 max
            if Ch>=$C0 then break;
            if Source>Tail then break;           //Source<=Tail
            Dest[0]:=WideChar(Ch);
            Ch:=byte(Source[0]);                 //Tail max
            inc(Dest, 2);
            inc(Source, 2);                      //Source<=Tail+2
            if Ch<$C0 then continue;
            dec(Dest);
            dec(Source);                         //Source<=Tail+1
            break;
            end;
          end
 
        //3-byte sequences
        else begin;
          repeat;                                //Source<=Tail-1
            Ch:=Ch shl 12 + byte(Source[0]) shl 6 + byte(Source[1]) - $000E2080; //Tail max
            inc(Source, 3);                      //Source<=Tail+2
            Dest[0]:=WideChar(Ch);
            Ch:=byte(Source[-1]);                //Tail+1 max
            inc(Dest);
            if Source>=Tail then break;
            until Ch<$E0;
          if Ch>=$C0 then continue;
 
          //1-byte sequences after 3-byte sequences
          while true do begin;
            if Source>Tail then break;           //Source<=Tail
            Dest[0]:=WideChar(Ch);
            Ch:=byte(Source[0]);                 //Tail max
            inc(Dest, 2);
            inc(Source, 2);                      //Source<=Tail+2
            if Ch<$C0 then begin;
              Dest[-1]:=WideChar(Ch);
              Ch:=byte(Source[-1]);              //Tail+1 max
              if Ch<$C0 then continue;
              end
            else begin;
              dec(Dest);
              dec(Source);                       //Source<=Tail+1
              end;
            break;
            end;
          end;
 
        end; //while
 
      //handle tail
      inc(Tail, 2);                              //Tail = terminator
      dec(Source);                               //Source[0] is the first unhandled byte
      while Source<Tail do begin;
        Ch:=byte(Source[0]);
        if Ch<$C0 then inc(Source)
        else if Ch and $20=0 then begin;         //here Ch>=$C0
          inc(Source, 2);
          if Source>Tail then break;
          Ch:=Ch shl 6 + byte(Source[-1]) - $00003080;
          end
        else begin;
          inc(Source, 3);
          if Source>Tail then break;
          Ch:=Ch shl 12 + byte(Source[-2]) shl 6 + byte(Source[-1]) - $000E2080;
          end;
        Dest[0]:=WideChar(Ch);
        inc(Dest);
        end;
 
      end;
    end;
  Result:=Dest;
  end;

function ShaUTF8ToAnsi(Dest: PAnsiChar; Source: PUTF8Char; SourceBytes: NInt;
                       pTable: PConvTable): PAnsiChar; overload;
var
  Tail: PUTF8Char;
  Ch: UInt;
begin;
  if (Source<>nil) and (NInt(SourceBytes)>0) then begin;
 
    //1-byte sequences by 4 AnsiChars
    Tail:=Source + SourceBytes + (-4);           //Tail = terminator-4
    if Source<=Tail then repeat;
      Ch:=pCardinal(Source)^;
      if (Ch + Ch) and Ch and $80808080<>0 then break; //break if multibyte
      pCardinal(Dest)^:=Ch;
      inc(Source, 4);
      inc(Dest, 4);
      until Source>Tail;
 
    if Source<Tail+4 then begin;                 //if Source < terminator
      inc(Tail, 2);                              //Tail = terminator-2
 
      Ch:=byte(Source[0]);
      inc(Source);
      if Ch<$C0 then repeat;
        Dest[0]:=AnsiChar(Ch);
        inc(Dest);
        Ch:=byte(Source[0]);
        inc(Source);
        until (Ch>=$C0) or (Source>=Tail);
 
      //multibyte sequences
      while true do begin;
        if Source>=Tail then break;              //Source<=Tail-1
 
        //2-byte sequences
        if Ch and $20=0 then begin;              //here Ch>=$C0
          Ch:=Ch shl 6 + byte(Source[0]) - $00003080; //Tail-1 max
          inc(Source, 2);                        //Source<=Tail+1
          Dest[0]:=AnsiChar(pTable.WideToAnsi[Ch and $0000FFFF]);
          Ch:=byte(Source[-1]);                  //Tail max
          inc(Dest);
          if Ch>=$C0 then continue;
 
          //1-byte sequences after 2-byte sequences
          inc(Dest);
          inc(Source);                           //Source<=Tail+2
          while true do begin;
            Dest[-1]:=AnsiChar(pTable.WideToAnsi[Ch]);
            Ch:=byte(Source[-1]);                //Tail+1 max
            if Ch>=$C0 then break;
            if Source>Tail then break;           //Source<=Tail
            Dest[0]:=AnsiChar(pTable.WideToAnsi[Ch]);
            Ch:=byte(Source[0]);                 //Tail max
            inc(Dest, 2);
            inc(Source, 2);                      //Source<=Tail+2
            if Ch<$C0 then continue;
            dec(Dest);
            dec(Source);                         //Source<=Tail+1
            break;
            end;
          end
 
        //3-byte sequences
        else begin;
          repeat;                                //Source<=Tail-1
            Ch:=Ch shl 12 + byte(Source[0]) shl 6 + byte(Source[1]) - $000E2080; //Tail max
            inc(Source, 3);                      //Source<=Tail+2
            Dest[0]:=AnsiChar(pTable.WideToAnsi[Ch and $0000FFFF]);
            Ch:=byte(Source[-1]);                //Tail+1 max
            inc(Dest);
            if Source>=Tail then break;
            until Ch<$E0;
          if Ch>=$C0 then continue;
 
          //1-byte sequences after 3-byte sequences
          while true do begin;
            if Source>Tail then break;           //Source<=Tail
            Dest[0]:=AnsiChar(pTable.WideToAnsi[Ch]);
            Ch:=byte(Source[0]);                 //Tail max
            inc(Dest, 2);
            inc(Source, 2);                      //Source<=Tail+2
            if Ch<$C0 then begin;
              Dest[-1]:=AnsiChar(pTable.WideToAnsi[Ch]);
              Ch:=byte(Source[-1]);              //Tail+1 max
              if Ch<$C0 then continue;
              end
            else begin;
              dec(Dest);
              dec(Source);                       //Source<=Tail+1
              end;
            break;
            end;
          end;
 
        end; //while
 
      //handle tail
      inc(Tail, 2);                              //Tail = terminator
      dec(Source);                               //Source[0] is the first unhandled byte
      while Source<Tail do begin;
        Ch:=byte(Source[0]);
        if Ch<$C0 then begin;
          inc(Source);
          end
        else if Ch and $20=0 then begin;         //here Ch>=$C0
          inc(Source, 2);
          if Source>Tail then break;
          Ch:=Ch shl 6 + byte(Source[-1]) - $00003080;
          end
        else begin;
          inc(Source, 3);
          if Source>Tail then break;
          Ch:=Ch shl 12 + byte(Source[-2]) shl 6 + byte(Source[-1]) - $000E2080;
          end;
        Dest[0]:=AnsiChar(pTable.WideToAnsi[Ch and $0000FFFF]);
        inc(Dest);
        end;
 
      end;
    end;
  Result:=Dest;
  end;

Прикрепленный файл ShaUnicode.pas (версия от 05.02.2012) содержит весь этот новогодний бред в одном флаконе, пока это бета для Delphi 7. Вроде, ничего не забыл. Ах, да: ни один дятел не пострадал при тестировании функций.

на главную

Прикрепленный файлРазмер
ShaUnicode.pas29 кб

Ответить

  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступны HTML теги: <h1> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Строки и параграфы переносятся автоматически.
  • You can enable syntax highlighting of source code with the following tags: <pre>, <code>, <asm>, <c>, <cpp>, <delphi>, <drupal5>, <drupal6>, <java>, <javascript>, <php>, <python>, <ruby>, <mytext>. Beside the tag style "<foo>" it is also possible to use "[foo]".

Подробнее о форматировании

CAPTCHA
Ведите текст с изображения. (вводить еще раз после предпросмотра а то не добавится комментарий)
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.