Created
May 6, 2013 15:46
-
-
Save xndcn/5525966 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Checksummed file writer | |
* | |
* This module collects data buffers until they should be written out to a | |
* file, prefixed by a checksum. | |
* | |
* Typical usage is: | |
* | |
* ChecksumWriter writer; | |
* cwriter_init(&writer); | |
* while (...) { | |
* buf = malloc(len); | |
* ...read len bytes into buf... | |
* cwriter_add_buf(&writer, buf, len); | |
* } | |
* cwriter_finish(&writer, stdout); | |
* | |
* The ChecksumWriter holds onto the buffers and finally emits the checksum, | |
* followed by the buffers themselves. | |
* | |
* The checksum algorithm is simple: unsigned addition modulo 256 of each byte | |
* in the buffer. | |
* | |
* The output written by cwriter_finish() is laid out like this: | |
* [checksum byte][buf1][buf2]...[bufn] | |
*/ | |
#include <assert.h> | |
#include <stdbool.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
typedef struct buflist { | |
uint8_t *buffer; | |
int length; | |
struct buflist *next; | |
} BufList; | |
void buflist_init(BufList *buflist) | |
{ | |
buflist->buffer = NULL; | |
buflist->length = 0; | |
buflist->next = NULL; | |
} | |
typedef struct { | |
BufList *tail; | |
BufList *head; | |
uint8_t checksum; | |
} ChecksumWriter; | |
/** | |
* cwriter_init: | |
* @writer: The writer instance. | |
* | |
* Initialize a ChecksumWriter so it is ready to be used in cwriter_add_buf() | |
* and cwriter_finish() calls. | |
*/ | |
void cwriter_init(ChecksumWriter *writer) | |
{ | |
writer->head = (BufList *) malloc(sizeof(BufList)); | |
buflist_init(writer->head); | |
writer->tail = writer->head; | |
writer->checksum = 0; | |
} | |
/** | |
* cwriter_add_buf: | |
* @writer: The writer instance. | |
* @buf: The buffer to add. | |
* @len: The length of the buffer, in bytes. | |
* | |
* Append a buffer to the ChecksumWriter. | |
* | |
* This function does not copy buf, instead the caller passes ownership of @buf | |
* to this function. | |
*/ | |
void cwriter_add_buf(ChecksumWriter *writer, void *buf, size_t len) | |
{ | |
int i; | |
for (i = 0; i < len; i++) { | |
writer->checksum = writer->checksum + ((uint8_t *)buf)[i]; | |
} | |
writer->tail->buffer = buf; | |
writer->tail->length = len; | |
writer->tail->next = (BufList *) malloc(sizeof(BufList)); | |
buflist_init(writer->tail->next); | |
writer->tail = writer->tail->next; | |
} | |
/** | |
* cwriter_finish: | |
* @writer: The writer instance. | |
* @fp: The output file. | |
* | |
* Write the checksum followed by the buffers themselves to @fp and ensure the | |
* contents are on stable storage. | |
* | |
* This function frees resources. To use the ChecksumWriter again, call | |
* cwriter_init() first. | |
* | |
* Returns: true on success, false if writing to file failed | |
*/ | |
bool cwriter_finish(ChecksumWriter *writer, FILE *fp) | |
{ | |
BufList *now = writer->head; | |
BufList *temp; | |
int i; | |
if (fp == NULL) | |
return false; | |
fprintf(fp, "%c", writer->checksum); | |
while (now != NULL) { | |
for (i = 0; i < now->length; i++) { | |
if (fprintf(fp, "%c", now->buffer[i]) <= 0) | |
return false; | |
} | |
temp = now; | |
now = now->next; | |
free(temp); | |
} | |
return true; | |
} | |
/* TEST CODE BELOW, DO NOT MODIFY */ | |
typedef struct BufferState { | |
uint8_t *buf; | |
uint8_t *buf_copy; | |
size_t buf_len; | |
} BufferState; | |
void do_test(ChecksumWriter *writer, const char *path) | |
{ | |
BufferState *buffer_states; | |
size_t buffer_count, i, j; | |
FILE *fp; | |
uint8_t checksum = 0, checksum_computed, val; | |
fp = fopen(path, "w+"); | |
assert(fp); | |
buffer_count = random() % 1024; | |
buffer_states = malloc(buffer_count * sizeof(BufferState)); | |
assert(buffer_count == 0 || buffer_states); | |
cwriter_init(writer); | |
for (i = 0; i < buffer_count; i++) { | |
BufferState *s = &buffer_states[i]; | |
s->buf_len = random() % 4096; | |
s->buf = malloc(s->buf_len); | |
assert(s->buf); | |
s->buf_copy = malloc(s->buf_len); | |
assert(s->buf_copy); | |
for (j = 0; j < s->buf_len; j++) { | |
s->buf[j] = s->buf_copy[j] = random(); | |
checksum += s->buf[j]; | |
} | |
cwriter_add_buf(writer, s->buf_copy, s->buf_len); | |
} | |
assert(cwriter_finish(writer, fp)); | |
rewind(fp); | |
checksum_computed = fgetc(fp); | |
assert(checksum_computed == checksum); | |
for (i = 0; i < buffer_count; i++) { | |
BufferState *s = &buffer_states[i]; | |
for (j = 0; j < s->buf_len; j++) { | |
val = fgetc(fp); | |
assert(feof(fp) == false); | |
assert(ferror(fp) == false); | |
assert(val == s->buf[j]); | |
} | |
free(s->buf); | |
} | |
fclose(fp); | |
if (buffer_count) { | |
free(buffer_states); | |
} | |
fprintf(stderr, "test completed, checksum: %u, buffers: %lu\n", | |
checksum_computed, buffer_count); | |
} | |
int main(int argc, char **argv) | |
{ | |
ChecksumWriter writer; | |
int i, seed = 574, iterations = 16; | |
char *path; | |
if (argc < 2 || argc > 4) { | |
fprintf(stderr, "usage: %s <output path> [<iterations> [<seed>]]\n", argv[0]); | |
return 1; | |
} | |
if (argc > 2) { | |
iterations = atoi(argv[2]); | |
} | |
if (argc > 3) { | |
seed = atoi(argv[3]); | |
} | |
srandom(seed); | |
path = argv[1]; | |
for (i = 0; i < iterations; i++) { | |
do_test(&writer, path); | |
} | |
fprintf(stderr, "all tests successful!\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment