You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/libkmime/tests/test_kmime_codec.cpp

450 lines
12 KiB

/* test program for KMime::Codec's:
compile with:
g++ -I$QTDIR/include -I$KDEDIR/include -L$QTDIR/lib -L$KDEDIR/lib \
-lqt-mt -lkdecore -lkdenetwork -O2 -pthread -DQT_THREAD_SUPPORT \
-o test_kmime_codec{,.cpp}
*/
// return codes:
#define USAGE_DISPLAYED 1
#define UNKNOWN_CODEC 2
#define INFILE_READ_ERR 3
#define OUTFILE_WRITE_ERR 4
#include <../kmime_codecs.h>
#include <kdebug.h>
#include <cstdlib>
#include <iostream>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <getopt.h>
#include <cassert>
#include <tqfile.h>
#include <tqcstring.h> // QByteArray
using namespace KMime;
using namespace std;
static struct option long_options[] = {
{ "encode", 1, 0, 0 },
{ "decode", 1, 0, 0 },
{ "output-buffer-size", 1, 0, 0 },
{ "input-buffer-size", 1, 0, 0 },
{ "outfile", 1, 0, 0 },
{ "with-crlf", 0, 0, 0 },
{ "iterations", 1, 0, 0 },
{ "without-finish", 0, 0, 0 },
{ "verbose", 0, 0, 0 },
{ "usage-pattern", 1, 0, 0 },
{ 0, 0, 0, 0 }
};
void usage( const char * msg=0 ) {
if ( msg && *msg )
cerr << msg << endl;
cerr << "usage: test_kmime_codec (--encode|--decode) "
"<encoding-name> [options] infile\n"
"where options include:\n\n"
" --outfile <outfile> write output into file <outfile>\n"
" --output-buffer-size <size> en/decode into chunks of <size> bytes\n"
" default: 4096\n"
" --input-buffer-size <size> en/decode from chunks of <size> bytes\n"
" default: slurp in whole file\n"
" --with-crlf use CRLF instead of LF in output\n"
" --iterations <number> do more than one iteration\n"
" default: 1\n"
" --usage-pattern { kio | chunkwise | convenience-qba }\n"
" use a certain usage pattern to be tested\n"
" (default: chunkwise)\n"
" --without-finish don't call the finish() method\n"
" --verbose output detailed progress information\n"
<< endl;
exit(USAGE_DISPLAYED);
}
void missingParameterTo( const char * option ) {
cerr << "Missing or malformed parameter to " << option << endl;
usage();
}
static enum { Kio = 0, ChunkWise = 1, ConvenienceQBA = 3 }
pattern = ChunkWise;
static int outbufsize = 4096;
static int inbufsize = -1; // whole file
static bool writing = false;
static bool withCRLF = false;
static bool withFinish = true;
static bool verbose = false;
void encode_decode_kio( bool, const Codec *, const TQByteArray &, TQFile & );
void encode_decode_chunkwise( bool, const Codec *,
const TQByteArray &, TQFile & );
void encode_decode_convenience_qba( bool, const Codec *, const TQByteArray &,
TQFile & );
int main( int argc, char * argv[] ) {
int iterations = 1;
bool encode = false;
bool decode = false;
TQCString outfilename, infilename;
TQCString encodingName;
// options parsing:
while( 1 ) {
int option_index = 0;
if ( getopt_long( argc, argv, "", long_options, &option_index ) )
break;
switch ( option_index ) {
case 0: // encode
if ( !optarg || !*optarg ) missingParameterTo( "--encode." );
encode = true;
encodingName = TQCString(optarg);
break;
case 1: // decode
if ( !optarg || !*optarg ) missingParameterTo( "--decode" );
decode = true;
encodingName = TQCString(optarg);
break;
case 2: // output-buffer-size
if ( !optarg || (outbufsize = atoi( optarg )) < 1 )
missingParameterTo( "--output-buffer-size" );
break;
case 3: // input-buffer-size
if ( !optarg || (inbufsize = atoi( optarg )) < 1 )
missingParameterTo( "--input-buffer-size" );
break;
case 4: // outfile
if ( !optarg || !*optarg ) missingParameterTo( "--outfile" );
outfilename = TQCString(optarg);
writing = true;
break;
case 5: // with-crlf
withCRLF = true;
break;
case 6: // iterations
if ( !optarg || (iterations = atoi( optarg )) < 1 )
missingParameterTo( "--iterations" );
break;
case 7: // without-finish
withFinish = false;
break;
case 8: // verbose
verbose = true;
break;
case 9: // usage-pattern
if ( !qstricmp( "kio", optarg ) )
pattern = Kio;
else if ( !qstricmp( "chunkwise", optarg ) )
pattern = ChunkWise;
else if ( !qstricmp( "convenience-qba", optarg ) )
pattern = ConvenienceQBA;
else {
cerr << "Unknown usage pattern \"" << optarg << "\"" << endl;
usage();
}
break;
default: usage( "Unknown option" );
}
}
if ( !decode && !encode )
usage( "You must specify exactly one of --encode, --decode." );
if ( decode && encode )
usage( "You must specify exactly one of --encode, --decode.");
if ( verbose ) {
if ( encode )
kdDebug() << "encoding as " << encodingName << endl;
else if ( decode )
kdDebug() << "decoding " << encodingName << endl;
}
if ( optind != argc - 1 ) usage();
TQFile infile( argv[ optind ] );
if (!infile.exists()) {
kdDebug() << "infile \"" << infile.name() << "\" does not exist!" << endl;
return INFILE_READ_ERR;
}
if (!infile.open( IO_ReadOnly )) {
kdDebug() << "cannot open " << infile.name() << " for reading!"
<< endl;
return INFILE_READ_ERR;
}
TQFile outfile( outfilename );
if ( !outfilename.isEmpty() ) {
if (!outfile.open( IO_WriteOnly|IO_Truncate )) {
kdDebug() << "cannot open " << outfile.name() << " for writing!"
<< endl;
return OUTFILE_WRITE_ERR;
}
}
if ( verbose ) {
kdDebug() << "using output buffer size of " << outbufsize << endl;
kdDebug() << "using input buffer size of " << inbufsize << endl;
}
if ( !withFinish )
kdWarning() << "omitting finish calls. Results may be truncated!" << endl;
if ( inbufsize <= 0 )
inbufsize = infile.size();
// get a codec. Don't delete it later!!
kdDebug( verbose ) << "obtaining codec for \""
<< encodingName << "\"" << endl;
Codec * codec = Codec::codecForName( encodingName );
if ( !codec ) {
kdDebug() << "unknown codec \"" << encodingName << "\"" << endl;
return UNKNOWN_CODEC;
}
TQByteArray infile_buffer = infile.readAll();
for ( int i = 0 ; i < iterations ; ++i ) {
kdDebug( verbose ) << "starting iteration " << i+1
<< " of " << iterations << endl;
switch ( pattern ) {
case ChunkWise:
encode_decode_chunkwise( encode, codec, infile_buffer, outfile );
break;
case Kio:
encode_decode_kio( encode, codec, infile_buffer, outfile );
break;
case ConvenienceQBA:
encode_decode_convenience_qba( encode, codec, infile_buffer, outfile );
break;
default:
usage();
}
}
return 0;
}
void encode_decode_convenience_qba( bool encode, const Codec * codec,
const TQByteArray & infile_buffer,
TQFile & outfile )
{
TQByteArray out;
if ( encode )
out = codec->encode( infile_buffer, withCRLF );
else
out = codec->decode( infile_buffer, withCRLF );
if ( writing ) {
TQ_LONG written = outfile.writeBlock( out );
assert( written == (TQ_LONG)out.size() );
}
}
void encode_kio_internal( Encoder * enc, TQByteArray::ConstIterator & iit,
TQByteArray::ConstIterator & iend,
TQByteArray & out )
{
out.resize( outbufsize );
TQByteArray::Iterator oit = out.begin();
TQByteArray::ConstIterator oend = out.end();
while ( !enc->encode( iit, iend, oit, oend ) )
if ( oit == oend ) return;
while ( !enc->finish( oit, oend ) )
if ( oit == oend ) return;
out.truncate( oit - out.begin() );
}
void decode_kio_internal( Decoder * dec, TQByteArray::ConstIterator & iit,
TQByteArray::ConstIterator & iend,
TQByteArray & out ) {
out.resize( outbufsize );
TQByteArray::Iterator oit = out.begin();
TQByteArray::ConstIterator oend = out.end();
while ( !dec->decode( iit, iend, oit, oend ) )
if ( oit == oend ) return;
while ( !dec->finish( oit, oend ) )
if ( oit == oend ) return;
out.truncate( oit - out.begin() );
}
void encode_decode_kio( bool encode, const Codec * codec,
const TQByteArray & infile_buffer, TQFile & outfile )
{
Encoder * enc = 0;
Decoder * dec = 0;
// Get an encoder. This one you have to delete!
if ( encode ) {
enc = codec->makeEncoder( withCRLF );
assert( enc );
} else {
dec = codec->makeDecoder( withCRLF );
assert( dec );
}
TQByteArray::ConstIterator iit = infile_buffer.begin();
TQByteArray::ConstIterator iend = infile_buffer.end();
TQByteArray out;
do {
out = TQByteArray();
if ( encode )
encode_kio_internal( enc, iit, iend, out );
else
decode_kio_internal( dec, iit, iend, out );
if ( writing && out.size() ) {
TQ_LONG written = outfile.writeBlock( out );
assert( written == (TQ_LONG)out.size() );
}
} while ( out.size() );
if ( encode )
delete enc;
else
delete dec;
}
void encode_decode_chunkwise( bool encode, const Codec * codec,
const TQByteArray & infile_buffer, TQFile & outfile )
{
Encoder * enc = 0;
Decoder * dec = 0;
TQByteArray indata( inbufsize );
TQByteArray outdata( outbufsize );
// we're going to need this below:
#define write_full_outdata_then_reset do { \
kdDebug( verbose ) << " flushing output buffer." << endl; \
if ( writing ) { \
TQ_LONG outlen = outfile.writeBlock( outdata.data(), \
outdata.size() ); \
if ( outlen != (int)outdata.size() ) \
exit(OUTFILE_WRITE_ERR); \
} \
oit = outdata.begin(); \
} while ( false )
#define report_status(x,y) do { \
kdDebug( verbose ) << " " #x "() returned " #y " after processing " \
<< iit - indata.begin() << " bytes of input.\n" \
<< " output iterator now at position " \
<< oit - outdata.begin() << " of " \
<< outdata.size() << endl; \
} while ( false )
#define report_finish_status(y) do { \
kdDebug( verbose ) << " finish() returned " #y "\n" \
<< " output iterator now at position " \
<< oit - outdata.begin() << " of " \
<< outdata.size() << endl; \
} while ( false )
// Initialize the output iterators:
TQByteArray::Iterator oit = outdata.begin();
TQByteArray::Iterator oend = outdata.end();
// Get an encoder. This one you have to delete!
if ( encode ) {
enc = codec->makeEncoder( withCRLF );
assert( enc );
} else {
dec = codec->makeDecoder( withCRLF );
assert( dec );
}
//
// Loop over input chunks:
//
uint offset = 0;
while ( offset < infile_buffer.size() ) {
uint reallyRead = QMIN( indata.size(), infile_buffer.size() - offset );
indata.duplicate( infile_buffer.begin() + offset, reallyRead );
offset += reallyRead;
kdDebug( verbose ) << " read " << reallyRead << " bytes (max: "
<< indata.size() << ") from input." << endl;
// setup input iterators:
TQByteArray::ConstIterator iit = indata.begin();
TQByteArray::ConstIterator iend = indata.begin() + reallyRead;
if ( encode ) {
//
// Loop over encode() calls:
//
while ( !enc->encode( iit, iend, oit, oend ) ) {
report_status( encode, false );
if ( oit == oend )
// output buffer full:
write_full_outdata_then_reset;
}
report_status( encode, true );
} else {
//
// Loop over decode() calls:
//
while ( !dec->decode( iit, iend, oit, oend ) ) {
report_status( decode, false );
if ( oit == oend )
// output buffer full:
write_full_outdata_then_reset;
}
report_status( decode, true );
}
} // end loop over input chunks
//
// Now finish the encoding/decoding:
// (same loops as above, just s/encode|decode/finish())
//
if ( withFinish )
if ( encode ) {
while ( !enc->finish( oit, oend ) ) {
report_finish_status( false );
if ( oit == oend )
write_full_outdata_then_reset;
}
report_finish_status( true );
} else {
while ( !dec->finish( oit, oend ) ) {
report_finish_status( false );
if ( oit == oend )
write_full_outdata_then_reset;
}
report_finish_status( true );
}
//
// Write out last (partial) output chunk:
//
if ( writing ) {
TQ_LONG outlen = outfile.writeBlock( outdata.data(),
oit - outdata.begin() );
if ( outlen != oit - outdata.begin() )
exit(OUTFILE_WRITE_ERR);
}
//
// Delete en/decoder:
//
if ( encode )
delete enc;
else
delete dec;
}