Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2004.10.24;
Скачать: [xml.tar.bz2];

Вниз

Поиск в бинарном файле   Найти похожие ветки 

 
Sha ©   (2004-09-30 09:24) [120]

> Defunct

Попробуй написать аналог Борландовской Pos на базе своей функции и померяйся с вот этим:

//(c) John O"Harrow
function PosJOH_MMX(const SubStr : AnsiString; const Str : AnsiString) : Integer;
asm
 test      eax, eax
 jz        @NotFoundExit    {Exit if SurStr = ""}
 test      edx, edx
 jz        @NotFound        {Exit if Str = ""}
 mov       ecx, [edx-4]     {Length(Str)}
 cmp       [eax-4], 1       {Length SubStr = 1?}
 je        @SingleChar      {Yes - Exit via CharPos}
 jl        @NotFound        {Exit if Length(SubStr) < 1}
 sub       ecx, [eax-4]     {Subtract Length(SubStr), -ve handled by
CharPos}
 add       ecx, 1           {Number of Chars to Check for 1st Char}
 push      esi              {Save Registers}
 push      edi
 push      ebx
 push      ebp
 mov       esi, eax         {Start Address of SubStr}
 mov       edi, ecx         {Initial Remainder Count}
 mov       eax, [eax]       {AL = 1st Char of SubStr}
 mov       ebp, edx         {Start Address of Str}
 mov       ebx, eax         {Maintain 1st Search Char in BL}
@StrLoop:
 mov       eax, ebx         {AL  = 1st char of SubStr}
 mov       ecx, edi         {Remaining Length}
 push      edx              {Save Start Position}
 call      @CharPos         {Search for 1st Character}
 pop       edx              {Restore Start Position}
 test      eax, eax         {Result = 0?}
 jz        @StrExit         {Exit if 1st Character Not Found}
 mov       ecx, [esi-4]     {Length SubStr}
 add       edx, eax         {Update Start Position for Next Loop}
 sub       edi, eax         {Update Remaining Length for Next Loop}
 sub       ecx, 1           {Remaining Characters to Compare}
@StrCheck:
 mov       al, [edx+ecx-1]  {Compare Next Char of SubStr and Str}
 cmp       al, [esi+ecx]
 jne       @StrLoop         {Different - Return to First Character Search}
 sub       ecx, 1
 jnz       @StrCheck        {Check each Remaining Character}
 mov       eax, edx         {All Characters Matched - Calculate Result}
 sub       eax, ebp
@StrExit:
 pop       ebp              {Restore Registers}
 pop       ebx
 pop       edi
 pop       esi
 ret
@NotFound:
 xor       eax, eax         {Return 0}
@NotFoundExit:
 ret
@SingleChar:
 mov       al, [eax]        {Search Character}
@CharPos:
 CMP       ECX, 8
 JG        @@NotSmall
@@Small:
 or        ecx, ecx
 jle       @@NotFound       {Exit if Length <= 0}
 CMP       AL, [EDX]
 JZ        @Found1
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+1]
 JZ        @Found2
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+2]
 JZ        @Found3
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+3]
 JZ        @Found4
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+4]
 JZ        @Found5
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+5]
 JZ        @Found6
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+6]
 JZ        @Found7
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+7]
 JZ        @Found8
@@NotFound:
 XOR       EAX, EAX
 RET
@Found1:
 MOV       EAX, 1
 RET
@Found2:
 MOV       EAX, 2
 RET
@Found3:
 MOV       EAX, 3
 RET
@Found4:
 MOV       EAX, 4
 RET
@Found5:
 MOV       EAX, 5
 RET
@Found6:
 MOV       EAX, 6
 RET
@Found7:
 MOV       EAX, 7
 RET
@Found8:
 MOV       EAX, 8
 RET

@@NotSmall:                  {Length(Str) > 8}
 MOV       AH, AL
 ADD       EDX, ECX
 MOVD      MM0, EAX
 PUNPCKLWD MM0, MM0
 PUNPCKLDQ MM0, MM0
 PUSH      ECX              {Save Length}
 NEG       ECX
@@First8:
 MOVQ      MM1, [EDX+ECX]
 ADD       ECX, 8
 PCMPEQB   MM1, MM0         {Compare All 8 Bytes}
 PACKSSWB  MM1, MM1         {Pack Result into 4 Bytes}
 MOVD      EAX, MM1
 TEST      EAX, EAX
 JNZ       @@Matched        {Exit on Match at any Position}
 CMP       ECX, -8          {Check if Next Loop would pass String End}
 JGE       @@Last8
@@Align:                     {Align to Previous 8 Byte Boundary}
 LEA       EAX, [EDX+ECX]
 AND       EAX, 7           {EAX -> 0 or 4}
 SUB       ECX, EAX
@@Loop:
 MOVQ      MM1, [EDX+ECX]
 ADD       ECX, 8
 PCMPEQB   MM1, MM0         {Compare All 8 Bytes}
 PACKSSWB  MM1, MM1         {Pack Result into 4 Bytes}
 MOVD      EAX, MM1
 TEST      EAX, EAX
 JNZ       @@Matched        {Exit on Match at any Position}
 CMP       ECX, -8          {Check if Next Loop would pass String End}
{$IFNDEF NoUnroll}
 JGE       @@Last8
 MOVQ      MM1, [EDX+ECX]
 ADD       ECX, 8
 PCMPEQB   MM1, MM0         {Compare All 8 Bytes}
 PACKSSWB  MM1, MM1         {Pack Result into 4 Bytes}
 MOVD      EAX, MM1
 TEST      EAX, EAX
 JNZ       @@Matched        {Exit on Match at any Position}
 CMP       ECX, -8          {Check if Next Loop would pass String End}
{$ENDIF}
 JL        @@Loop
@@Last8:
 MOVQ      MM1, [EDX-8]     {Position for Last 8 Used Characters}
 POP       EDX              {Original Length}
 PCMPEQB   MM1, MM0         {Compare All 8 Bytes}
 PACKSSWB  MM1, MM1         {Pack Result into 4 Bytes}
 MOVD      EAX, MM1
 TEST      EAX, EAX
 JNZ       @@Matched2       {Exit on Match at any Position}
 EMMS
 RET                        {Finished - Not Found}
