#include "tape.h" #include "common.h" #include #include #include unsigned reads = 0; unsigned writes = 0; void _tape_load_block(tape_t* tape, unsigned n); void _tape_flush(tape_t* tape); tape_t* tape_open(const char* filename, const char* mode) { FILE* file = fopen(filename, mode); printfv(VERBOSITY_VERBOSE, "Opening tape %s in mode %s.\n", filename, mode); if (!file) { fprintf(stderr, "Cannot open %s in %s mode.", filename, mode); return NULL; } tape_t* tape = malloc(sizeof(tape_t)); tape->file = file; tape->mode = mode; tape->name = malloc(strlen(filename)); tape->buffer = malloc(PAGE_SIZE); strcpy(tape->name, filename); if (strcmp(mode, TAPE_READ) == 0) { tape->read = 0; tape->offset = 0; tape->block = -1; } else if (strcmp(mode, TAPE_WRITE) == 0) { tape->read = 0; tape->offset = 0; tape->block = 0; } else if (strcmp(mode, TAPE_APPEND) == 0) { size_t pos = ftell(tape->file); tape->offset = pos % PAGE_SIZE; tape->block = pos / PAGE_SIZE; _tape_load_block(tape, tape->block); } else { fprintf(stderr, "Mode %s is unknown for tapes.", mode); } return tape; } void tape_close(tape_t* tape) { printfv(VERBOSITY_VERBOSE, "Closing tape %s.\n", tape->name); if (strcmp(tape->mode, TAPE_WRITE) == 0 || strcmp(tape->mode, TAPE_APPEND) == 0) { _tape_flush(tape); } fclose(tape->file); free(tape->name); free(tape->buffer); free(tape); } void _tape_load_block(tape_t* tape, unsigned n) { printfv(VERBOSITY_DEBUG, "Loading block %u of %s.\n", n, tape->name); fseek(tape->file, n * PAGE_SIZE, SEEK_SET); tape->offset = 0; tape->read = fread(tape->buffer, 1, PAGE_SIZE, tape->file); tape->block = n; reads++; } void _tape_flush(tape_t* tape) { if (tape->offset == 0) { return; } printfv(VERBOSITY_DEBUG, "Flushing block %d (%u bytes) of %s.\n", tape->block, tape->offset, tape->name); fseek(tape->file, tape->block * PAGE_SIZE, SEEK_SET); fwrite(tape->buffer, 1, tape->offset, tape->file); tape->offset = 0; tape->block++; writes++; } void* tape_read(tape_t* tape, void* record, size_t size) { if (tape->offset >= tape->read) { // load next block _tape_load_block(tape, tape->block + 1); } if (tape->offset + size > tape->read && feof(tape->file)) { return NULL; } if (tape->offset + size > tape->read) { size_t rest = tape->read - tape->offset; memcpy(record, tape->buffer + tape->offset, rest); _tape_load_block(tape, tape->block + 1); memcpy(record + rest, tape->buffer + tape->offset, size - rest); // move tape offset tape->offset += size - rest; } else { memcpy(record, tape->buffer + tape->offset, size); tape->offset += size; } return record; } int tape_rewind(tape_t* tape, size_t n, size_t size) { int bytes = n * size; int take = tape->offset < bytes ? tape->offset : bytes; bytes -= take; tape->offset -= take; while (bytes > 0) { _tape_load_block(tape, tape->block - 1); bytes -= PAGE_SIZE; } tape->offset -= bytes; return tape->block; } int tape_write(tape_t* tape, void* record, size_t size) { size_t written = 0; while (tape->offset + size > PAGE_SIZE) { size_t available = PAGE_SIZE - tape->offset; memcpy(tape->buffer + tape->offset, record + written, available); tape->offset += available; size -= available; written += available; _tape_flush(tape); } if (size) { memcpy(tape->buffer + tape->offset, record + written, size); tape->offset += size; written += size; } return written; }