// distribution boxbackup-0.10 (svn version: 494)
//  
// Copyright (c) 2003 - 2006
//      Ben Summers and contributors.  All rights reserved.
//  
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must 
//    display the following acknowledgment:
//        This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
//    products derived from this software without specific prior written
//    permission.
// 
// [Where legally impermissible the Authors do not disclaim liability for 
// direct physical injury or death caused solely by defects in the software 
// unless it is modified by a third party.]
// 
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//  
//  
//  
// --------------------------------------------------------------------------
//
// File
//		Name:    testcompress.cpp
//		Purpose: Test lib/compress
//		Created: 5/12/03
//
// --------------------------------------------------------------------------

#include "Box.h"

#include <stdio.h>
#include <string.h>

#include "Test.h"
#include "Compress.h"
#include "CompressStream.h"
#include "CollectInBufferStream.h"

#include "MemLeakFindOn.h"

#define DATA_SIZE			(1024*128+103)
#define CHUNK_SIZE			2561
#define DECOMP_CHUNK_SIZE	3

// Stream for testing
class CopyInToOutStream : public IOStream
{
public:
	CopyInToOutStream() : currentBuffer(0) {buffers[currentBuffer].SetForReading();}
	~CopyInToOutStream() {}
	int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite)
	{
		if(buffers[currentBuffer].StreamDataLeft())
		{
			return buffers[currentBuffer].Read(pBuffer, NBytes, Timeout);
		}
		
		// Swap buffers?
		if(buffers[(currentBuffer + 1) & 1].GetSize() > 0)
		{
			buffers[currentBuffer].Reset();
			currentBuffer = (currentBuffer + 1) & 1;
			buffers[currentBuffer].SetForReading();
			return buffers[currentBuffer].Read(pBuffer, NBytes, Timeout);		
		}

		return 0;
	}
	void Write(const void *pBuffer, int NBytes)
	{
		buffers[(currentBuffer + 1) & 1].Write(pBuffer, NBytes);
	}
	bool StreamDataLeft()
	{
		return buffers[currentBuffer].StreamDataLeft() || buffers[(currentBuffer + 1) % 1].GetSize() > 0;
	}
	bool StreamClosed()
	{
		return false;
	}
	int currentBuffer;
	CollectInBufferStream buffers[2];
};

// Test stream based interface
int test_stream()
{
	// Make a load of compressible data to compress
	CollectInBufferStream source;
	uint16_t data[1024];
	for(int x = 0; x < 1024; ++x)
	{
		data[x] = x;
	}
	for(int x = 0; x < (32*1024); ++x)
	{
		source.Write(data, (x % 1024) * 2);
	}
	source.SetForReading();

	// Straight compress from one stream to another
	{
		CollectInBufferStream *poutput = new CollectInBufferStream;
		CompressStream compress(poutput, true /* take ownership */, false /* read */, true /* write */);
		
		source.CopyStreamTo(compress);
		compress.Close();
		poutput->SetForReading();
		
		// Check sizes
		TEST_THAT(poutput->GetSize() < source.GetSize());
		TRACE2("compressed size = %d, source size = %d\n", poutput->GetSize(), source.GetSize());
		
		// Decompress the data
		{
			CollectInBufferStream decompressed;
			CompressStream decompress(poutput, false /* don't take ownership */, true /* read */, false /* write */);
			decompress.CopyStreamTo(decompressed);
			decompress.Close();
			
			TEST_THAT(decompressed.GetSize() == source.GetSize());
			TEST_THAT(::memcmp(decompressed.GetBuffer(), source.GetBuffer(), decompressed.GetSize()) == 0);
		}
		
		// Don't delete poutput, let mem leak testing ensure it's deleted.
	}

	// Set source to the beginning
	source.Seek(0, IOStream::SeekType_Absolute);

	// Test where the same stream compresses and decompresses, should be fun!
	{
		CollectInBufferStream output;
		CopyInToOutStream copyer;
		CompressStream compress(&copyer, false /* no ownership */, true, true);

		bool done = false;
		int count = 0;
		int written = 0;
		while(!done)
		{
			++count;
			bool do_sync = (count % 256) == 0;
			uint8_t buffer[4096];
			int r = source.Read(buffer, sizeof(buffer), IOStream::TimeOutInfinite);
			if(r == 0)
			{
				done = true;
				compress.Close();
			}
			else
			{
				compress.Write(buffer, r);
				written += r;
				if(do_sync)
				{
					compress.WriteAllBuffered();
				}
			}

			int r2 = 0;
			do
			{
				r2 = compress.Read(buffer, sizeof(buffer), IOStream::TimeOutInfinite);
				if(r2 > 0)
				{
					output.Write(buffer, r2);
				}
			} while(r2 > 0);
			if(do_sync && r != 0)
			{
				// Check that everything is synced
				TEST_THAT(output.GetSize() == written);
				TEST_THAT(::memcmp(output.GetBuffer(), source.GetBuffer(), output.GetSize()) == 0);
			}
		}
		output.SetForReading();
		
		// Test that it's the same
		TEST_THAT(output.GetSize() == source.GetSize());
		TEST_THAT(::memcmp(output.GetBuffer(), source.GetBuffer(), output.GetSize()) == 0);
	}

	return 0;
}

