1 |
commit: 9a4c654be5e759b4bb0fed1ac802a57fb1cb5c30 |
2 |
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
3 |
AuthorDate: Tue Jun 8 15:28:47 2021 +0000 |
4 |
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Jun 8 15:28:47 2021 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=9a4c654b |
7 |
|
8 |
libq/tree: build cache for Packages/binpkgs |
9 |
|
10 |
Because we cannot trust Packages file to be in sorted order, and the |
11 |
order isn't what we want (most recent match on top), we have to sort the |
12 |
pkgs found in Packages, so make that exploit the cache, which will speed |
13 |
up any subsequent tree_match_atom calls, which is good. |
14 |
|
15 |
Do the same for binpkg trees, since they also cannot be cached |
16 |
otherwise. |
17 |
|
18 |
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org> |
19 |
|
20 |
libq/tree.c | 180 ++++++++++++++++++++++++++++++------------------------------ |
21 |
libq/tree.h | 1 + |
22 |
2 files changed, 90 insertions(+), 91 deletions(-) |
23 |
|
24 |
diff --git a/libq/tree.c b/libq/tree.c |
25 |
index 358b1f2..d7ec882 100644 |
26 |
--- a/libq/tree.c |
27 |
+++ b/libq/tree.c |
28 |
@@ -312,8 +312,29 @@ tree_next_cat(tree_ctx *ctx) |
29 |
|
30 |
if (ctx->do_sort) { |
31 |
if (ctx->cat_de == NULL) { |
32 |
- ctx->cat_cnt = scandirat(ctx->tree_fd, |
33 |
- ".", &ctx->cat_de, tree_filter_cat, alphasort); |
34 |
+ if (ctx->cache.all_categories) { |
35 |
+ char **cats; |
36 |
+ size_t i; |
37 |
+ size_t len; |
38 |
+ struct dirent **ret; |
39 |
+ |
40 |
+ /* exploit the cache instead of reading from directory */ |
41 |
+ ctx->cat_cnt = cnt_set(ctx->cache.categories); |
42 |
+ ctx->cat_de = ret = xmalloc(sizeof(*ret) * ctx->cat_cnt); |
43 |
+ list_set(ctx->cache.categories, &cats); |
44 |
+ for (i = 0; i < ctx->cat_cnt; i++) { |
45 |
+ len = strlen(cats[i]) + 1; |
46 |
+ ret[i] = xzalloc(sizeof(*de) + len); |
47 |
+ snprintf(ret[i]->d_name, len, "%s", cats[i]); |
48 |
+ } |
49 |
+ if (i > 1) |
50 |
+ qsort(ret, ctx->cat_cnt, sizeof(*ret), |
51 |
+ (int (*)(const void *, const void *))alphasort); |
52 |
+ free(cats); |
53 |
+ } else { |
54 |
+ ctx->cat_cnt = scandirat(ctx->tree_fd, |
55 |
+ ".", &ctx->cat_de, tree_filter_cat, alphasort); |
56 |
+ } |
57 |
ctx->cat_cur = 0; |
58 |
} |
59 |
|
60 |
@@ -1511,7 +1532,7 @@ tree_foreach_pkg(tree_ctx *ctx, tree_pkg_cb callback, void *priv, |
61 |
ctx->cat_de = NULL; |
62 |
ctx->cat_cur = 0; |
63 |
ctx->cat_cnt = 0; |
64 |
- ctx->do_sort = 0; |
65 |
+ ctx->do_sort = false; |
66 |
|
67 |
return ret; |
68 |
} |
69 |
@@ -1624,77 +1645,33 @@ tree_get_atoms(tree_ctx *ctx, bool fullcpv, set *satoms) |
70 |
return state.cpf; |
71 |
} |
72 |
|
73 |
-struct tree_match_pkgs_cb_ctx { |
74 |
- int flags; |
75 |
- tree_match_ctx *ret; |
76 |
-}; |
77 |
- |
78 |
static int |
79 |
-tree_match_atom_packages_cb(tree_pkg_ctx *ctx, void *priv) |
80 |
+tree_match_atom_cache_populate_cb(tree_pkg_ctx *ctx, void *priv) |
81 |
{ |
82 |
- struct tree_match_pkgs_cb_ctx *rctx = priv; |
83 |
- depend_atom *a; |
84 |
- tree_match_ctx *n; |
85 |
- |
86 |
- /* skip anything after finding first match */ |
87 |
- if (rctx->flags & TREE_MATCH_FIRST && rctx->ret != NULL) |
88 |
- return 1; |
89 |
- |
90 |
- a = tree_get_atom(ctx, rctx->flags & TREE_MATCH_FULL_ATOM); |
91 |
- |
92 |
- /* skip virtual category if not requested */ |
93 |
- if (!(rctx->flags & TREE_MATCH_VIRTUAL) && |
94 |
- strcmp(a->CATEGORY, "virtual") == 0) |
95 |
- return 1; |
96 |
- |
97 |
- n = xzalloc(sizeof(tree_match_ctx)); |
98 |
- n->free_atom = true; |
99 |
- n->atom = atom_clone(a); |
100 |
- snprintf(n->path, sizeof(n->path), "%s/%s/%s.tbz2", |
101 |
- (char *)ctx->cat_ctx->ctx->path, ctx->cat_ctx->name, ctx->name); |
102 |
- if (rctx->flags & TREE_MATCH_METADATA) { |
103 |
- n->meta = xmalloc(sizeof(*n->meta)); |
104 |
- /* for Packages, all pointers to meta here are to the in memory |
105 |
- * copy of the Packages file, so these pointers can just be |
106 |
- * copied since the tree has to remain open, thus the pointers |
107 |
- * will stay valid */ |
108 |
- memcpy(n->meta, ctx->cat_ctx->ctx->pkgs, sizeof(*n->meta)); |
109 |
+ set *cache = priv; |
110 |
+ tree_cat_ctx *cat_ctx; |
111 |
+ tree_pkg_ctx *pkg; |
112 |
+ tree_ctx *tctx = ctx->cat_ctx->ctx; |
113 |
+ depend_atom *atom = tree_get_atom(ctx, true); |
114 |
+ |
115 |
+ (void)priv; |
116 |
+ |
117 |
+ cat_ctx = get_set(atom->CATEGORY, cache); |
118 |
+ if (cat_ctx == NULL) { |
119 |
+ cat_ctx = tree_open_cat(tctx, "."); |
120 |
+ add_set_value(atom->CATEGORY, cat_ctx, cache); |
121 |
+ /* get a pointer from the set */ |
122 |
+ cat_ctx->name = contains_set(atom->CATEGORY, cache); |
123 |
} |
124 |
|
125 |
- n->next = rctx->ret; |
126 |
- rctx->ret = n; |
127 |
- |
128 |
- return 0; |
129 |
-} |
130 |
- |
131 |
-static int |
132 |
-tree_match_atom_binpkg_cb(tree_pkg_ctx *ctx, void *priv) |
133 |
-{ |
134 |
- struct tree_match_pkgs_cb_ctx *rctx = priv; |
135 |
- depend_atom *a; |
136 |
- tree_match_ctx *n; |
137 |
- |
138 |
- /* skip anything after finding first match */ |
139 |
- if (rctx->flags & TREE_MATCH_FIRST && rctx->ret != NULL) |
140 |
- return 1; |
141 |
- |
142 |
- a = tree_get_atom(ctx, rctx->flags & TREE_MATCH_FULL_ATOM); |
143 |
- |
144 |
- /* skip virtual category if not requested */ |
145 |
- if (!(rctx->flags & TREE_MATCH_VIRTUAL) && |
146 |
- strcmp(a->CATEGORY, "virtual") == 0) |
147 |
- return 1; |
148 |
- |
149 |
- n = xzalloc(sizeof(tree_match_ctx)); |
150 |
- n->free_atom = true; |
151 |
- n->atom = atom_clone(a); |
152 |
- snprintf(n->path, sizeof(n->path), "%s/%s/%s.tbz2", |
153 |
- (char *)ctx->cat_ctx->ctx->path, ctx->cat_ctx->name, ctx->name); |
154 |
- if (rctx->flags & TREE_MATCH_METADATA) |
155 |
- n->meta = tree_pkg_read(ctx); |
156 |
- |
157 |
- n->next = rctx->ret; |
158 |
- rctx->ret = n; |
159 |
+ /* FIXME: this really could use a set */ |
160 |
+ cat_ctx->pkg_cnt++; |
161 |
+ cat_ctx->pkg_ctxs = xrealloc(cat_ctx->pkg_ctxs, |
162 |
+ sizeof(*cat_ctx->pkg_ctxs) * cat_ctx->pkg_cnt); |
163 |
+ cat_ctx->pkg_ctxs[cat_ctx->pkg_cnt - 1] = |
164 |
+ pkg = tree_open_pkg(cat_ctx, atom->PF); |
165 |
+ pkg->atom = atom_clone(atom); |
166 |
+ pkg->name = pkg->atom->PF; |
167 |
|
168 |
return 0; |
169 |
} |
170 |
@@ -1707,24 +1684,48 @@ tree_match_atom(tree_ctx *ctx, depend_atom *query, int flags) |
171 |
tree_match_ctx *ret = NULL; |
172 |
depend_atom *atom; |
173 |
|
174 |
- if (ctx->cachetype == CACHE_PACKAGES) { |
175 |
- struct tree_match_pkgs_cb_ctx rctx; |
176 |
- /* Packages needs to be serviced separately because it doesn't |
177 |
- * use a tree internally, but reads off of the Packages file */ |
178 |
- rctx.flags = flags; |
179 |
- rctx.ret = NULL; |
180 |
- ctx->query_atom = query; |
181 |
- tree_foreach_packages(ctx, tree_match_atom_packages_cb, &rctx); |
182 |
- ctx->query_atom = NULL; |
183 |
- return rctx.ret; |
184 |
- } else if (ctx->cachetype == CACHE_BINPKGS) { |
185 |
- struct tree_match_pkgs_cb_ctx rctx; |
186 |
- /* this sulks, but binpkgs modify the pkg_ctx->name to strip off |
187 |
- * .tbz2, and that makes it non-reusable */ |
188 |
- rctx.flags = flags; |
189 |
- rctx.ret = NULL; |
190 |
- tree_foreach_pkg(ctx, tree_match_atom_binpkg_cb, &rctx, true, query); |
191 |
- return rctx.ret; |
192 |
+ ctx->do_sort = true; /* sort uses buffer, which cache relies on */ |
193 |
+ ctx->query_atom = NULL; /* ensure the cache contains ALL pkgs */ |
194 |
+ |
195 |
+ if ((ctx->cachetype == CACHE_PACKAGES || ctx->cachetype == CACHE_BINPKGS) |
196 |
+ && ctx->cache.categories == NULL) |
197 |
+ { |
198 |
+ set *cache; |
199 |
+ DECLARE_ARRAY(cats); |
200 |
+ size_t n; |
201 |
+ |
202 |
+ /* Reading these formats requires us to traverse the tree for |
203 |
+ * each operation, so we better cache the entire thing in one |
204 |
+ * go. Packages in addition may not store the atoms sorted |
205 |
+ * (it's a file with content after all) and since we rely on |
206 |
+ * this for latest and first match, it is important that we sort |
207 |
+ * the contents so we're sure */ |
208 |
+ |
209 |
+ cache = ctx->cache.categories; |
210 |
+ if (ctx->cache.categories != NULL) |
211 |
+ ctx->cache.categories = NULL; |
212 |
+ else |
213 |
+ cache = create_set(); |
214 |
+ |
215 |
+ if (ctx->cachetype == CACHE_PACKAGES) |
216 |
+ tree_foreach_packages(ctx, |
217 |
+ tree_match_atom_cache_populate_cb, cache); |
218 |
+ else /* BINPKG */ |
219 |
+ tree_foreach_pkg(ctx, |
220 |
+ tree_match_atom_cache_populate_cb, cache, true, NULL); |
221 |
+ ctx->do_sort = true; /* turn it back on */ |
222 |
+ ctx->cache.all_categories = true; |
223 |
+ |
224 |
+ ctx->cache.categories = cache; |
225 |
+ |
226 |
+ /* loop through all categories, and sort the pkgs */ |
227 |
+ values_set(cache, cats); |
228 |
+ array_for_each(cats, n, cat_ctx) { |
229 |
+ if (cat_ctx->pkg_cnt > 1) { |
230 |
+ qsort(cat_ctx->pkg_ctxs, cat_ctx->pkg_cnt, |
231 |
+ sizeof(*cat_ctx->pkg_ctxs), tree_pkg_compar); |
232 |
+ } |
233 |
+ } |
234 |
} |
235 |
|
236 |
/* activate cache for future lookups, tree_match_atom relies on |
237 |
@@ -1733,9 +1734,6 @@ tree_match_atom(tree_ctx *ctx, depend_atom *query, int flags) |
238 |
if (ctx->cache.categories == NULL) |
239 |
ctx->cache.categories = create_set(); |
240 |
|
241 |
- ctx->do_sort = true; /* sort uses buffer, which cache relies on */ |
242 |
- ctx->query_atom = NULL; /* ensure the cache contains ALL pkgs */ |
243 |
- |
244 |
#define search_cat(C) \ |
245 |
{ \ |
246 |
char *lastpn = NULL; \ |
247 |
|
248 |
diff --git a/libq/tree.h b/libq/tree.h |
249 |
index 5a12c92..8741bad 100644 |
250 |
--- a/libq/tree.h |
251 |
+++ b/libq/tree.h |
252 |
@@ -48,6 +48,7 @@ struct tree_ctx { |
253 |
depend_atom *query_atom; |
254 |
struct tree_cache { |
255 |
set *categories; |
256 |
+ bool all_categories:1; |
257 |
} cache; |
258 |
}; |