Blockpadding mit Strings



  • Hallo !

    Ich habe hier einen Code der String auf eine bestimmte Größe bringt. -> Blockpadding. Zur Funktionsweise: Ein String von zum 10 zeichen muss auf eine Blockgrösse von 128-Bit (Stringlänge muss durch 16 teilbar sein) gebracht werden. Das heißt man füllt ihn auf mit den fehlenden 6 Zeichen. Zum Auffüllen werden 0 benutzen und die letzte Zahl(einstellig oder zweistellig) gibt die Anzahl der Füllbytes an.

    Jetzt tut der Code auch eigentlich genau dieses, aber es kommt mir so vor als könnte man etwas an ihm pfeilen bzw. optimieren.

    Würde mir wohl jemand dabei behilflich sein ?

    Hier der Code ...

    #include <iostream>
    using namespace std;
    
    #include <mbstring.h>  /* Gibt's die nur im MS-VC++ ??? */
    
    /* gibt die Grösse des Blockpaddings zurück */
    inline unsigned int __fastcall len_with_padding(unsigned len, int blocksize) {
    
    	if (!(len % blocksize)) return len;  
    
    	return (len + (blocksize - ( len % blocksize)));
    }
    
    /* füllt den String "out" auf */
    unsigned int str_blockpadding(const unsigned char *in, unsigned char *out, unsigned int len, int blocksize) {
    
    	register int i, lenZero;
    	unsigned int padlen  = (len_with_padding(len,blocksize) - len);
    	unsigned int padZero;
    
    	if (padlen > 8) { padZero = padlen - 1; } else { padZero = padlen; }
    
    	cout << "padlen: " << padlen << endl << "padZero: " << padZero << endl << endl << endl;
    
    	memcpy(out, in, len);
    
    	if (padlen != 0) {
    
    		for (i = 0; i < (padZero - 1); i++) { 
    
    			sprintf((char*)out + (len + i), "%d", 0);
    			lenZero = i + 1;
    
    		}
    
    		sprintf((char*)out + (len + lenZero), "%d", padlen);
    
    	}
    
    	return padlen;
    
    }
    
    int main(void) {
    
    	int padlen;
    	unsigned char in[] = "Schönen Guzen Tag auch";
    
    	padlen = len_with_padding(_mbslen(in), 16);
    
    	unsigned char *out = new unsigned char[padlen];
    
    	cout << "Funktionsrückgabe : " << str_blockpadding(in, out, _mbslen(in), 16) << endl << endl;
    
    	out[padlen] = '\0';
    
    	cout << "Neuer String: " << out << endl << endl;
    
    	cout << "Länge von in : " << _mbslen(in) << endl << "Länge von out: " << _mbslen(out) << endl << endl;
    
    	return 0;
    }
    

    P.S.: Habe es nicht im C++ Forum gepostet da ich die Header Datei <mbstring.h> benutze und die ist imho nur im MS-VC++ zu Hause.

    P.P.S.: Die Funktion str_blockpadding ruft im Release nicht zusätzlich die Funktion len_with_padding auf. Dafür gibt es dann einen zusätzlichen Funktionsparameter. Habe ich jtzt nur der Übersichtlichkeit gemacht.

    🙂



  • a) padlen:

    unsigned int len_with_padding(unsigned len, int blocksize) 
    { 
      len = len + blocksize - 1;
      return len - (len % blocksize);
    } 
    
    // oder noch schneller - aber nur wenn blocksize eine zweierpotenz ist
    unsigned int len_with_padding(unsigned len, int blocksize) 
    { 
      blocksize -= 1;
      return (len + blocksize) & ~(blocksize);
    }
    
    unsigned int str_blockpadding(const unsigned char *in, unsigned char *out, unsigned int len, int blocksize) { 
    
        int i, lenZero; 
        unsigned int padlen  = (len_with_padding(len,blocksize) - len); 
    
        memcpy(out, in, len);
        if (padlen > 0)
        {
           int padzero = padlen-1;   // ähnliche rechnung wie bei dir - glaub ich
           if (padlen >= 10) 
             --padzero;            
    
           out += len;
           if (padzero)
           {   // (1) memset instead of repeated sprintf:
              memset(out+len, '0', padzero);  // mit 0 füllen
              out += padzero;
           }
    
           // (2) "custom itoa" instead of sprintf:
    
           // if you have you can use _itoa(padlen, out+len+padzero, 10); 
           // or - for ANSI compatibility - 
           if (padlen >= 10)
             *out++ = '0' + (padlen/10);
    
           *out++ = '0' + (padlen % 10);
        }
    
        return padlen; 
    }
    

    Unter vorbehalt von Obi-Ones.



  • Super. Herzlichen Dank, das sieht schon einmal ganz gut aus. Aber ein paar Fragen hätte ich da noch ...

    peterchen schrieb:

    a) padlen:

    Ja. Eigentlich ist 16 doch eine Zweierpotenz. 8 auch, da ich auch "8ter" Blockpadding brauche. Oder irre ich mich da jetzt ?
    Hier verstehe ich allerdings nicht den theoretischen Teil, wie man durch Bitmanipulation extrem effizient rechnet wie z.B. auch * und / mit 2er-Potenzen.

    Den b)-Teil verstehe ich allerdings nicht ganz. Mit memset ist klar. 😉 Hätte ich auch selber drauf kommen können. Aber die _itoa Funktion (ya' i have) kann ich nicht ganz nachvollziehen. Habe auch schon in der MSDN "geblättert".

    Trotzdem ersteinmal "Besten Dank" 🙂



  • a) ja - alles was 2^n ist (2, 4, 8, 16, 32, 64, 128, 256, ...)

    Kurzübersicht:
    x >> n = (x / (2^n))
    x << n = (x * (2^n))

    x & (2^n-1) == x % 2^n
    x & ~(2^n-1) == x - x(x % 2^n)

    x |= 1 == if ((x%2)==0) ++x

    ----------
    ich versuch', das sprintf durch was einfacheres zu ersetzten.
    MSVC kennt _itoa (viele compiler haben ähnliches, nur ist im ANSI-Standard glaub ich nix dabei)

    Mit den letzten modifikationen (out "weiterrücken") wäre das natürlich _itoa(padlen, out, 10) 🤡

    Die Hand-Implementierung (geht von einem zeichensatz aus, bei dem die Ziffern sequentiell liegen, ist also nicht über-portabel):

    - wenn padlen >= 10 (zwei ziffrig),
    padlen/10 ==> erste Ziffer (0..9, hofft man)
    +'0' (addiert code von '0', so das man - mit obiger annahme - das entsprechende Ziffernzeichen erhält)

    das gleiche noch mal für den rest (padlen%10)

    HTH



  • peterchen schrieb:

    Mit den letzten modifikationen (out "weiterrücken") wäre das natürlich _itoa(padlen, out, 10)

    Hmm. Theoretisch könnte man auch wieder ein memset nutzen mit if, je nachdem ob das Padding zwei-/einstellig ist, ne ?
    Erscheint mir immernoch sicherer besser gesagt klarer als ein gewagtes _itoa bzw. die portable Handimplementierung.

    P.S.: Portabel muss es ja nicht sein. Soll ja als gelinktes Kompilat herausgehen. 🙂



  • > Theoretisch könnte man auch wieder ein memset nutzen mit if, je nachdem ob das Padding zwei-/einstellig ist, ne ?

    nee, da mußte das ja die Zahl wieder genauso auseinandernehmen. Das memset lohnt sich nur, wenn du mehrere bytes hintereinander auf den gleichen Wert setzt. Ansonsten halt das out[index] = ... (bzw. *out++=)

    Im endeffekt ist ein sprintf hier die portable lösung und nicht sooo schlimm (es sei denn du machst das 10.000 mal hinterienander)


Log in to reply