Лишь годные дятлы собираются в стаи,
Юникодом пугая мозги января.
Их песни не стихнут, они не устанут.
А елка как кактус беспокоит меня.
Как испортить кашу
Юникодные преобразования в 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.pas | 29 кб |
Comments (2)
русификация synopse
уважаемый а не подскажешь ли как сделать
1) чтобы при инкрементном поиске по гриду можно было искать русские символы
2) можно было искать по базе Иванов/иВанов/иванов без геммороя, а то счас делаю проще - ввод только заглавными буквами
3) возможно ли реализовать полнотекстовый поиск по базе русских слов с помощью FTS
Буду признателен за любую помощь
русификация synopse
Вряд ли чем смогу помочь, т.к. пока только изучаю возможности Synopse и еще не продвинулся дальше запуска TestSQL3.exe.
Попробуй задать эти вопросы на родном форуме Synopse http://synopse.info/forum/viewforum.php?id=2