Sync with 2.31.4
[git.git] / chunk-format.c
blobda191e59a29db6d8ae6b576dc927a9fae35d094a
1 #include "cache.h"
2 #include "chunk-format.h"
3 #include "csum-file.h"
5 /*
6 * When writing a chunk-based file format, collect the chunks in
7 * an array of chunk_info structs. The size stores the _expected_
8 * amount of data that will be written by write_fn.
9 */
10 struct chunk_info {
11 uint32_t id;
12 uint64_t size;
13 chunk_write_fn write_fn;
15 const void *start;
18 struct chunkfile {
19 struct hashfile *f;
21 struct chunk_info *chunks;
22 size_t chunks_nr;
23 size_t chunks_alloc;
26 struct chunkfile *init_chunkfile(struct hashfile *f)
28 struct chunkfile *cf = xcalloc(1, sizeof(*cf));
29 cf->f = f;
30 return cf;
33 void free_chunkfile(struct chunkfile *cf)
35 if (!cf)
36 return;
37 free(cf->chunks);
38 free(cf);
41 int get_num_chunks(struct chunkfile *cf)
43 return cf->chunks_nr;
46 void add_chunk(struct chunkfile *cf,
47 uint32_t id,
48 size_t size,
49 chunk_write_fn fn)
51 ALLOC_GROW(cf->chunks, cf->chunks_nr + 1, cf->chunks_alloc);
53 cf->chunks[cf->chunks_nr].id = id;
54 cf->chunks[cf->chunks_nr].write_fn = fn;
55 cf->chunks[cf->chunks_nr].size = size;
56 cf->chunks_nr++;
59 int write_chunkfile(struct chunkfile *cf, void *data)
61 int i;
62 uint64_t cur_offset = hashfile_total(cf->f);
64 /* Add the table of contents to the current offset */
65 cur_offset += (cf->chunks_nr + 1) * CHUNK_TOC_ENTRY_SIZE;
67 for (i = 0; i < cf->chunks_nr; i++) {
68 hashwrite_be32(cf->f, cf->chunks[i].id);
69 hashwrite_be64(cf->f, cur_offset);
71 cur_offset += cf->chunks[i].size;
74 /* Trailing entry marks the end of the chunks */
75 hashwrite_be32(cf->f, 0);
76 hashwrite_be64(cf->f, cur_offset);
78 for (i = 0; i < cf->chunks_nr; i++) {
79 off_t start_offset = hashfile_total(cf->f);
80 int result = cf->chunks[i].write_fn(cf->f, data);
82 if (result)
83 return result;
85 if (hashfile_total(cf->f) - start_offset != cf->chunks[i].size)
86 BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",
87 cf->chunks[i].size, cf->chunks[i].id,
88 hashfile_total(cf->f) - start_offset);
91 return 0;
94 int read_table_of_contents(struct chunkfile *cf,
95 const unsigned char *mfile,
96 size_t mfile_size,
97 uint64_t toc_offset,
98 int toc_length)
100 int i;
101 uint32_t chunk_id;
102 const unsigned char *table_of_contents = mfile + toc_offset;
104 ALLOC_GROW(cf->chunks, toc_length, cf->chunks_alloc);
106 while (toc_length--) {
107 uint64_t chunk_offset, next_chunk_offset;
109 chunk_id = get_be32(table_of_contents);
110 chunk_offset = get_be64(table_of_contents + 4);
112 if (!chunk_id) {
113 error(_("terminating chunk id appears earlier than expected"));
114 return 1;
117 table_of_contents += CHUNK_TOC_ENTRY_SIZE;
118 next_chunk_offset = get_be64(table_of_contents + 4);
120 if (next_chunk_offset < chunk_offset ||
121 next_chunk_offset > mfile_size - the_hash_algo->rawsz) {
122 error(_("improper chunk offset(s) %"PRIx64" and %"PRIx64""),
123 chunk_offset, next_chunk_offset);
124 return -1;
127 for (i = 0; i < cf->chunks_nr; i++) {
128 if (cf->chunks[i].id == chunk_id) {
129 error(_("duplicate chunk ID %"PRIx32" found"),
130 chunk_id);
131 return -1;
135 cf->chunks[cf->chunks_nr].id = chunk_id;
136 cf->chunks[cf->chunks_nr].start = mfile + chunk_offset;
137 cf->chunks[cf->chunks_nr].size = next_chunk_offset - chunk_offset;
138 cf->chunks_nr++;
141 chunk_id = get_be32(table_of_contents);
142 if (chunk_id) {
143 error(_("final chunk has non-zero id %"PRIx32""), chunk_id);
144 return -1;
147 return 0;
150 static int pair_chunk_fn(const unsigned char *chunk_start,
151 size_t chunk_size,
152 void *data)
154 const unsigned char **p = data;
155 *p = chunk_start;
156 return 0;
159 int pair_chunk(struct chunkfile *cf,
160 uint32_t chunk_id,
161 const unsigned char **p)
163 return read_chunk(cf, chunk_id, pair_chunk_fn, p);
166 int read_chunk(struct chunkfile *cf,
167 uint32_t chunk_id,
168 chunk_read_fn fn,
169 void *data)
171 int i;
173 for (i = 0; i < cf->chunks_nr; i++) {
174 if (cf->chunks[i].id == chunk_id)
175 return fn(cf->chunks[i].start, cf->chunks[i].size, data);
178 return CHUNK_NOT_FOUND;