// Test basic interface
int test(int argc, const char *argv[])
{
	// Bad data to compress!
	char *data = (char *)malloc(DATA_SIZE);
	for(int l = 0; l < DATA_SIZE; ++l)
	{
		data[l] = l*23;
	}
	
	// parameters about compression	
	int maxOutput = Compress_MaxSizeForCompressedData(DATA_SIZE);
	TEST_THAT(maxOutput >= DATA_SIZE);

	char *compressed = (char *)malloc(maxOutput);
	int compressedSize = 0;
	
	// Do compression, in small chunks
	{
		Compress<true> compress;
		
		int in_loc = 0;
		while(!compress.OutputHasFinished())
		{
			int ins = DATA_SIZE - in_loc;
			if(ins > CHUNK_SIZE) ins = CHUNK_SIZE;
			
			if(ins == 0)
			{
				compress.FinishInput();
			}
			else
			{
				compress.Input(data + in_loc, ins);
			}
			in_loc += ins;
			
			// Get output data
			int s = 0;
			do
			{
				TEST_THAT(compressedSize < maxOutput);
				s = compress.Output(compressed + compressedSize, maxOutput - compressedSize);
				compressedSize += s;
			} while(s > 0);
		}
	}
	
	// a reasonable test, especially given the compressability of the input data.
	TEST_THAT(compressedSize < DATA_SIZE);

	// decompression
	char *decompressed = (char*)malloc(DATA_SIZE * 2);
	int decomp_size = 0;
	{	
		Compress<false> decompress;
		
		int in_loc = 0;
		while(!decompress.OutputHasFinished())
		{
			int ins = compressedSize - in_loc;
			if(ins > DECOMP_CHUNK_SIZE) ins = DECOMP_CHUNK_SIZE;
			
			if(ins == 0)
			{
				decompress.FinishInput();
			}
			else
			{
				decompress.Input(compressed + in_loc, ins);
			}
			in_loc += ins;
			
			// Get output data
			int s = 0;
			do
			{
				TEST_THAT(decomp_size <= DATA_SIZE);
				s = decompress.Output(decompressed + decomp_size, (DATA_SIZE*2) - decomp_size);
				decomp_size += s;
			} while(s > 0);
		}
	}
	
	TEST_THAT(decomp_size == DATA_SIZE);
	TEST_THAT(::memcmp(data, decompressed, DATA_SIZE) == 0);

	::free(data);
	::free(compressed);
	::free(decompressed);
	
	return test_stream();
}


syntax highlighted by Code2HTML, v. 0.9.1