Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Interna of trie functionality.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #ifdef HAVE_KDBCONFIG_H
10 : #include "kdbconfig.h"
11 : #endif
12 :
13 : #if VERBOSE && defined(HAVE_STDIO_H)
14 : #include <stdio.h>
15 : #endif
16 :
17 : #ifdef HAVE_LOCALE_H
18 : #include <locale.h>
19 : #endif
20 :
21 : #ifdef HAVE_STDLIB_H
22 : #include <stdlib.h>
23 : #endif
24 :
25 : #ifdef HAVE_STRING_H
26 : #include <string.h>
27 : #endif
28 :
29 : #ifdef HAVE_STDIO_H
30 : #include <stdio.h>
31 : #endif
32 :
33 : #include "kdbinternal.h"
34 :
35 : static char * elektraTrieStartsWith (const char * str, const char * substr);
36 : static Backend * elektraTriePrefixLookup (Trie * trie, const char * name);
37 :
38 : /**
39 : * @brief The Trie structure
40 : */
41 :
42 : /**
43 : * Lookups a backend inside the trie.
44 : *
45 : * @return the backend if found
46 : * @return 0 otherwise
47 : * @param trie the trie object to work with
48 : * @param key the name of this key will be looked up
49 : * @ingroup trie
50 : */
51 496171 : Backend * trieLookup (Trie * trie, const Key * key)
52 : {
53 496171 : char * where = 0;
54 496171 : Backend * ret = 0;
55 496171 : size_t len = 0;
56 :
57 496171 : if (!key) return 0;
58 496171 : if (!trie) return 0;
59 :
60 316317 : len = keyGetNameSize (key) + 1;
61 316317 : if (len <= 1) return 0; // would crash otherwise
62 316317 : where = elektraMalloc (len);
63 316317 : strncpy (where, keyName (key), len);
64 316317 : where[len - 2] = '/';
65 :
66 316317 : ret = elektraTriePrefixLookup (trie, where);
67 316317 : elektraFree (where);
68 :
69 316317 : return ret;
70 : }
71 :
72 : /**
73 : * Closes the trie and all opened backends within.
74 : *
75 : * @param trie the trie to close
76 : * @param errorKey the key used to report warnings
77 : * @ingroup trie
78 : * @retval 0 on success
79 : */
80 103489 : int trieClose (Trie * trie, Key * errorKey)
81 : {
82 : size_t i;
83 103489 : if (trie == NULL) return 0;
84 10150400 : for (i = 0; i < KDB_MAX_UCHAR; ++i)
85 : {
86 10150400 : if (trie->text[i] != NULL)
87 : {
88 92842 : trieClose (trie->children[i], errorKey);
89 92842 : if (trie->value[i]) backendClose (trie->value[i], errorKey);
90 92842 : elektraFree (trie->text[i]);
91 : }
92 : }
93 39650 : if (trie->empty_value)
94 : {
95 250 : backendClose (trie->empty_value, errorKey);
96 : }
97 39650 : elektraFree (trie);
98 39650 : return 0;
99 : }
100 :
101 : /**
102 : * @brief Insert into trie
103 : *
104 : * @ingroup trie
105 : *
106 : * @param trie the trie to insert to (0 to create a new trie)
107 : * @param name the key's name to insert
108 : * @param value the value to insert
109 : *
110 : * @retval trie on success
111 : */
112 198254 : Trie * trieInsert (Trie * trie, const char * name, Backend * value)
113 : {
114 : unsigned char idx;
115 :
116 198254 : if (name == 0)
117 : {
118 0 : name = "";
119 : }
120 198254 : idx = (unsigned char) name[0];
121 :
122 198254 : if (trie == NULL)
123 : {
124 39650 : trie = elektraCalloc (sizeof (Trie));
125 :
126 39650 : if (!strcmp ("", name))
127 : {
128 244 : trie->empty_value = value;
129 244 : return trie;
130 : }
131 :
132 39406 : trie->textlen[idx] = strlen (name);
133 :
134 39406 : trie->text[idx] = elektraStrDup (name);
135 :
136 39406 : trie->value[idx] = value;
137 39406 : return trie;
138 : }
139 :
140 158604 : if (!strcmp ("", name))
141 : {
142 6 : trie->empty_value = value;
143 6 : return trie;
144 : }
145 :
146 158598 : if (trie->text[idx])
147 : {
148 : char * p;
149 : /* there exists an entry with the same first character */
150 120782 : if ((p = elektraTrieStartsWith (name, trie->text[idx])) == 0)
151 : {
152 : /* the name in the trie is part of the searched name --> continue search */
153 105162 : trie->children[idx] = trieInsert (trie->children[idx], name + trie->textlen[idx], value);
154 : }
155 : else
156 : {
157 : /* name in trie doesn't match name --> split trie */
158 : char * newname;
159 : Trie * child;
160 : unsigned char idx2;
161 :
162 15620 : newname = elektraStrDup (p);
163 15620 : *p = 0; /* shorten the old name in the trie */
164 15620 : trie->textlen[idx] = strlen (trie->text[idx]);
165 :
166 15620 : child = trie->children[idx];
167 :
168 : /* insert the name given as a parameter into the new trie entry */
169 15620 : trie->children[idx] = trieInsert (NULL, name + (p - trie->text[idx]), value);
170 :
171 : /* insert the split try into the new trie entry */
172 :
173 15620 : idx2 = (unsigned char) newname[0];
174 15620 : trie->children[idx]->text[idx2] = newname;
175 15620 : trie->children[idx]->textlen[idx2] = strlen (newname);
176 15620 : trie->children[idx]->value[idx2] = trie->value[idx];
177 15620 : trie->children[idx]->children[idx2] = child;
178 :
179 15620 : trie->value[idx] = 0;
180 : }
181 : }
182 : else
183 : {
184 : /* there doesn't exist an entry with the same first character */
185 37816 : trie->text[idx] = elektraStrDup (name);
186 37816 : trie->value[idx] = (void *) value;
187 37816 : trie->textlen[idx] = strlen (name);
188 : }
189 :
190 : return trie;
191 : }
192 :
193 :
194 : #if 0
195 :
196 : static Trie *delete_trie(Trie *trie, char *name, CloseMapper closemapper)
197 : {
198 : Trie *tr;
199 : unsigned char idx;
200 : if (trie==NULL) {
201 : return NULL;
202 : }
203 :
204 : idx=(unsigned char) name[0];
205 :
206 : if (trie->text[idx]==NULL) {
207 : return NULL;
208 : }
209 :
210 : if (elektraTrieStartsWith(name,trie->text[idx])==0) {
211 :
212 : tr=delete_trie(trie->children[idx],name+trie->textlen[idx],closemapper);
213 :
214 : if (tr==NULL) {
215 : /* child trie has been deleted */
216 : trie->children[idx]=NULL;
217 : elektraFree (trie->text[idx]);
218 : closemapper(trie->value[idx]);
219 : trie->text[idx]=NULL;
220 : }
221 :
222 : return trie;
223 : }
224 : return NULL;
225 : }
226 :
227 : #endif
228 :
229 : /**
230 : * return NULL if string starts with substring, except for the terminating '\0',
231 : * otherwise return a pointer to the first mismatch in substr.
232 : *
233 : * Takes const char* arguments but return char* pointer to it.
234 : * (like e.g. strchr)
235 : */
236 506048 : static char * elektraTrieStartsWith (const char * str, const char * substr)
237 : {
238 506048 : size_t i = 0;
239 506048 : size_t sublen = strlen (substr);
240 :
241 6384142 : for (i = 0; i < sublen; i++)
242 : {
243 5979992 : if (substr[i] != str[i]) return (char *) substr + i;
244 : }
245 : return 0;
246 : }
247 :
248 615305 : static Backend * elektraTriePrefixLookup (Trie * trie, const char * name)
249 : {
250 615305 : if (trie == NULL) return NULL;
251 :
252 446683 : unsigned char idx = (unsigned char) name[0];
253 446683 : const char * trieText = trie->text[idx];
254 :
255 446683 : if (trieText == NULL)
256 : {
257 61417 : return trie->empty_value;
258 : }
259 :
260 385266 : void * ret = NULL;
261 385266 : if (elektraTrieStartsWith (name, trieText) == 0)
262 : {
263 298988 : ret = elektraTriePrefixLookup (trie->children[idx], name + trie->textlen[idx]);
264 : }
265 : else
266 : {
267 86278 : return trie->empty_value;
268 : }
269 :
270 522911 : if (ret == NULL && trie->value[idx] == NULL)
271 : {
272 12087 : return trie->empty_value;
273 : }
274 286901 : if (ret == NULL) return trie->value[idx];
275 :
276 : return ret;
277 : }
|