// This may look like C code, but it is really -*- C++ -*- /* ************************************************************************ * * Verify integer and bit I/O * * Note, this code also tests reading and writing into extended * files (pipes), if EXT_OPEN is defined and set to 1. * Another defined variable, EXT_NETIO may be set to a 'hostname'. * In that case, this code tests opening "files" as * tcp://hostname:7 * tcp://hostname:13 * * $Id: vendian_io.cc,v 2.13 2005/06/26 21:46:10 oleg Exp oleg $ * ************************************************************************ */ #include "endian_io.h" #include #if defined(WIN32) #include #else #include #endif #include #include #include #define MAXDOUBLE DBL_MAX #define MINDOUBLE DBL_MIN using std::cout; using std::cerr; #define strstream std::stringstream using std::fstream; using std::endl; using std::ends; #if defined(unix) || defined(macintosh) #define TMP_FILE_NAME "/tmp/aa" #else #define TMP_FILE_NAME "vendian.tmp" #endif /* *------------------------------------------------------------------------ * Reading and checking functions */ static void read_and_check_long(EndianIn& i_stream, const unsigned long ethalon) { const unsigned long read = i_stream.read_long("Reading a long integer"); if( read != ethalon ) _error("The read long int %d differs from what was expected %d", read,ethalon); } static void read_and_check_short(EndianIn& i_stream, const unsigned short ethalon) { const unsigned short read = i_stream.read_short("Reading a short integer"); if( read != ethalon ) _error("The read short int %d differs from what was expected %d", read,ethalon); } static void read_and_check_byte(EndianIn& i_stream, const unsigned char ethalon) { const unsigned char read = i_stream.read_byte("Reading a byte"); if( read != ethalon ) _error("The read byte %d differs from what was expected %d", read,ethalon); } /* *------------------------------------------------------------------------ * Reading and writing ethalon patterns */ static const unsigned long MyPattern [] = { 1, (unsigned)-1, 0, 0xffff0000, 0x0000ffff, 0x5a5a5a5a, 0xa5a5a5a5 }; static void write_patterns(EndianOut& o_stream) { { const unsigned long * p = (unsigned long *)MyPattern; for(; (char *)p < (char *)MyPattern + sizeof(MyPattern); p++) o_stream.write_long(*p); } { const unsigned short * p = (unsigned short *)MyPattern; for(; (char *)p < (char *)MyPattern + sizeof(MyPattern); p++) o_stream.write_short(*p); } { const unsigned char * p = (unsigned char *)MyPattern; for(; (char *)p < (char *)MyPattern + sizeof(MyPattern); p++) o_stream.write_byte(*p); } } static void read_and_check_patterns(EndianIn& i_stream) { { const unsigned long * p = (unsigned long *)MyPattern; for(; (char *)p < (char *)MyPattern + sizeof(MyPattern); p++) read_and_check_long(i_stream,*p); } { const unsigned short * p = (unsigned short *)MyPattern; for(; (char *)p < (char *)MyPattern + sizeof(MyPattern); p++) read_and_check_short(i_stream,*p); } { const unsigned char * p = (unsigned char *)MyPattern; for(; (char *)p < (char *)MyPattern + sizeof(MyPattern); p++) read_and_check_byte(i_stream,*p); } } /* *------------------------------------------------------------------------ * Verify reading/writing integers in * littleendian mode (most significant byte last) */ static void test_littleendian() { cout << "\n--> Test reading/writing integers in the littleendian mode\n"; cout << "Opening the output stream to file " TMP_FILE_NAME << endl; EndianOut stream(TMP_FILE_NAME); stream.set_littlendian(); cout << "Writing patterns\n"; write_patterns(stream); stream.close(); EndianIn istream; #if defined(EXT_OPEN) && EXT_OPEN cout << "Opening the file as a reading stream through 'cat'" << endl; istream.open("cat " TMP_FILE_NAME " |"); #else cout << "Opening the file as a reading stream" << endl; istream.open(TMP_FILE_NAME); #endif istream.set_littlendian(); cout << "Reading what we've written back\n"; read_and_check_patterns(istream); istream.close(); cout << "\nDone\n"; } /* *------------------------------------------------------------------------ * Verify reading/writing integers in * bigendian mode (most significant byte first) */ static void test_bigendian() { cout << "\n--> Test reading/writing integers in the bigendian mode\n"; { #if defined(EXT_OPEN) && EXT_OPEN cout << "Opening the output stream to file " TMP_FILE_NAME " through 'cat'" << endl; EndianOut stream("| cat > " TMP_FILE_NAME); #else cout << "Opening the output stream to file " TMP_FILE_NAME << endl; EndianOut stream(TMP_FILE_NAME); #endif stream.set_bigendian(); cout << "Writing patterns\n"; write_patterns(stream); // Stream should be closed upon destruction } #if defined(EXT_OPEN) && EXT_OPEN sleep(1); #endif cout << "Opening the file as a reading stream straight\n"; EndianIn istream; istream.open(TMP_FILE_NAME); istream.set_bigendian(); cout << "Reading what we've written back\n"; read_and_check_patterns(istream); istream.close(); cout << "\nDone\n"; } /* *------------------------------------------------------------------------ * Verify reading/writing floating-point numbers ('doubles') * in both modes */ // Reading and writing 'doubles' in a portable // way. See README for more discussion static void write_double(EndianOut& out_stream, const double d) { int exponent; const double mantissa = frexp(d,&exponent); double mantissa_part1; // first 31 bits of mantissa const double mantissa_part2 = modf(ldexp(mantissa,31), &mantissa_part1); out_stream.write_short(exponent); out_stream.write_long((long)mantissa_part1); // the last 31 bits of mantissa out_stream.write_long((long)ldexp(mantissa_part2,31)); } static double read_double(EndianIn& in_stream) { const int exponent = (signed short)in_stream.read_short("reading exp"); const long mantissa_part1 = in_stream.read_long("reading the first 31 bits of mantissa and sign"); const long mantissa_part2 = in_stream.read_long("reading the last 31 bits of mantissa and sign"); return ldexp(mantissa_part2,exponent-62) + ldexp(mantissa_part1,exponent-31); } static void read_and_check_double(EndianIn& i_stream, const double expected) { const double read = read_double(i_stream); if( read != expected ) _error("The read double %g differs from what was expected %g", read,expected); } static void test_doubles() { static const double patterns [] = { 0.0, -1.0, 1.0/3.0, 2.2204460492503131E-16, 1+2.2204460492503131E-16, 1e-23, -1e-23, MAXDOUBLE, -MAXDOUBLE, MINDOUBLE, -MINDOUBLE, 3.14159265358979323846}; const unsigned long delim = 0xdeadbeef; const unsigned long delim_rev = 0xefbeadde; cout << "\n--> Test reading/writing floating-point numbers\n"; { #if defined(EXT_OPEN) && EXT_OPEN cout << "Opening the output stream to file " TMP_FILE_NAME " through 'cat'" << endl; EndianOut stream("| cat > " TMP_FILE_NAME); #else cout << "Opening the output stream to file " TMP_FILE_NAME << endl; EndianOut stream(TMP_FILE_NAME); #endif cout << "Writing patterns in big-endian mode\n"; stream.set_bigendian(); for(register size_t i=0; i Test mixed int/bit stream I/O with file attachments\n"; #if defined(EXT_OPEN) && EXT_OPEN cout << "Opening the output stream to file " TMP_FILE_NAME " through 'cat'" << endl; EndianOut stream("| cat > " TMP_FILE_NAME); #else cout << "Opening the output stream to file " TMP_FILE_NAME << endl; EndianOut stream(TMP_FILE_NAME); #endif stream.set_bigendian(); cout << "Writing integer patterns\n"; write_patterns(stream); cout << "Attaching the bitstream\n"; BitOut bitstream; bitstream.share_with(stream); cout << "Writing 8 bits of ones followed by 3*8 zero bits several times\n"; register int i; for(i=0; i<5; i++) { register int i; for(i=0; i<8; i++) bitstream.put_bit(1); for(i=0; i<3*8; i++) bitstream.put_bit(0); } bitstream.put_bit(1); // Put two extra bits bitstream.put_bit(1); bitstream.close(); { cout << "Attaching the second bitstream\n"; BitOut bitstream; bitstream.share_with(stream); cout << "Writing 8 bits of zeros followed by 3*8 one bits several times\n"; register int i; for(i=0; i<5; i++) { register int i; for(i=0; i<8; i++) bitstream.put_bit(0); for(i=0; i<3*8; i++) bitstream.put_bit(1); } bitstream.put_bit(0); // Put two extra bits bitstream.put_bit(1); } stream.close(); #if defined(EXT_OPEN) && EXT_OPEN sleep(1); #endif cout << "Opening the file as a reading stream straight\n"; EndianIn istream; istream.open(TMP_FILE_NAME); istream.set_bigendian(); cout << "Reading what we've written back\n"; read_and_check_patterns(istream); cout << "Attaching the input bitstream\n"; BitIn ibitstream; ibitstream.share_with(istream); #if defined(unix) || defined(B_BEOS_VERSION) system("ls -l " TMP_FILE_NAME "; od -x " TMP_FILE_NAME); #endif cout << "Reading the bit pattern\n"; for(i=0; i<5; i++) { register int i; for(i=0; i<8; i++) assert( ibitstream.get_bit() == 1 ); for(i=0; i<3*8; i++) assert( ibitstream.get_bit() == 0 ); } assert( ibitstream.get_bit() == 1 ); assert( ibitstream.get_bit() == 1 ); ibitstream.close(); { cout << "Attaching the second input bitstream\n"; BitIn ibitstream; ibitstream.share_with(istream); cout << "Reading the bit pattern\n"; for(i=0; i<5; i++) { register int i; for(i=0; i<8; i++) assert( ibitstream.get_bit() == 0 ); for(i=0; i<3*8; i++) assert( ibitstream.get_bit() == 1 ); } assert( ibitstream.get_bit() == 0 ); assert( ibitstream.get_bit() == 1 ); } cout << "\nDone\n"; } /* *------------------------------------------------------------------------ * Test the mixed int/bit stream I/O */ static void test_varbit_int(void) { cout << "\n--> Test reading/writing short ints using variable number of bits" << endl; const short ethalon [] = { 0, 1, 2, -1, -31, -17, 31, 15, -32, 63, 10000, 64+512, 64+511, -64-511, -4096, -3, -64-512-4095, 64+512+4096, 0, 1 }; { BitOut outbs; outbs.open(TMP_FILE_NAME); assert( outbs.good() ); for(register unsigned int i=0; i Test reading/writing 'files' which are actually TCP pipes" << endl; { strstream filename; filename << "tcp://" << hostname << ":13" << ends; cout << "\tReading from a datetime port: opening a FILE '" << filename.str() << "'" << endl; FILE * fp = fopen(filename.str().c_str(),"r"); if( fp == 0 ) perror("Opening failed"), _error("Failure"); cout << "\t\tthe result is: "; int c; while( (c = fgetc(fp)) != EOF ) cout << (char)c; fclose(fp); } #if !(defined(__GNUC__) && __GNUC__ >= 3) { strstream filename; filename << "tcp://" << hostname << ":7" << ends; cout << "\tEchoing: opening a fstream '" << filename.str() << "'" << endl; fstream fp(filename.str().c_str(),ios::in|ios::out); if( !fp.good() ) perror("Opening failed"), _error("Failure"); const char pattern [] = "1234567\r\n\007\r\n"; char reply [sizeof(pattern)]; cout <<"\t\tsending " << pattern << "..." << endl; fp << pattern << endl; fp.flush(); assert( fp.good() ); cout << "\t\treceiving..."; fp.read(reply,sizeof(reply)-1); reply[sizeof(reply)-1] = '\0'; assert( fp.good() ); cout << reply << endl; assert( strcmp(pattern,reply) == 0 ); fp.close(); } #endif { const int input_port = 5000; const char test_string [] = "abc\n\n\tend"; strstream filename; filename << "ltcp://" << "0" << ":" << input_port << ends; cout << "\tListening on a port: opening a FILE '" << filename.str() << "'" << endl; { // Launch the program that will initiate // the connection, with a delay pid_t pid; if((pid = fork()) != 0) { // In the parent process if (pid < 0) perror("Fork error"), _error("Failure"); } else { strstream filename; filename << "tcp://" << "127.0.0.1" << ":" << input_port << ends; sleep(1); cout << "In the child process: opening the file name '" << filename.str() << "'" << endl; fstream fp(filename.str().c_str(),ios::out); fp << test_string; fp.close(); exit(0); } } FILE * fp = fopen(filename.str().c_str(),"r"); if( fp == 0 ) perror("Opening failed"), _error("Failure"); cout << "\t\tthe result is: "; int c; while( (c = fgetc(fp)) != EOF ) cout << (char)c; fclose(fp); } cout << "\nDone\n"; } static void test_bidirectional_pipe(void) { cout << "\n--> Test a bidirectional pipe to a separate process " << endl; { const char filename [] = "| while read i; do echo $i >&2; echo $i; done"; cout << "\tEchoing: opening a fstream '" << filename << "'" << endl; fstream fp(filename,ios::in|ios::out|ios::binary); fp.rdbuf()->pubsetbuf(0,0); if( !fp.good() ) perror("Opening failed"), _error("Failure"); const char pattern [] = "1234567\r\n\007\r\n"; char reply [sizeof(pattern)]; cout <<"\t\tsending " << pattern << "..." << endl; //fp.write(pattern,sizeof(reply)-1); fp << pattern << endl; fp.flush(); fp.sync(); assert( fp.good() ); cout << "\t\treceiving..."; fp.read(reply,sizeof(reply)-1); reply[sizeof(reply)-1] = '\0'; assert( fp.good() ); cout << reply << endl; assert( strcmp(pattern,reply) == 0 ); fp.close(); } cout << "\nDone\n"; } /* *------------------------------------------------------------------------ * Root module */ int main() { cout << "\n\n\t\tVerify integer stream I/O in big/little endian modes" "\n\t\t\t\tand bit stream I/O\n\n"; test_littleendian(); test_bigendian(); test_doubles(); test_int_bit_IO(); test_varbit_int(); #if defined(EXT_OPEN) && EXT_OPEN && !(defined(__GNUC__) && __GNUC__ >= 3) test_bidirectional_pipe(); #endif #if defined(EXT_NETIO) #define TOSTR1(X) #X #define TOSTR(X) TOSTR1(X) test_tcp_as_file(TOSTR(EXT_NETIO)); #endif cout << "\nAll tests passed" << endl; return 0; }