//----------------------------------------------------------- // TOOLS // C++ Templates & Tools, 2nd Edition //----------------------------------------------------------- // // TestHuffman.cpp // // Test Huffman compression class // //----------------------------------------------------------- // Copyright 1996 by Scott Robert Ladd. All rights reserved. //----------------------------------------------------------- #include "strstrea.h" #include "fstream.h" #include "iomanip.h" #include "string.h" #include "sys/types.h" #include "sys/stat.h" #include "Huffman.h" #include "RandDev.h" #include "ToolsThread.h" using namespace Coyote; //---------------------- // Test data compression //---------------------- static const char Text1[] = " WHEN I WROTE the following pages, or rather the bulk\r\n" "of them, I lived alone, in the woods, a mile from any\r\n" "neighbor, in a house which I had built myself, on the\r\n" "shore of Walden Pond, in Concord, Massachusetts, and\r\n" "earned my living by the labor of my hands only. I lived\r\n" "there two years and two months. At present I am a\r\n" "sojourner in civilized life again.\r\n" " I should not obtrude my affairs so much on the notice\r\n" "of my readers if very particular inquiries had not been\r\n" "made by my townsmen concerning my mode of life, which\r\n" "some would call impertinent, though they do not appear\r\n" "to me at all impertinent, but, considering the\r\n" "circumstances, very natural and pertinent. Some have\r\n" "asked what I got to eat; if I did not feel lonesome; if\r\n" "I was not afraid; and the like. Others have been\r\n" "curious to learn what portion of my income I devoted to\r\n" "charitable purposes; and some, who have large families,\r\n" "how many poor children I maintained. I will therefore\r\n" "ask those of my readers who feel no particular interest\r\n" "in me to pardon me if I undertake to answer some of\r\n" "these questions in this book. In most books, the I, or\r\n" "first person, is omitted; in this it will be retained;\r\n" "that, in respect to egotism, is the main difference. We\r\n" "commonly do not remember that it is, after all, always\r\n" "the first person that is speaking. I should not talk\r\n" "so much about myself if there were anybody else whom\r\n" "I knew as well. Unfortunately, I am confined to this\r\n" "theme by the narrowness of my experience. Moreover, I,\r\n" "on my side, require of every writer, first or last, a\r\n" "simple and sincere account of his own life, and not\r\n" "merely what he has heard of other men's lives; some\r\n" "such account as he would send to his kindred from a\r\n" "distant land; for if he has lived sincerely, it must\r\n" "have been in a distant land to me. Perhaps these pages\r\n" "are more particularly addressed to poor students. As\r\n" "for the rest of my readers, they will accept such\r\n" "portions as apply to them. I trust that none will\r\n" "stretch the seams in putting on the coat, for it may\r\n" "do good service to him whom it fits.\r\n" "(WALDEN, H.D. THOREAU)"; static const char Text2[] = "A SIMPLE STRING TO BE ENCODED USING A MINIMAL NUMBER OF BITS"; static const TEST_SZ = 100000; static char Text3[TEST_SZ]; void TestHuffman ( void * args ) { // retrieve parameters for simpler syntax ToolsThreadData * ttd = (ToolsThreadData *)args; HINSTANCE inst = ttd->inst; HWND wdw = ttd->mainWdw; ostrstream & buffer = *(ttd->ostrm); // change main window heading SetWindowText(wdw,"Huffman (internal compression tests)"); // display a headline buffer << "Huffman compression test\r\n" "------------------------\r\n\r\n"; // test on text size_t tlen1 = strlen(Text1) + 1; size_t tlen2 = strlen(Text2) + 1; Huffman h1(Text1,tlen1); buffer << "was " << tlen1 << " and now is " << h1.GetEncodedLen() << "\r\n\r\n"; HuffDecoded dec = h1.GetDecoded(); buffer << "original:\r\n" << Text1 << "\r\n\r\n"; buffer << "after encoding and decoding:\r\n" << (const char *)dec.GetData() << "\r\n\r\n"; buffer << "original:\r\n" << Text2 << "\r\n\r\n"; Huffman h2(Text2,tlen2); dec = h2.GetDecoded(); buffer << "after encoding and decoding:\r\n" << (const char *)dec.GetData() << "\r\n\r\n"; // change main window heading SetWindowText(wdw,"Huffman (buffer tests)"); // test on random file size_t i; RandDev gen; for (i = 0; i < TEST_SZ; ++i) Text3[i] = gen(256); Huffman h3(Text3,TEST_SZ); dec = h3.GetDecoded(); const char * ptr = (const char *)dec.GetData(); for (i = 0; i < TEST_SZ; ++i) { if ((*ptr) != Text3[i]) { buffer << "Text3 random test: error @" << i << "\r\n"; break; } ++ptr; } if (i == TEST_SZ) buffer << "Text3 random test: compressed & decompressed okay!\r\n"; // test on single-value file for (i = 0; i < TEST_SZ; ++i) Text3[i] = 1; Huffman h4(Text3,TEST_SZ); dec = h4.GetDecoded(); ptr = (const char *)dec.GetData(); for (i = 0; i < TEST_SZ; ++i) { if ((*ptr) != Text3[i]) { buffer << "Text3 all-one test: error @" << i << "\r\n"; break; } ++ptr; } if (i == TEST_SZ) buffer << "Text3 all-one test: compressed & decompressed okay!\r\n"; // test on single-value file for (i = 0; i < TEST_SZ; ++i) Text3[i] = 0; Huffman h5(Text3,TEST_SZ); dec = h5.GetDecoded(); ptr = (const char *)dec.GetData(); for (i = 0; i < TEST_SZ; ++i) { if ((*ptr) != Text3[i]) { buffer << "Text3 all-zero test: error @" << i << "\r\n"; break; } ++ptr; } if (i == TEST_SZ) buffer << "Text3 all-zero test: compressed & decompressed okay!\r\n"; // Test on two-byte file const char TEST_CHAR1 = 'H'; const char TEST_CHAR2 = 'U'; Text3[0] = TEST_CHAR1; Text3[1] = TEST_CHAR2; Huffman h6(Text3,2); dec = h6.GetDecoded(); ptr = (const char *)dec.GetData(); if ((ptr[0] == TEST_CHAR1) && (ptr[1] == TEST_CHAR2)) buffer << "Text3 two-byte test: compressed & decompressed okay!\r\n"; else buffer << "Text3 two-byte test: ERROR!\r\n"; // Test on one-byte file Text3[0] = TEST_CHAR1; Huffman h7(Text3,1); dec = h7.GetDecoded(); ptr = (const char *)dec.GetData(); if (*ptr == TEST_CHAR1) buffer << "Text3 one-byte test: compressed & decompressed okay!\r\n"; else buffer << "Text3 one-byte test: ERROR!\r\n"; // now test saving and restoring Huffman data const char * TESTFILE = "huffman.dat"; // change main window heading SetWindowText(wdw,"Huffman (stream I/O tests)"); // open output file ofstream ofile(TESTFILE,ios::out | ios::binary | ios::trunc); // store data ofile << h1; // close output file ofile.close(); // open input file ifstream ifile(TESTFILE,ios::in | ios::binary); // read compressed data ifile >> h1; // display output dec = h1.GetDecoded(); buffer << "\r\nafter writing and reading to file:\r\n" << (const char *)dec.GetData() << "\r\n\r\n"; // close input file ifile.close(); // now compress an entire file const char * FLorig = "Database.txt"; const char * FLcomp = "Database.huf"; const char * FLdcmp = "Database.dec"; // change main window heading SetWindowText(wdw,"Huffman (file compression tests)"); // find length of file struct _stat flstat; _stat(FLorig,&flstat); // open a stream ifstream flin(FLorig,ios::in|ios::binary); // compress it Huffman flhuf(flin,flstat.st_size); // show statistics buffer << "\r\n" << FLorig << " was " << flstat.st_size << " bytes long, compressed to " << flhuf.GetEncodedLen() << " bytes.\r\n\r\n"; // write compressed data to disk ofstream flout(FLcomp,ios::out|ios::binary|ios::trunc); flout << flhuf; flout.close(); // read compressed file ifstream flcomp(FLcomp,ios::in|ios::binary); flcomp >> flhuf; flcomp.close(); flin.seekg(0,ios::beg); // decompress to original dec = flhuf.GetDecoded(); // write decompressed information ofstream fldone(FLdcmp,ios::out|ios::binary|ios::trunc); fldone.write((const char *)dec.GetData(),dec.GetLen()); fldone.close(); // Done TerminateTest(wdw); }