2022-05-27 18:26:48 +02:00
#include "DataIngestionLayer.h"
#include "CommandlineInterface.h"
2022-05-31 21:40:13 +02:00
#include "DataFormatter.h"
2022-05-31 15:32:17 +02:00
#include "Bases.h"
2022-05-27 18:26:48 +02:00
#include <iostream>
2022-05-31 14:25:23 +02:00
#include <istream>
#include <fstream>
#include <sstream>
2022-05-27 18:26:48 +02:00
#include <cstring>
using namespace IO ;
void DataIngestionLayer :: Init () {
// Set our istream
switch ( Configuration :: inputFrom ) {
// Are we reading from stdin?
case Configuration :: INPUT_FROM :: STDIN :
// Redirect our istream to stdin
in = & std :: cin ;
break ;
// Are we reading from a file?
case Configuration :: INPUT_FROM :: FILE :
// Open the file
ifs . open (
Configuration :: inputFilename ,
std :: ios :: in | std :: ios :: binary
);
// A little bit of error handling
if ( ! ifs . good ()) {
2022-05-31 17:06:13 +02:00
throw std :: runtime_error ( "Unable to open infilestream!" );
2022-05-27 18:26:48 +02:00
}
// Redirect our istream to this infilestream
in = & ifs ;
break ;
2022-05-31 17:06:13 +02:00
// Are we reading from a parameter?
case Configuration :: INPUT_FROM :: PARAMETER :
2022-05-27 18:26:48 +02:00
// Create an instringstream with our parameter
iss = std :: istringstream (
CommandlineInterface :: Get ()[ "--intext" ]. GetString ()
);
2022-05-31 10:44:05 +02:00
// Redirect our istream to this instringstream
2022-05-27 18:26:48 +02:00
in = & iss ;
break ;
}
2022-05-31 21:40:13 +02:00
// Derive from our the current module if we're reading ciphertext or not
if (
( Configuration :: activeModule == Configuration :: MODULE :: DECRYPTION )
) {
isReadingCiphertext = true ;
}
else {
isReadingCiphertext = false ;
}
2022-05-31 14:25:23 +02:00
initialized = true ;
reachedEof = false ;
2022-05-31 15:32:17 +02:00
return ;
}
2022-05-27 18:26:48 +02:00
2022-05-31 15:32:17 +02:00
void DataIngestionLayer :: Destruct () {
2022-05-27 18:26:48 +02:00
2022-05-31 15:32:17 +02:00
if ( Configuration :: inputFrom == Configuration :: INPUT_FROM :: FILE ) {
ifs . close ();
}
2022-05-27 18:26:48 +02:00
return ;
}
2022-05-31 14:25:23 +02:00
void DataIngestionLayer :: ReadBlock () {
if ( ! initialized ) {
throw std :: runtime_error ( "Attempted to read on uninitialized DataIngestionLayer!" );
}
if ( ! reachedEof ) {
2022-05-31 21:40:13 +02:00
// A block is this many digits wide, in encoding
const std :: size_t blockWidth = blockLengthByBase [ Configuration :: formatIn ];
2022-05-31 15:32:17 +02:00
2022-05-31 21:40:13 +02:00
// Iterate over the string, and parse all blocks
// We now have to differentiate between single-char digit sets (like hex),
// and multi-char digit sets (like uwu):
switch ( Configuration :: formatIn ) {
case Configuration :: IOBASE_FORMAT :: BASE_BYTES :
case Configuration :: IOBASE_FORMAT :: BASE_2 :
case Configuration :: IOBASE_FORMAT :: BASE_8 :
case Configuration :: IOBASE_FORMAT :: BASE_10 :
case Configuration :: IOBASE_FORMAT :: BASE_16 :
case Configuration :: IOBASE_FORMAT :: BASE_64 :
// Easy case: Each digit is exactly one char in size.
// We can just calculate how many bytes we have to read.
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
// bytesRead is always of the correct length, 0-padded.
std :: size_t n_bytes_read ;
const std :: string dataRead = ReadBytes ( blockWidth , n_bytes_read );
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
// If we've read 0 bytes, this was the last block
// and it's completely empty. We can abort without doing anything.
// The ReadBytes function takes care of setting the reachedEof flag.
if ( n_bytes_read == 0 ) {
return ;
}
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
// If we are reading ciphertext
// make sure we've read enough bytes to compose a block.
if (
( isReadingCiphertext ) &&
( n_bytes_read < blockWidth )
) {
throw std :: runtime_error ( "DataIngestionLayer::ReadBlock() read an input-data fragment that is smaller than a data block should be. Is your cipher text incomplete?" );
}
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
// This should decode to a block just like this.
Block newBlock ;
// Special-case: We are reading cleartext (no ciphertext)
// cleartext is always base_bytes
if ( ! isReadingCiphertext ) {
// When just reading cleartext-bytes, we also allow shorter strings
// than BLOCK_SIZE. These will just get zero-padded.
newBlock . FromTextString ( dataRead );
}
else {
// Else: recode to a block.
newBlock = DataFormatter :: DecodeFormat (
dataRead ,
Configuration :: formatIn
);
}
blocks . emplace ( newBlock );
break ;
}
2022-05-31 14:25:23 +02:00
}
return ;
}
2022-05-31 21:40:13 +02:00
std :: string DataIngestionLayer :: ReadBytes ( const std :: size_t n , std :: size_t & out_bytes_read ) {
// Prepare a buffer to read to
char * buf = new char [ n + 1 ];
memset ( buf , 0 , ( n + 1 ) * sizeof ( buf [ 0 ]));
// Read
in -> read ( buf , n * sizeof ( buf [ 0 ]));
// Fetch how much we've read
out_bytes_read = in -> gcount ();
// Is this fewer bytes than got requested?
if ( out_bytes_read < n ) {
// Yes: EOF reached.
reachedEof = true ;
}
// Translate buffer to a standard string
const std :: string sbuf ( buf , n );
delete [] buf ;
// Return our buffer
return sbuf ;
}
2022-05-31 14:25:23 +02:00
bool DataIngestionLayer :: ReachedEOF () {
return reachedEof ;
}
bool DataIngestionLayer :: IsBlockReady () {
2022-05-31 15:32:17 +02:00
// We're not ready, if we haven't reached EOF, if we should puffer
// the input.
if (
( CommandlineInterface :: Get (). HasParam ( "--puffer-input" )) &&
( ! reachedEof )
) {
return false ;
}
// If we're not puffering, just return whether or not
// we have any blocks...
2022-05-31 14:25:23 +02:00
return blocks . size () > 0 ;
}
2022-05-31 15:32:17 +02:00
bool DataIngestionLayer :: IsFinished () {
return ( reachedEof ) && ( blocks . size () == 0 );
}
2022-05-31 14:25:23 +02:00
Block DataIngestionLayer :: GetNextBlock () {
if ( ! IsBlockReady ()) {
throw std :: runtime_error ( "Attempted to get the next block, but there are none left!" );
}
// Why... why not just return a T in pop()???
const Block popped = blocks . front ();
blocks . pop ();
return popped ;
}
2022-05-27 18:26:48 +02:00
std :: istream * DataIngestionLayer :: in ;
std :: ifstream DataIngestionLayer :: ifs ;
std :: istringstream DataIngestionLayer :: iss ;
2022-05-31 14:25:23 +02:00
bool DataIngestionLayer :: reachedEof = false ;
bool DataIngestionLayer :: initialized = false ;
2022-05-31 21:40:13 +02:00
bool DataIngestionLayer :: isReadingCiphertext ;
2022-05-31 14:25:23 +02:00
std :: queue < Block > DataIngestionLayer :: blocks ;
2022-05-27 18:26:48 +02:00