From 93c6b29368d1e0487937b433bc6e678da0058055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Tue, 1 Dec 2020 15:25:23 +0100 Subject: given code l6 --- labb6/src/bitstream.cpp | 372 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100755 labb6/src/bitstream.cpp (limited to 'labb6/src/bitstream.cpp') diff --git a/labb6/src/bitstream.cpp b/labb6/src/bitstream.cpp new file mode 100755 index 0000000..c96beaf --- /dev/null +++ b/labb6/src/bitstream.cpp @@ -0,0 +1,372 @@ +/* + * TDDD86 Huffman Encoding + * This file contains the implementation of ibitstream and obitstream classes. + * These classes are patterned after (and, in fact, inherit from) the standard + * ifstream and ofstream classes. Please see bitstream.h for information about + * how a client properly uses these classes. + * + * Please do not modify this provided file. Your turned-in files should work + * with an unmodified version of all provided code files. + */ + +#include +#include "bitstream.h" +#include "error.h" +#include "strlib.h" + +static const int NUM_BITS_IN_BYTE = 8; + +inline int GetNthBit(int n, int fromByte) { + return ((fromByte & (1 << n)) != 0); +} + +inline void SetNthBit(int n, int & inByte) { + inByte |= (1 << n); +} + +/* + * Returns a printable string for the given character. + */ +static string toPrintable(int ch) { + if (ch == '\n') { + return "'\\n'"; + } else if (ch == '\t') { + return "'\\t'"; + } else if (ch == '\r') { + return "'\\r'"; + } else if (ch == '\f') { + return "'\\f'"; + } else if (ch == '\b') { + return "'\\b'"; + } else if (ch == '\0') { + return "'\\0'"; + } else if (ch == ' ') { + return "' '"; + } else if (ch == (int) PSEUDO_EOF) { + return "EOF"; + } else if (ch == (int) NOT_A_CHAR) { + return "NONE"; + } else if (!isgraph(ch)) { + return "???"; + } else { + return string("'") + (char) ch + string("'"); + } +} + +/* Constructor ibitstream::ibitstream + * ------------------------------ + * Each ibitstream tracks 3 integers as private data. + * "lastTell" is streampos of the last byte that was read (this is used + * to detect when other non-readBit activity has changed the tell) + * "curByte" contains contents of byte currently being read + * "pos" is the bit position within curByte that is next to read + * We set initial state for lastTell and curByte to 0, then pos is + * set at 8 so that next readBit will trigger a fresh read. + */ +ibitstream::ibitstream() : istream(nullptr), lastTell(0), curByte(0), pos(NUM_BITS_IN_BYTE) {} + +/* Member function ibitstream::readBit + * --------------------------------- + * If bits remain in curByte, retrieve next and increment pos + * Else if end of curByte (or some other read happened), then read next byte + * and start reading from bit position 0 of that byte. + * If read byte from file at EOF, return EOF. + */ +int ibitstream::readBit() { + if (!is_open()) { + error("Cannot read a bit from a stream that is not open."); + } + + // if just finished bits from curByte or if data read from stream after last readBit() + if (lastTell != tellg() || pos == NUM_BITS_IN_BYTE) { + if ((curByte = get()) == EOF) { + // read next single byte from file + return EOF; + } + pos = 0; // start reading from first bit of new byte + lastTell = tellg(); + } + int result = GetNthBit(pos, curByte); + pos++; // advance bit position for next call to readBit + return result; +} + +/* Member function ibitstream::rewind + * --------------------------------- + * Simply seeks back to beginning of file, so reading begins again + * from start. + */ +void ibitstream::rewind() { + if (!is_open()) { + error("Cannot rewind stream that is not open."); + } + clear(); + seekg(0, ios::beg); +} + +/* Member function ibitstream::size + * ------------------------------ + * Seek to file end and use tell to retrieve position. + * In order to not disrupt reading, we also record cur streampos and + * re-seek to there before returning. + */ +long ibitstream::size() { + if (!is_open()) { + error("Cannot get size of stream which is not open."); + } + clear(); // clear any error state + streampos cur = tellg(); // save current streampos + seekg(0, ios::end); // seek to end + streampos end = tellg(); // get offset + seekg(cur); // seek back to original pos + return long(end); +} + +/* Member function ibitstream::is_open + * ------------------------------------------- + * Default implementation of is_open has the stream always + * open. Subclasses can customize this if they'd like. + */ +bool ibitstream::is_open() { + return true; +} + +/* Constructor obitstream::obitstream + * ---------------------------------- + * Each obitstream tracks 3 integers as private data. + * "lastTell" is streampos of the last byte that was written (this is used + * to detect when other non-writeBit activity has changed the tell) + * "curByte" contains contents of byte currently being written + * "pos" is the bit position within curByte that is next to write + * We set initial state for lastTell and curByte to 0, then pos is + * set at 8 so that next writeBit will start a new byte. + */ +obitstream::obitstream() : ostream(nullptr), lastTell(0), curByte(0), pos(NUM_BITS_IN_BYTE) {} + +/* Member function obitstream::writeBit + * ---------------------------------- + * If bits remain to be written in curByte, add bit into byte and increment pos + * Else if end of curByte (or some other write happened), then start a fresh + * byte at position 0. + * We write the byte out for each bit (backing up to overwrite as needed), rather + * than waiting for 8 bits. This is because the client might make + * 3 writeBit calls and then start using << so we can't wait til full-byte + * boundary to flush any partial-byte bits. + */ +void obitstream::writeBit(int bit) { + if (bit != 0 && bit != 1) { + error(string("writeBit must be passed an integer argument of 0 or 1. You passed the integer ") + + toPrintable(bit) + " (" + integerToString(bit) + ")."); + } + if (!is_open()) { + error("Cannot writeBit to stream which is not open."); + } + + // if just filled curByte or if data written to stream after last writeBit() + if (lastTell != tellp() || pos == NUM_BITS_IN_BYTE) { + curByte = 0; // zero out byte for next writes + pos = 0; // start writing to first bit of new byte + } + + if (bit) { + // only need to change if bit needs to be 1 (byte starts already zeroed) + SetNthBit(pos, curByte); + } + + if (pos == 0 || bit) { // only write if first bit in byte or changing 0 to 1 + if (pos != 0) { + seekp(-1, ios::cur); // back up to overwite if pos > 0 + } + put(curByte); + } + + pos++; // advance to next bit position for next write + lastTell = tellp(); +} + + +/* Member function obitstream::size + * ------------------------------ + * Seek to file end and use tell to retrieve position. + * In order to not disrupt writing, we also record cur streampos and + * re-seek to there before returning. + */ +long obitstream::size() { + if (!is_open()) { + error("Cannot get size of stream which is not open."); + } + clear(); // clear any error state + streampos cur = tellp(); // save current streampos + seekp(0, ios::end); // seek to end + streampos end = tellp(); // get offset + seekp(cur); // seek back to original pos + return long(end); +} + +/* Member function obitstream::is_open + * ------------------------------------------- + * Default implementation of is_open has the stream always + * open. Subclasses can customize this if they'd like. + */ +bool obitstream::is_open() { + return true; +} + +/* Constructor ifbitstream::ifbitstream + * ------------------------------------------- + * Wires up the stream class so that it knows to read data + * from disk. + */ +ifbitstream::ifbitstream() { + init(&fb); +} + +/* Constructor ifbitstream::ifbitstream + * ------------------------------------------- + * Wires up the stream class so that it knows to read data + * from disk, then opens the given file. + */ +ifbitstream::ifbitstream(const char* filename) { + init(&fb); + open(filename); +} +ifbitstream::ifbitstream(string filename) { + init(&fb); + open(filename); +} + +/* Member function ifbitstream::open + * ------------------------------------------- + * Attempts to open the specified file, failing if unable + * to do so. + */ +void ifbitstream::open(const char* filename) { + if (!fb.open(filename, ios::in | ios::binary)) { + setstate(ios::failbit); + } +} + +void ifbitstream::open(string filename) { + open(filename.c_str()); +} + +/* Member function ifbitstream::is_open + * ------------------------------------------- + * Determines whether the file stream is open. + */ +bool ifbitstream::is_open() { + return fb.is_open(); +} + +/* Member function ifbitstream::close + * ------------------------------------------- + * Closes the file stream, if one is open. + */ +void ifbitstream::close() { + if (!fb.close()) { + setstate(ios::failbit); + } +} + +/* Constructor ofbitstream::ofbitstream + * ------------------------------------------- + * Wires up the stream class so that it knows to write data + * to disk. + */ +ofbitstream::ofbitstream() { + init(&fb); +} + +/* Constructor ofbitstream::ofbitstream + * ------------------------------------------- + * Wires up the stream class so that it knows to write data + * to disk, then opens the given file. + */ +ofbitstream::ofbitstream(const char* filename) { + init(&fb); + open(filename); +} + +ofbitstream::ofbitstream(string filename) { + init(&fb); + open(filename); +} + +/* Member function ofbitstream::open + * ------------------------------------------- + * Attempts to open the specified file, failing if unable + * to do so. + */ +void ofbitstream::open(const char* filename) { + /* Confirm we aren't about to do something that could potentially be a + * Very Bad Idea. + */ + if (endsWith(filename, ".cpp") || endsWith(filename, ".h") || + endsWith(filename, ".hh") || endsWith(filename, ".cc")) { + error(string("It is potentially dangerous to write to file ") + + filename + ", because that might be your own source code. " + + "We are explicitly disallowing this operation. Please choose a " + + "different filename."); + setstate(ios::failbit); + } else { + if (!fb.open(filename, ios::out | ios::binary)) { + setstate(ios::failbit); + } + } +} +void ofbitstream::open(string filename) { + open(filename.c_str()); +} + +/* Member function ofbitstream::is_open + * ------------------------------------------- + * Determines whether the file stream is open. + */ +bool ofbitstream::is_open() { + return fb.is_open(); +} + +/* Member function ofbitstream::close + * ------------------------------------------- + * Closes the given file. + */ +void ofbitstream::close() { + if (!fb.close()) { + setstate(ios::failbit); + } +} + +/* Constructor istringbitstream::istringbitstream + * ------------------------------------------- + * Sets the stream to use the string buffer, then sets + * the initial string to the specified value. + */ +istringbitstream::istringbitstream(string s) { + init(&sb); + sb.str(s); +} + +/* Member function istringbitstream::str + * ------------------------------------------- + * Sets the underlying string in the buffer to the + * specified string. + */ +void istringbitstream::str(string s) { + sb.str(s); +} + +/* Member function ostringbitstream::ostringbitstream + * ------------------------------------------- + * Sets the stream to use the string buffer. + */ +ostringbitstream::ostringbitstream() { + init(&sb); +} + +/* Member function ostringbitstream::str + * ------------------------------------------- + * Retrives the underlying string data. + */ +string ostringbitstream::str() { + return sb.str(); +} -- cgit v1.2.1