#include "AftrConfig.h" #include #ifdef AFTR_CONFIG_USE_BOOST #include #include #include "NetMessengerStreamBuffer.h" template< Aftr::SensorDatumConcept T > std::vector Aftr::SensorDatumFileIO::fromFile( const std::string& fileName ) { std::vector< T > d; std::ifstream fin; size_t linesProcessed = 0; fin.open( fileName, std::ios::in | std::ios::binary ); if( fin ) { boost::timer::auto_cpu_timer t{}; //time how long loading takes fin.seekg( 0, std::ios::end ); std::streamoff len = fin.tellg(); //length in bytes fin.seekg( 0, std::ios::beg ); len = len + std::streamoff( 1 ); //add one extra byte for null terminator char* contents = new char[len]; //ownership is given to the boost::interprocess::ibufferstream contents[len-1] = '\0'; //null terminate very last byte to ensure very last ascii text number is fully read before eof is encountered by stream fin.read( contents, len ); //ownership is given to the boost::interprocess::ibufferstream size_t numDataPoints = std::count( &contents[0], &contents[0] + len, '\n' ); d.reserve( numDataPoints + 1 ); //in case last line doesn't have a newline we reserve one extra spot //now let's turn this back into a stream that gets used to create each datum boost::interprocess::ibufferstream finTxt( contents, len ); /// OWNERSHIP is transferred to the ibufferstream finTxt.clear(); //reset the file stream's state so it can operate on the new char buffer already in memory finTxt.seekg( 0, std::ios::beg ); std::cout << "Loading Datum from " << fileName << "...\n"; while( !finTxt.eof() && finTxt.peek() != std::char_traits::eof() ) { std::streampos pos = finTxt.tellg(); //save current position within stream if( pos >= len ) break; //we reached the end of the file if( auto datum = T::fromStreamOneDatum( finTxt ); datum ) d.push_back( *datum ); else { //we are at the end of last successfully parsed line (or beginning of a file w/ an invalid first line). //We find the next \n and begin processing from there. We have to do that without walking past the end of file. //So let's use the original char* buffer and march until the next newline, seekg to that newline and begin processing the next line from there long long idx = pos; while( idx < len-1 ) { if( contents[idx] == '\n' ) { ++idx; break; } ++idx; } if( idx == len - 1 ) break; //we've reached the end of the file. We exit here and we don't have to tell the user that this datum "failed" since EOF isn't a datum to parse! finTxt.clear(); //clear out the error flags in the stream finTxt.seekg( idx ); //seek to the next \n and process from there //notify the user that a particular datum wasn't successfully processed std::cout << " WARNING: " << AFTR_FILE_LINE_STR << "...\n" << " Couldn't parse a datum around byte offset " << pos << " (approx " << static_cast( pos ) / static_cast( len ) << " through data set)...\n" << " Continuing with next datum...\n"; } ++linesProcessed; if( linesProcessed % 1000 == 0 ) std::cout << "."; if( linesProcessed % 100000 == 0 ) std::cout << linesProcessed; //std::cout << "Peek is " << (int)finTxt.peek() << "\n"; } std::cout << "Loaded " << fileName << " Parsed " << d.size() << " datum across " << linesProcessed << " lines...\n" << " Time taken: "; } else { std::cout << "Failed to open file " << fileName << ", 0 datum loaded...\n"; } return std::move( d ); } template< Aftr::SensorDatumConcept T > Aftr::NetMessengerStreamBuffer Aftr::SensorDatumFileIO::fromFileAsNetBuffer( const std::string& fileName ) { std::ifstream fin; fin.open( fileName, std::ios::in | std::ios::binary ); if( fin ) { boost::timer::auto_cpu_timer t{}; //time how long loading takes fin.seekg( 0, std::ios::end ); std::streamoff len = fin.tellg(); //length in bytes fin.seekg( 0, std::ios::beg ); char* contents = new char[len]; //ownership is given to the boost::interprocess::ibufferstream fin.read( contents, len ); //ownership is given to the NetMessengerStreamBuffer Aftr::NetMessengerStreamBuffer buff( contents, static_cast( len ), true ); std::cout << "Loaded " << fileName << " Time taken: "; buff.enableHostNetworkEndianAutoConversion( false ); //disable endian auto conversion return std::move( buff ); } else { std::cout << "Failed to open file " << fileName << ", 0 datum loaded...\n"; } return std::move( Aftr::NetMessengerStreamBuffer( nullptr, 0, true ) ); } template< Aftr::SensorDatumConcept T> bool Aftr::SensorDatumFileIO::toFile( const std::string& fileName, const std::vector& dat ) { std::ofstream fout; fout.open( fileName.c_str(), std::ios::app ); if( fout ) { for( size_t i = 0; i < dat.size(); ++i ) dat[i].toStreamOneDatum( fout ); //each datum should end with a newline inside its insertion (toStream()... method). } else { std::cout << "Failed to open file " << fileName << " to append output of " << dat.size() << " datums...\n"; return false; } return false; } #endif