public static class XCompress { public static byte[] DeCompress(byte[] data, ref ushort dataSize) { const byte flag_Comp = 0xE0; // Bin: 1110 0000 const byte flag_Maske = 0x1F; // Bin: 0001 1111 // Zahlenbereich 0 bis 31 // Das kleinste was gepackt werden kann, ist 5x das gleiche Zeichen (also 5 Byte) auf: // + + <1. Zeichen> + = 4 Byte. // Es macht also keinen Sinn ein Byte-Array zu versuchen zu komprimieren, wenn es kleiner als 5 Bytes ist. // Entsprechend ist jedes Byte-Array das kleiner 4 Bytes ist, auf garkeinen Fall ein komprimiert. if(dataSize < 4) return data; // Pos 0. Hier ist zur Prüfung für die Dekomprimierung, das allererste Byte der (komprimierten) Nutzdaten. // Um die Prüfung noch sicherer zu machen, ist das Byte der Nutzdaten mit ^ flag_Comp gemischt. // Schlägt die Prüfung fehl, dann handelt es sich NICHT um komprimierte Daten! => Return Input-Daten. // Zwischen diesem Prüf-Byte auf Pos 0, und dem allererste Byte der (komprimierten) Nutzdaten, befindet sich // die Soll-Größe der 'dekomprimierten' Daten. Weil die Soll-Größe entweder als 1, oder 2 Byte gespeichert wird, // liegt das allererste Byte der (komprimierten) Nutzdaten, entweder an Pos 2 oder Pos 3. int dataSizeSoll; int posIStart = 2; if(data[0] == (byte)(flag_Comp ^ (data[2]))) dataSizeSoll = data[1]; else if(data[0] == (byte)(flag_Comp ^ (data[3]))) { posIStart = 3; dataSizeSoll = (ushort) (data[1] | data[2] << 8); } else return data; // Die 'dekomprimierte' Größe (dataSizeSoll) wäre IMMER größer als die Eingangs-Daten, weil wenn beim komprimiervorgang // die Größe nicht kleiner geworden wäre, dann hätte die Komprimier-Funktion, einfach nur die Original-Daten // zurück gegeben. Wenn also nun die 'dekomprimierte' Größe (dataSizeSoll) <= Eingangsdaten ist, dann muss das daran liegen, // weill die Eingangsdaten unkomprimiert sind und einfach nur Zufällig bei 'Pos 0' entsprechende Daten standen. if(dataSizeSoll <= dataSize) return data; byte [] dOut = new byte[dataSizeSoll]; int posO = 0; byte c; byte cLastWrite = 0; byte repeat = 0; for(int posI = posIStart ; posI < dataSize ; ++posI) { // Entpackte Daten wären größer als Soll! Input-Daten sind offenbar NICHT komprimierte Daten! => Return Input-Daten if(posO >= dataSizeSoll) return data; c = data[posI]; // flag_Comp (mit repeat 0) als Signal, dass das nächste Zeichen kein Steuerzeichen ist (obwohl es die Bits von flag_Comp enthält) if( c == flag_Comp ) { cLastWrite = data[++posI]; // Nächstes Zeichen lesen dOut[posO++] = cLastWrite; continue; } if( ((byte)(c & flag_Comp)) == flag_Comp ) { repeat = ((byte)(c & flag_Maske)); if(posO + repeat > dataSizeSoll) return data; for(int r = 0 ; r < repeat ; ++r) dOut[posO++] = cLastWrite; continue; } dOut[posO++] = c; cLastWrite = c; } // Als 2. Prüfung für die Dekomprimierung, die Größe der Originaldaten (soll) verglichen mit den nun entpackten Daten. // Schlägt die Prüfung fehl, dann handelt es sich NICHT um komprimierte Daten! => Return Input-Daten if(dataSizeSoll != posO) return data; dataSize = (ushort)posO; return dOut; } public static byte[] Compress(byte[] data, ref ushort dataSize) { const byte flag_Comp = 0xE0; // Bin: 1110 0000 // const byte flag_Maske = 0x1F; // Bin: 0001 1111 // Zahlenbereich 0 bis 31 // Das kleinste was gepackt werden kann, ist 5x das gleiche Zeichen (also 5 Byte) auf: // + + <1. Zeichen> + = 4 Byte. // Es macht also keinen Sinn ein Byte-Array zu versuchen zu komprimieren, wenn es kleiner als 5 Bytes ist. if(dataSize < 5) return data; byte [] dOut = new byte [dataSize + 100]; int dOut_max = dOut.Length-8; byte c; byte repeat = 0; byte cLastWrite = (byte)(data[0]+254); // irgend ein Wert, der aber anders sein muss als das was c nun gleich bei Pos-0 einlesen wird int posO = 1; // Pos 0 überspringen. Hier kommt am Ende der Komprimierung, eine Zahl zur Prüfung rein für die spätere Dekomprimierung. // Speichern der Soll-Size zur prüfung beim enpacken. if(dataSize <= 255) dOut[posO++] = (byte)dataSize; // Als 2. Prüfung für die Dekomprimierung, an Pos 1 (Pos oder 1 und 2), die Größe der Originaldaten. else { dOut[posO++] = (byte) (dataSize & 0xFFU); dOut[posO++] = (byte) ((dataSize & 0xFF00U) >> 8); } for(int posI = 0 ; posI < dataSize ; ++posI) { // Komprimierte Größe würde die Originalgrößse übersteigen! => Abbruch und Original Array zurück geben. if(posO >= dOut_max) return data; c = data[posI]; if(c == cLastWrite) ++repeat; // Das gerade gelesene Zeichen, ist identisch zum zuletzt ausgegebenen Zeichen else if( ((byte)(c & flag_Comp)) == flag_Comp ) dOut[posO++] = flag_Comp; // flag_Comp (mit repeat 0) als Signal, dass das nächste Zeichen kein Steuerzeichen ist (obwohl es die Bits von flag_Comp enthält) if(c != cLastWrite || repeat >= 31 || posI + 1 == dataSize) { // Erst Output, wenn ein eingelesenes Zeichen unterschiedlich ist zum zuletzt ausgegebenen Zeichens if(repeat > 0) { // Gab es eine wiederholung des zuletzt ausgegebenen zeichens? if(repeat <= 1) { //if: Zu wenige wiederholungen als das sich eine Komprimierung lohnt. deswegen einfach nur das Zeichen weiderholt ausgeben if( ((byte)(cLastWrite & flag_Comp)) == flag_Comp ) dOut[posO++] = (byte)(flag_Comp | (repeat)); else dOut[posO++] = cLastWrite; } else { //else: Genug wiederholungen des letzten Zeichens, so das es sich lohnt zu komprimieren // repeat-1 weil repeat sowieso immer über 0 wäre. Also wird 0 als 1 imterpretiert. // Dadurch erhöht sich der Zahlenbereich der möglichen Wiederholungen um 1 dOut[posO++] = (byte)(flag_Comp | (repeat)); } repeat = 0; } // Output des gerade neu gelesenen Zeichens (dies bildet auch wiederum // die grundlage für evtl. nachfolgende Wiederholungen)... // ...und merken was zuletzt geschrieben wurde. if(c != cLastWrite) { dOut[posO++] = c; cLastWrite = c; } } } // Komprimierte Größe übersteigt die Originalgrößse! => Abbruch und Original Array zurück geben. if(posO >= dataSize) return data; // Pos 0. Hier kommt zur Prüfung für die spätere Dekomprimierung, das allererste Byte der (komprimierten) Nutzdaten // Um die Prüfung noch sicherer zu machen, wird das Byte der Nutzdaten mit ^ flag_Comp gemischt. if(dataSize <= 255) dOut[0] = (byte)(flag_Comp ^ (dOut[2])); else dOut[0] = (byte)(flag_Comp ^ (dOut[3])); dataSize = (ushort)posO; return dOut; } }