Warum klappt Binary bei mir nicht



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

    Für den notwendigen Vergleich muss man deshalb immer die zu vergleichende Länge mitgeben, die ist meistens deutlich kleiner, als die Pufferlänge.

    @Jockelx hatte dir doch im anderen Thema den Code dazu gegeben:

    ByPu1.Take(count1).SequenceEqual(ByPu2.Take(count2)

    Aber da diese Methode in .NET 4 noch unoptimiert ist (sondern allgemeingültig für alle Arten von Sequenzen (IEnumerable)), ist sie deswegen für Arrays nicht zu empfehlen.

    Daher hätte ich ja Spans vorgeschlagen 😉

    @hkdd

    [DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr RtlCompareMemory(byte[] b1, byte[] b2, IntPtr length);
    

    Sollte funktionieren. Zu beachten ist natürlich wie der Rückgabewert funktioniert: das Ding gibt die Anzahl der Bytes zurück die ab Start der Arrays gleich sind.
    Auf jeden Fall besser als sich unnötig selbst eine DLL zu stricken.



  • @hustbaer
    das habe ich so gemacht, bei mir klappt nur dieser DllImport

     [DllImport("ntdll.dll", EntryPoint = "RtlCompareMemory", SetLastError = false)]
            static extern long RtlCompareMemory(byte[] b1, byte[] b2, long length);
    

    Wenn ich den Import aus kernel32.dll benutze, findet die Routine unzutreffender Weise eine Menge ungleiche Dateien.

    Start = 08.12.2022 05:27:02
    Vergleichsmethode = RtlCompareMemory [DllImport] kernel32.dll
    MitRead=True, MitComp=True
    16.777.216 Bytes = Puffergröße
    2.918.526.970 Bytes eingelesen
    770 Directories
    15.228 Dateien
    15.217 Puffer
    0 fehlende Dateien
    0 fehlende Unterpfade
    0 Dateien mit unterschiedlicher Länge
    15.217 ungleiche Dateien
    Ende  = 08.12.2022 05:28:04, Dauer = 00:01:01.8
    

    Im Debugger kann ich nachverfolgen, um welche Dateien es sich Handelt.
    Beide Dateien sind zwingend identisch, da ich ja den Pfad mit sich selbst vergleiche.
    Es sind jpg-, zip- u.a. Dateien.

    Wenn ich den von Dir vorgeschlagenen DllImport

    [DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr RtlCompareMemory(byte[] b1, byte[] b2, IntPtr length);
    

    benutze und dazu folgenden Aufruf

      case compRtlCompareMemory: // [DllImport] kernel32.dll RtlCompareMemory
    
        IntPtr len = (IntPtr)LeseLen;
        IntPtr rc = RtlCompareMemory(ByPu1, ByPu2, len);
    
        if ((int) rc != LeseLen)
        {
            ByteDiff++; // es gibt Differenzen
            return;     // Vergleich abbrechen
        }
        break;
    

    erhalte ich folgende Fehlermeldung beim Ausführen:

    System.EntryPointNotFoundException: "Der Einstiegspunkt "RtlCompareMemory" wurde nicht in der DLL "kernel32.dll" gefunden."

    Ich benutze Windo10 Pro 64 bit die aktuelle 22H2 Version.

    @Th69 sagte in Warum klappt Binary bei mir nicht:

    zuletzt noch byte [= 1])

    Dazu habe ich noch eine Frage

     if ((length & 1) != 0) 
       return *x1 == *x2;
    

    Müsste das nicht so lauten ?

     if ((length & 1) != 0) 
      return *(byte*)x1 == *(byte*)x2;
    

    bei *x1 == *x2 würden doch 8 Bytes verglichen, obwohl nur noch eines im Puffer vorhanden ist.



  • Dieser Beitrag wurde gelöscht!


  • @hkdd: Ja, du hast Recht, der Code ist jetzt komplett falsch.
    Ich wollte die Hauptschleife direkt mit long-Zeiger durchführen, anstatt den Cast zu verwenden (und x++ anstatt x += 8), aber danach wird für die Berechnungen ein byte* benötigt...
    Mea culpa - also nimm wieder den Originalcode aus dem von mir verlinkten Artikel.

    Warum jedoch "kernel32.dll" bei dir nicht funktioniert, weiß ich nicht - ich bin davon ausgegangen, daß "ntdll.dll" eher für Treiber ist (ich selber habe auch Win10 Pro, aber noch 21H2).
    Evtl. findet er bei dir im PATH eine andere "kernel32.dll" (das wäre dann aber generell fatal) - ruf mal "where kernel32.dll" in der Konsole auf.



  • @Th69
    ich denke so ist es OK in der letzten Zeile.

      return *(byte*)x1 == *(byte*)x2;
    


  • Nee, dann passen die Inkrements x1 += 4 etc. nicht!



  • @Th69
    so sieht es bei mir jetzt aus

            //========================================
            // Zwei Byte-Array vergleichen
            //========================================
            static unsafe bool MemCmp(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 = 8 Bytes = 64 Bit
                    for (int i = 0; i < length / sizeof(long); i++, x1++, x2++)
                        if (*x1 != *x2) // 8 Bytes vergleichen
                            return false;
                    // Rest 0..7 vergleichen
                    if ((length & 4) != 0) // Rest >= 4 ?
                    {
                        if (*(int*)x1 != *(int*)x2) // 4 Bytes vergleichen
                            return false;
                        x1 += 4;
                        x2 += 4;
                    }
                    if ((length & 2) != 0) // Rest >= 2 ?
                    {
                        if (*(short*)x1 != *(short*)x2) // 2 Bytes vergleichen
                            return false;
                        x1 += 2;
                        x2 += 2;
                    }
                    if ((length & 1) != 0) // Rest = 1 ?
                        return *(byte*)x1 == *(byte*)x2; // 1 Byte vergleichen
    
                    return true;
                }
            } //  static unsafe bool MemCmp(byte[] a1, byte[] a2, int length)
    

    was klappt da nicht.
    wenn das x1 += 4; steht macht der + 8 * 4 statt nur +4 ?



  • Exakt, dann werden 4 long-Adressen aufaddiert, anstatt 4 Bytes.
    Nimm byte* x1 = p1, x2 = p2 sowie die Originalschleife aus 5 ways to compare two byte arrays. Benchmarking ("Optimized unsafe method from Hafor Stefanson").



  • @Th69
    ich habe gerade bei Debugger nachgesehen.
    der pointer x1 wird von 49a8 auf 49c8 erhöht, d.h. + 0x020 = +32 = +4*8



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

    sowie die Originalschleife aus

    Ich glaube, dort ist auch eine kleine Unkorrektheit

         static unsafe bool MemCmp(byte[] a1, byte[] a2, int length)
            {
                if (a1 == null || a2 == null || a1.Length != a2.Length || a1.Length < length)
                    return false;
                fixed (byte* p1 = a1, p2 = a2)
                {
                    byte* x1 = p1, x2 = p2;
                    int l = a1.Length;
                    for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8)
                        if (*((long*)x1) != *((long*)x2))
                            return false;
    
                    if ((l & 4) != 0)
                    {
                        if (*((int*)x1) != *((int*)x2)) return false;
                        x1 += 4;
                        x2 += 4;
                    }
                    if ((l & 2) != 0)
                    {
                        if (*((short*)x1) != *((short*)x2)) return false;
                        x1 += 2;
                        x2 += 2;
                    }
                    if ((l & 1) != 0)
                        if (*((byte*)x1) != *((byte*)x2))
                            return false;
                    return true;
                }
            }
    

    Das wird mit int l = a1.Length; die Länge von a1 benutzt, um später die zu vergleichende Restlänge zu bestimmen.
    Wenn a1.Length > length ( zu vergleichende Länge bei großen Dateien ein Zwischenblock, der meistens ein Vielfaches von 16 lang ist) ist, dann muss die Restlänge aus length und nicht aus l bestimmt werden. Das war bei Deiner Version richtig.



  • Den length-Parameter habe ich ja hinzugefügt, und die Bedingung entsprechend geändert.

    Ich habe aber jetzt mal meine Variante abgewandelt (und noch ca. 0.2s Performance herausgeholt: 10.2s statt vorher 10.4s):

    static unsafe bool MemCmp(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;
    	}
    }
    

    PS:
    Was ich die ganze Zeit schon schreiben wollte:
    Diese Methode geht davon aus, daß die übergebenen byte-Arrays immer auf einer long-Adresse beginnen (da sie ja vom OS bereitgestellt werden).
    In der nativen memcmp wird jedoch auch überprüft, ob die Anfangsadresse "ungerade" ist, s. z.B. memcmp.c.



  • @Th69
    jetzt ist das ja alles korrekt und sauber programmiert, falls int mal 20 Bytes lang wird in der übernächsten Generation.
    Ich habe mal versucht, auch noch decimal einzubinden ( 16 Bytes statt long=8 Bytes), das klappt, es dauert aber länger.



  • Decimal ist kein nativer Prozessor-Datentyp, daher hier nicht zu gebrauchen.



  • @Th69
    bei c++ gibt es long long, das kennt C# wohl (noch) nicht.

    So ist es bei Delphi

    Ausrichtung von strukturierten Typen
    Per Voreinstellung sind die Werte in einem strukturierten Typ zur Beschleunigung des Zugriffs an Word- oder Double-Word-Grenzen ausgerichtet.

    Ich habe mir im Debugger die Adressen angeschaut, sie haben in der letzten Stelle eine 8, sind demnach auf long-Grenzen ausgerichtet.



  • @hkdd

    In C# gibt´s Int64 und UInt64.



  • Bei C++ sind die Datentypen aber nur mit Mindestgrößen definiert und je nach Plattform unterschiedlich groß. Aber bei einem 64Bit System macht es keinen Sinn noch größere Datentypen zum iterieren zu verwenden (denn C# long = Int64).
    Und noch größere Prozessor-Adressbusbreiten (für PCs) machen keinen Sinn, denn 18 Trillionen (~ 18 Mrd. GB) hat man auch in weiterer Zukunft wohl nicht als RAM zur Verfügung.



  • @Th69
    es geht ja nicht nur um Adressen, sondern um Daten.
    Ich habe vor Jahren ein Programm zur Ermittlung der IBAN aus Konto und Bankleitzahl geschrieben.
    Um die Prüfziffer zu berechnen muss man Modulo rechnen mit sehr langen Zahlen, die hatte ich damals in Delphi nicht.
    Jetzt gibt es in C# BigInteger, damit war das problemlos möglich.


  • Mod

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



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

    Um die Prüfziffer zu berechnen muss man Modulo rechnen mit sehr langen Zahlen, die hatte ich damals in Delphi nicht.

    Lange Zahlen braucht man nicht. In einem Produkt (a*b*c) % m kann man das Modulo nach jedem einzelnen Schritt auswerten. Ist also dasselbe wie ((((a%m)*(b%m))%m) * c) % m



  • @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;
            }
    

Anmelden zum Antworten