: C# 2008 Programmer

Decompression

Decompression

The following Decompress() function decompresses the data compressed by the Compress() function. The first parameter specifies the algorithm to use, while the byte array containing the compressed data is passed in as the second parameter, which is then copied into a MemoryStream object.

static byte[] Decompress(string algo, byte[] data) {
try {
//---copy the data (compressed) into ms---
MemoryStream ms = new MemoryStream(data);
Stream zipStream = null;
//---decompressing using data stored in ms---
switch (algo) {
case "Gzip":
zipStream =
new GZipStream(ms, CompressionMode.Decompress, true);
break;
case "Deflat":
zipStream =
new DeflateStream(ms, CompressionMode.Decompress, true);
break;
default:
return null;
}
//---used to store the de-compressed data---
byte[] dc_data;
//---the de-compressed data is stored in zipStream;
// extract them out into a byte array---
dc_data = RetrieveBytesFromStream(zipStream, data.Length);
return dc_data;
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
return null;
}
}

The compression classes then decompress the data stored in the memory stream and store the decompressed data into another Stream object. To obtain the decompressed data, you need to read the data from the Stream object. This is accomplished by the RetrieveBytesFromStream() function, which is defined next:

static byte[] RetrieveBytesFromStream(Stream stream, int bytesblock) {
//---retrieve the bytes from a stream object---
byte[] data = null;
int totalCount = 0;
try {
while (true) {
//---progressively increase the size of the data byte array---
Array.Resize(ref data, totalCount + bytesblock);
int bytesRead = stream.Read(data, totalCount, bytesblock);
if (bytesRead == 0) {
break;
}
totalCount += bytesRead;
}
//---make sure the byte array contains exactly the number
// of bytes extracted---
Array.Resize(ref data, totalCount);
return data;
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
return null;
}
}

The RetrieveBytesFromStream() function takes in two parameters a Stream object and an integer and returns a byte array containing the decompressed data. The integer parameter is used to determine how many bytes to read from the stream object into the byte array at a time. This is necessary because you do not know the exact size of the decompressed data in the stream object. And hence it is necessary to dynamically expand the byte array in blocks to hold the decompressed data during runtime. Reserving too large a block wastes memory, and reserving too small a block loses valuable time while you continually expand the byte array. It is therefore up to the calling routine to determine the optimal block size to read.

The block size is the size of the compressed data (data.Length):

//---the de-compressed data is stored in zipStream;
// extract them out into a byte array---
dc_data = RetrieveBytesFromStream(zipStream, data.Length);

In most cases, the uncompressed data is a few times larger than the compressed data, so you would at most expand the byte array dynamically during runtime a couple of times. For instance, suppose that the compression ratio is 20% and the size of the compressed data is 2MB. In that case, the uncompressed data would be 10MB, and the byte array would be expanded dynamically five times. Ideally, the byte array should not be expanded too frequently during runtime because it severely slows down the application. Using the size of the compressed data as a block size is a good compromise.

Use the following statements to test the Compress() and Decompress() functions:

static void Main(string[] args) {
byte[] compressedData =
Compress("Gzip", System.Text.Encoding.ASCII.GetBytes(
"This is a uncompressed string"));
Console.WriteLine("Compressed: {0}",
ASCIIEncoding.ASCII.GetString(compressedData));
Console.WriteLine("Uncompressed: {0}",
ASCIIEncoding.ASCII.GetString(Decompress("Gzip", compressedData)));
Console.ReadLine();
}

The output is as shown in Figure 11-9.


Figure 11-9

The compressed data contains some unprintable characters, so you may hear some beeps when it prints. To display the compressed data using printable characters, you can define two helper functions byteArrayToString() and stringToByteArray():

//---converts a byte array to a string---
static string byteArrayToString(byte[] data) {
//---copy the compressed data into a string for presentation---
System.Text.StringBuilder s = new System.Text.StringBuilder();
for (int i = 0; i <= data.Length - 1; i++) {
if (i != data.Length - 1) s.Append(data[i] + " ");
else s.Append(data[i]);
}
return s.ToString();
}
//---converts a string into a byte array---
static byte[] stringToByteArray(string str) {
//---format the compressed string into a byte array---
string[] eachByte = str.Split(' ');
byte[] data = new byte[eachByte.Length];
for (int i = 0; i <= eachByte.Length - 1; i++)
data[i] = Convert.ToByte(eachByte[i]);
return data;
}

To use the two helper functions, make the following changes to the statements:

static void Main(string[] args) {
byte[] compressedData =
Compress("Gzip", System.Text.Encoding.ASCII.GetBytes(
"This is a uncompressed string"));
string compressedDataStr = byteArrayToString(compressedData);
Console.WriteLine("Compressed: {0}", compressedDataStr);
byte[] data = stringToByteArray(compressedDataStr);
Console.WriteLine("Uncompressed: {0}",
ASCIIEncoding.ASCII.GetString(Decompress("Gzip", data)));
Console.ReadLine();
}

Figure 11-10 shows the output when using the two helper functions.


Figure 11-10

Alternatively, you can also convert the compressed data to a Base64-encoded string, like this:

byte[] compressedData =
Compress("Gzip", System.Text.Encoding.ASCII.GetBytes(
"This is a uncompressed string"));
string compressedDataStr = Convert.ToBase64String(compressedData);
Console.WriteLine("Compressed: {0}", compressedDataStr);
byte[] data = Convert.FromBase64String((compressedDataStr));
Console.WriteLine("Uncompressed: {0}",
ASCIIEncoding.ASCII.GetString(Decompress("Gzip", data)));

Figure 11-11 shows the output using the base64 encoding.


Figure 11-11


: 1.490. /Cache: 3 / 1