Sync with 'maint'
[git/gitster.git] / chunk-format.c
blob2dde24e6a317ce82ac23f8ba8d1c9da6315bfca0
1 #define USE_THE_REPOSITORY_VARIABLE
3 #include "git-compat-util.h"
4 #include "chunk-format.h"
5 #include "csum-file.h"
6 #include "gettext.h"
7 #include "hash.h"
8 #include "trace2.h"
11 * When writing a chunk-based file format, collect the chunks in
12 * an array of chunk_info structs. The size stores the _expected_
13 * amount of data that will be written by write_fn.
15 struct chunk_info {
16 uint32_t id;
17 uint64_t size;
18 chunk_write_fn write_fn;
20 const void *start;
23 struct chunkfile {
24 struct hashfile *f;
26 struct chunk_info *chunks;
27 size_t chunks_nr;
28 size_t chunks_alloc;
31 struct chunkfile *init_chunkfile(struct hashfile *f)
33 struct chunkfile *cf = xcalloc(1, sizeof(*cf));
34 cf->f = f;
35 return cf;
38 void free_chunkfile(struct chunkfile *cf)
40 if (!cf)
41 return;
42 free(cf->chunks);
43 free(cf);
46 int get_num_chunks(struct chunkfile *cf)
48 return cf->chunks_nr;
51 void add_chunk(struct chunkfile *cf,
52 uint32_t id,
53 size_t size,
54 chunk_write_fn fn)
56 ALLOC_GROW(cf->chunks, cf->chunks_nr + 1, cf->chunks_alloc);
58 cf->chunks[cf->chunks_nr].id = id;
59 cf->chunks[cf->chunks_nr].write_fn = fn;
60 cf->chunks[cf->chunks_nr].size = size;
61 cf->chunks_nr++;
64 int write_chunkfile(struct chunkfile *cf, void *data)
66 int i, result = 0;
67 uint64_t cur_offset = hashfile_total(cf->f);
69 trace2_region_enter("chunkfile", "write", the_repository);
71 /* Add the table of contents to the current offset */
72 cur_offset += (cf->chunks_nr + 1) * CHUNK_TOC_ENTRY_SIZE;
74 for (i = 0; i < cf->chunks_nr; i++) {
75 hashwrite_be32(cf->f, cf->chunks[i].id);
76 hashwrite_be64(cf->f, cur_offset);
78 cur_offset += cf->chunks[i].size;
81 /* Trailing entry marks the end of the chunks */
82 hashwrite_be32(cf->f, 0);
83 hashwrite_be64(cf->f, cur_offset);
85 for (i = 0; i < cf->chunks_nr; i++) {
86 off_t start_offset = hashfile_total(cf->f);
87 result = cf->chunks[i].write_fn(cf->f, data);
89 if (result)
90 goto cleanup;
92 if (hashfile_total(cf->f) - start_offset != cf->chunks[i].size)
93 BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",
94 cf->chunks[i].size, cf->chunks[i].id,
95 hashfile_total(cf->f) - start_offset);
98 cleanup:
99 trace2_region_leave("chunkfile", "write", the_repository);
100 return result;
103 int read_table_of_contents(struct chunkfile *cf,
104 const unsigned char *mfile,
105 size_t mfile_size,
106 uint64_t toc_offset,
107 int toc_length,
108 unsigned expected_alignment)
110 int i;
111 uint32_t chunk_id;
112 const unsigned char *table_of_contents = mfile + toc_offset;
114 ALLOC_GROW(cf->chunks, toc_length, cf->chunks_alloc);
116 while (toc_length--) {
117 uint64_t chunk_offset, next_chunk_offset;
119 chunk_id = get_be32(table_of_contents);
120 chunk_offset = get_be64(table_of_contents + 4);
122 if (!chunk_id) {
123 error(_("terminating chunk id appears earlier than expected"));
124 return 1;
126 if (chunk_offset % expected_alignment != 0) {
127 error(_("chunk id %"PRIx32" not %d-byte aligned"),
128 chunk_id, expected_alignment);
129 return 1;
132 table_of_contents += CHUNK_TOC_ENTRY_SIZE;
133 next_chunk_offset = get_be64(table_of_contents + 4);
135 if (next_chunk_offset < chunk_offset ||
136 next_chunk_offset > mfile_size - the_hash_algo->rawsz) {
137 error(_("improper chunk offset(s) %"PRIx64" and %"PRIx64""),
138 chunk_offset, next_chunk_offset);
139 return -1;
142 for (i = 0; i < cf->chunks_nr; i++) {
143 if (cf->chunks[i].id == chunk_id) {
144 error(_("duplicate chunk ID %"PRIx32" found"),
145 chunk_id);
146 return -1;
150 cf->chunks[cf->chunks_nr].id = chunk_id;
151 cf->chunks[cf->chunks_nr].start = mfile + chunk_offset;
152 cf->chunks[cf->chunks_nr].size = next_chunk_offset - chunk_offset;
153 cf->chunks_nr++;
156 chunk_id = get_be32(table_of_contents);
157 if (chunk_id) {
158 error(_("final chunk has non-zero id %"PRIx32""), chunk_id);
159 return -1;
162 return 0;
165 struct pair_chunk_data {
166 const unsigned char **p;
167 size_t *size;
170 static int pair_chunk_fn(const unsigned char *chunk_start,
171 size_t chunk_size,
172 void *data)
174 struct pair_chunk_data *pcd = data;
175 *pcd->p = chunk_start;
176 *pcd->size = chunk_size;
177 return 0;
180 int pair_chunk(struct chunkfile *cf,
181 uint32_t chunk_id,
182 const unsigned char **p,
183 size_t *size)
185 struct pair_chunk_data pcd = { .p = p, .size = size };
186 return read_chunk(cf, chunk_id, pair_chunk_fn, &pcd);
189 int read_chunk(struct chunkfile *cf,
190 uint32_t chunk_id,
191 chunk_read_fn fn,
192 void *data)
194 int i;
196 for (i = 0; i < cf->chunks_nr; i++) {
197 if (cf->chunks[i].id == chunk_id)
198 return fn(cf->chunks[i].start, cf->chunks[i].size, data);
201 return CHUNK_NOT_FOUND;
204 uint8_t oid_version(const struct git_hash_algo *algop)
206 switch (hash_algo_by_ptr(algop)) {
207 case GIT_HASH_SHA1:
208 return 1;
209 case GIT_HASH_SHA256:
210 return 2;
211 default:
212 die(_("invalid hash version"));