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 ] ;
// 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.
// bytesRead is always of the correct length, 0-padded.
std : : size_t n_bytes_read ;
const std : : string dataRead = ReadBytes ( blockWidth , n_bytes_read ) ;
// 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 ;
}
// 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? " ) ;
}
// 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 15:32:17 +02:00
2022-05-31 21:40:13 +02:00
}
return ;
}
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
std : : string DataIngestionLayer : : ReadBytes ( const std : : size_t n , std : : size_t & out_bytes_read ) {
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
// Prepare a buffer to read to
char * buf = new char [ n + 1 ] ;
memset ( buf , 0 , ( n + 1 ) * sizeof ( buf [ 0 ] ) ) ;
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
// Read
in - > read ( buf , n * sizeof ( buf [ 0 ] ) ) ;
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
// Fetch how much we've read
out_bytes_read = in - > gcount ( ) ;
2022-05-31 14:25:23 +02:00
2022-05-31 21:40:13 +02:00
// Is this fewer bytes than got requested?
if ( out_bytes_read < n ) {
// Yes: EOF reached.
reachedEof = true ;
2022-05-31 14:25:23 +02:00
}
2022-05-31 21:40:13 +02:00
// 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