| 1 | // -------------------------------------------------------------------------- |
|---|
| 2 | // |
|---|
| 3 | // File |
|---|
| 4 | // Name: testcompress.cpp |
|---|
| 5 | // Purpose: Test lib/compress |
|---|
| 6 | // Created: 5/12/03 |
|---|
| 7 | // |
|---|
| 8 | // -------------------------------------------------------------------------- |
|---|
| 9 | |
|---|
| 10 | #include "Box.h" |
|---|
| 11 | |
|---|
| 12 | #include <stdio.h> |
|---|
| 13 | #include <string.h> |
|---|
| 14 | |
|---|
| 15 | #include "Test.h" |
|---|
| 16 | #include "Compress.h" |
|---|
| 17 | #include "CompressStream.h" |
|---|
| 18 | #include "CollectInBufferStream.h" |
|---|
| 19 | |
|---|
| 20 | #include "MemLeakFindOn.h" |
|---|
| 21 | |
|---|
| 22 | #define DATA_SIZE (1024*128+103) |
|---|
| 23 | #define CHUNK_SIZE 2561 |
|---|
| 24 | #define DECOMP_CHUNK_SIZE 3 |
|---|
| 25 | |
|---|
| 26 | // Stream for testing |
|---|
| 27 | class CopyInToOutStream : public IOStream |
|---|
| 28 | { |
|---|
| 29 | public: |
|---|
| 30 | CopyInToOutStream() : currentBuffer(0) {buffers[currentBuffer].SetForReading();} |
|---|
| 31 | ~CopyInToOutStream() {} |
|---|
| 32 | int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite) |
|---|
| 33 | { |
|---|
| 34 | if(buffers[currentBuffer].StreamDataLeft()) |
|---|
| 35 | { |
|---|
| 36 | return buffers[currentBuffer].Read(pBuffer, NBytes, Timeout); |
|---|
| 37 | } |
|---|
| 38 | |
|---|
| 39 | // Swap buffers? |
|---|
| 40 | if(buffers[(currentBuffer + 1) & 1].GetSize() > 0) |
|---|
| 41 | { |
|---|
| 42 | buffers[currentBuffer].Reset(); |
|---|
| 43 | currentBuffer = (currentBuffer + 1) & 1; |
|---|
| 44 | buffers[currentBuffer].SetForReading(); |
|---|
| 45 | return buffers[currentBuffer].Read(pBuffer, NBytes, Timeout); |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | return 0; |
|---|
| 49 | } |
|---|
| 50 | void Write(const void *pBuffer, int NBytes) |
|---|
| 51 | { |
|---|
| 52 | buffers[(currentBuffer + 1) & 1].Write(pBuffer, NBytes); |
|---|
| 53 | } |
|---|
| 54 | bool StreamDataLeft() |
|---|
| 55 | { |
|---|
| 56 | return buffers[currentBuffer].StreamDataLeft() || buffers[(currentBuffer + 1) % 1].GetSize() > 0; |
|---|
| 57 | } |
|---|
| 58 | bool StreamClosed() |
|---|
| 59 | { |
|---|
| 60 | return false; |
|---|
| 61 | } |
|---|
| 62 | int currentBuffer; |
|---|
| 63 | CollectInBufferStream buffers[2]; |
|---|
| 64 | }; |
|---|
| 65 | |
|---|
| 66 | // Test stream based interface |
|---|
| 67 | int test_stream() |
|---|
| 68 | { |
|---|
| 69 | // Make a load of compressible data to compress |
|---|
| 70 | CollectInBufferStream source; |
|---|
| 71 | uint16_t data[1024]; |
|---|
| 72 | for(int x = 0; x < 1024; ++x) |
|---|
| 73 | { |
|---|
| 74 | data[x] = x; |
|---|
| 75 | } |
|---|
| 76 | for(int x = 0; x < (32*1024); ++x) |
|---|
| 77 | { |
|---|
| 78 | source.Write(data, (x % 1024) * 2); |
|---|
| 79 | } |
|---|
| 80 | source.SetForReading(); |
|---|
| 81 | |
|---|
| 82 | // Straight compress from one stream to another |
|---|
| 83 | { |
|---|
| 84 | CollectInBufferStream *poutput = new CollectInBufferStream; |
|---|
| 85 | CompressStream compress(poutput, true /* take ownership */, false /* read */, true /* write */); |
|---|
| 86 | |
|---|
| 87 | source.CopyStreamTo(compress); |
|---|
| 88 | compress.Close(); |
|---|
| 89 | poutput->SetForReading(); |
|---|
| 90 | |
|---|
| 91 | // Check sizes |
|---|
| 92 | TEST_THAT(poutput->GetSize() < source.GetSize()); |
|---|
| 93 | BOX_TRACE("compressed size = " << poutput->GetSize() << |
|---|
| 94 | ", source size = " << source.GetSize()); |
|---|
| 95 | |
|---|
| 96 | // Decompress the data |
|---|
| 97 | { |
|---|
| 98 | CollectInBufferStream decompressed; |
|---|
| 99 | CompressStream decompress(poutput, false /* don't take ownership */, true /* read */, false /* write */); |
|---|
| 100 | decompress.CopyStreamTo(decompressed); |
|---|
| 101 | decompress.Close(); |
|---|
| 102 | |
|---|
| 103 | TEST_THAT(decompressed.GetSize() == source.GetSize()); |
|---|
| 104 | TEST_THAT(::memcmp(decompressed.GetBuffer(), source.GetBuffer(), decompressed.GetSize()) == 0); |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | // Don't delete poutput, let mem leak testing ensure it's deleted. |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | // Set source to the beginning |
|---|
| 111 | source.Seek(0, IOStream::SeekType_Absolute); |
|---|
| 112 | |
|---|
| 113 | // Test where the same stream compresses and decompresses, should be fun! |
|---|
| 114 | { |
|---|
| 115 | CollectInBufferStream output; |
|---|
| 116 | CopyInToOutStream copyer; |
|---|
| 117 | CompressStream compress(©er, false /* no ownership */, true, true); |
|---|
| 118 | |
|---|
| 119 | bool done = false; |
|---|
| 120 | int count = 0; |
|---|
| 121 | int written = 0; |
|---|
| 122 | while(!done) |
|---|
| 123 | { |
|---|
| 124 | ++count; |
|---|
| 125 | bool do_sync = (count % 256) == 0; |
|---|
| 126 | uint8_t buffer[4096]; |
|---|
| 127 | int r = source.Read(buffer, sizeof(buffer), IOStream::TimeOutInfinite); |
|---|
| 128 | if(r == 0) |
|---|
| 129 | { |
|---|
| 130 | done = true; |
|---|
| 131 | compress.Close(); |
|---|
| 132 | } |
|---|
| 133 | else |
|---|
| 134 | { |
|---|
| 135 | compress.Write(buffer, r); |
|---|
| 136 | written += r; |
|---|
| 137 | if(do_sync) |
|---|
| 138 | { |
|---|
| 139 | compress.WriteAllBuffered(); |
|---|
| 140 | } |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | int r2 = 0; |
|---|
| 144 | do |
|---|
| 145 | { |
|---|
| 146 | r2 = compress.Read(buffer, sizeof(buffer), IOStream::TimeOutInfinite); |
|---|
| 147 | if(r2 > 0) |
|---|
| 148 | { |
|---|
| 149 | output.Write(buffer, r2); |
|---|
| 150 | } |
|---|
| 151 | } while(r2 > 0); |
|---|
| 152 | if(do_sync && r != 0) |
|---|
| 153 | { |
|---|
| 154 | // Check that everything is synced |
|---|
| 155 | TEST_THAT(output.GetSize() == written); |
|---|
| 156 | TEST_THAT(::memcmp(output.GetBuffer(), source.GetBuffer(), output.GetSize()) == 0); |
|---|
| 157 | } |
|---|
| 158 | } |
|---|
| 159 | output.SetForReading(); |
|---|
| 160 | |
|---|
| 161 | // Test that it's the same |
|---|
| 162 | TEST_THAT(output.GetSize() == source.GetSize()); |
|---|
| 163 | TEST_THAT(::memcmp(output.GetBuffer(), source.GetBuffer(), output.GetSize()) == 0); |
|---|
| 164 | } |
|---|
| 165 | |
|---|
| 166 | return 0; |
|---|
| 167 | } |
|---|
| 168 | |
|---|
| 169 | // Test basic interface |
|---|
| 170 | int test(int argc, const char *argv[]) |
|---|
| 171 | { |
|---|
| 172 | // Bad data to compress! |
|---|
| 173 | char *data = (char *)malloc(DATA_SIZE); |
|---|
| 174 | for(int l = 0; l < DATA_SIZE; ++l) |
|---|
| 175 | { |
|---|
| 176 | data[l] = l*23; |
|---|
| 177 | } |
|---|
| 178 | |
|---|
| 179 | // parameters about compression |
|---|
| 180 | int maxOutput = Compress_MaxSizeForCompressedData(DATA_SIZE); |
|---|
| 181 | TEST_THAT(maxOutput >= DATA_SIZE); |
|---|
| 182 | |
|---|
| 183 | char *compressed = (char *)malloc(maxOutput); |
|---|
| 184 | int compressedSize = 0; |
|---|
| 185 | |
|---|
| 186 | // Do compression, in small chunks |
|---|
| 187 | { |
|---|
| 188 | Compress<true> compress; |
|---|
| 189 | |
|---|
| 190 | int in_loc = 0; |
|---|
| 191 | while(!compress.OutputHasFinished()) |
|---|
| 192 | { |
|---|
| 193 | int ins = DATA_SIZE - in_loc; |
|---|
| 194 | if(ins > CHUNK_SIZE) ins = CHUNK_SIZE; |
|---|
| 195 | |
|---|
| 196 | if(ins == 0) |
|---|
| 197 | { |
|---|
| 198 | compress.FinishInput(); |
|---|
| 199 | } |
|---|
| 200 | else |
|---|
| 201 | { |
|---|
| 202 | compress.Input(data + in_loc, ins); |
|---|
| 203 | } |
|---|
| 204 | in_loc += ins; |
|---|
| 205 | |
|---|
| 206 | // Get output data |
|---|
| 207 | int s = 0; |
|---|
| 208 | do |
|---|
| 209 | { |
|---|
| 210 | TEST_THAT(compressedSize < maxOutput); |
|---|
| 211 | s = compress.Output(compressed + compressedSize, maxOutput - compressedSize); |
|---|
| 212 | compressedSize += s; |
|---|
| 213 | } while(s > 0); |
|---|
| 214 | } |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | // a reasonable test, especially given the compressability of the input data. |
|---|
| 218 | TEST_THAT(compressedSize < DATA_SIZE); |
|---|
| 219 | |
|---|
| 220 | // decompression |
|---|
| 221 | char *decompressed = (char*)malloc(DATA_SIZE * 2); |
|---|
| 222 | int decomp_size = 0; |
|---|
| 223 | { |
|---|
| 224 | Compress<false> decompress; |
|---|
| 225 | |
|---|
| 226 | int in_loc = 0; |
|---|
| 227 | while(!decompress.OutputHasFinished()) |
|---|
| 228 | { |
|---|
| 229 | int ins = compressedSize - in_loc; |
|---|
| 230 | if(ins > DECOMP_CHUNK_SIZE) ins = DECOMP_CHUNK_SIZE; |
|---|
| 231 | |
|---|
| 232 | if(ins == 0) |
|---|
| 233 | { |
|---|
| 234 | decompress.FinishInput(); |
|---|
| 235 | } |
|---|
| 236 | else |
|---|
| 237 | { |
|---|
| 238 | decompress.Input(compressed + in_loc, ins); |
|---|
| 239 | } |
|---|
| 240 | in_loc += ins; |
|---|
| 241 | |
|---|
| 242 | // Get output data |
|---|
| 243 | int s = 0; |
|---|
| 244 | do |
|---|
| 245 | { |
|---|
| 246 | TEST_THAT(decomp_size <= DATA_SIZE); |
|---|
| 247 | s = decompress.Output(decompressed + decomp_size, (DATA_SIZE*2) - decomp_size); |
|---|
| 248 | decomp_size += s; |
|---|
| 249 | } while(s > 0); |
|---|
| 250 | } |
|---|
| 251 | } |
|---|
| 252 | |
|---|
| 253 | TEST_THAT(decomp_size == DATA_SIZE); |
|---|
| 254 | TEST_THAT(::memcmp(data, decompressed, DATA_SIZE) == 0); |
|---|
| 255 | |
|---|
| 256 | ::free(data); |
|---|
| 257 | ::free(compressed); |
|---|
| 258 | ::free(decompressed); |
|---|
| 259 | |
|---|
| 260 | return test_stream(); |
|---|
| 261 | } |
|---|