1 /* ################################################################### */
2 /* Copyright 2015, Pierre Gentile (p.gen.progs@gmail.com) */
4 /* This Source Code Form is subject to the terms of the Mozilla Public */
5 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
6 /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 /* ################################################################### */
9 /* ************************************************************************ */
10 /* Ternary Search Tree and sorted array creation functions. */
11 /* Inspired by a code described in "Ternary Search Trees" by Jon */
12 /* Bentley and Robert Sedgewick in the April, 1998, Dr. Dobb's Journal. */
14 /* https://www.drdobbs.com/database/ternary-search-trees/184410528?pgno=1 */
15 /* https://www.cs.princeton.edu/~rs/strings/tstdemo.c. */
16 /* ************************************************************************ */
29 /* List of words matching the current search. */
30 /* """""""""""""""""""""""""""""""""""""""""" */
31 ll_t
*tst_search_list
; /* Must be initialized by ll_new() before use. */
33 /* ======================================= */
34 /* Ternary search tree insertion function. */
35 /* ======================================= */
37 tst_insert(tst_node_t
*p
, wchar_t *w
, void *data
)
41 p
= (tst_node_t
*)xmalloc(sizeof(tst_node_t
));
43 p
->lokid
= p
->eqkid
= p
->hikid
= NULL
;
47 if (*w
< p
->splitchar
)
48 p
->lokid
= tst_insert(p
->lokid
, w
, data
);
49 else if (*w
== p
->splitchar
)
57 p
->eqkid
= tst_insert(p
->eqkid
, w
+ 1, data
);
60 p
->hikid
= tst_insert(p
->hikid
, w
, data
);
65 /* ====================================== */
66 /* Ternary search tree deletion function. */
67 /* User data area not cleaned. */
68 /* ====================================== */
70 tst_cleanup(tst_node_t
*p
)
74 tst_cleanup(p
->lokid
);
75 if (p
->splitchar
!= L
'\0')
76 tst_cleanup(p
->eqkid
);
77 tst_cleanup(p
->hikid
);
82 /* ========================================================== */
83 /* Recursive traversal of a ternary tree. A callback function */
84 /* is also called when a complete string is found. */
85 /* Returns 1 if the callback function succeed (returned 1) at */
87 /* The first_call argument is for initializing the static */
89 /* ========================================================== */
91 tst_traverse(tst_node_t
*p
, int (*callback
)(void *), int first_call
)
100 tst_traverse(p
->lokid
, callback
, 0);
101 if (p
->splitchar
!= L
'\0')
102 tst_traverse(p
->eqkid
, callback
, 0);
104 rc
+= (*callback
)(p
->data
);
105 tst_traverse(p
->hikid
, callback
, 0);
110 /* ======================================================================= */
111 /* Traverses the word tst looking for a wchar and build a list of pointers */
112 /* containing all the sub-tst potentially leading to words containing the */
113 /* next wchar of the search string. */
114 /* ======================================================================= */
116 tst_substring_traverse(tst_node_t
*p
,
117 int (*callback
)(void *),
129 if (p
->splitchar
== w
)
132 sub_tst_t
*sub_tst_data
;
134 node
= tst_search_list
->tail
;
135 sub_tst_data
= (sub_tst_t
*)(node
->data
);
137 if (p
->eqkid
!= NULL
)
138 insert_sorted_ptr(&(sub_tst_data
->array
),
139 &(sub_tst_data
->size
),
140 &(sub_tst_data
->count
),
146 tst_substring_traverse(p
->lokid
, callback
, 0, w
);
147 if (p
->splitchar
!= L
'\0')
148 tst_substring_traverse(p
->eqkid
, callback
, 0, w
);
149 else if (callback
!= NULL
)
150 rc
+= (*callback
)(p
->data
);
151 tst_substring_traverse(p
->hikid
, callback
, 0, w
);
156 /* ======================================================================== */
157 /* Traverses the word tst looking for a wchar and build a list of pointers */
158 /* containing all the sub-tst nodes potentially leading to words containing */
159 /* the next wchar os the search string. */
160 /* ======================================================================== */
162 tst_fuzzy_traverse(tst_node_t
*p
,
163 int (*callback
)(void *),
171 w1s
[1] = w2s
[1] = L
'\0';
179 w1s
[0] = p
->splitchar
;
182 if (my_wcscasecmp(w1s
, w2s
) == 0)
185 sub_tst_t
*sub_tst_data
;
187 node
= tst_search_list
->tail
;
188 sub_tst_data
= (sub_tst_t
*)(node
->data
);
190 if (p
->eqkid
!= NULL
)
191 insert_sorted_ptr(&(sub_tst_data
->array
),
192 &(sub_tst_data
->size
),
193 &(sub_tst_data
->count
),
199 tst_fuzzy_traverse(p
->lokid
, callback
, 0, w
);
200 if (p
->splitchar
!= L
'\0')
201 tst_fuzzy_traverse(p
->eqkid
, callback
, 0, w
);
202 else if (callback
!= NULL
)
203 rc
+= (*callback
)(p
->data
);
204 tst_fuzzy_traverse(p
->hikid
, callback
, 0, w
);
209 /* ======================================================================= */
210 /* Searches a complete string in a ternary tree starting from a root node. */
211 /* ======================================================================= */
213 tst_search(tst_node_t
*root
, wchar_t *w
)
221 if (*w
< p
->splitchar
)
223 else if (*w
== p
->splitchar
)
236 /* ============================================================== */
237 /* Searches all strings beginning with the same prefix. */
238 /* the callback function will be applied to each of these strings */
239 /* returns NULL if no string matched the prefix. */
240 /* ============================================================== */
242 tst_prefix_search(tst_node_t
*root
, wchar_t *w
, int (*callback
)(void *))
244 tst_node_t
*p
= root
;
245 size_t len
= wcslen(w
);
250 if (*w
< p
->splitchar
)
252 else if (*w
== p
->splitchar
)
259 rc
= tst_traverse(p
->eqkid
, callback
, 1);
260 return (void *)(long)rc
;
271 /* ========================================================= */
272 /* Insertion of an pointer in a already sorted pointer array */
273 /* without duplications. */
274 /* ========================================================= */
276 insert_sorted_ptr(tst_node_t
***array_ptr
,
282 long left
= 0, right
= *nb
, middle
;
286 /* Bisection search. */
287 /* """"""""""""""""" */
290 middle
= (left
+ right
) / 2;
291 if ((intptr_t)((*array_ptr
)[middle
]) == (intptr_t)ptr
)
292 return; /* Value already in array. */
294 if ((intptr_t)ptr
< (intptr_t)((*array_ptr
)[middle
]))
305 *array_ptr
= xrealloc(*array_ptr
, *size
* sizeof(long));
309 memmove((*array_ptr
) + pos
+ 1,
311 sizeof(ptr
) * (*nb
- pos
));
315 (*array_ptr
)[pos
] = ptr
;