@@Matched:                   {Set Result from 1st Match in EDX}
 POP       EDX              {Original Length}
 ADD       EDX, ECX
@@Matched2:
 EMMS
 SUB       EDX, 8           {Adjust for Extra ADD ECX,8 in Loop}
 TEST      AL, AL
 JNZ       @@MatchDone      {Match at Position 1 or 2}
 TEST      AH, AH
 JNZ       @@Match1         {Match at Position 3 or 4}
 SHR       EAX, 16
 TEST      AL, AL
 JNZ       @@Match2         {Match at Position 5 or 6}
 SHR       EAX, 8
 ADD       EDX, 6
 JMP       @@MatchDone
@@Match2:
 ADD       EDX, 4
 JMP       @@MatchDone
@@Match1:
 SHR       EAX, 8           {AL <- AH}
 ADD       EDX, 2
@@MatchDone:
 XOR       EAX, 2
 AND       EAX, 3           {EAX <- 1 or 2}
 ADD       EAX, EDX

end;


 
Игорь Шевченко ©   (2004-09-30 10:18) [121]

Defunct ©   (29.09.04 18:14) [117]

В процитированном


 
Romkin ©   (2004-09-30 10:31) [122]

Sha ©  (30.09.04 09:24) [120] Бррр...
Может, так достаточно? http://delphibase.endimus.ru/?action=viewfunc&topic=strsearch&id=10271
Интересно, и насколько отстает?


 
Sha ©   (2004-09-30 16:44) [123]

Romkin ©   (30.09.04 10:31) [122]

Вот и мне интересно, насколько Defunct отстанет.
Думаю, в среднем по разным длинам, отстанет прилично.

А в 2 слишним раза перекрыть RTL можно и на Паскале:

function PosShaPas(const SubStr: AnsiString; const Str: AnsiString): Integer;
var
 len, lenSub: integer;
 ch: char;
 p, pSub, pStart, pEnd: pchar;
label
 Ret, Ret0, Ret1, Next0, Next1;
begin;
 p:=pointer(Str);
 pSub:=pointer(SubStr);

 //if you need pure Pascal uncomment this paragraph
 //and comment out the next 3 paragraphs
{
 len:=length(Str);
 lenSub:=length(SubStr);
 pEnd:=p+len;
 pStart:=p;
 pEnd:=pEnd-lenSub;
 if (len<=0) or (lenSub<=0) or (p>pEnd) then begin;
   Result:=0;
   exit;
   end;
}

 if (p=nil) or (pSub=nil) then begin;
   Result:=0;
   exit;
   end;

 len:=pinteger(p-4)^;
 lenSub:=pinteger(pSub-4)^;
 if (len<lenSub) or (lenSub<=0) then begin;
   Result:=0;
   exit;
   end;

 pEnd:=p+len;
 pStart:=p;
 pEnd:=pEnd-lenSub;

 ch:=pSub[0];

 if lenSub=1 then begin;
   repeat;
     if ch=p[0] then goto Ret0;
     if ch=p[1] then goto Ret1;
     p:=p+2;
     until p>pEnd;
   Result:=0;
   exit;
   end;

 repeat;
   if ch=p[0] then begin;
     len:=lensub;
     repeat;
       if psub[len-1]<>p[len-1] then goto Next0;
       if psub[len-2]<>p[len-2] then goto Next0;
       len:=len-2;
       until len<2;
     goto Ret0;
Next0:end;

   if ch=p[1] then begin;
     len:=lensub;
     repeat;
       if psub[len-1]<>p[len] then goto Next1;
       if psub[len-2]<>p[len-1] then goto Next1;
       len:=len-2;
       until len<2;
     goto Ret1;
Next1:end;

   p:=p+2;
   until p>pEnd;
 Result:=0;
 exit;

Ret1:
 inc(pEnd);
 p:=p+2;
 if p<=pEnd then goto Ret;
 Result:=0;
 exit;
Ret0:
 inc(p);
Ret:
 Result:=p-pStart;
 end;


 
Defunct ©   (2004-09-30 21:05) [124]

Sha ©   (30.09.04 09:24) [120]

Пока не не адаптировал фукнкцию к Ansi String, но к приведенной вами функции - Respect.

строка SubString = "Test Stringx" - 12 симоволов.
5.978/ 4.153 / 3.900 ([120]/[123]/[112]).

строка SubString = "Test String" - 11 симоволов.
6.278/ 4.753 / 4.085 ([120]/[123]/[112]).

Но это и понятно, там рассмотрены почти все частные случаи.
Попробую и своего зверька [112] привести к нормальному виду.

To Sha: За счет чего достигается такое быстродействие в [123]?


 
GuAV ©   (2004-09-30 21:20) [125]

У меня быстрее [120], [123] немного медленнее.
на коротком файле (autoecex.bat) быстрее всего [112].

Celeron 2000, win9x.

не ММХ - ф-ция ваще в пролёте.


 
Defunct ©   (2004-09-30 21:37) [126]

Defunct ©   (30.09.04 21:05) [124]
опечатка:

строка SubString = "Test Stringx" - 12 симоволов.
5.978/ 4.153 / 3.900 ([112]/[120]/[123]).

строка SubString = "Test String" - 11 симоволов.
6.278/ 4.753 / 4.085 ([112]/[120]/[123]).


 
Sha ©   (2004-09-30 22:22) [127]

> Defunct ©   (30.09.04 21:05) [124]
> За счет чего достигается такое быстродействие в [123]?

За счет алгоритма, основанного на использовании внутреннего представления AnsiString, двухкратного развертывания циклов поиска первого совпадения и сравнения последующих символов, а также маленькой хитрости - изменения направления сравнения.

Так что дело тут, как видишь, совсем не в выборе языка :)


 
Defunct ©   (2004-09-30 22:24) [128]

С вот таким вот изменением, скорость [112] сравнялась с [120]

@@Scan:
{$ifdef MMX}
  MovD      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
//   PCMPGTD   MM7, MM5    // make a dword mask
//   PUNPCKHWD MM7, MM7    // mix 2 dwords into a qword
  MovD      EAx, MM7    // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,4           // correct index
  Sub   ECx,4           // and buffer size
  Jns   @@Scan          // continue scan if buffer not empty
  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)
{$endif}
  Repne ScasB
{$ifndef MMX}
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
{$endif}


