Warum klappt Binary bei mir nicht



  • @wob
    das kenne ich, bei meinem Delphi-Programm von knapp 10 Jahren sah es z.B. so aus

    Procedure MakePZ;
      Label
        ExUP;
      Var
        sBBAN : Str254;
        iBBAN : Int64;
        iPZ   : Integer;
        rc    : Integer;
      Begin
    //  sBBAN := sBLZ+sKTO+'131400'; // 8st + 10st + 6st = 24st BBAN BLZ KTO 1314(DE) 00(PZ)
        sBBAN := sBLZ+sKTO;
        {$R-}
        Val(sBBAN,iBBAN,rc);
        if rc <> 0 then
          Begin
            sIBAN := 'Fehler bei VAL';
            goto ExUP;
          End;
    
        While iBBAN > 970 do
          Begin
            if      iBBAN > 970000000000000000 then iBBAN := iBBAN -       970000000000000000
            else if iBBAN >  97000000000000000 then iBBAN := iBBAN -        97000000000000000
            else if iBBAN >   9700000000000000 then iBBAN := iBBAN -         9700000000000000
            else if iBBAN >    970000000000000 then iBBAN := iBBAN -          970000000000000
            else if iBBAN >     97000000000000 then iBBAN := iBBAN -           97000000000000
            else if iBBAN >      9700000000000 then iBBAN := iBBAN -            9700000000000
            else if iBBAN >       970000000000 then iBBAN := iBBAN -             970000000000
            else if iBBAN >        97000000000 then iBBAN := iBBAN -              97000000000
            else if iBBAN >         9700000000 then iBBAN := iBBAN -               9700000000
            else if iBBAN >          970000000 then iBBAN := iBBAN -                970000000
            else if iBBAN >           97000000 then iBBAN := iBBAN -                 97000000
            else if iBBAN >            9700000 then iBBAN := iBBAN -                  9700000
            else if iBBAN >             970000 then iBBAN := iBBAN -                   970000
            else if iBBAN >              97000 then iBBAN := iBBAN -                    97000
            else if iBBAN >               9700 then iBBAN := iBBAN -                     9700
            else if iBBAN >                970 then iBBAN := iBBAN -                      970;
          End; // While iBBAN > 970
    
        iBBAN := (iBBAN * 1000000) + 131400;
    
        While iBBAN > 96 do
          Begin
            if      iBBAN >          970000000 then iBBAN := iBBAN -                970000000
            else if iBBAN >           97000000 then iBBAN := iBBAN -                 97000000
            else if iBBAN >            9700000 then iBBAN := iBBAN -                  9700000
            else if iBBAN >             970000 then iBBAN := iBBAN -                   970000
            else if iBBAN >              97000 then iBBAN := iBBAN -                    97000
            else if iBBAN >               9700 then iBBAN := iBBAN -                     9700
            else if iBBAN >                970 then iBBAN := iBBAN -                      970
            else if iBBAN >                 96 then iBBAN := iBBAN -                       97;
          End; // While iBBAN > 96
    
        iPZ := 98 - iBBAN;
        Str(iPZ:2,sPZ);
        While Length(sPZ) < 2 do sPZ := '0' + sPZ; // Vornull
    
        sIBAN := 'DE' + ' ' + sPZ + ' ' + sBLZ + ' ' + sKTO;
    
       ExUP:
        Form1.edIBAN.Text := sIBAN;
      End;
    

    Und im C# Programm unter Nutzung von BigInteger sieht es so aus:

            //===========================
            // Prüfziffer erstellen
            //===========================
            void MakePZ()
            {
                //  sBBAN = sBLZ+sKTO+'131400'; // 8st + 10st + 6st = 24st BBAN BLZ KTO 1314(DE) 00(PZ)
                string sBBAN = sBLZ + sKTO + "1314" + "00";
                //                        D E    PZ=Prüfziffer 00
                BigInteger iBBAN = (BigInteger.Parse(sBBAN, NumberStyles.Integer, CultureInfo.CurrentCulture));
    
                BigInteger q; // Quotient
                BigInteger r; // Rest
                BigInteger d = 97; // Divisor
    
                q = BigInteger.DivRem(iBBAN, d, out r); // Modulo 97
    
                string sRest = r.ToString();  // Rest
                int iRest = int.Parse(sRest); // Rest
                sPZ = (98 - iRest).ToString();
                while (sPZ.Length < 2) { sPZ = "0" + sPZ; } // ggf Vornull ergänzen
    
                string s = "DE" + sPZ + sBLZ + sKTO; // IBAN 22-stellig
                sIBAN = " " +
                        s.Substring(00, 4) + " " + s.Substring(04, 4) + " " + // IBAN aufbereitet
                        s.Substring(08, 4) + " " + s.Substring(12, 4) + " " +
                        s.Substring(16, 4) + " " + s.Substring(20, 2);
    
                edIBAN.Text = sIBAN;
            }
    

  • Mod

    @SeppJ sagte in Warum klappt Binary bei mir nicht:

    Ich hoffe doch, du speicherst Bankleitzahlen & Co. nicht als Integer. Das sind Ziffernfolgen, keine Zahlen!

    Also doch wie befürchtet. Du speicherst Bankleitzahlen & Co. als Integer.

    Ganz schlecht.👎
    Es sind schon ungeheuer große Schäden dadurch verursacht worden, weil man bei wichtigen Programmen die billigsten Programmierer angeheuert hat, und dann Rechnungen nicht bezahlt wurden, Briefe nicht ankamen, Anrufe an die falsche Nummer gingen, …

    Starke Anhaltspunkte dafür, ob etwas eine Zahl oder eeine Ziffernfolge ist:

    • Es macht keinen Unterschied, ob ich sie in Hexadezimal, base69, oder binär angebe -> Zahl
    • Die Differenz zwischen zwei Objekten ist wieder ein sinnvolles Objekt -> Zahl
    • Führende Nullen sind signifikant -> Ziffernfolge

    Jeweils mit gültigem Umkehrschluss.

    Bankleitzahlen, IBANs, Telefonnummern, Postleitzahlen, etc. haben jeweils 3/3 der Indikatoren für Ziffernfolge. (Weil sie es sind!)

    PS: Modulo einer Ziffernfolge, falls du dir das aus dem Beitrag von wob nicht erschließen konntest:

    modulo(ziffernfolge, divisor):
        ergebnis = 0
        für jede ziffer in der ziffernfolge:
            ergebnis = (ergebnis * 10 + numerischer Wert der Ziffer) modulo divisor;
     
        return ergebnis
    

    https://ideone.com/DC82Hw

    Ein bisschen einfacher, als dein Monster, oder?



  • @SeppJ sagte in Warum klappt Binary bei mir nicht:

    Bankleitzahlen & Co. als Integer.

    In diesem Programm geht es darum, aus einer BLZ und KTO-Nummer die IBAN zu ermitteln und dort insbesondere die Prüfziffer. Dafür gibt es eine Formel, wie man das machen muss. Da wird nichts gespeichert und es gibt auch keine Unsicherheiten irgendwelcher Art.
    Am Bildschirm gibst LZ und KTO-Nummer ein und es wird die zugehörige IBAN angezeigt, das ist wie ein Taschenrechner.
    Die lange 24-stelligen Zahl muss / 97 dividiert werden - und das geht mit INT64 nicht.
    Da muss man eben immer wieder 97 bzw. ein Vielfaches davon subtrahieren, um den Rest zu ermitteln, den man für die Prüfziffer braucht.



  • Das hat @SeppJ doch geschrieben. Der Modulo ändert sich nicht, auch wenn es einen Überlauf bei der int-Zahl gibt.

    Hier auch ein Code in C#: IBAN-Validator (auch wenn man statt int.Parse(...) einfach checkSumString[i] - '0' benutzen sollte).


  • Mod

    Ist schon irgendwie lustig, wenn du darauf hinweist, wieso du für das Modulo 97 Integer nehmen willst, wenn die letzten 3 Beiträge davon handeln, wieso man das nicht braucht. Mein Beispiel nutzt sogar "zufällig" 97 als Divisor.



  • @Th69 sagte in Warum klappt Binary bei mir nicht:

    Der Modulo ändert sich nicht, auch wenn es einen Überlauf bei der int-Zahl gibt.

    What? Doch!

    uint8_t u8 = 240;
    cout << u8 % 17 << '\n';
    u8 += 17;
    cout << u8 % 17 << '\n';
    

    Vergleiche mit:

    uint16_t u16 = 240;
    cout << u16 % 17 << '\n';
    u16 += 17;
    cout << u16 % 17 << '\n';
    


  • @SeppJ
    Ja, das geht wirklich einfach. Es kommt die richtige IBAN heraus

    using System;
    
    namespace IbanTest
    {
        internal class Program
        {
            static void Main(string[] args)
            {
                //          BLZ+++++   KTO#######   D E    00
                string k = "11111111"+"2222222222"+"1314"+"00";
                int e = 0;
                for (int i=0; i < k.Length; i++) 
                {
                    e = (e * 10 + int.Parse(k[i] + "")) % 97;
                }
                string pz = (98-e).ToString("00");
                Console.WriteLine("BLZ="+k.Substring(0,8)+
                                  ",KTO="+k.Substring(8,10)+
                                  " => IBAN="+"DE"+ pz + k.Substring(0,18));
                Console.ReadLine();
            }
        }
    }
    


  • @wob: Da habe ich mich wohl etwas falsch ausgedrückt.
    Ich meinte beim 'vermeintlichen Überlauf`: da ja immer wieder der Modulo benutzt wird, bleibt die Zahl klein und es können somit beliebig lange Ziffernfolgen bearbeitet werden.

    So wie ich es geschrieben habe, hast du natürlich mit deinem Code Recht - das würde zu einem falschen Ergebnis führen.

    Schon das 2. Mal jetzt, daß ich nicht richtig überlegt habe - ich werde wohl alt.



  • @hkdd
    Ah, ja. "kernel32.dll" ist vermutlich auch falsch. Sorry. Probier mal meine Variante nur mit "ntdll.dll". Das sollte passen. long ist auf jeden Fall nicht korrekt. Wenn das Programm als 64 Bit läuft wird es zufällig funktionieren. Als 32 Bit Prozess aber ... nicht. Was auch erklären würde dass er haufenweise Unterschiede meldet - weil der Rückgabewert halt total falsch ist. Mit IntPtr sollte es bei 32 und 64 Bit passen.



  • @hustbaer
    ich habe jetzt auch mal sehr große Videodateien verglichen, da sind die Puffer-Vergleiche von Th69 und meine ASM-DLL die schnellsten. Sie kommen nahe an mein Delphi-Programm heran.
    Die Methode von Th69 hat den Charm, dass man kein DllImport und damit keine Zusatzdateien zur EXE braucht, also alles läuft C# intern.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    CompHKw - Dateien Vergleichen - (C) 07.09.2022 Hartmut, Dresden
    -----------------------------------------------------------------
    Parameter: "N:\VideosN" "N:\VideosN" "/U" "/VJ" "/F" "/M:2" "/A"
    -----------------------------------------------------------------
    Vergleich: N:\VideosN\
          mit: N:\VideosN\
    ~~~~~~~~~~~~~~~~~~~~~~~
    2.446.Datei: N:\VideosN\Youtube-HK\...den 1971\zz09 So scheid ich denn (Finale).mp4
    =================================================================
         82 Unter-Pfade gefunden   - identisch.
      2.446 Dateien     gefunden   - und verglichen...
      2.446 Dateien     verglichen - identisch.
          Es wurden 208.144.569.612 Bytes = 203.266.181 K-Bytes verglichen
          Start 18:42:22  Ende 19:34:59, Dauer = 00:52:37
    ========================================================<Ende>===
    Linke MausTaste / ESC = Schließen   Rechte MausTaste / F1 = Info
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Start = 08.12.2022 18:30:45  CompCs
    Vergleichsmethode = unsafe MemCmp von Th69
    MitRead=True, MitComp=True
    16.777.216 Bytes = Puffergröße
    208.144.569.612 Bytes eingelesen
    83 Directories
    2.446 Dateien
    14.071 Puffer
    2.446 identische Dateien
    Ende  = 08.12.2022 19:28:56, Dauer = 00:58:11.2
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Start = 08.12.2022 19:45:03  CompCs
    Vergleichsmethode = CompareByteArray (C++ ASM DLL von HK)
    MitRead=True, MitComp=True
    16.777.216 Bytes = Puffergröße
    208.144.569.612 Bytes eingelesen
    83 Directories
    2.446 Dateien
    14.071 Puffer
    2.446 identische Dateien
    Ende  = 08.12.2022 20:38:14, Dauer = 00:53:11.7
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    


  • Hast du denn immer das Programm mindestens 2x laufen lassen, ehe du die Zeiten vergleichst?
    Beim ersten Programmstart, direkt nach dem Kompilieren, muß ja erst noch der Jitter aktiviert werden, um das Programm zu optimieren.
    Es werden zwar nur einige Prozente sein, aber nur dann ist der Vergleich sinnvoll.



  • @Th69 sagte in Warum klappt Binary bei mir nicht:

    Hast du denn immer das Programm mindestens 2x laufen lassen, ehe du die Zeiten vergleichst?
    Beim ersten Programmstart, direkt nach dem Kompilieren, muß ja erst noch der Jitter aktiviert werden, um das Programm zu optimieren.

    Soweit ich weiss muss das Programm immer geJITed werden, sofern es keinen "strong name" hat, also signiert ist. Und selbst bei Programmen mit "strong name" bin ich nicht sicher ob die native images automatisch gecached werden. Sollte aber eh egal sein, weil die JIT Zeit da wohl keinen signifikanten Unterschied machen wird. Das Programm ist ja nicht gross. Das Framework JITen dauert lange, aber das sollte ja bereits lange gecached sein.



  • @Th69
    bei knapp einer Stunde Laufzeit überlegt man es sich, das zweimal laufen zu lassen.
    Ich kann es aber noch einmal machen.
    Ich werde berichten



  • @Th69 sagte in Warum klappt Binary bei mir nicht:

      	if ((length & 4) != 0)
      	{
      		if (*(int*)x1 != *(int*)x2)
      			return false;
      		x1 += 4;
      		x2 += 4;
      	}
      	if ((length & 2) != 0)
      	{
      		if (*(short*)x1 != *(short*)x2)
      			return false;
      		x1 += 2;
      		x2 += 2;
      	}
      	if ((length & 1) != 0) // ????
      		return *x1 == *x2;
    

    Das stimmt so nicht.
    x1 und x2 sind long Zeiger, mit += 4 bzw. += 2 schiebst du die 4x8 bzw. 2x8 Bytes weiter. Und beim letzten Vergleich für das einzelne Byte fehlt der Cast nach byte*.



  • @hustbaer
    So sieht das in der überarbeiteten Version aus, da ist dieser Fehler behoben.

    		const int SizeInt = sizeof(int);
    		if ((length & SizeInt) != 0)
    		{
    			if (*(int*)b1 != *(int*)b2)
    				return false;
    			b1 += SizeInt;
    			b2 += SizeInt;
    		}
    
    		const int SizeShort = sizeof(short);
    		if ((length & SizeShort) != 0)
    		{
    			if (*(short*)b1 != *(short*)b2)
    				return false;
    			b1 += SizeShort;
    			b2 += SizeShort;
    		}
    


  • @hkdd @Th69
    Upps, sorry, hab übersehen dass der Code in der alten Version stand 👍



  • @hustbaer: Es gibt aber immer Unterschiede zwischen dem 1. und weiteren Läufen (ab dem 2. sind die Zeiten dann bis auf 1/10s gleich, beim ersten Lauf sind es aber auch mal einige Sekunden mehr - bei meinen Tests mit ca. 10s Laufzeit.)

    @hkdd: Du kannst es ja erst einmal mit einem kleinen Ordner laufen lassen.



  • @Th69
    Das kann viele Ursachen haben. Falls nicht das gesamte Datenset in deinen File-Cache passt kann es z.B. schonmal daran liegen. Beim Kompilieren fliegen ein paar Teile wieder aus dem File-Cache - die müssen beim 1. Durchlauf dann wieder geladen werden. Der 2. Durchlauf kann daher dann mehr aus dem Cache bekommen -> schneller.



  • Ich hab mir mal den Spass gemacht die Vergleiche in einen Hilfs-Thread auszuladern. Damit bleibt quasi nur mehr die Zeit zum Lesen übrig, die Vergleiche laufen dann Asynchron im 2. Thread:

    Alles sehr Q&D und ich kann nicht garantieren dass kein Bug drinnen ist:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Threading;
    
    namespace AllFilesInDir // Alle Dateien eines Ordners (Kommandozeile) lesen
    {
    	internal class Program
    	{
    		public static bool MitRead = true;  // Read abschalten TEST TEST TEST
    		public static bool MitComp = true;  // Vergleich abschalten TEST TEST TEST
    
    		public enum Method
    		{
    			SequenceEqual,
    			MemCmp,
    			ForNxt,
    			Unsafe,
    			RtlCompareMemory,
    		}
    
    		public static Method VergleichsMethode = Method.RtlCompareMemory;
    
    		public static Int64 GesBytes = 0; // Summe  aller gelesenen Bytes
    		public static int Datei2Fehlt = 0; // Anzahl fehlender Dateien in Directory 2
    		public static int Pfad2Fehlt = 0; // Anzahl fehlender Unterpfade in Directory 2
    		public static int LenDiff = 0; // Anzahl Dateien mit unterschiedlicher Länge
    		public static int ByteDiff = 0; // Anzahl Dateien mit inhaltlichen Differenzen
    		public static int AnzDir = 0; // Anzahl aller Directories incl. Start-Dir
    		public static int AnzFiles = 0; // Anzahl aller gelesenen Dateien
    		public static int AnzPuff = 0; // Anzahl aller gelesenen Puffer
    
    		public static BufferPairPool Pool = new BufferPairPool();
    		public static AsyncComparer Comparer;
    
    		public const int MaxPuffL = 128 * 1024;
    
    		[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
    		static extern int memcmp(byte[] b1, byte[] b2, IntPtr count);
    
    		[DllImport("ntdll.dll", EntryPoint = "RtlCompareMemory", SetLastError = false)]
    		private static extern IntPtr RtlCompareMemory(byte[] Source1, byte[] Source2, IntPtr length);
    
    		static void Main(string[] args)
    		{
    			using (var comp = new AsyncComparer(Pool))
    			{
    				Comparer = comp;
    				Main2(args);
    				Comparer = null;
    			}
    		}
    
    		static void Main2(string[] args)
    		{
    			DateTime dtStart = DateTime.Now;  // Start-Zeitpunkt merken
    			Console.WriteLine("Start = " + dtStart.ToString());
    			if (MitComp) Console.WriteLine("Vergleichsmethode = " + VergleichsMethode.ToString());
    			Console.WriteLine("MitRead=" + MitRead + ", MitComp=" + MitComp);
    			Console.WriteLine(MaxPuffL.ToString("###,###,###,###,##0") + " Bytes = Puffergröße");
    			//string StartPfad1 = args[0]; // Pfad 1 aus Kommandozeile holen
    			//string StartPfad2 = args[1]; // Pfad 2 aus Kommandozeile holen
    			string StartPfad1 = "C:\\temp\\data";
    			string StartPfad2 = "C:\\temp\\data_copy";
    
    			PfadVerarbeiten(StartPfad1, StartPfad2);
    
    			Console.WriteLine(GesBytes.ToString("###,###,###,###,##0") + " Bytes eingelesen");
    			Console.WriteLine(AnzDir.ToString("###,###,###,###,##0") + " Directories");
    			Console.WriteLine(AnzFiles.ToString("###,###,###,###,##0") + " Dateien");
    			Console.WriteLine(AnzPuff.ToString("###,###,###,###,##0") + " Puffer");
    			Console.WriteLine(Datei2Fehlt.ToString("###,###,###,###,##0") + " fehlende Dateien");
    			Console.WriteLine(Pfad2Fehlt.ToString("###,###,###,###,##0") + " fehlende Unterpfade");
    			Console.WriteLine(LenDiff.ToString("###,###,###,###,##0") + " Dateien mit unterschiedlicher Länge");
    			Console.WriteLine(ByteDiff.ToString("###,###,###,###,##0") + " ungleiche Dateien");
    			DateTime dtEnde = DateTime.Now;  // Ende-Zeitpunkt merken
    			System.TimeSpan Dauer = dtEnde - dtStart;
    			Console.WriteLine("Ende  = " + dtEnde.ToString() + ", Dauer = " + Dauer.ToString().Substring(0, 10));
    
    			Console.ReadLine(); // Warten auf ENTER
    		}
    
    		static void PfadVerarbeiten(string pPfad1, string pPfad2) // wird rekursiv aufgerufen
    		{
    			AnzDir++; // Anzahl verglichene DIR gesamt
    
    			DirectoryInfo di1 = new DirectoryInfo(pPfad1);     // Aktuelles Directory
    			FileInfo[] fi1 = di1.GetFiles("*.*");              // Dateien im Directory
    			int FilesAnz1 = fi1.Count();                       // Anzahl Dateien im Directory
    			DirectoryInfo[] ui1 = di1.GetDirectories("*.*");   // Unterordner im Directory
    			int DirAnz1 = ui1.Count();                         // Anzahl Unterordner im Directory
    
    			DirectoryInfo di2 = new DirectoryInfo(pPfad2);     // Aktuelles Directory
    			FileInfo[] fi2 = di2.GetFiles("*.*");              // Dateien im Directory
    			int FilesAnz2 = fi2.Count();                       // Anzahl Dateien im Directory
    			DirectoryInfo[] ui2 = di2.GetDirectories("*.*");   // Unterordner im Directory
    			int DirAnz2 = ui2.Count();                         // Anzahl Unterordner im Directory
    
    			bool gefunden = false;
    			string SuchName = "";
    
    			for (int i = 0; i < FilesAnz1; i++) // zu allen Dateien aus Pfad1 zugehörige Datei in Pfad2 suchen
    			{
    				gefunden = false;
    				SuchName = fi1[i].Name.ToLower(); // ohne Lw:\Pfad
    
    				for (int j = 0; j < FilesAnz2 && !gefunden; j++)
    				{
    					if (SuchName.CompareTo(fi2[j].Name.ToLower()) == 0)
    					{
    						gefunden = true;
    						if (fi1[i].Length == fi2[j].Length)
    						{ LeseDateien(fi1[i].FullName, fi2[j].FullName, fi1[i].Length); }
    						else
    						{ LenDiff++; }
    					}
    				}
    				if (!gefunden)
    				{ Datei2Fehlt++; }
    			}
    
    			for (int i = 0; i < DirAnz1; i++) // Alle Unterordner verarbeiten
    			{
    				gefunden = false;
    				SuchName = ui1[i].Name.ToLower(); // ohne Lw:\Pfad
    
    				for (int j = 0; j < DirAnz2 && !gefunden; j++)
    				{
    					if (SuchName.CompareTo(ui2[j].Name.ToLower()) == 0)
    					{
    						gefunden = true;
    						PfadVerarbeiten(ui1[i].FullName, ui2[j].FullName);  // Rekursiver Aufruf
    					}
    				}
    				if (!gefunden)
    				{ Pfad2Fehlt++; }
    			}
    		}
    
    		static void LeseDateien(string pDsn1, string pDsn2, Int64 lenFile)
    		{
    			Comparer.Reset();
    
    			AnzFiles++; // Anzahl Dateien gesamt
    			Int64 RestLen = lenFile; // Dateilänge beider Dateien
    			using (var fs1 = new FileStream(pDsn1, FileMode.Open, FileAccess.Read))
    			using (var fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read))
    			{
    				Int64 LeseLen = Math.Min(RestLen, MaxPuffL);
    				while (LeseLen > 0)
    				{
    					if (Comparer.PeekDifferences() > 0)
    						break;
    
    					if (MitRead)
    					{
    						GesBytes += LeseLen;
    
    						var p = Pool.Get();
    
    						p.Length = (int)LeseLen;
    						fs1.Read(p.Buffer1, 0, (int)LeseLen);
    						fs2.Read(p.Buffer2, 0, (int)LeseLen);
    
    						if (MitComp)
    						{
    							AnzPuff++;
    							Comparer.Enqueue(p);
    						}
    					}
    
    					RestLen -= LeseLen;
    					LeseLen = Math.Min(RestLen, MaxPuffL);
    				}
    			}
    
    			if (Comparer.GetDifferences() > 0)
    				ByteDiff++;
    		}
    
    		static unsafe bool MemCmp2(byte[] a1, byte[] a2, int length)
    		{
    			if (a1 == null || a2 == null || a1.Length < length || a2.Length < length)
    				return false;
    
    			fixed (byte* p1 = a1, p2 = a2)
    			{
    				long* x1 = (long*)p1, x2 = (long*)p2;
    				long* x0 = x1 + length / sizeof(long);
    				for (; x1 < x0; x1++, x2++)
    					if (*x1 != *x2)
    						return false;
    
    				byte* b1 = (byte*)x1, b2 = (byte*)x2;
    
    				const int SizeInt = sizeof(int);
    				if ((length & SizeInt) != 0)
    				{
    					if (*(int*)b1 != *(int*)b2)
    						return false;
    					b1 += SizeInt;
    					b2 += SizeInt;
    				}
    
    				const int SizeShort = sizeof(short);
    				if ((length & SizeShort) != 0)
    				{
    					if (*(short*)b1 != *(short*)b2)
    						return false;
    					b1 += SizeShort;
    					b2 += SizeShort;
    				}
    
    				if ((length & 1) != 0)
    					return *b1 == *b2;
    
    				return true;
    			}
    		}
    
    		public class BufferPair
    		{
    			public int Length;
    			public byte[] Buffer1 = new byte[MaxPuffL];
    			public byte[] Buffer2 = new byte[MaxPuffL];
    		}
    
    		public class BufferPairPool
    		{
    			public BufferPairPool()
    			{
    			}
    
    			public BufferPair Get()
    			{
    				lock (m_lock)
    				{
    					if (m_pool.Count == 0)
    						return new BufferPair();
    					var p = m_pool[m_pool.Count - 1];
    					m_pool.RemoveAt(m_pool.Count - 1);
    					return p;
    				}
    			}
    
    			public void Return(BufferPair p)
    			{
    				lock (m_lock)
    					m_pool.Add(p);
    			}
    
    			readonly object m_lock = new object();
    			readonly List<BufferPair> m_pool = new List<BufferPair>();
    		}
    
    		public class AsyncComparer : IDisposable
    		{
    			public AsyncComparer(BufferPairPool pool)
    			{
    				m_pool = pool;
    
    				m_comparerThread = new Thread(ThreadFn);
    				m_comparerThread.IsBackground = true;
    				m_comparerThread.Start();
    			}
    
    			public void Dispose()
    			{
    				lock (m_lock)
    				{
    					m_stop = true;
    					Monitor.PulseAll(m_lock);
    				}
    
    				m_comparerThread?.Join();
    				m_comparerThread = null;
    			}
    
    			public void Enqueue(BufferPair p)
    			{
    				lock (m_lock)
    				{
    					if (m_stop)
    						throw new ObjectDisposedException(nameof(AsyncComparer));
    
    					while (m_queue.Count > 1)
    						Monitor.Wait(m_lock);
    
    					m_queue.Enqueue(p);
    					Monitor.PulseAll(m_lock);
    				}
    			}
    
    			public int PeekDifferences()
    			{
    				lock (m_lock)
    				{
    					if (m_stop)
    						throw new ObjectDisposedException(nameof(AsyncComparer));
    
    					return m_differences;
    				}
    			}
    
    			public int GetDifferences()
    			{
    				lock (m_lock)
    				{
    					if (m_stop)
    						throw new ObjectDisposedException(nameof(AsyncComparer));
    
    					while (m_queue.Count > 0)
    						Monitor.Wait(m_lock);
    
    					return m_differences;
    				}
    			}
    
    			public void Reset()
    			{
    				lock (m_lock)
    				{
    					if (m_stop)
    						throw new ObjectDisposedException(nameof(AsyncComparer));
    
    					while (m_queue.Count > 0)
    						Monitor.Wait(m_lock);
    
    					m_differences = 0;
    				}
    			}
    
    			void ThreadFn()
    			{
    				ThreadFn0();
    
    				lock (m_lock)
    				{
    					m_queue.Clear();
    					Monitor.PulseAll(m_lock);
    				}
    			}
    
    			void ThreadFn0()
    			{
    				while (true)
    				{
    					BufferPair p = null;
    
    					lock (m_lock)
    					{
    						while (m_queue.Count == 0 && !m_stop)
    							Monitor.Wait(m_lock);
    
    						if (m_stop)
    							return;
    
    						p = m_queue.Peek();
    					}
    
    					bool result = Compare(p, VergleichsMethode);
    					m_pool.Return(p);
    
    					lock (m_lock)
    					{
    						if (m_stop)
    							return;
    
    						m_queue.Dequeue();
    						if (!result)
    							m_differences++;
    
    						Monitor.PulseAll(m_lock);
    					}
    				}
    			}
    
    			static bool Compare(BufferPair p, Method method)
    			{
    				switch (method)
    				{
    					case Method.SequenceEqual:
    						return p.Buffer1.Take(p.Length).SequenceEqual(p.Buffer2.Take(p.Length));
    
    					case Method.MemCmp:
    						return memcmp(p.Buffer1, p.Buffer2, (IntPtr)p.Length) == 0;
    
    					case Method.ForNxt:
    						int l = p.Length;
    						var b1 = p.Buffer1;
    						var b2 = p.Buffer2;
    						if (l > b1.Length)
    							throw new IndexOutOfRangeException();
    						if (l > b2.Length)
    							throw new IndexOutOfRangeException();
    						for (int b = 0; b < l; b++)
    						{
    							if (b1[b] != b2[b])
    								return false;
    						}
    						return true;
    
    					case Method.Unsafe:
    						return MemCmp2(p.Buffer1, p.Buffer2, p.Length);
    
    					case Method.RtlCompareMemory:
    						var eqCount = RtlCompareMemory(p.Buffer1, p.Buffer2, (IntPtr)p.Length);
    						return eqCount == (IntPtr)p.Length;
    
    					default:
    						throw new ArgumentException(nameof(method));
    				}
    			}
    
    			Thread m_comparerThread;
    			bool m_stop;
    			readonly BufferPairPool m_pool;
    
    			readonly object m_lock = new object();
    			readonly Queue<BufferPair> m_queue = new Queue<BufferPair>();
    			int m_differences;
    		}
    	}
    }
    


  • ps: Puffer-Grösse hab ich runtergesetzt weil kleinere Puffer bei mir schneller sind. Vermutlich auch wieder ne Caching-Sache (CPU Caches).


Anmelden zum Antworten