summaryrefslogtreecommitdiffstats
path: root/labb6/src/bitstream.cpp
blob: c96beaf17f1646f382170f355d5da62f93c320f2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
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 <iostream>
#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();
}