Я даже не думал, что MovQ работает настолько медленней чем 2 MovD
[123] действительно получается в пролете.

строка SubString = "Test String" - 11 симоволов.
4.078/4.085/4.753  ([112 с указанным изменением]/[120]/[123])

Просмотренный буфер - 2.5Gb (читался порциями по 1Mb, файл 25Mb, 100 раз подряд).


 
Defunct ©   (2004-09-30 22:26) [129]

> а также маленькой хитрости - изменения направления сравнения.

О, стоит применить к [112] ;)


 
Sha ©   (2004-09-30 22:27) [130]

> Defunct ©   (30.09.04 22:24) [128]

Дай полный код своего аналога Pos. Я его завтра потестирую.


 
GuAV ©   (2004-09-30 22:40) [131]


> Пока не не адаптировал фукнкцию к Ansi String

для своих тестов я так сделал:

Function DefunctMMX(const Constant: string;
const Buffer: string): Integer; Assembler;
Asm
 Push EDi
 Push ESi
 Push EBx
 Push EBp
// a fix to make this fcn Pos compatible
 MOV  ECX, [EDX-4] // should not affect perfomance much
 Push ECx            // store total buffer size for later calculation pos


 
Defunct ©   (2004-09-30 23:22) [132]

> Дай полный код своего аналога Pos. Я его завтра потестирую.

unit Search;

interface
{$define MMX}

Function ConstantPos(const Constant, Buffer: string): Integer;

implementation

Function ConstantPos(const Constant, Buffer: string): Integer; Assembler;
Asm
  Push EDi
  Push ESi
  Push EBx
  Push EBp
  MOV  ESI, EAX       // ESi pointer to constant
  Mov  EDi, EDx       // EDi pointer to buffer
  Mov  ECx, [EDi-4]
  Push ECx            // store total buffer size for later calculation pos
  Mov  EBX, [ESI-4]
  Dec  EBX
  Mov  Al, [ESi]      // Store first char of the constant in AL
{$ifdef MMX}
  Mov  Ah, Al         // Spreading char to 32bit register
  Push Ax
  Push Ax
  Pop  EAx
  MovD MM6, EAx
  PSLLQ MM6,MM6       // Spreading char to mmx register
  MovD MM6, EAx

  PXOR  MM5,MM5       // Z mask for later calculations
{$endif}
  Mov  Ah, [ESi+EBx]  // Store last char of the constant in AH
  Dec  EBx            // EBx - length of the constant w/o 1st and last chars
  Mov  EDx,ECx        // EDx - Stored residual buffer size
  Inc  ESi
  Mov  EBp, ESi       // EBp - stored pointer to 2nd char of constant

{$ifdef MMX}
  MovD MM4, EAx       // store EAx in MM buffer
{$endif}

@@Scan:
{$ifdef MMX}
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PCMPEQD   MM7, MM5    // make a dword mask
  PCMPEQD   MM7, MM5    // invert dword mask
  PSLLQ     MM7,16      // mix 2 dwords into a qword
  MovD      EAx, MM7    // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,8           // correct index
  Sub   ECx,8           // and buffer size
  Jns   @@Scan          // continue scan if buffer not empty
  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)
{$endif}
  Repne ScasB
{$ifndef MMX}
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
{$endif}
  Mov   EDx, ECx      // store new residual buffer size
  Cmp   EDx, EBx      // constant still can be settled in buffer
  Ja    @@CheckLastChar  // no - exit with false result

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx        // False = (0), check delphi help for more info
  Dec  EAx
  Jmp  @@Leave_Proc

@@CheckLastChar:      // as proposed in Boyer-Mur alhorithm

  Cmp   Ah, [EDi+EBx] // check found substring with last char of the constant
  Jnz   @@Scan        // not match - continue searching

@@CompareWholeString:

  Mov  ECx, Ebx       // ECx - constant chars amount
  shr  ECx, 2         // ECx - constant chars amount div 4
  jz   @@SkipDTest
  Repe CmpsD
  Jnz  @@SkipBTest
@@SkipDTest:
  Mov  ECx, Ebx
  and  ECx, 3
  jz   @@Constant_Exists // two least significant bits
                      // of amount are zero so the Constant matches
  Repe CmpsB
  Jz   @@Constant_Exists
@@SkipBTest:

  Mov  ESi, EBp       // restore pointer to 2nd char of constant
  Add  EDx, ECx
  Sub  EDx, EBx       // correct residual buffer size
  Mov  ECx, EDx       // restore residual buffer size to ECx

  Jnz  @@Scan

@@Constant_Exists:
  Pop  EAx
  Sub  EAx, EDx

@@Leave_Proc:
{$ifdef MMX}
  EMMS                // finish mmx operations
{$endif}

  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;

end.


 
Defunct ©   (2004-09-30 23:46) [133]

Вот более достоверные результаты (просмотр 25Gb):

[132] - 42.230 сек
[120] - 38.800 сек
[123] - 46.831 сек

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

2 Sha, если вашу функцию сделать в MMX исполнении, она поидее побъет всех ;) т.к. [132] без MMX - 65.453


 
Sha ©   (2004-09-30 23:53) [134]

> Defunct

1. Функция не является полным аналогом Pos.
Не будет работать для пустых строк, т.к. за длиной строки полезет по адресу (0-4)=FFFFFFFC.

2. Не корректно замерять скорость Pos на файлах или на буфере длиной 2Gb. Погрешности за счет дисковых очень велики и не предсказуемы.


 
GuAV ©   (2004-09-30 23:58) [135]

Sha ©   (30.09.04 23:53) [134]
Какие размеры для замера предлагаете?
имхо 0, 128 байт, 1 кБ и 1 МБ.


 
Sha ©   (2004-10-01 00:12) [136]

> Defunct

3. Оригинальная Pos из RTL в случае неуспеха возвращает 0, твоя -1:
@@Exit_False:
 Pop  EAx
 Xor  EAx,EAx        // False = (0), check delphi help for more info
 Dec  EAx
 Jmp  @@Leave_Proc


