Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ast.c: Support routines for put/get |
3 | | * |
4 | | * Copyright (C) 2008-2016 David Lutterkort |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with this library; if not, write to the Free Software |
18 | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | | * |
20 | | * Author: David Lutterkort <dlutter@redhat.com> |
21 | | */ |
22 | | |
23 | | #include <config.h> |
24 | | #include <stdint.h> |
25 | | |
26 | | #include "internal.h" |
27 | | #include "memory.h" |
28 | | #include "lens.h" |
29 | | |
30 | | /* A dictionary that maps key to a list of (skel, dict) */ |
31 | | struct dict_entry { |
32 | | struct dict_entry *next; |
33 | | struct skel *skel; |
34 | | struct dict *dict; |
35 | | }; |
36 | | |
37 | | /* Associates a KEY with a list of skel/dict pairs. |
38 | | |
39 | | Dicts are used in two phases: first they are constructed, through |
40 | | repeated calls to dict_append. In the second phase, items are looked up |
41 | | and removed from the dict. |
42 | | |
43 | | During construction, MARK points to the end of the list ENTRY. Once |
44 | | construction is done, MARK points to the head of that list, and ENTRY is |
45 | | moved towards the tail everytime an item is looked up. |
46 | | */ |
47 | | struct dict_node { |
48 | | char *key; |
49 | | struct dict_entry *entry; /* This will change as entries are looked up */ |
50 | | struct dict_entry *mark; /* Pointer to initial entry, will never change */ |
51 | | }; |
52 | | |
53 | | /* Nodes are kept sorted by their key, with NULL being smaller than any |
54 | | string */ |
55 | | struct dict { |
56 | | struct dict_node **nodes; |
57 | | uint32_t size; |
58 | | uint32_t used; |
59 | | bool marked; |
60 | | }; |
61 | | |
62 | | static const int dict_initial_size = 2; |
63 | | static const int dict_max_expansion = 128; |
64 | | static const uint32_t dict_max_size = (1<<24) - 1; |
65 | | |
66 | 0 | struct dict *make_dict(char *key, struct skel *skel, struct dict *subdict) { |
67 | 0 | struct dict *dict = NULL; |
68 | 0 | if (ALLOC(dict) < 0) |
69 | 0 | goto error; |
70 | 0 | if (ALLOC_N(dict->nodes, dict_initial_size) < 0) |
71 | 0 | goto error; |
72 | 0 | if (ALLOC(dict->nodes[0]) < 0) |
73 | 0 | goto error; |
74 | 0 | if (ALLOC(dict->nodes[0]->entry) < 0) |
75 | 0 | goto error; |
76 | | |
77 | 0 | dict->size = dict_initial_size; |
78 | 0 | dict->used = 1; |
79 | 0 | dict->nodes[0]->key = key; |
80 | 0 | dict->nodes[0]->entry->skel = skel; |
81 | 0 | dict->nodes[0]->entry->dict = subdict; |
82 | 0 | dict->nodes[0]->mark = dict->nodes[0]->entry; |
83 | |
|
84 | 0 | return dict; |
85 | 0 | error: |
86 | 0 | if (dict->nodes) { |
87 | 0 | if (dict->nodes[0]) |
88 | 0 | FREE(dict->nodes[0]->entry); |
89 | 0 | FREE(dict->nodes[0]); |
90 | 0 | } |
91 | 0 | FREE(dict->nodes); |
92 | 0 | FREE(dict); |
93 | 0 | return NULL; |
94 | 0 | } |
95 | | |
96 | 0 | void free_dict(struct dict *dict) { |
97 | 0 | if (dict == NULL) |
98 | 0 | return; |
99 | | |
100 | 0 | for (int i=0; i < dict->used; i++) { |
101 | 0 | struct dict_node *node = dict->nodes[i]; |
102 | 0 | if (! dict->marked) |
103 | 0 | node->mark = node->entry; |
104 | 0 | while (node->mark != NULL) { |
105 | 0 | struct dict_entry *del = node->mark; |
106 | 0 | node->mark = del->next; |
107 | 0 | free_skel(del->skel); |
108 | 0 | free_dict(del->dict); |
109 | 0 | free(del); |
110 | 0 | } |
111 | 0 | free(node->key); |
112 | 0 | FREE(node); |
113 | 0 | } |
114 | 0 | FREE(dict->nodes); |
115 | 0 | FREE(dict); |
116 | 0 | } |
117 | | |
118 | | /* Return the position of KEY in DICT as an integer between 0 and |
119 | | DICT->USED. If KEY is not in DICT, return a negative number P such that |
120 | | -(P + 1) is the position at which KEY must be inserted to keep the keys |
121 | | of the nodes in DICT sorted. |
122 | | */ |
123 | 0 | static int dict_pos(struct dict *dict, const char *key) { |
124 | 0 | if (key == NULL) { |
125 | 0 | return (dict->nodes[0]->key == NULL) ? 0 : -1; |
126 | 0 | } |
127 | | |
128 | 0 | int l = dict->nodes[0]->key == NULL ? 1 : 0; |
129 | 0 | int h = dict->used; |
130 | 0 | while (l < h) { |
131 | 0 | int m = (l + h)/2; |
132 | 0 | int cmp = strcmp(dict->nodes[m]->key, key); |
133 | 0 | if (cmp > 0) |
134 | 0 | h = m; |
135 | 0 | else if (cmp < 0) |
136 | 0 | l = m + 1; |
137 | 0 | else |
138 | 0 | return m; |
139 | 0 | } |
140 | 0 | return -(l + 1); |
141 | 0 | } |
142 | | |
143 | 0 | static int dict_expand(struct dict *dict) { |
144 | 0 | uint32_t size = dict->size; |
145 | |
|
146 | 0 | if (size == dict_max_size) |
147 | 0 | return -1; |
148 | 0 | if (size > dict_max_expansion) |
149 | 0 | size += dict_max_expansion; |
150 | 0 | else |
151 | 0 | size *= 2; |
152 | 0 | if (size > dict_max_size) |
153 | 0 | size = dict_max_size; |
154 | 0 | dict->size = size; |
155 | 0 | return REALLOC_N(dict->nodes, dict->size); |
156 | 0 | } |
157 | | |
158 | 0 | int dict_append(struct dict **dict, struct dict *d2) { |
159 | 0 | if (d2 == NULL) |
160 | 0 | return 0; |
161 | | |
162 | 0 | if (*dict == NULL) { |
163 | 0 | *dict = d2; |
164 | 0 | return 0; |
165 | 0 | } |
166 | | |
167 | 0 | struct dict *d1 = *dict; |
168 | 0 | for (int i2 = 0; i2 < d2->used; i2++) { |
169 | 0 | struct dict_node *n2 = d2->nodes[i2]; |
170 | 0 | int i1 = dict_pos(d1, n2->key); |
171 | 0 | if (i1 < 0) { |
172 | 0 | i1 = - i1 - 1; |
173 | 0 | if (d1->size == d1->used) { |
174 | 0 | if (dict_expand(d1) < 0) |
175 | 0 | return -1; |
176 | 0 | } |
177 | 0 | memmove(d1->nodes + i1 + 1, d1->nodes + i1, |
178 | 0 | sizeof(*d1->nodes) * (d1->used - i1)); |
179 | 0 | d1->nodes[i1] = n2; |
180 | 0 | d1->used += 1; |
181 | 0 | } else { |
182 | 0 | struct dict_node *n1 = d1->nodes[i1]; |
183 | 0 | list_tail_cons(n1->entry, n1->mark, n2->entry); |
184 | 0 | FREE(n2->key); |
185 | 0 | FREE(n2); |
186 | 0 | } |
187 | 0 | } |
188 | 0 | FREE(d2->nodes); |
189 | 0 | FREE(d2); |
190 | 0 | return 0; |
191 | 0 | } |
192 | | |
193 | | void dict_lookup(const char *key, struct dict *dict, |
194 | 0 | struct skel **skel, struct dict **subdict) { |
195 | 0 | *skel = NULL; |
196 | 0 | *subdict = NULL; |
197 | 0 | if (dict != NULL) { |
198 | 0 | if (! dict->marked) { |
199 | 0 | for (int i=0; i < dict->used; i++) { |
200 | 0 | dict->nodes[i]->mark = dict->nodes[i]->entry; |
201 | 0 | } |
202 | 0 | dict->marked = 1; |
203 | 0 | } |
204 | 0 | int p = dict_pos(dict, key); |
205 | 0 | if (p >= 0) { |
206 | 0 | struct dict_node *node = dict->nodes[p]; |
207 | 0 | if (node->entry != NULL) { |
208 | 0 | *skel = node->entry->skel; |
209 | 0 | *subdict = node->entry->dict; |
210 | 0 | node->entry = node->entry->next; |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | |
217 | | |
218 | | /* |
219 | | * Local variables: |
220 | | * indent-tabs-mode: nil |
221 | | * c-indent-level: 4 |
222 | | * c-basic-offset: 4 |
223 | | * tab-width: 4 |
224 | | * End: |
225 | | */ |