- Commit
- d8bfb69668ee5cd3dcf60c0a8f023465b4040fda
- Parent
- 8e80513468b619ca5c4593736deb50705525cbe9
- Author
- Pablo <pablo-pie@riseup.net>
- Date
Implemented buffered IO
NOTE: substantially changed the API
Minimalist header-only library for generating TAR files
Implemented buffered IO
NOTE: substantially changed the API
2 files changed, 118 insertions, 24 deletions
| Status | Name | Changes | Insertions | Deletions |
| Modified | example.c | 2 files changed | 12 | 7 |
| Modified | tarw.h | 2 files changed | 106 | 17 |
diff --git a/example.c b/example.c @@ -7,29 +7,34 @@ int main(void) { const char *output_path = "./test.tar"; - FILE *sink = fopen(output_path, "wb"); + FILE *output = fopen(output_path, "wb"); - if (sink == NULL) { + if (output == NULL) { fprintf(stderr, "ERROR: could not open \"%s\"\n", output_path); return EXIT_FAILURE; } TarWriter w = { - .sink = sink, .owner = "user", .group = "group", .owner_id = 1000, .group_id = 1000, }; - if (!tarw_write_directory(w, "test/", 0755)) goto error; + if (!tarw_add_directory(&w, "test/", 0755)) goto not_enought_memmory; const char *msg = "Hello from C!\n"; - if (!tarw_write_file(w, "test/a", 0644, msg, strlen(msg))) goto error; + if (!tarw_add_file(&w, "test/a", 0644, msg, strlen(msg))) + goto not_enought_memmory; + + if (!tarw_write(w, output)) { + fprintf(stderr, "ERROR: could write all bytes to \"%s\"\n", output_path); + return EXIT_FAILURE; + } return EXIT_SUCCESS; -error: - fprintf(stderr, "ERROR: could write all bytes to \"%s\"\n", output_path); +not_enought_memmory: + fprintf(stderr, "ERROR: could not allocate enough memmory\n"); return EXIT_FAILURE; }
diff --git a/tarw.h b/tarw.h @@ -12,17 +12,22 @@ extern "C" { #endif // __cplusplus typedef struct { - FILE *sink; - const char *owner; const char *group; size_t owner_id; size_t group_id; + + char *data; + size_t cursor; + size_t capacity; } TarWriter; -bool tarw_write_directory(TarWriter w, const char *path, uint16_t mode); -bool tarw_write_file(TarWriter w, const char *path, uint16_t mode, - const char *buff, size_t buff_size); +bool tarw_add_directory(TarWriter *w, const char *path, uint16_t mode); +bool tarw_add_file(TarWriter *w, const char *path, uint16_t mode, + const char *buff, size_t buff_size); +bool tarw_write(TarWriter w, FILE *f); + +void tarw_free(TarWriter *w); #define TARW_CHUNK_SIZE 512 @@ -68,9 +73,34 @@ enum { TARW_PIPE = '6', // TODO: unsupported }; +// = Implementation =========================================================== #ifdef TARW_IMPLEMENTATION + #define TARW_USTARW_INDICATOR "ustar" + +#if !defined(TARW_REALLOC) || !defined(TARW_FREE) +#include <stdlib.h> +#endif + +/* + * NOTE: should behave like realloc from libc: + * - returns `NULL` on fail + * - if `p` is `NULL` then behave like malloc + */ +#ifndef TARW_REALLOC +#define TARW_REALLOC(p, size) realloc((p), (size)) +#endif // TARW_REALLOC + +#ifndef TARW_FREE +#define TARW_FREE(p) free((p)) +#endif // TARW_FREE + + +#ifdef defined(TARW_EMIT_ASSERTS) && !defined(TARW_ASSERT) +#define TARW_ASSERT(cond, msg) assert((cond) && (msg)) +#endif + void tarw_header_set_checksum(TarHeader *h) { uint8_t *data = (uint8_t*)h; @@ -110,33 +140,92 @@ TarHeader tarw_make_header(const char *path, uint16_t mode, return h; } -bool tarw_write_directory(TarWriter w, const char *path, uint16_t mode) +bool tarw__reserve_bytes(TarWriter *w, size_t bytes) +{ + if (w->capacity - w->cursor >= bytes) return true; + + size_t new_capacity = 2 * w->capacity; + if (new_capacity - w->cursor < bytes) new_capacity = w->capacity + bytes; + + void *new_data = TARW_REALLOC(w->data, new_capacity); + if (new_data == NULL) return false; + w->data = new_data; + w->capacity = new_capacity; + + return true; +} + +/* + * NOTE: it is only safe to call this function when buff fits inside w->data! + */ +void tarw__write_bytes(TarWriter *w, const char *buff, size_t buff_size) +{ +#ifdef TARW_EMIT_ASSERTS + TARW_ASSERT(w->capactiry - w->cursor >= buff_size, "buffer overflow"); +#endif // TARW_EMIT_ASSERTS + memcpy(w->data + w->cursor, buff, buff_size); + w->cursor += buff_size; +} + +/* + * NOTE: it is only safe to call this function when buff fits inside w->data! + */ +void tarw__write_zeros(TarWriter *w, size_t n) { +#ifdef TARW_EMIT_ASSERTS + TARW_ASSERT(w->capactiry - w->cursor >= buff_size, "buffer overflow"); +#endif // TARW_EMIT_ASSERTS + memset(w->data + w->cursor, 0, n); + w->cursor += n; +} + +bool tarw_add_directory(TarWriter *w, const char *path, uint16_t mode) +{ + if (!tarw__reserve_bytes(w, TARW_CHUNK_SIZE)) return false; + TarHeader h = tarw_make_header(path, mode, - w.owner, w.group, w.owner_id, w.group_id, + w->owner, w->group, w->owner_id, w->group_id, TARW_DIRECTORY, 0); - if (fwrite((char*)&h, 1, sizeof(h), w.sink) != sizeof(h)) return false; + tarw__write_bytes(w, (char*)&h, TARW_CHUNK_SIZE); return true; } -bool tarw_write_file(TarWriter w, const char *path, uint16_t mode, - const char *buff, size_t buff_size) +bool tarw_add_file(TarWriter *w, const char *path, uint16_t mode, + const char *buff, size_t buff_size) { + size_t chunks_ceil = (buff_size + (TARW_CHUNK_SIZE-1))/TARW_CHUNK_SIZE; + size_t padding = chunks_ceil*TARW_CHUNK_SIZE - buff_size; + size_t capacity_needed = TARW_CHUNK_SIZE + buff_size + padding; + if (!tarw__reserve_bytes(w, capacity_needed)) return false; + TarHeader h = tarw_make_header(path, mode, - w.owner, w.group, w.owner_id, w.group_id, + w->owner, w->group, w->owner_id, w->group_id, TARW_NORMAL, buff_size); - if (fwrite((char*)&h, 1, sizeof(h), w.sink) != sizeof(h)) return false; - if (fwrite(buff, 1, buff_size, w.sink) != buff_size) return false; + tarw__write_bytes(w, (char*)&h, sizeof(h)); + tarw__write_bytes(w, buff, buff_size); + tarw__write_zeros(w, padding); + + return true; +} - static const char zeros[TARW_CHUNK_SIZE] = {0}; - size_t chunks_ceil = (buff_size + (TARW_CHUNK_SIZE-1))/TARW_CHUNK_SIZE; - size_t padding = chunks_ceil*TARW_CHUNK_SIZE - buff_size; - if (fwrite(zeros, 1, padding, w.sink) != padding) return false; +bool tarw_write(TarWriter w, FILE *f) +{ + if (fwrite(w.data, 1, w.cursor, f) != w.cursor) return false; return true; } +void tarw_free(TarWriter *w) +{ + if (w->data) { + TARW_FREE(w->data); + w->data = NULL; + } + w->capacity = 0; + w->cursor = 0; +} + #endif // TARW_IMPLEMENTATION #ifdef __cplusplus }