4. Оригинальная Pos никогда не тестирует символы, идущие за терминатором. Твоя тестирует, что теоретически может вызвать AV.


 
Sha ©   (2004-10-01 00:16) [137]

> GuAV ©   (30.09.04 23:58) [135]
> Какие размеры для замера предлагаете?

Предлагаю использовать неплохой (хотя и не без нереканий) вариант
http://dennishomepage.gugs-cats.dk/PosBV45.zip


 
Sha ©   (2004-10-01 00:38) [138]

> Defunct

5. Похоже, твоя функция иногда будет выдавать неверный результат (следствие пункта 4):
ConstantPos(#0,"Мы находим даже терминатор")>0


 
Defunct ©   (2004-10-01 02:25) [139]

Ок, со всем здесь услышанным, подправил [132], так, что возвращается "0" если строка не найдена, подправлена работа со строками нулевой длинны, и плюс сделал выравнивание адреса как в [120] (спасибо за пример [120]).
Ну теперь получается по скорости, на некоторых строках выигрывает новая функция, на некоторых - [120]. По времени, приблизительно получилось следующее:

на разных строках функция [120] (1.25Gb пересмотр).
результат от 19.069 сек до 19.984.

новая функция тестировалась в тех же условиях (1.25Gb пересмотр)
результат от 18.768 сек до 20.364.

Буфер поиска - 1Mb.

Следует отметить неоспоримое преимущество приведенной здесь функции, она в отличие от [120] способна работать на процессорах у которых нет MMX.

unit Search;

interface
{$define MMX}

Function ConstantPos(const Constant, Buffer: string): Integer;

implementation

Function ConstantPos(const Constant, Buffer: string): Integer; Assembler;
Asm
  Push EDi
  Push ESi
  Push EBx
  Push EBp
  MOV  ESI, EAX       // ESi pointer to constant
  Mov  EDi, EDx       // EDi pointer to buffer
  Mov  ECx, [EDi-4]
  Push ECx            // store total buffer size for later calculation pos
  Test ECx,ECx
  Jz   @@Exit_False
  Mov  EBX, [ESI-4]
  Test EBx,EBx
  Jz   @@Exit_False
  Dec  EBX
  Mov  Al, [ESi]      // Store first char of the constant in AL
{$ifdef MMX}
  Mov  Ah, Al         // Spreading char to 32bit register
  MovD MM6, EAx
  PUNPCKLWD MM6, MM6
  PUNPCKLDQ MM6, MM6
  PXOR  MM5,MM5       // Z mask for later calculations
  Dec  EBx            // EBx - length of the constant w/o 1st and last chars
  Mov  Ax,[ESi+EBx]
  Dec  EBx
{$endif}

{$ifndef MMX}
  Mov  Ah, [ESi+EBx]  // Store last char of the constant in AH
{$endif}
  Mov  EDx,ECx        // EDx - Stored residual buffer size
  Inc  ESi
  Mov  EBp, ESi       // EBp - stored pointer to 2nd char of constant

{$ifdef MMX}
  MovD MM4, EAx       // store EAx in MM buffer
{$endif}

@@Scan:
{$ifdef MMX}

  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PACKSSWB  MM7, MM7
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi, 8
  Mov   EAx, Edi
  And   EAx, 7
  Sub   EDi,EAx         // Aligning index
  Sub   ECx,8
  Add   ECx,EAx         // and correcting buffer size
  Js    @@Exit_False

@@Scan2:
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PACKSSWB  MM7,MM7
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,8           // correct index
  Sub   ECx,8           // and buffer size

  Jns   @@Scan2         // continue scan if buffer not empty
  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  MovD      EAx, MM7
  Test      EAx, EAx
  Jnz       @@Loop1
  PSRLQ     MM7,32
  MovD      EAx, MM7
@@Loop1:
  inc   EDi
  dec   Ecx
  Shr   EAx,8
  Jz    @@ContinueHere
  jmp   @@Loop1

{$endif}
  Repne ScasB
{$ifndef MMX}
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
{$else}
@@ContinueHere:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)
{$endif}

@@CheckLastChar:      // as proposed in Boyer-Mur alhorithm

{$ifndef MMX}
  Cmp   Ah, [EBx+Edi] // check found substring with last char of the constant
{$else}
  Cmp   Ax, [EBx+EDi]
{$endif}
  Jz    @@CompareWholeString  // not match - continue searching
  Jmp   @@Scan

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx        // 0 if constant was not found
  Jmp  @@Leave_Proc

@@CompareWholeString:
  Mov   EDx, ECx      // store new residual buffer size
  Cmp   EDx, EBx      // constant still can be settled in buffer
  Jna   @@Exit_False   // no - exit with false result

  Mov  ECx, Ebx       // ECx - constant chars amount
  shr  ECx, 2         // ECx - constant chars amount div 4
  jz   @@SkipDTest
  Repe CmpsD
  Jnz  @@SkipBTest
@@SkipDTest:
  Mov  ECx, Ebx
  and  ECx, 3
  jz   @@Constant_Exists // two least significant bits
                      // of amount are zero so the Constant matches
  Repe CmpsB
  Jz   @@Constant_Exists
@@SkipBTest:

  Mov  ESi, EBp       // restore pointer to 2nd char of constant
  Add  EDx, ECx
  Sub  EDx, EBx       // correct residual buffer size
  Mov  ECx, EDx       // restore residual buffer size to ECx

  Jnz  @@Scan

@@Constant_Exists:
  Pop  EAx
  Sub  EAx, EDx

@@Leave_Proc:
{$ifdef MMX}
  EMMS                // finish mmx operations
{$endif}

  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;

end.


На будущее думаю попробовать сделать поиск через SSE/SSE2

> 4. Оригинальная Pos никогда не тестирует символы, идущие за терминатором. Твоя тестирует, что теоретически может вызвать AV.

ни старая, ни исправленная функции не выходили за границу буфера.


 
Defunct ©   (2004-10-01 02:28) [140]

> [139] (1.25Gb пересмотр).

не там точку поставил.

12.5Gb пересмотр.


 
Defunct ©   (2004-10-01 03:14) [141]

