Skip to content

Instantly share code, notes, and snippets.

@xndcn
Created May 6, 2013 15:46
Show Gist options
  • Save xndcn/5525966 to your computer and use it in GitHub Desktop.
Save xndcn/5525966 to your computer and use it in GitHub Desktop.
/*
* 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