| #include "cache.h" |
| #include "chunk-format.h" |
| #include "csum-file.h" |
| |
| /* |
| * When writing a chunk-based file format, collect the chunks in |
| * an array of chunk_info structs. The size stores the _expected_ |
| * amount of data that will be written by write_fn. |
| */ |
| struct chunk_info { |
| uint32_t id; |
| uint64_t size; |
| chunk_write_fn write_fn; |
| }; |
| |
| struct chunkfile { |
| struct hashfile *f; |
| |
| struct chunk_info *chunks; |
| size_t chunks_nr; |
| size_t chunks_alloc; |
| }; |
| |
| struct chunkfile *init_chunkfile(struct hashfile *f) |
| { |
| struct chunkfile *cf = xcalloc(1, sizeof(*cf)); |
| cf->f = f; |
| return cf; |
| } |
| |
| void free_chunkfile(struct chunkfile *cf) |
| { |
| if (!cf) |
| return; |
| free(cf->chunks); |
| free(cf); |
| } |
| |
| int get_num_chunks(struct chunkfile *cf) |
| { |
| return cf->chunks_nr; |
| } |
| |
| void add_chunk(struct chunkfile *cf, |
| uint32_t id, |
| size_t size, |
| chunk_write_fn fn) |
| { |
| ALLOC_GROW(cf->chunks, cf->chunks_nr + 1, cf->chunks_alloc); |
| |
| cf->chunks[cf->chunks_nr].id = id; |
| cf->chunks[cf->chunks_nr].write_fn = fn; |
| cf->chunks[cf->chunks_nr].size = size; |
| cf->chunks_nr++; |
| } |
| |
| int write_chunkfile(struct chunkfile *cf, void *data) |
| { |
| int i; |
| uint64_t cur_offset = hashfile_total(cf->f); |
| |
| /* Add the table of contents to the current offset */ |
| cur_offset += (cf->chunks_nr + 1) * CHUNK_TOC_ENTRY_SIZE; |
| |
| for (i = 0; i < cf->chunks_nr; i++) { |
| hashwrite_be32(cf->f, cf->chunks[i].id); |
| hashwrite_be64(cf->f, cur_offset); |
| |
| cur_offset += cf->chunks[i].size; |
| } |
| |
| /* Trailing entry marks the end of the chunks */ |
| hashwrite_be32(cf->f, 0); |
| hashwrite_be64(cf->f, cur_offset); |
| |
| for (i = 0; i < cf->chunks_nr; i++) { |
| off_t start_offset = hashfile_total(cf->f); |
| int result = cf->chunks[i].write_fn(cf->f, data); |
| |
| if (result) |
| return result; |
| |
| if (hashfile_total(cf->f) - start_offset != cf->chunks[i].size) |
| BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead", |
| cf->chunks[i].size, cf->chunks[i].id, |
| hashfile_total(cf->f) - start_offset); |
| } |
| |
| return 0; |
| } |