Resolved memory haemorrhage in perl replay implementation.
[svn-fe.git] / repo_tree.c
blobf3cf8f94b7a5ba9fa424f1faff481d334026f51f
1 #include <string.h>
3 #include "string_pool.h"
4 #include "repo_tree.h"
5 #include "obj_pool.h"
6 #include "fast_export.h"
8 typedef struct repo_dirent_s repo_dirent_t;
10 struct repo_dirent_s {
11 uint32_t name_offset;
12 uint32_t mode;
13 uint32_t content_offset;
16 typedef struct repo_dir_s repo_dir_t;
18 struct repo_dir_s {
19 uint32_t size;
20 uint32_t first_offset;
23 typedef struct repo_commit_s repo_commit_t;
25 struct repo_commit_s {
26 uint32_t mark;
27 uint32_t root_dir_offset;
30 obj_pool_gen(commit, repo_commit_t, 4096);
31 obj_pool_gen(dir, repo_dir_t, 4096);
32 obj_pool_gen(dirent, repo_dirent_t, 4096);
34 static uint32_t num_dirs_saved = 0;
35 static uint32_t num_dirents_saved = 0;
36 static uint32_t active_commit = -1;
38 static repo_dir_t *repo_commit_root_dir(repo_commit_t * commit)
40 return dir_pointer(commit->root_dir_offset);
43 static repo_dirent_t *repo_first_dirent(repo_dir_t * dir)
45 return dirent_pointer(dir->first_offset);
48 static int repo_dirent_name_cmp(const void *a, const void *b)
50 return (((repo_dirent_t *) a)->name_offset
51 > ((repo_dirent_t *) b)->name_offset) -
52 (((repo_dirent_t *) a)->name_offset
53 < ((repo_dirent_t *) b)->name_offset);
56 static repo_dirent_t *repo_dirent_by_name(repo_dir_t * dir,
57 uint32_t name_offset)
59 repo_dirent_t key;
60 if (dir == NULL || dir->size == 0)
61 return NULL;
62 key.name_offset = name_offset;
63 return bsearch(&key, repo_first_dirent(dir), dir->size,
64 sizeof(repo_dirent_t), repo_dirent_name_cmp);
67 static int repo_dirent_is_dir(repo_dirent_t * dirent)
69 return dirent != NULL && dirent->mode == REPO_MODE_DIR;
72 static repo_dir_t *repo_dir_from_dirent(repo_dirent_t * dirent)
74 if (!repo_dirent_is_dir(dirent))
75 return NULL;
76 return dir_pointer(dirent->content_offset);
79 static uint32_t dir_with_dirents_alloc(uint32_t size)
81 uint32_t offset = dir_alloc(1);
82 dir_pointer(offset)->size = size;
83 dir_pointer(offset)->first_offset = dirent_alloc(size);
84 return offset;
87 static repo_dir_t *repo_clone_dir(repo_dir_t * orig_dir, uint32_t padding)
89 uint32_t orig_o, new_o, dirent_o;
90 orig_o = dir_offset(orig_dir);
91 if (orig_o < num_dirs_saved) {
92 new_o = dir_with_dirents_alloc(orig_dir->size + padding);
93 orig_dir = dir_pointer(orig_o);
94 dirent_o = dir_pointer(new_o)->first_offset;
95 } else {
96 if (padding == 0)
97 return orig_dir;
98 new_o = orig_o;
99 dirent_o = dirent_alloc(orig_dir->size + padding);
101 memcpy(dirent_pointer(dirent_o), repo_first_dirent(orig_dir),
102 orig_dir->size * sizeof(repo_dirent_t));
103 dir_pointer(new_o)->size = orig_dir->size + padding;
104 dir_pointer(new_o)->first_offset = dirent_o;
105 return dir_pointer(new_o);
108 static char repo_path_buffer[REPO_MAX_PATH_LEN];
109 static repo_dirent_t *repo_read_dirent(uint32_t revision, char *path)
111 char *ctx = NULL;
112 uint32_t name = 0;
113 repo_dir_t *dir = NULL;
114 repo_dirent_t *dirent = NULL;
115 dir = repo_commit_root_dir(commit_pointer(revision));
116 strncpy(repo_path_buffer, path, REPO_MAX_PATH_LEN);
117 repo_path_buffer[REPO_MAX_PATH_LEN - 1] = '\0';
118 path = repo_path_buffer;
119 for (name = pool_tok_r(path, "/", &ctx);
120 ~name; name = pool_tok_r(NULL, "/", &ctx)) {
121 dirent = repo_dirent_by_name(dir, name);
122 if (dirent == NULL) {
123 return NULL;
124 } else if (repo_dirent_is_dir(dirent)) {
125 dir = repo_dir_from_dirent(dirent);
126 } else {
127 break;
130 return dirent;
133 static void
134 repo_write_dirent(char *path, uint32_t mode, uint32_t content_offset,
135 uint32_t del)
137 char *ctx;
138 uint32_t name, revision, dirent_o, dir_o, parent_dir_o;
139 repo_dir_t *dir;
140 repo_dirent_t *dirent = NULL;
141 revision = active_commit;
142 dir = repo_commit_root_dir(commit_pointer(revision));
143 dir = repo_clone_dir(dir, 0);
144 commit_pointer(revision)->root_dir_offset = dir_offset(dir);
145 strncpy(repo_path_buffer, path, REPO_MAX_PATH_LEN);
146 repo_path_buffer[REPO_MAX_PATH_LEN - 1] = '\0';
147 path = repo_path_buffer;
148 for (name = pool_tok_r(path, "/", &ctx); ~name;
149 name = pool_tok_r(NULL, "/", &ctx)) {
150 parent_dir_o = dir_offset(dir);
151 dirent = repo_dirent_by_name(dir, name);
152 if (dirent == NULL) {
153 dir = repo_clone_dir(dir, 1);
154 dirent = &repo_first_dirent(dir)[dir->size - 1];
155 dirent->name_offset = name;
156 dirent->mode = REPO_MODE_DIR;
157 qsort(repo_first_dirent(dir), dir->size,
158 sizeof(repo_dirent_t), repo_dirent_name_cmp);
159 dirent = repo_dirent_by_name(dir, name);
160 dir_o = dir_with_dirents_alloc(0);
161 dirent->content_offset = dir_o;
162 dir = dir_pointer(dir_o);
163 } else if ((dir = repo_dir_from_dirent(dirent))) {
164 dirent_o = dirent_offset(dirent);
165 dir = repo_clone_dir(dir, 0);
166 if (dirent_o != ~0)
167 dirent_pointer(dirent_o)->content_offset = dir_offset(dir);
168 } else {
169 dirent->mode = REPO_MODE_DIR;
170 dirent_o = dirent_offset(dirent);
171 dir_o = dir_with_dirents_alloc(0);
172 dirent = dirent_pointer(dirent_o);
173 dir = dir_pointer(dir_o);
174 dirent->content_offset = dir_o;
177 if (dirent) {
178 dirent->mode = mode;
179 dirent->content_offset = content_offset;
180 if (del && ~parent_dir_o) {
181 dirent->name_offset = ~0;
182 dir = dir_pointer(parent_dir_o);
183 qsort(repo_first_dirent(dir), dir->size,
184 sizeof(repo_dirent_t), repo_dirent_name_cmp);
185 dir->size--;
190 uint32_t repo_copy(uint32_t revision, char *src, char *dst)
192 uint32_t mode = 0, content_offset = 0;
193 repo_dirent_t *src_dirent;
194 src_dirent = repo_read_dirent(revision, src);
195 if (src_dirent != NULL) {
196 mode = src_dirent->mode;
197 content_offset = src_dirent->content_offset;
198 repo_write_dirent(dst, mode, content_offset, 0);
200 return mode;
203 void repo_add(char *path, uint32_t mode, uint32_t blob_mark)
205 repo_write_dirent(path, mode, blob_mark, 0);
208 uint32_t repo_replace(char *path, uint32_t blob_mark)
210 uint32_t mode = 0;
211 repo_dirent_t *src_dirent;
212 src_dirent = repo_read_dirent(active_commit, path);
213 if (src_dirent != NULL) {
214 mode = src_dirent->mode;
215 repo_write_dirent(path, mode, blob_mark, 0);
217 return mode;
220 void repo_modify(char *path, uint32_t mode, uint32_t blob_mark)
222 repo_write_dirent(path, mode, blob_mark, 0);
225 void repo_delete(char *path)
227 repo_write_dirent(path, 0, 0, 1);
230 static void
231 repo_git_add_r(uint32_t depth, uint32_t * path, repo_dir_t * dir);
233 static void
234 repo_git_add(uint32_t depth, uint32_t * path, repo_dirent_t * dirent)
236 if (repo_dirent_is_dir(dirent)) {
237 repo_git_add_r(depth, path, repo_dir_from_dirent(dirent));
238 } else {
239 fast_export_modify(depth, path, dirent->mode, dirent->content_offset);
243 static void
244 repo_git_add_r(uint32_t depth, uint32_t * path, repo_dir_t * dir)
246 uint32_t o;
247 repo_dirent_t *de;
248 de = repo_first_dirent(dir);
249 for (o = 0; o < dir->size; o++) {
250 path[depth] = de[o].name_offset;
251 repo_git_add(depth + 1, path, &de[o]);
255 static void
256 repo_diff_r(uint32_t depth, uint32_t * path, repo_dir_t * dir1,
257 repo_dir_t * dir2)
259 repo_dirent_t *de1, *de2, *max_de1, *max_de2;
260 de1 = repo_first_dirent(dir1);
261 de2 = repo_first_dirent(dir2);
262 max_de1 = &de1[dir1->size];
263 max_de2 = &de2[dir2->size];
265 while (de1 < max_de1 && de2 < max_de2) {
266 if (de1->name_offset < de2->name_offset) {
267 path[depth] = (de1++)->name_offset;
268 fast_export_delete(depth + 1, path);
269 } else if (de1->name_offset == de2->name_offset) {
270 path[depth] = de1->name_offset;
271 if (de1->content_offset != de2->content_offset) {
272 if (repo_dirent_is_dir(de1) && repo_dirent_is_dir(de2)) {
273 repo_diff_r(depth + 1, path,
274 repo_dir_from_dirent(de1),
275 repo_dir_from_dirent(de2));
276 } else {
277 if (repo_dirent_is_dir(de1) != repo_dirent_is_dir(de2)) {
278 fast_export_delete(depth + 1, path);
280 repo_git_add(depth + 1, path, de2);
283 de1++;
284 de2++;
285 } else {
286 path[depth] = de2->name_offset;
287 repo_git_add(depth + 1, path, de2++);
290 while (de1 < max_de1) {
291 path[depth] = (de1++)->name_offset;
292 fast_export_delete(depth + 1, path);
294 while (de2 < max_de2) {
295 path[depth] = de2->name_offset;
296 repo_git_add(depth + 1, path, de2++);
300 static uint32_t path_stack[1000];
301 void repo_diff(uint32_t r1, uint32_t r2)
303 repo_diff_r(0,
304 path_stack,
305 repo_commit_root_dir(commit_pointer(r1)),
306 repo_commit_root_dir(commit_pointer(r2)));
309 void repo_commit(uint32_t revision, char * author, char * log, char * uuid,
310 char * url, time_t timestamp)
312 if (revision == 0) {
313 active_commit = commit_alloc(1);
314 commit_pointer(active_commit)->root_dir_offset =
315 dir_with_dirents_alloc(0);
316 } else {
317 fast_export_commit(revision, author, log, uuid, url, timestamp);
319 num_dirs_saved = dir_pool.size;
320 num_dirents_saved = dirent_pool.size;
321 active_commit = commit_alloc(1);
322 commit_pointer(active_commit)->root_dir_offset =
323 commit_pointer(active_commit - 1)->root_dir_offset;