@ms61:
Ja, das ist ja 'ne super Idee, die Buchstaben in "einem Rutsch" zu verarbeiten.
Aber zu Ende gedacht heißt das ja, daß man auch mehrere Ziffern in einem Rutsch verarbeiten kann.
Ich habe das mal umgesetzt in IsIBAN3 und weils so schön ist auch in der
Asm-Funktion IsIBAN4, die allerdings
nur auf 64 Bit Systemen läuft.
Für jeweils 1 Mio Durchläufe hab ich folgende Zeiten gemessen
CheckIban (von ms61 aus #4) : 249 ms
CheckIban (von ms61 aus #8) : 156 ms
IsIBAN (aus #6) : 203 ms
IsIBAN2 (aus #6 mit Tabelle) : 94 ms
IsIBAN3 : 124 ms
IsIBAN4 (64 Bit
Asm) : 78 ms
Überraschend für mich ist, daß die
Asm-Version noch deutlich schneller ist, als die Version, die eine Tabelle benutzt.
Vielleicht hat jemand, der einen gewissen Bestand an IBANs hat, Lust, zu prüfen, ob die Funktionen korrekte Ergebnisse bringen.
Ich habe es nur an einer einzigen IBAN getestet (meiner eigenenen).
Geändert :
Prüfung ob Puffer voll ist korrigiert. Bei der bisherigen Version hätte ev. ein Zahlenüberlauf auftreten können.
Delphi-Quellcode:
FUNCTION IsIBAN3(const s:string): boolean;
var n:Integer; cs,ci:UInt64;
PROCEDURE GetMod;
begin
cs:=(cs+ci) mod 97;
ci:=0;
n:=0;
end;
FUNCTION GetCheckSum(first,last:integer):boolean;
var i,c:integer;
begin
for i:=first to last do begin
if n>=15 then GetMod;
c:=Ord(s[i])-48;
case c of
0..9 : begin
cs:=cs*10;
ci:=ci*10+c;
inc(n);
end;
17..42 : begin
cs:=cs*100;
ci:=ci*100+c-7;
inc(n,2);
end;
else Exit(False);
end;
end;
result:=true;
end;
var len:integer;
begin
len:=length(s);
if (len<5) or (len>34) then Exit(false);
cs:=0;
ci:=0;
n:=0;
if not GetCheckSum(5,len) then Exit(false);
if not GetCheckSum(1,4) then Exit(false);
if n>0 then GetMod;
result:=cs=1;
end;
Geändert :
1) Zugriff auf die Zeichen in s optimiert.
2) Prüfung ob Puffer voll ist korrigiert. Bei der bisherigen Version hätte ev. ein Zahlenüberlauf auftreten können.
3) Prüfung ob s ungültige Zeichen enthält, korrigiert.
Delphi-Quellcode:
FUNCTION IsIBAN4(
const s:
string):boolean;
asm
test rcx,rcx
je @Fail
xor r9,r9
mov r9d,[rcx-4]
cmp r9,5
jb @Fail
cmp r9,34
ja @Fail
jmp @Work
@PopFail: pop rcx
@Fail:
xor al,al
ret
@GetMod: lea rax,[rax+r10]
// = Rest + Ziffernpuffer
xor rdx,rdx
mov r11,97
div r11
mov rax,rdx
// = (Rest + Ziffernpuffer) Mod 97
xor r10,r10
// Ziffernpuffer löschen
xor r11,r11
// Ziffernzähler löschen
ret
@GetCS: cmp r11,15
jb @ReadChar
call @GetMod
@ReadChar: movzx rdx,word [r8+r9*2]
// Zeichen aus s
sub dx,'
0'
cmp dx,9
ja @CheckLetter
// keine Ziffer
lea rax,[rax+rax*4]
// = Rest * 5
lea rax,[rax+rax]
// = Rest * 10
lea r10,[r10+r10*4]
// = Ziffernpuffer * 5
lea r10,[r10*2+rdx]
// = Ziffernpuffer * 10 + Ziffer
add r11,1
// Zifferzähler + 1
jmp @Next
@CheckLetter: sub dx,'
A'-'
0'
cmp dx,25
ja @PopFail
// weder Ziffer noch Buchstabe
lea rax,[rax+rax*4]
// = Rest * 5
lea rax,[rax+rax*4]
// = Rest * 25
lea rax,[rax*4]
// = Rest * 100
lea r10,[r10+r10*4]
// = Ziffernpuffer * 5
lea r10,[r10+r10*4]
// = Ziffernpuffer * 25
lea r10,[r10*4+rdx+10]
// = Ziffernpuffer * 100 + BuchstabenIndex
add r11,2
// Zifferzähler + 2
@Next: add r9,1
jne @GetCS
ret
@Work:
xor rax,rax
// Rest
xor r10,r10
// Ziffernpuffer
xor r11,r11
// Ziffernzähler
lea r8,[rcx+r9*2]
// Hinter s[Length(s)]
sub r9,4
// Length(s)-4 Zeichen lesen
neg r9
call @GetCS
lea r8,[rcx+8]
// Hinter s[4]
mov r9,-4
// 4 Zeichen lesen
call @GetCS
test r11,r11
je @
End
call @GetMod
@
End: cmp rax,1
sete al
end;