посмотрел тест [137] недостаток теста в том, что он был нацелен именно, на поиск подстроки в строке. Функция же [139] больше нацелена на поиск подстроки в файле, т.е. предполагается просмотр не одного буфера а немкольких. Поэтому по одному тесту скорости у нее высокая оценка (SubBench2), а по второму (SubBench1) - низкая, суммарная оценка получалась лучше чем RTL/RTLa/b но значительно хуже чем у PosJoh и PosSha, сейчас внесу поправки с учетом изменения задачи.


 
Defunct ©   (2004-10-01 06:17) [142]

> Sha

Провозился всю ночь с этой функцией, выкладывать пока не буду так как еще не доделал. Есть вопрос, что в вашем тесте означает SubBench1 и SubBench2?

В изначальном варианте [139] было:
SubBench1 = 261
SubBench2 = 159

Сейчас почемуто получается:
SubBench1 = 295
SubBench2 = 121

Суммарный Bench индекс практически не изменился все на том же уровне 410-420.

В функциях PosJoh_MMX и PosSha наблюдаю примерно такую картину:
SubBench1 = 177, 180
SubBench2 = 147, 152

SubBench1 относится к маленьким строкам я правильно понимаю?


 
Sha ©   (2004-10-01 09:44) [143]

> Defunct

> Буфер поиска - 1Mb.
Многовато будет :) Наиболее частое применение Pos - поиск первого вхождения в строке до 100 символов. В случае  успеха в среднем результат поиска не превышает 40. Именно для этих условий и должна проводиться оптимизация. Для других условий пишутся другие функции, которые сравнивать с Pos будет неверно.

> Следует отметить неоспоримое преимущество приведенной здесь
> функции, она в отличие от [120] способна работать на
> процессорах у которых нет MMX.
По приведенной мной ссылке ты можешь найти реализации Pos на любой вкус.

> ни старая, ни исправленная функции не выходили за границу буфера.
Извини, значит, я был недостаточно внимателен. Тогда пункты 4 и 5 снимаются.

> посмотрел тест [137] недостаток теста в том, что он был
> нацелен именно, на поиск подстроки в строке. Функция же [139]
> больше нацелена на поиск подстроки в файле, т.е.
> предполагается просмотр не одного буфера а немкольких
Вот это я совсем не понял.

> SubBench1 относится к маленьким строкам я правильно понимаю?
Поиск малых строк среди малых строк (первые 35 тестов).


 
Defunct ©   (2004-10-01 10:27) [144]

> посмотрел тест [137] недостаток теста в том, что он был
> нацелен именно, на поиск подстроки в строке. Функция же [139]
> больше нацелена на поиск подстроки в файле, т.е.
> предполагается просмотр не одного буфера а немкольких

Имел в виду, что тест не полностью отражает возможности функций. Не учитывается скорость поиска в больших см. сабж до 1Gb строках.

> Поиск малых строк среди малых строк (первые 35 тестов).
Я так и думал. Крутил и так и сяк, на малых строках существенно проигрываю, PosJoh и PosSha. (в PosJoh заметил ведется отдельная обработка маленьких строк)

Зато на больших строках получилось обогнать все функции:
просматриваемый буфер делится на 2 кадра, в каждом кадре просмотр ведется в двух направлениях.

> Для других условий пишутся другие функции, которые сравнивать с Pos будет неверно.

согласен, но так или иначе, даже с оптимизацией совсем под другую задачу (сабж) результат сравнения с Pos тоже не плох, индекс 416 (как я понял в 3 раза быстрее стандартной Pos) по вашему тесту дают не многие функции.


 
Sha ©   (2004-10-01 10:41) [145]

> Defunct ©   (01.10.04 10:27) [144]
> Зато на больших строках получилось обогнать все функции

Наверное, точнее так: на больших строках с образом в конце.
Не забывай, что просто для таких строк Джон еще не написал свою функцию :)

PS. Твой результат действительно очень хорош.
Не желаешь принять участие в FastCode?


 
Defunct ©   (2004-10-01 11:44) [146]

> Не желаешь принять участие в FastCode?

на тему?


 
Sha ©   (2004-10-01 11:52) [147]

Тем там много...
Можно заново открыть PosChallenge, если ты усовершенствуешь свою функцию.
Кстати, было бы неплохо выложить здесь окончательный вариант.


 
Defunct ©   (2004-10-01 12:01) [148]

> Тем там много...
это хорошо

> Кстати, было бы неплохо выложить здесь окончательный вариант.
Не вопрос, как только закончу, и тест покажет что функция прошла Validate тест сразу выложу.


 
GuAV ©   (2004-10-01 20:32) [149]


> функция прошла Validate

вот, например
const S1="AV"; S2="Guru AV";

procedure TForm1.Button1Click(Sender: TObject);
begin
 Caption := Format("Sha = %d, Pos = %d, defunct = %d",
     [PosShaPas(S1, S2), Pos(S1, S2), ConstantPos(S1, S2)]);
// Sha = 6, Pos = 6, defunct = 0
end;


 
Alekc   (2004-10-01 20:38) [150]

Кто-то спрашивал почему у борланда такие неоптимальные функции: если тут столько народу одну Pos так долго оптимизирует, то сколько б времени у Borland"ы ушло на всё RTL/VCL ? 10 лет ? :о)


 
Defunct ©   (2004-10-02 01:50) [151]

> Sha

Я не понимаю, что я делаю не так, для маленьких строк никак не могу улучшить показания теста. Привожу функцию, которая прошла все 11 Validate тестов. Гляньте, может заметите, что там не так при маленьких строках. На больших строках эта функция побила все, в т.ч. и PosJohMMX_a.

Function ConstantPos(const Constant, Buffer: string): Integer; Assembler;
Asm
  Test eax, eax
  Jz   @@NotFound_Exit    {Exit if SubStr = ""}
  Test edx, edx
  Jz   @@NotFound_Exit    {Exit if Str = ""}
  Mov  ECx, [EDx-4]
  Test ECx, ECx
  Jz   @@NotFound_Exit
  Cmp  [EAx-4],1
  Jnz   @@WideSearch

  Cmp   ECx,8
  Ja    @@WideScan

// CharPos
@@Scan4:
  Mov  Al,[EAx]
  Push EDi
  Add  EDx, ECx
  Xchg EDi, EDx
  Mov  EDx, ECx
  Not  ECx
  Inc  ECx

