]>
git.proxmox.com Git - libgit2.git/blob - src/tree-cache.c
2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
8 #include "tree-cache.h"
13 static git_tree_cache
*find_child(
14 const git_tree_cache
*tree
, const char *path
, const char *end
)
16 size_t i
, dirlen
= end
? (size_t)(end
- path
) : strlen(path
);
18 for (i
= 0; i
< tree
->children_count
; ++i
) {
19 git_tree_cache
*child
= tree
->children
[i
];
21 if (child
->namelen
== dirlen
&& !memcmp(path
, child
->name
, dirlen
))
28 void git_tree_cache_invalidate_path(git_tree_cache
*tree
, const char *path
)
30 const char *ptr
= path
, *end
;
35 tree
->entry_count
= -1;
38 end
= strchr(ptr
, '/');
40 if (end
== NULL
) /* End of path */
43 tree
= find_child(tree
, ptr
, end
);
44 if (tree
== NULL
) /* We don't have that tree */
47 tree
->entry_count
= -1;
52 const git_tree_cache
*git_tree_cache_get(const git_tree_cache
*tree
, const char *path
)
54 const char *ptr
= path
, *end
;
61 end
= strchr(ptr
, '/');
63 tree
= find_child(tree
, ptr
, end
);
64 if (tree
== NULL
) /* Can't find it */
67 if (end
== NULL
|| *end
+ 1 == '\0')
74 static int read_tree_internal(git_tree_cache
**out
,
75 const char **buffer_in
, const char *buffer_end
,
78 git_tree_cache
*tree
= NULL
;
79 const char *name_start
, *buffer
;
82 buffer
= name_start
= *buffer_in
;
84 if ((buffer
= memchr(buffer
, '\0', buffer_end
- buffer
)) == NULL
)
87 if (++buffer
>= buffer_end
)
90 if (git_tree_cache_new(&tree
, name_start
, pool
) < 0)
93 /* Blank-terminated ASCII decimal number of entries in this tree */
94 if (git__strntol32(&count
, buffer
, buffer_end
- buffer
, &buffer
, 10) < 0)
97 tree
->entry_count
= count
;
99 if (*buffer
!= ' ' || ++buffer
>= buffer_end
)
102 /* Number of children of the tree, newline-terminated */
103 if (git__strntol32(&count
, buffer
, buffer_end
- buffer
, &buffer
, 10) < 0 || count
< 0)
106 tree
->children_count
= count
;
108 if (*buffer
!= '\n' || ++buffer
> buffer_end
)
111 /* The SHA1 is only there if it's not invalidated */
112 if (tree
->entry_count
>= 0) {
113 /* 160-bit SHA-1 for this tree and it's children */
114 if (buffer
+ GIT_OID_RAWSZ
> buffer_end
)
117 git_oid_fromraw(&tree
->oid
, (const unsigned char *)buffer
);
118 buffer
+= GIT_OID_RAWSZ
;
121 /* Parse children: */
122 if (tree
->children_count
> 0) {
125 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bufsize
, tree
->children_count
, sizeof(git_tree_cache
*));
127 tree
->children
= git_pool_malloc(pool
, bufsize
);
128 GIT_ERROR_CHECK_ALLOC(tree
->children
);
130 memset(tree
->children
, 0x0, bufsize
);
132 for (i
= 0; i
< tree
->children_count
; ++i
) {
133 if (read_tree_internal(&tree
->children
[i
], &buffer
, buffer_end
, pool
) < 0)
143 git_error_set(GIT_ERROR_INDEX
, "corrupted TREE extension in index");
147 int git_tree_cache_read(git_tree_cache
**tree
, const char *buffer
, size_t buffer_size
, git_pool
*pool
)
149 const char *buffer_end
= buffer
+ buffer_size
;
151 if (read_tree_internal(tree
, &buffer
, buffer_end
, pool
) < 0)
154 if (buffer
< buffer_end
) {
155 git_error_set(GIT_ERROR_INDEX
, "corrupted TREE extension in index (unexpected trailing data)");
162 static int read_tree_recursive(git_tree_cache
*cache
, const git_tree
*tree
, git_pool
*pool
)
164 git_repository
*repo
;
165 size_t i
, j
, nentries
, ntrees
, alloc_size
;
168 repo
= git_tree_owner(tree
);
170 git_oid_cpy(&cache
->oid
, git_tree_id(tree
));
171 nentries
= git_tree_entrycount(tree
);
174 * We make sure we know how many trees we need to allocate for
175 * so we don't have to realloc and change the pointers for the
179 for (i
= 0; i
< nentries
; i
++) {
180 const git_tree_entry
*entry
;
182 entry
= git_tree_entry_byindex(tree
, i
);
183 if (git_tree_entry_filemode(entry
) == GIT_FILEMODE_TREE
)
187 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_size
, ntrees
, sizeof(git_tree_cache
*));
189 cache
->children_count
= ntrees
;
190 cache
->children
= git_pool_mallocz(pool
, alloc_size
);
191 GIT_ERROR_CHECK_ALLOC(cache
->children
);
194 for (i
= 0; i
< nentries
; i
++) {
195 const git_tree_entry
*entry
;
198 entry
= git_tree_entry_byindex(tree
, i
);
199 if (git_tree_entry_filemode(entry
) != GIT_FILEMODE_TREE
) {
200 cache
->entry_count
++;
204 if ((error
= git_tree_cache_new(&cache
->children
[j
], git_tree_entry_name(entry
), pool
)) < 0)
207 if ((error
= git_tree_lookup(&subtree
, repo
, git_tree_entry_id(entry
))) < 0)
210 error
= read_tree_recursive(cache
->children
[j
], subtree
, pool
);
211 git_tree_free(subtree
);
212 cache
->entry_count
+= cache
->children
[j
]->entry_count
;
222 int git_tree_cache_read_tree(git_tree_cache
**out
, const git_tree
*tree
, git_pool
*pool
)
225 git_tree_cache
*cache
;
227 if ((error
= git_tree_cache_new(&cache
, "", pool
)) < 0)
230 if ((error
= read_tree_recursive(cache
, tree
, pool
)) < 0)
237 int git_tree_cache_new(git_tree_cache
**out
, const char *name
, git_pool
*pool
)
239 size_t name_len
, alloc_size
;
240 git_tree_cache
*tree
;
242 name_len
= strlen(name
);
244 GIT_ERROR_CHECK_ALLOC_ADD3(&alloc_size
, sizeof(git_tree_cache
), name_len
, 1);
246 tree
= git_pool_malloc(pool
, alloc_size
);
247 GIT_ERROR_CHECK_ALLOC(tree
);
249 memset(tree
, 0x0, sizeof(git_tree_cache
));
250 /* NUL-terminated tree name */
251 tree
->namelen
= name_len
;
252 memcpy(tree
->name
, name
, name_len
);
253 tree
->name
[name_len
] = '\0';
259 static void write_tree(git_buf
*out
, git_tree_cache
*tree
)
263 git_buf_printf(out
, "%s%c%"PRIdZ
" %"PRIuZ
"\n", tree
->name
, 0, tree
->entry_count
, tree
->children_count
);
265 if (tree
->entry_count
!= -1)
266 git_buf_put(out
, (const char *) &tree
->oid
, GIT_OID_RAWSZ
);
268 for (i
= 0; i
< tree
->children_count
; i
++)
269 write_tree(out
, tree
->children
[i
]);
272 int git_tree_cache_write(git_buf
*out
, git_tree_cache
*tree
)
274 write_tree(out
, tree
);
276 return git_buf_oom(out
) ? -1 : 0;