1 |
commit: dadb2666f54fed0478e0914d9fc4349f27730d58 |
2 |
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
3 |
AuthorDate: Sun Jan 19 09:46:18 2020 +0000 |
4 |
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
5 |
CommitDate: Sun Jan 19 09:46:18 2020 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=dadb2666 |
7 |
|
8 |
libq/tree: add initial tree_match_atom with caching |
9 |
|
10 |
tree_match_atom is meant for retrieving the best matching element from a |
11 |
tree based on a query. It caches the underlying packages it traverses, |
12 |
such that repetitive lookups will benefit from previous lookups. This |
13 |
comes in handy for recursive scenarios such as when |
14 |
calculating/resolving dependencies. |
15 |
|
16 |
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org> |
17 |
|
18 |
libq/tree.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
19 |
libq/tree.h | 4 ++++ |
20 |
2 files changed, 84 insertions(+) |
21 |
|
22 |
diff --git a/libq/tree.c b/libq/tree.c |
23 |
index 0df2e0d..f87e751 100644 |
24 |
--- a/libq/tree.c |
25 |
+++ b/libq/tree.c |
26 |
@@ -148,6 +148,24 @@ tree_open_binpkg(const char *sroot, const char *spkg) |
27 |
void |
28 |
tree_close(tree_ctx *ctx) |
29 |
{ |
30 |
+ if (ctx->cache.categories != NULL) { |
31 |
+ DECLARE_ARRAY(t); |
32 |
+ size_t n; |
33 |
+ tree_cat_ctx *cat; |
34 |
+ |
35 |
+ values_set(ctx->cache.categories, t); |
36 |
+ free_set(ctx->cache.categories); |
37 |
+ ctx->cache.categories = NULL; /* must happen before close_cat */ |
38 |
+ |
39 |
+ array_for_each(t, n, cat) { |
40 |
+ /* ensure we cleanup all pkgs */ |
41 |
+ cat->pkg_cur = 0; |
42 |
+ tree_close_cat(cat); |
43 |
+ } |
44 |
+ |
45 |
+ xarrayfree_int(t); |
46 |
+ } |
47 |
+ |
48 |
closedir(ctx->dir); |
49 |
/* closedir() above does this for us: */ |
50 |
/* close(ctx->tree_fd); */ |
51 |
@@ -212,6 +230,21 @@ tree_open_cat(tree_ctx *ctx, const char *name) |
52 |
int fd; |
53 |
DIR *dir; |
54 |
|
55 |
+ /* lookup in the cache, if any */ |
56 |
+ if (ctx->cache.categories != NULL) { |
57 |
+ cat_ctx = get_set(name, ctx->cache.categories); |
58 |
+ if (cat_ctx != NULL) { |
59 |
+ /* reset state so it can be re-iterated (sort benefits the |
60 |
+ * most here) */ |
61 |
+ if (ctx->do_sort) { |
62 |
+ cat_ctx->pkg_cur = 0; |
63 |
+ } else { |
64 |
+ rewinddir(cat_ctx->dir); |
65 |
+ } |
66 |
+ return cat_ctx; |
67 |
+ } |
68 |
+ } |
69 |
+ |
70 |
/* Cannot use O_PATH as we want to use fdopendir() */ |
71 |
fd = openat(ctx->tree_fd, name, O_RDONLY | O_CLOEXEC); |
72 |
if (fd == -1) |
73 |
@@ -229,6 +262,13 @@ tree_open_cat(tree_ctx *ctx, const char *name) |
74 |
cat_ctx->dir = dir; |
75 |
cat_ctx->ctx = ctx; |
76 |
cat_ctx->pkg_ctxs = NULL; |
77 |
+ |
78 |
+ if (ctx->cache.categories != NULL) { |
79 |
+ add_set_value(name, cat_ctx, ctx->cache.categories); |
80 |
+ /* ensure name doesn't expire after this instantiation is closed */ |
81 |
+ cat_ctx->name = contains_set(name, ctx->cache.categories); |
82 |
+ } |
83 |
+ |
84 |
return cat_ctx; |
85 |
} |
86 |
|
87 |
@@ -289,6 +329,14 @@ tree_next_cat(tree_ctx *ctx) |
88 |
void |
89 |
tree_close_cat(tree_cat_ctx *cat_ctx) |
90 |
{ |
91 |
+ if (cat_ctx->ctx->cache.categories != NULL && |
92 |
+ contains_set(cat_ctx->name, cat_ctx->ctx->cache.categories)) |
93 |
+ return; |
94 |
+ |
95 |
+ /* cleanup unreturned pkgs when sorted (or cache in use) */ |
96 |
+ while (cat_ctx->pkg_cur < cat_ctx->pkg_cnt) |
97 |
+ tree_close_pkg(cat_ctx->pkg_ctxs[cat_ctx->pkg_cur++]); |
98 |
+ |
99 |
closedir(cat_ctx->dir); |
100 |
/* closedir() above does this for us: */ |
101 |
/* close(ctx->fd); */ |
102 |
@@ -1439,3 +1487,35 @@ tree_get_atoms(tree_ctx *ctx, bool fullcpv, set *satoms) |
103 |
|
104 |
return state.cpf; |
105 |
} |
106 |
+ |
107 |
+tree_pkg_ctx * |
108 |
+tree_match_atom(tree_ctx *ctx, depend_atom *a) |
109 |
+{ |
110 |
+ tree_cat_ctx *cat_ctx; |
111 |
+ tree_pkg_ctx *pkg_ctx; |
112 |
+ depend_atom *atom; |
113 |
+ |
114 |
+ if (ctx->cache.categories == NULL) |
115 |
+ ctx->cache.categories = create_set(); |
116 |
+ |
117 |
+ if (a->P == NULL) { |
118 |
+ return NULL; |
119 |
+ } else if (a->CATEGORY == NULL) { |
120 |
+ /* loop through all cats and recurse */ |
121 |
+ /* TODO: some day */ |
122 |
+ return NULL; |
123 |
+ } else { |
124 |
+ /* try CAT, and PN for latest version */ |
125 |
+ if ((cat_ctx = tree_open_cat(ctx, a->CATEGORY)) == NULL) |
126 |
+ return NULL; |
127 |
+ ctx->do_sort = true; /* sort uses buffer, which cache relies on */ |
128 |
+ ctx->query_atom = NULL; /* ensure the cache contains ALL pkgs */ |
129 |
+ while ((pkg_ctx = tree_next_pkg(cat_ctx)) != NULL) { |
130 |
+ atom = tree_get_atom(pkg_ctx, a->SLOT != NULL); |
131 |
+ if (atom_compare(atom, a) == EQUAL) |
132 |
+ return pkg_ctx; |
133 |
+ } |
134 |
+ |
135 |
+ return NULL; |
136 |
+ } |
137 |
+} |
138 |
|
139 |
diff --git a/libq/tree.h b/libq/tree.h |
140 |
index 6627e80..eaee7ad 100644 |
141 |
--- a/libq/tree.h |
142 |
+++ b/libq/tree.h |
143 |
@@ -44,6 +44,9 @@ struct tree_ctx { |
144 |
char *pkgs; |
145 |
size_t pkgslen; |
146 |
depend_atom *query_atom; |
147 |
+ struct tree_cache { |
148 |
+ set *categories; |
149 |
+ } cache; |
150 |
}; |
151 |
|
152 |
/* Category context */ |
153 |
@@ -135,5 +138,6 @@ int tree_foreach_pkg(tree_ctx *ctx, tree_pkg_cb callback, void *priv, |
154 |
tree_foreach_pkg(ctx, cb, priv, true, query); |
155 |
set *tree_get_atoms(tree_ctx *ctx, bool fullcpv, set *satoms); |
156 |
depend_atom *tree_get_atom(tree_pkg_ctx *pkg_ctx, bool complete); |
157 |
+tree_pkg_ctx *tree_match_atom(tree_ctx *t, depend_atom *a); |
158 |
|
159 |
#endif |