@@SLoop4:
  Cmp  AL, [EDi+ECx]
  Jz   @@Found4
  Inc  ECx
  Jnz  @@SLoop4
  Xor  EAx, EAx
  Pop  EDi
  Ret

@@Found4:
  Mov  EAx, EDx
  Add  EAx, ECx
  Inc  EAx
  Pop  EDi
  Ret

@@WideScan:
  Mov  Al,[EAx]
  Mov  Ah, Al         // Spreading char to 32bit register
  MovD MM6, EAx
  PUNPCKLWD MM6, MM6
  PUNPCKLDQ MM6, MM6

  Push ECx
@@Scan5:
  MovQ      MM7, [EDx]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PACKSSWB  MM7, MM7
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound2    // yes - 1st char of the constant was found
  Add   EDx,8           // correct index
  Sub   ECx,8           // and buffer size

  Jns   @@Scan5         // continue scan if buffer not empty

@@Exit_False2:
  Emms
  Pop   EAx
  Xor   EAx, EAx
  Ret

@@CharFound2:
  MovQ      MM7, [EDx]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@Loop2
  Sub   ECx,4
  PSRLQ MM7,32
  MovD  EAx, MM7
@@Loop2:
  Dec   Ecx
  Test  Al,Al
  Js    @@Check2
  Shr   EAx,8
  Jmp   @@Loop2

@@Check2:
  Test  ECx,ECx
  Js    @@Exit_False2
  Emms
  Pop   EAx
  Sub   EAx, ECx
  Ret

@@WideSearch:

  Push EDi
  Push ESi
  Push EBx
  Push EBp

  MOV  ESI, EAX       // ESi pointer to constant
  Mov  EDi, EDx       // EDi pointer to buffer
  Push ECx            // store total buffer size for later calculation pos
  Mov  EBX, [ESI-4]
  Test EBx, EBx
  Jz   @@Exit_False
  Mov  Al, [ESi]      // Store first char of the constant in AL
  Cmp  EBx, ECx
  Ja   @@Exit_False
  Jnz  @@Normal_Work
  Cmp  AL, [Edi]
  Jnz  @@Exit_False

@@Normal_Work:
  Dec  EBX

  Mov  Ah, [ESi+EBx]  // Store last char of the constant in AH
  Dec  EBx
  Mov  EDx,ECx        // EDx - Stored residual buffer size
  Inc  ESi
  Mov  EBp, ESi       // EBp - stored pointer to 2nd char of constant
  Cmp  ECx, 8
  Ja   @@Wide_String

@@Scan3:
  Xor  ECx, ECx
@@SLoop:
  Cmp  AL, [Edi+ECx]
  Jz   @@Found
  Inc  ECx
  Cmp  ECx, EDx
  Jb   @@SLoop
  Jmp  @@Exit_False

@@Found:
  Inc  ECx
  Add  EDi,ECx
  Sub  EDx, ECx
  Mov  ECx, EDx
  Cmp  EBx, EDx
  Jge  @@Exit_False
  Jmp  @@CheckLastChar

{}

@@Wide_String:
  MovD MM4, EAx       // store EAx in MM buffer
  Mov  Ah, Al         // Spreading char to 32bit register
  MovD MM6, EAx
  PUNPCKLWD MM6, MM6
  PUNPCKLDQ MM6, MM6
  PXOR  MM5,MM5       // Z mask for later calculations

@@Scan:
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PACKSSWB  MM7,MM7
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,8           // correct index
  Sub   ECx,8           // and buffer size

  Jns   @@Scan          // continue scan if buffer not empty

  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@Loop1
  Add   EDi,4
  Sub   ECx,4
  PSRLQ MM7,32
  MovD  EAx, MM7
@@Loop1:
  inc   EDi
  dec   Ecx
  Test  Al,Al
  Js    @@Check
  Shr   EAx,8
  Jmp   @@Loop1

@@Check:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)

@@CheckLastChar:       // as proposed in Boyer-Mur alhorithm
  Mov   EDx, ECx      // store new residual buffer size
  Test  EDx, EDx
  Js    @@Exit_False
  Test  EBx, EBx
  Js    @@Constant_Exists

  Cmp   Ah, [EBx+Edi] // check found substring with last char of the constant
  Jz    @@CompareWholeString  // not match - continue searching
  Cmp   EDx,8
  Jnb   @@Scan
  Jmp   @@Scan3

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx        // False = (0), check delphi help for more info
  Jmp  @@Leave_Proc

@@NotFound_Exit:
  Xor  EAx, EAx
  Ret

@@CompareWholeString:
  Test  EBx, EBx
  Jz    @@Constant_Exists
  Cmp   EDx, EBx      // constant still can be settled in buffer
  Jna   @@Exit_False   // no - exit with false result

  Push EDi
  Mov  ECx, Ebx       // ECx - constant chars amount
  shr  ECx, 2         // ECx - constant chars amount div 4
  jz   @@SkipDTest
  Repe CmpsD
  Jnz  @@SkipBTest
@@SkipDTest:
  Mov  ECx, Ebx
  and  ECx, 3
  jz   @@Constant_Exists2 // two least significant bits
                      // of amount are zero so the Constant matches
  Repe CmpsB
  Jz   @@Constant_Exists2

@@SkipBTest:

  Pop  EDi
  Mov  ESi, EBp       // restore pointer to 2nd char of constant
//   Add  EDx, ECx
//   Sub  EDx, EBx       // correct residual buffer size
  Mov  ECx, EDx       // restore residual buffer size to ECx

  Cmp  EDx,8
  Jb   @@Scan3
  Jmp  @@Scan

@@Constant_Exists2:
  Add  ESp, 4

@@Constant_Exists:
  Pop  EAx
  Sub  EAx, EDx

@@Leave_Proc:
  EMMS                // finish mmx operations

  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;


 
Sha ©   (2004-10-02 11:18) [152]

>Defunct ©   (02.10.04 01:50) [151]

