Merge branch 'py/git-gui-updates'
[alt-git.git] / chunk-format.c
blob140dfa0dcc4629fec59f723d025ec36019ff29eb
1 #include "git-compat-util.h"
2 #include "chunk-format.h"
3 #include "csum-file.h"
4 #include "gettext.h"
5 #include "hash.h"
6 #include "trace2.h"
8 /*
9 * When writing a chunk-based file format, collect the chunks in
10 * an array of chunk_info structs. The size stores the _expected_
11 * amount of data that will be written by write_fn.
13 struct chunk_info {
14 uint32_t id;
15 uint64_t size;
16 chunk_write_fn write_fn;
18 const void *start;
21 struct chunkfile {
22 struct hashfile *f;
24 struct chunk_info *chunks;
25 size_t chunks_nr;
26 size_t chunks_alloc;
29 struct chunkfile *init_chunkfile(struct hashfile *f)
31 struct chunkfile *cf = xcalloc(1, sizeof(*cf));
32 cf->f = f;
33 return cf;
36 void free_chunkfile(struct chunkfile *cf)
38 if (!cf)
39 return;
40 free(cf->chunks);
41 free(cf);
44 int get_num_chunks(struct chunkfile *cf)
46 return cf->chunks_nr;
49 void add_chunk(struct chunkfile *cf,
50 uint32_t id,
51 size_t size,
52 chunk_write_fn fn)
54 ALLOC_GROW(cf->chunks, cf->chunks_nr + 1, cf->chunks_alloc);
56 cf->chunks[cf->chunks_nr].id = id;
57 cf->chunks[cf->chunks_nr].write_fn = fn;
58 cf->chunks[cf->chunks_nr].size = size;
59 cf->chunks_nr++;
62 int write_chunkfile(struct chunkfile *cf, void *data)
64 int i, result = 0;
65 uint64_t cur_offset = hashfile_total(cf->f);
67 trace2_region_enter("chunkfile", "write", the_repository);
69 /* Add the table of contents to the current offset */
70 cur_offset += (cf->chunks_nr + 1) * CHUNK_TOC_ENTRY_SIZE;
72 for (i = 0; i < cf->chunks_nr; i++) {
73 hashwrite_be32(cf->f, cf->chunks[i].id);
74 hashwrite_be64(cf->f, cur_offset);
76 cur_offset += cf->chunks[i].size;
79 /* Trailing entry marks the end of the chunks */
80 hashwrite_be32(cf->f, 0);
81 hashwrite_be64(cf->f, cur_offset);
83 for (i = 0; i < cf->chunks_nr; i++) {
84 off_t start_offset = hashfile_total(cf->f);
85 result = cf->chunks[i].write_fn(cf->f, data);
87 if (result)
88 goto cleanup;
90 if (hashfile_total(cf->f) - start_offset != cf->chunks[i].size)
91 BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",
92 cf->chunks[i].size, cf->chunks[i].id,
93 hashfile_total(cf->f) - start_offset);
96 cleanup:
97 trace2_region_leave("chunkfile", "write", the_repository);
98 return result;
101 int read_table_of_contents(struct chunkfile *cf,
102 const unsigned char *mfile,
103 size_t mfile_size,
104 uint64_t toc_offset,
105 int toc_length)
107 int i;
108 uint32_t chunk_id;
109 const unsigned char *table_of_contents = mfile + toc_offset;
111 ALLOC_GROW(cf->chunks, toc_length, cf->chunks_alloc);
113 while (toc_length--) {
114 uint64_t chunk_offset, next_chunk_offset;
116 chunk_id = get_be32(table_of_contents);
117 chunk_offset = get_be64(table_of_contents + 4);
119 if (!chunk_id) {
120 error(_("terminating chunk id appears earlier than expected"));
121 return 1;
124 table_of_contents += CHUNK_TOC_ENTRY_SIZE;
125 next_chunk_offset = get_be64(table_of_contents + 4);
127 if (next_chunk_offset < chunk_offset ||
128 next_chunk_offset > mfile_size - the_hash_algo->rawsz) {
129 error(_("improper chunk offset(s) %"PRIx64" and %"PRIx64""),
130 chunk_offset, next_chunk_offset);
131 return -1;
134 for (i = 0; i < cf->chunks_nr; i++) {
135 if (cf->chunks[i].id == chunk_id) {
136 error(_("duplicate chunk ID %"PRIx32" found"),
137 chunk_id);
138 return -1;
142 cf->chunks[cf->chunks_nr].id = chunk_id;
143 cf->chunks[cf->chunks_nr].start = mfile + chunk_offset;
144 cf->chunks[cf->chunks_nr].size = next_chunk_offset - chunk_offset;
145 cf->chunks_nr++;
148 chunk_id = get_be32(table_of_contents);
149 if (chunk_id) {
150 error(_("final chunk has non-zero id %"PRIx32""), chunk_id);
151 return -1;
154 return 0;
157 static int pair_chunk_fn(const unsigned char *chunk_start,
158 size_t chunk_size,
159 void *data)
161 const unsigned char **p = data;
162 *p = chunk_start;
163 return 0;
166 int pair_chunk(struct chunkfile *cf,
167 uint32_t chunk_id,
168 const unsigned char **p)
170 return read_chunk(cf, chunk_id, pair_chunk_fn, p);
173 int read_chunk(struct chunkfile *cf,
174 uint32_t chunk_id,
175 chunk_read_fn fn,
176 void *data)
178 int i;
180 for (i = 0; i < cf->chunks_nr; i++) {
181 if (cf->chunks[i].id == chunk_id)
182 return fn(cf->chunks[i].start, cf->chunks[i].size, data);
185 return CHUNK_NOT_FOUND;
188 uint8_t oid_version(const struct git_hash_algo *algop)
190 switch (hash_algo_by_ptr(algop)) {
191 case GIT_HASH_SHA1:
192 return 1;
193 case GIT_HASH_SHA256:
194 return 2;
195 default:
196 die(_("invalid hash version"));