replace-object.h: move read_replace_refs declaration from cache.h to here
[git.git] / chunk-format.c
blobf65e9a1e4290c1b704e61c0c9909c61c14bf67da
1 #include "git-compat-util.h"
2 #include "alloc.h"
3 #include "chunk-format.h"
4 #include "csum-file.h"
6 /*
7 * When writing a chunk-based file format, collect the chunks in
8 * an array of chunk_info structs. The size stores the _expected_
9 * amount of data that will be written by write_fn.
11 struct chunk_info {
12 uint32_t id;
13 uint64_t size;
14 chunk_write_fn write_fn;
16 const void *start;
19 struct chunkfile {
20 struct hashfile *f;
22 struct chunk_info *chunks;
23 size_t chunks_nr;
24 size_t chunks_alloc;
27 struct chunkfile *init_chunkfile(struct hashfile *f)
29 struct chunkfile *cf = xcalloc(1, sizeof(*cf));
30 cf->f = f;
31 return cf;
34 void free_chunkfile(struct chunkfile *cf)
36 if (!cf)
37 return;
38 free(cf->chunks);
39 free(cf);
42 int get_num_chunks(struct chunkfile *cf)
44 return cf->chunks_nr;
47 void add_chunk(struct chunkfile *cf,
48 uint32_t id,
49 size_t size,
50 chunk_write_fn fn)
52 ALLOC_GROW(cf->chunks, cf->chunks_nr + 1, cf->chunks_alloc);
54 cf->chunks[cf->chunks_nr].id = id;
55 cf->chunks[cf->chunks_nr].write_fn = fn;
56 cf->chunks[cf->chunks_nr].size = size;
57 cf->chunks_nr++;
60 int write_chunkfile(struct chunkfile *cf, void *data)
62 int i, result = 0;
63 uint64_t cur_offset = hashfile_total(cf->f);
65 trace2_region_enter("chunkfile", "write", the_repository);
67 /* Add the table of contents to the current offset */
68 cur_offset += (cf->chunks_nr + 1) * CHUNK_TOC_ENTRY_SIZE;
70 for (i = 0; i < cf->chunks_nr; i++) {
71 hashwrite_be32(cf->f, cf->chunks[i].id);
72 hashwrite_be64(cf->f, cur_offset);
74 cur_offset += cf->chunks[i].size;
77 /* Trailing entry marks the end of the chunks */
78 hashwrite_be32(cf->f, 0);
79 hashwrite_be64(cf->f, cur_offset);
81 for (i = 0; i < cf->chunks_nr; i++) {
82 off_t start_offset = hashfile_total(cf->f);
83 result = cf->chunks[i].write_fn(cf->f, data);
85 if (result)
86 goto cleanup;
88 if (hashfile_total(cf->f) - start_offset != cf->chunks[i].size)
89 BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",
90 cf->chunks[i].size, cf->chunks[i].id,
91 hashfile_total(cf->f) - start_offset);
94 cleanup:
95 trace2_region_leave("chunkfile", "write", the_repository);
96 return result;
99 int read_table_of_contents(struct chunkfile *cf,
100 const unsigned char *mfile,
101 size_t mfile_size,
102 uint64_t toc_offset,
103 int toc_length)
105 int i;
106 uint32_t chunk_id;
107 const unsigned char *table_of_contents = mfile + toc_offset;
109 ALLOC_GROW(cf->chunks, toc_length, cf->chunks_alloc);
111 while (toc_length--) {
112 uint64_t chunk_offset, next_chunk_offset;
114 chunk_id = get_be32(table_of_contents);
115 chunk_offset = get_be64(table_of_contents + 4);
117 if (!chunk_id) {
118 error(_("terminating chunk id appears earlier than expected"));
119 return 1;
122 table_of_contents += CHUNK_TOC_ENTRY_SIZE;
123 next_chunk_offset = get_be64(table_of_contents + 4);
125 if (next_chunk_offset < chunk_offset ||
126 next_chunk_offset > mfile_size - the_hash_algo->rawsz) {
127 error(_("improper chunk offset(s) %"PRIx64" and %"PRIx64""),
128 chunk_offset, next_chunk_offset);
129 return -1;
132 for (i = 0; i < cf->chunks_nr; i++) {
133 if (cf->chunks[i].id == chunk_id) {
134 error(_("duplicate chunk ID %"PRIx32" found"),
135 chunk_id);
136 return -1;
140 cf->chunks[cf->chunks_nr].id = chunk_id;
141 cf->chunks[cf->chunks_nr].start = mfile + chunk_offset;
142 cf->chunks[cf->chunks_nr].size = next_chunk_offset - chunk_offset;
143 cf->chunks_nr++;
146 chunk_id = get_be32(table_of_contents);
147 if (chunk_id) {
148 error(_("final chunk has non-zero id %"PRIx32""), chunk_id);
149 return -1;
152 return 0;
155 static int pair_chunk_fn(const unsigned char *chunk_start,
156 size_t chunk_size,
157 void *data)
159 const unsigned char **p = data;
160 *p = chunk_start;
161 return 0;
164 int pair_chunk(struct chunkfile *cf,
165 uint32_t chunk_id,
166 const unsigned char **p)
168 return read_chunk(cf, chunk_id, pair_chunk_fn, p);
171 int read_chunk(struct chunkfile *cf,
172 uint32_t chunk_id,
173 chunk_read_fn fn,
174 void *data)
176 int i;
178 for (i = 0; i < cf->chunks_nr; i++) {
179 if (cf->chunks[i].id == chunk_id)
180 return fn(cf->chunks[i].start, cf->chunks[i].size, data);
183 return CHUNK_NOT_FOUND;
186 uint8_t oid_version(const struct git_hash_algo *algop)
188 switch (hash_algo_by_ptr(algop)) {
189 case GIT_HASH_SHA1:
190 return 1;
191 case GIT_HASH_SHA256:
192 return 2;
193 default:
194 die(_("invalid hash version"));