1. Главное, что отличает функцию-победитель от других - очень быстрая выдача результата в случае нахождения образца в первых 8 позициях после начала сканирования первого символа. Анализ этих позиций вынесен из цикла и проводится очень быстро. Это позволяет отложить подготовительные действия для организации цикла (и сам цикл) до тех пор пока они действительно не потребуются, или иногда вообще их не выполнять.
В результате, даже, если даже цикл и будет выполняться, то будет сэкономлено несколько команд перехода и насыщение цикла наступит позднее.
Также имей ввиду, что использование MMX для строк короче 20 сиволов неоправданно из-за больших расходов на подготовку.

2. В следующем цикле
@@SLoop4:
 Cmp  AL, [EDi+ECx]
 Jz   @@Found4
 Inc  ECx
 Jnz  @@SLoop4

имеет смысл "inc ecx" заменить на "add ecx,1". После такой замены состояние регистра флагов для второго сравнения будет полностью определяться предыдущей командой, а не формироваться как результат выполнения cmp и inc.
Еще лучше развернуть цикл в 2 или 4 раза.

3. Этот цикл тоже лучше развернуть.
@@SLoop:
 Cmp  AL, [Edi+ECx]
 Jz   @@Found
 Inc  ECx
 Cmp  ECx, EDx
 Jb   @@SLoop


 
GuAV ©   (2004-10-02 15:33) [153]

так имхо немного быстрее будет CompareWholeString -
поиск в обратном порядке.
@@CompareWholeString:
 Test  EBx, EBx
 Jz    @@Constant_Exists
 Cmp   EDx, EBx      // constant still can be settled in buffer
 Jna   @@Exit_False   // no - exit with false result

//  Push  EDi

 MOV   ECX, EBX
 @@CW_Loop:
 SUB   ECX, 4
 JZ    @@CW_LastDWord
 JL    @@CW_Last123bytes
 MOV   EAX, [ESi+ECx]
 CMP   EAX, [EDi+ECx]
 JNE   @@SkipBTest
 JMP   @@CW_Loop

 @@CW_LastDWord:
 MOV   EAX, [ESi]
 CMP   EAX, [EDi]
 JNE   @@SkipBTest
 JMP   @@Constant_Exists

 @@CW_Last123bytes:

 ADD   ECX, 2
 JZ    @@Last2
 JNS   @@Last3
 JS    @@Last1

 @@Last3:
 MOV   AX, [ESi]
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 MOV   AL, [ESi+2]
 CMP   AL, [EDi+2]
 JNE   @@SkipBTest
 JMP   @@Constant_Exists

 @@Last2:
 MOV   AX, [ESi]
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 JMP   @@Constant_Exists

 @@Last1:
 MOV   AX, [ESi]
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 JMP   @@Constant_Exists

@@SkipBTest:

// Pop  EDi
//  Mov  ESi, EBp       // restore pointer to 2nd char of constant
//   Add  EDx, ECx
//   Sub  EDx, EBx       // correct residual buffer size
 Mov  ECx, EDx       // restore residual buffer size to ECx

 Cmp  EDx,8
 Jb   @@Scan3
 Jmp  @@Scan

@@Constant_Exists:
 Pop  EAx
 Sub  EAx, EDx


 
GuAV ©   (2004-10-02 15:43) [154]


>  @@Last1:
>  MOV   AX, [ESi]
>  CMP   AX, [EDi]
>  JNE   @@SkipBTest
>  JMP   @@Constant_Exists

MOV   AL, [ESi] // Of course a byte-sized operand is meant
CMP   AL, [EDi] // That"s a kinda ctrl-C+ctrl-V eror :)


 
Sha ©   (2004-10-02 18:31) [155]

> GuAV

Сейчас я сижу за K6 и не могу проверить скорость на P4,
но два перехода подряд приводят к потерям тактов.


 
GuAV ©   (2004-10-02 18:34) [156]

Sha ©   (02.10.04 18:31) [155]

ок. не знал. у меня целерон. ща оптимизирую.


 
GuAV ©   (2004-10-02 18:50) [157]

Вот... тестить ща не могу... могут быть ошибки..
@@CompareWholeString:
 Test  EBx, EBx
 Jz    @@Constant_Exists
 Cmp   EDx, EBx      // constant still can be settled in buffer
 Jna   @@Exit_False   // no - exit with false result

//  Push  EDi

 MOV   ECX, EBX
 @@CW_Loop:
 SUB   ECX, 4
 JL    @@CW_Last123bytes
 MOV   EAX, [ESi+ECx]
 JZ    @@CW_LastDWord  // SUB ECX, 4 result was 0

 CMP   EAX, [EDi+ECx]
 JE   @@CW_Loop
 Mov   ECx, EDx
 JMP   @@SkipBTest1

 @@CW_LastDWord:
// MOV   EAX, [ESi]
 CMP   EAX, [EDi]
 JE    @@Constant_Exists
 Mov   ECx, EDx
 JMP   @@SkipBTest1

 @@CW_Last123bytes:

 ADD   ECX, 2
 JS    @@Last1
// JNS   @@Last3

 @@Last3:
 MOV   AX, [ESi]
 JZ    @@Last2
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 MOV   AL, [ESi+2]
 CMP   AL, [EDi+2]
 JE   @@Constant_Exists
 Mov  ECx, EDx
 JMP   @@SkipBTest1

 @@Last2:
// MOV   AX, [ESi]
 CMP   AX, [EDi]
 JE   @@Constant_Exists
 Mov  ECx, EDx
 JMP   @@SkipBTest1

 @@Last1:
 MOV   AL, [ESi]
 CMP   AL, [EDi]
 //JNE   @@SkipBTest
 JE   @@Constant_Exists

@@SkipBTest:

// Pop  EDi
//  Mov  ESi, EBp       // restore pointer to 2nd char of constant
//   Add  EDx, ECx
//   Sub  EDx, EBx       // correct residual buffer size
 Mov  ECx, EDx       // restore residual buffer size to ECx

@@SkipBTest1:   //  Mov  ECx, EDx   moved to places to jump from

 Cmp  EDx,8
 Jb   @@Scan3
 Jmp  @@Scan


 
GuAV ©   (2004-10-03 00:34) [158]

@@WideScan:
 Mov  Al,[EAx]
 Mov  Ah, Al         // Spreading char to 32bit register
 MovD MM6, EAx
 PUNPCKLWD MM6, MM6
 PUNPCKLDQ MM6, MM6

 Push ECx
