3 #include "string_pool.h"
6 #include "fast_export.h"
11 uint32_t content_offset
;
16 uint32_t first_offset
;
21 uint32_t root_dir_offset
;
24 /* Generate memory pools for commit, dir and dirent */
25 obj_pool_gen(commit
, struct repo_commit
, 4096);
26 obj_pool_gen(dir
, struct repo_dir
, 4096);
27 obj_pool_gen(dirent
, struct repo_dirent
, 4096);
29 static uint32_t num_dirs_saved
;
30 static uint32_t num_dirents_saved
;
31 static uint32_t active_commit
;
32 static uint32_t _mark
;
34 uint32_t next_blob_mark(void)
39 static struct repo_dir
*repo_commit_root_dir(struct repo_commit
*commit
)
41 return dir_pointer(commit
->root_dir_offset
);
44 static struct repo_dirent
*repo_first_dirent(struct repo_dir
*dir
)
46 return dirent_pointer(dir
->first_offset
);
49 static int repo_dirent_name_cmp(const void *a
, const void *b
)
51 const struct repo_dirent
*dirent1
= a
, *dirent2
= b
;
52 uint32_t a_offset
= dirent1
->name_offset
;
53 uint32_t b_offset
= dirent2
->name_offset
;
54 return (a_offset
> b_offset
) - (a_offset
< b_offset
);
57 static struct repo_dirent
*repo_dirent_by_name(struct repo_dir
*dir
,
60 struct repo_dirent key
;
61 if (dir
== NULL
|| dir
->size
== 0)
63 key
.name_offset
= name_offset
;
64 return bsearch(&key
, repo_first_dirent(dir
), dir
->size
,
65 sizeof(struct repo_dirent
), repo_dirent_name_cmp
);
68 static int repo_dirent_is_dir(struct repo_dirent
*dirent
)
70 return dirent
!= NULL
&& dirent
->mode
== REPO_MODE_DIR
;
73 static struct repo_dir
*repo_dir_from_dirent(struct repo_dirent
*dirent
)
75 if (!repo_dirent_is_dir(dirent
))
77 return dir_pointer(dirent
->content_offset
);
80 static uint32_t dir_with_dirents_alloc(uint32_t size
)
82 uint32_t offset
= dir_alloc(1);
83 dir_pointer(offset
)->size
= size
;
84 dir_pointer(offset
)->first_offset
= dirent_alloc(size
);
88 static struct repo_dir
*repo_clone_dir(struct repo_dir
*orig_dir
, uint32_t padding
)
90 uint32_t orig_o
, new_o
, dirent_o
;
91 orig_o
= dir_offset(orig_dir
);
92 if (orig_o
< num_dirs_saved
) {
93 new_o
= dir_with_dirents_alloc(orig_dir
->size
+ padding
);
94 orig_dir
= dir_pointer(orig_o
);
95 dirent_o
= dir_pointer(new_o
)->first_offset
;
100 dirent_o
= dirent_alloc(orig_dir
->size
+ padding
);
102 memcpy(dirent_pointer(dirent_o
), repo_first_dirent(orig_dir
),
103 orig_dir
->size
* sizeof(struct repo_dirent
));
104 dir_pointer(new_o
)->size
= orig_dir
->size
+ padding
;
105 dir_pointer(new_o
)->first_offset
= dirent_o
;
106 return dir_pointer(new_o
);
109 static struct repo_dirent
*repo_read_dirent(uint32_t revision
, uint32_t *path
)
112 struct repo_dir
*dir
= NULL
;
113 struct repo_dirent
*dirent
= NULL
;
114 dir
= repo_commit_root_dir(commit_pointer(revision
));
115 while (~(name
= *path
++)) {
116 dirent
= repo_dirent_by_name(dir
, name
);
117 if (dirent
== NULL
) {
119 } else if (repo_dirent_is_dir(dirent
)) {
120 dir
= repo_dir_from_dirent(dirent
);
129 repo_write_dirent(uint32_t *path
, uint32_t mode
, uint32_t content_offset
,
132 uint32_t name
, revision
, dirent_o
= ~0, dir_o
= ~0, parent_dir_o
= ~0;
133 struct repo_dir
*dir
;
134 struct repo_dirent
*dirent
= NULL
;
135 revision
= active_commit
;
136 dir
= repo_commit_root_dir(commit_pointer(revision
));
137 dir
= repo_clone_dir(dir
, 0);
138 commit_pointer(revision
)->root_dir_offset
= dir_offset(dir
);
139 while (~(name
= *path
++)) {
140 parent_dir_o
= dir_offset(dir
);
141 dirent
= repo_dirent_by_name(dir
, name
);
142 if (dirent
== NULL
) {
143 dir
= repo_clone_dir(dir
, 1);
144 dirent
= &repo_first_dirent(dir
)[dir
->size
- 1];
145 dirent
->name_offset
= name
;
146 dirent
->mode
= REPO_MODE_DIR
;
147 qsort(repo_first_dirent(dir
), dir
->size
,
148 sizeof(struct repo_dirent
), repo_dirent_name_cmp
);
149 dirent
= repo_dirent_by_name(dir
, name
);
150 dir_o
= dir_with_dirents_alloc(0);
151 dirent
->content_offset
= dir_o
;
152 dir
= dir_pointer(dir_o
);
153 } else if ((dir
= repo_dir_from_dirent(dirent
))) {
154 dirent_o
= dirent_offset(dirent
);
155 dir
= repo_clone_dir(dir
, 0);
157 dirent_pointer(dirent_o
)->content_offset
= dir_offset(dir
);
159 dirent
->mode
= REPO_MODE_DIR
;
160 dirent_o
= dirent_offset(dirent
);
161 dir_o
= dir_with_dirents_alloc(0);
162 dirent
= dirent_pointer(dirent_o
);
163 dir
= dir_pointer(dir_o
);
164 dirent
->content_offset
= dir_o
;
169 dirent
->content_offset
= content_offset
;
170 if (del
&& ~parent_dir_o
) {
171 dirent
->name_offset
= ~0;
172 dir
= dir_pointer(parent_dir_o
);
173 qsort(repo_first_dirent(dir
), dir
->size
,
174 sizeof(struct repo_dirent
), repo_dirent_name_cmp
);
180 uint32_t repo_copy(uint32_t revision
, uint32_t *src
, uint32_t *dst
)
182 uint32_t mode
= 0, content_offset
= 0;
183 struct repo_dirent
*src_dirent
;
184 src_dirent
= repo_read_dirent(revision
, src
);
185 if (src_dirent
!= NULL
) {
186 mode
= src_dirent
->mode
;
187 content_offset
= src_dirent
->content_offset
;
188 repo_write_dirent(dst
, mode
, content_offset
, 0);
193 void repo_add(uint32_t *path
, uint32_t mode
, uint32_t blob_mark
)
195 repo_write_dirent(path
, mode
, blob_mark
, 0);
198 uint32_t repo_replace(uint32_t *path
, uint32_t blob_mark
)
201 struct repo_dirent
*src_dirent
;
202 src_dirent
= repo_read_dirent(active_commit
, path
);
203 if (src_dirent
!= NULL
) {
204 mode
= src_dirent
->mode
;
205 repo_write_dirent(path
, mode
, blob_mark
, 0);
210 void repo_modify(uint32_t *path
, uint32_t mode
, uint32_t blob_mark
)
212 struct repo_dirent
*src_dirent
;
213 src_dirent
= repo_read_dirent(active_commit
, path
);
214 if (src_dirent
!= NULL
&& blob_mark
== 0) {
215 blob_mark
= src_dirent
->content_offset
;
217 repo_write_dirent(path
, mode
, blob_mark
, 0);
220 void repo_delete(uint32_t *path
)
222 repo_write_dirent(path
, 0, 0, 1);
225 static void repo_git_add_r(uint32_t depth
, uint32_t *path
, struct repo_dir
*dir
);
227 static void repo_git_add(uint32_t depth
, uint32_t *path
, struct repo_dirent
*dirent
)
229 if (repo_dirent_is_dir(dirent
)) {
230 repo_git_add_r(depth
, path
, repo_dir_from_dirent(dirent
));
232 fast_export_modify(depth
, path
, dirent
->mode
, dirent
->content_offset
);
236 static void repo_git_add_r(uint32_t depth
, uint32_t *path
, struct repo_dir
*dir
)
239 struct repo_dirent
*de
;
240 de
= repo_first_dirent(dir
);
241 for (o
= 0; o
< dir
->size
; o
++) {
242 path
[depth
] = de
[o
].name_offset
;
243 repo_git_add(depth
+ 1, path
, &de
[o
]);
247 static void repo_diff_r(uint32_t depth
, uint32_t *path
, struct repo_dir
*dir1
,
248 struct repo_dir
*dir2
)
250 struct repo_dirent
*de1
, *de2
, *max_de1
, *max_de2
;
251 de1
= repo_first_dirent(dir1
);
252 de2
= repo_first_dirent(dir2
);
253 max_de1
= &de1
[dir1
->size
];
254 max_de2
= &de2
[dir2
->size
];
256 while (de1
< max_de1
&& de2
< max_de2
) {
257 if (de1
->name_offset
< de2
->name_offset
) {
258 path
[depth
] = (de1
++)->name_offset
;
259 fast_export_delete(depth
+ 1, path
);
260 } else if (de1
->name_offset
> de2
->name_offset
) {
261 path
[depth
] = de2
->name_offset
;
262 repo_git_add(depth
+ 1, path
, de2
++);
264 path
[depth
] = de1
->name_offset
;
265 if (de1
->mode
!= de2
->mode
||
266 de1
->content_offset
!= de2
->content_offset
) {
267 if (repo_dirent_is_dir(de1
) && repo_dirent_is_dir(de2
)) {
268 repo_diff_r(depth
+ 1, path
,
269 repo_dir_from_dirent(de1
),
270 repo_dir_from_dirent(de2
));
272 if (repo_dirent_is_dir(de1
) != repo_dirent_is_dir(de2
)) {
273 fast_export_delete(depth
+ 1, path
);
275 repo_git_add(depth
+ 1, path
, de2
);
282 while (de1
< max_de1
) {
283 path
[depth
] = (de1
++)->name_offset
;
284 fast_export_delete(depth
+ 1, path
);
286 while (de2
< max_de2
) {
287 path
[depth
] = de2
->name_offset
;
288 repo_git_add(depth
+ 1, path
, de2
++);
292 static uint32_t path_stack
[REPO_MAX_PATH_DEPTH
];
294 void repo_diff(uint32_t r1
, uint32_t r2
)
298 repo_commit_root_dir(commit_pointer(r1
)),
299 repo_commit_root_dir(commit_pointer(r2
)));
302 void repo_commit(uint32_t revision
, uint32_t author
, char *log
, uint32_t uuid
,
303 uint32_t url
, time_t timestamp
)
305 fast_export_commit(revision
, author
, log
, uuid
, url
, timestamp
);
306 num_dirs_saved
= dir_pool
.size
;
307 num_dirents_saved
= dirent_pool
.size
;
308 active_commit
= commit_alloc(1);
309 commit_pointer(active_commit
)->root_dir_offset
=
310 commit_pointer(active_commit
- 1)->root_dir_offset
;
313 static void mark_init(void)
317 for (i
= 0; i
< dirent_pool
.size
; i
++)
318 if (!repo_dirent_is_dir(dirent_pointer(i
)) &&
319 dirent_pointer(i
)->content_offset
> _mark
)
320 _mark
= dirent_pointer(i
)->content_offset
;
330 num_dirs_saved
= dir_pool
.size
;
331 num_dirents_saved
= dirent_pool
.size
;
332 active_commit
= commit_pool
.size
- 1;
333 if (active_commit
== -1) {
335 /* Create empty tree for commit 0. */
336 commit_pointer(0)->root_dir_offset
=
337 dir_with_dirents_alloc(0);
338 /* Preallocate commit 1, ready for changes. */
339 commit_pointer(1)->root_dir_offset
=
340 commit_pointer(0)->root_dir_offset
;
342 num_dirs_saved
= dir_pool
.size
;
343 num_dirents_saved
= dirent_pool
.size
;
347 void repo_reset(void)