/**************************************************************************** * Huffile.c * * Computer Science 50 * Problem Set 6 * * Implements a layer of abstraction for Huffman-coded files ***************************************************************************/ #include #include #include #include "huffile.h" /* * int * bread(Huffile *hf) * * Returns file's next bit (as 0 or 1) or EOF upon EOF or error. */ int bread(Huffile *hf) { // ensure source isn't NULL if (hf == NULL) return EOF; // check for file's end if (ftell(hf->stream) == hf->size - 1) if (hf->ith == hf->zth) return EOF; // read another buffer's worth of bits if necessary if (hf->ith == 0) { if (fread(&hf->buffer, sizeof(hf->buffer), 1, hf->stream) != 1) return EOF; } // prepare mask unsigned char mask = 1 << (7 - hf->ith); // advance to next bit hf->ith = (hf->ith + 1) % 8; // grab i'th bit return (hf->buffer & mask) ? 1 : 0; } /* * bool * bwrite(int b, Huffile *hf) * * Lazily writes b (which must be 0 or 1) to Huffile as a single bit. * * Returns TRUE on success and FALSE on failure. */ bool bwrite(int b, Huffile *hf) { // ensure destination isn't NULL if (hf == NULL) return FALSE; // check mode if (hf->io != WRITE) return FALSE; // ensure bit is valid if (b != 0 && b != 1) return FALSE; // prepare mask unsigned mask = 1 << (7 - hf->ith); // set i'th bit in buffer if 1 if (b == TRUE) { // set i'th bit in buffer hf->buffer |= mask; } // clear i'th bit in buffer if 0 else { // clear i'th bit in buffer hf->buffer &= ~mask; } // advance to next bit hf->ith = hf->ith + 1; // write buffer to disk if full if (hf->ith == 8) { if (fwrite(&hf->buffer, sizeof(hf->buffer), 1, hf->stream) == 1) { hf->buffer = 0x00; hf->ith = 0; } else return FALSE; } // success! return TRUE; } /* * bool * hfclose(Huffile *hf) * * Closes file. * * Returns TRUE on success and FALSE on failure. */ bool hfclose(Huffile *hf) { // ensure pointer isn't NULL if (hf == NULL) return FALSE; // ensure there's something to close if (hf->stream == NULL) return FALSE; // be sure final buffer and its index get written out if writing if (hf->io == WRITE) { // write out final buffer if necessary if (hf->ith > 0) if (fwrite(&hf->buffer, sizeof(hf->buffer), 1, hf->stream) != 1) return FALSE; // write out final buffer's index if (fwrite(&hf->ith, sizeof(hf->ith), 1, hf->stream) != 1) return FALSE; } // close file if (fclose(hf->stream) != 0) return FALSE; // free memory free(hf); // that's all folks return TRUE; } /* * Huffile * * hfopen(const char *path, const char *mode) * * Opens path as a Huffile, returning pointer thereto or NULL on error. * * Mode must be "r" or "w". */ Huffile * hfopen(const char *path, const char *mode) { // ensure pointers aren't NULL if (path == NULL || mode == NULL) return NULL; // ensure mode is valid if (strcmp(mode, "r") != 0 && strcmp(mode, "w") != 0) return NULL; // allocate Huffile Huffile * hf = malloc(sizeof(Huffile)); if (hf == NULL) return NULL; // open file hf->stream = fopen(path, mode); if (hf->stream == NULL) { free(hf); return NULL; } // remember mode hf->io = (strcmp(mode, "w") == 0) ? WRITE : READ; // remember size and final buffer's index if reading if (hf->io == READ) { // fast-forward to end of file if (fseek(hf->stream, -1, SEEK_END) != 0) { fclose(hf->stream); free(hf); return NULL; } // remember size if ((hf->size = ftell(hf->stream) + 1) == 0) { fclose(hf->stream); free(hf); return NULL; } // remember final buffer's index if (fread(&hf->zth, sizeof(hf->zth), 1, hf->stream) != 1) { fclose(hf->stream); free(hf); return NULL; } // rewind to start of file if (fseek(hf->stream, 0, SEEK_SET) != 0) { fclose(hf->stream); free(hf); return NULL; } } // initialize state hf->buffer = 0x00; hf->ith = 0; // return Huffile return hf; } /* * bool * hread(Huffeader *h, Huffile *hf) * * Reads in header if still at start of file. * * Returns TRUE on success and FALSE on failure (e.g., not at start of file). */ bool hread(Huffeader *h, Huffile *hf) { // ensure destination and source aren't NULL if (h == NULL || hf == NULL) return FALSE; // ensure we're still at start of file if (ftell(hf->stream) != 0) return FALSE; // read in header if (fread(h, sizeof(Huffeader), 1, hf->stream) != 1) return FALSE; // success! return TRUE; } /* * bool * hwrite(Huffeader *h, Huffile *hf) * * Writes out header if still at start of file. * * Returns TRUE on success and FALSE on failure (e.g., not at start of file). */ bool hwrite(Huffeader *h, Huffile *hf) { // ensure source and destination aren't NULL if (h == NULL || hf == NULL) return FALSE; // ensure we're still at start of file if (ftell(hf->stream) != 0) return FALSE; // write out header if (fwrite(h, sizeof(Huffeader), 1, hf->stream) != 1) return FALSE; // success! return TRUE; }