SBDP01/tape.c
2018-10-21 13:51:28 +02:00

162 lines
3.9 KiB
C

#include "tape.h"
#include "common.h"
#include <stdlib.h>
#include <memory.h>
#include <string.h>
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;
}