@@Scan5:
 MovQ      MM7, [EDx]  // load a part of buffer that needs to be scaned
 PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
 PACKSSWB  MM7, MM7
 MovD  EAx, MM7        // get mixed dword
 Test  EAx, EAx        // was there at least 1 bit set?
 Jnz   @@CharFound2    // yes - 1st char of the constant was found
 Add   EDx,8           // correct index
 Sub   ECx,8           // and buffer size

 Jns   @@Scan5         // continue scan if buffer not empty


тут, в слуаче ECx<8 выход за пределы буфера, причём аж до 7 байт.

при Validate test надо делать как нибудь чтобы нельзя было читать из S[Length(S)+1], где S это и Constant и Buffer
У меня сейчас две идеи: или Break Point на чтение данных или VirtualProtect...

возможно с WideSearch то же, этого кода я вообще не понял...


 
Sha ©   (2004-10-03 08:33) [159]

GuAV ©   (03.10.04 00:34) [158]

Я делал проверку через VirtualProtect в одном из Challenge, не помню в каком. Надо порыться... Позже...


 
Sha ©   (2004-10-03 11:35) [160]

> GuAV ©   (03.10.04 00:34) [158]
> тут, в слуаче ECx<8 выход за пределы буфера, причём аж до 7 байт.

Я тут по такому случаю написал еще одну процедуру проверки.
Поразительно, но данный тест ConstantPos проходит.
Разберусь в понедельник если время будет, сейчас еду на дачу.

//Protect/unprotect 4k-block at the end of string
procedure SetStringProtected(var s: string; var flags: dword);
const
 block= 4*1024;
var
 p, q: pchar;
begin
 if (flags and PAGE_NOACCESS)<>0
 then begin;
   SetLength(s,3*block);
   FillChar(s[1],Length(s),0);
   end;

 p:=pointer(s);
 integer(q):=(integer(p) and -block) + 2*block;

 if (flags and PAGE_NOACCESS)<>0
 then pinteger(p-4)^:=(q-p)-1  //Trunk the string
 else pinteger(p-4)^:=3*block; //Restore string length

 VirtualProtect(pchar(q),1,flags,@flags); //Set protection flags
 end;

function TMainForm.Validate0: boolean;
var
 s: string;
 p: pchar;
 q: pointer;
 flags: dword;
 len: integer;
begin;
 flags:=PAGE_NOACCESS;
 SetStringProtected(s, flags);
 try
   len:=Length(s); //Now Length(s)>4k.
   s[len-0]:="h";
   s[len-1]:="g";
   s[len-2]:="f";
   s[len-3]:="e";
   s[len-4]:="d";
   s[len-5]:="c";
   s[len-6]:="b";
   s[len-7]:="a";
   Result:=(PosFunction("abcdefgh", s)=len-7)
       and (PosFunction("bcdefgh", s)=len-6)
       and (PosFunction("cdefgh", s)=len-5)
       and (PosFunction("defgh", s)=len-4)
       and (PosFunction("efgh", s)=len-3)
       and (PosFunction("fgh", s)=len-2)
       and (PosFunction("gh", s)=len-1)
       and (PosFunction("h", s)=len-0)
       and (PosFunction("abcdefg9", s)=0)
       and (PosFunction("bcdefg9", s)=0)
       and (PosFunction("cdefg9", s)=0)
       and (PosFunction("defg9", s)=0)
       and (PosFunction("efg9", s)=0)
       and (PosFunction("fg9", s)=0)
       and (PosFunction("g9", s)=0)
       and (PosFunction("9", s)=0)
       and (PosFunction("9h", s)=0)
       and (PosFunction("9gh", s)=0)
       and (PosFunction("9fgh", s)=0)
       and (PosFunction("9efgh", s)=0)
       and (PosFunction("9defgh", s)=0)
       and (PosFunction("9cdefgh", s)=0)
       and (PosFunction("9bcdefgh", s)=0);
   //Construct the string inplace.
   p:=pchar(pointer(s))+(len+1-8); //Set address
   pInteger(p-4)^:=7; //Set length
   pInteger(p-8)^:=1; //Set reference count
   q:=p;
   Result:=Result
       and (PosFunction("bcdefgh", string(q))=1)
       and (PosFunction("cdefgh", string(q))=2)
       and (PosFunction("defgh", string(q))=3)
       and (PosFunction("efgh", string(q))=4)
       and (PosFunction("fgh", string(q))=5)
       and (PosFunction("gh", string(q))=6)
       and (PosFunction("h", string(q))=7)
       and (PosFunction("bcdefg9", string(q))=0)
       and (PosFunction("cdefg9", string(q))=0)
       and (PosFunction("defg9", string(q))=0)
       and (PosFunction("efg9", string(q))=0)
       and (PosFunction("fg9", string(q))=0)
       and (PosFunction("g9", string(q))=0)
       and (PosFunction("9", string(q))=0)
       and (PosFunction("9h", string(q))=0)
       and (PosFunction("9gh", string(q))=0)
       and (PosFunction("9fgh", string(q))=0)
       and (PosFunction("9efgh", string(q))=0)
       and (PosFunction("9defgh", string(q))=0)
       and (PosFunction("9cdefgh", string(q))=0);
 except
   Result:=false;
   end;
 SetStringProtected(s, flags);

 if Result then begin;
   ErrorEdit0.Text := "Passed Validate0";
   ErrorEdit0.Color := clGreen;
  end
 else begin;
   ErrorEdit0.Text := "Failed Validate0";
   ErrorEdit0.Color := clRed;
  end;
 Update;
 end;



Страницы: 1 2 3 4 5 6 7 вся ветка

Форум: "Основная";
Текущий архив: 2004.10.24;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.92 MB
Время: 0.079 c
14-1097139658
lipskiy
2004-10-07 13:00
2004.10.24
Ищу программу для нажимания кнопок в чужих окнах


3-1096013860
Term
2004-09-24 12:17
2004.10.24
Автоинкремент в MSSQL2000


14-1096645317
Amonimus
2004-10-01 19:41
2004.10.24
Помогите с IE


4-1095856824
Vikont
2004-09-22 16:40
2004.10.24
Сканкоды


11-1081912814
nsvi
2004-04-14 07:20
2004.10.24
Помогите разобраться с формой





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский