forked from kamailio/kamailio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdt.c
360 lines (272 loc) · 7.69 KB
/
dt.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/*
* Copyright (C) 2009 1&1 Internet AG
*
* This file is part of sip-router, a free SIP server.
*
* sip-router is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* sip-router is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dt.h"
#include "dtm.h"
#include "carrier.h"
#include "log.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct dt_node_t *dt_init()
{
struct dt_node_t *root;
root = malloc(sizeof(struct dt_node_t));
if (root == NULL) {
LERR("dt_init() cannot allocate memory for dt_node_t.\n");
return NULL;
}
memset(root, 0, sizeof(struct dt_node_t));
return root;
}
void dt_delete(struct dt_node_t *root, struct dt_node_t *node)
{
int i;
if (node==NULL) return;
for (i=0; i<10; i++) {
dt_delete(root, node->child[i]);
node->child[i] = NULL;
}
if (node != root) free(node);
}
void dt_destroy(struct dt_node_t **root)
{
if ((root != NULL) && (*root != NULL)) {
dt_delete(*root, *root);
free(*root);
*root = NULL;
}
}
void dt_clear(struct dt_node_t *root)
{
dt_delete(root, root);
memset(root, 0, sizeof(struct dt_node_t));
}
int dt_insert(struct dt_node_t *root, const char *number, int numberlen, carrier_t carrier)
{
struct dt_node_t *node = root;
int i=0;
unsigned int digit;
while (i<numberlen) {
digit = number[i] - '0';
if (digit>9) {
LERR("dt_insert() cannot insert non-numerical number\n");
return -1;
}
if (node->child[digit] == NULL) {
node->child[digit] = malloc(sizeof(struct dt_node_t));
#ifdef DT_MEM_ASSERT
assert(node->child[digit] != NULL);
#else
if (node->child[digit] == NULL) {
LERR("dt_insert() cannot allocate memory\n");
return -1;
}
#endif
/* non-mapping intermediary nodes carry the special carrier id 0 */
memset(node->child[digit], 0, sizeof(struct dt_node_t));
}
node = node->child[digit];
i++;
}
node->carrier = carrier;
return 0;
}
int dt_size(struct dt_node_t *root)
{
int i;
int sum = 0;
if (root == NULL) return 0;
for (i=0; i<10; i++) {
sum += dt_size(root->child[i]);
}
return sum+1;
}
int dt_loaded_nodes(struct dt_node_t *root) {
int i;
int sum = 0;
if (root == NULL) return 0;
for (i=0; i<10; i++) {
sum += dt_loaded_nodes(root->child[i]);
}
if (root->carrier > 0) sum++;
return sum;
}
int dt_leaves(struct dt_node_t *root)
{
int i;
int sum = 0;
int leaf = 1;
for (i=0; i<10; i++) {
if (root->child[i]) {
sum += dt_leaves(root->child[i]);
leaf = 0;
}
}
return sum+leaf;
}
int dt_longest_match(struct dt_node_t *root, const char *number, int numberlen, carrier_t *carrier)
{
struct dt_node_t *node = root;
int nmatch = -1;
int i=0;
unsigned int digit;
if (node->carrier > 0) {
nmatch=0;
*carrier = node->carrier;
}
while (i<numberlen) {
digit = number[i] - '0';
if (digit>9) return nmatch;
if (node->child[digit] == NULL) return nmatch;
node = node->child[digit];
i++;
if (node->carrier > 0) {
nmatch=i;
*carrier = node->carrier;
}
}
return nmatch;
}
int dt_contains(struct dt_node_t *root, const char *number, int numberlen, carrier_t *carrier)
{
return (dt_longest_match(root, number, numberlen, carrier) == numberlen);
}
/*
Returns the carrier if all children have the same carrier,
0 otherwise.
*/
carrier_t dt_allcce(struct dt_node_t *root) {
int i;
carrier_t ret = 0;
/* determine single child carrier */
for (i=0; i<10; i++) {
if (root->child[i] == NULL)
return 0;
else if (root->child[i]->carrier > 0) {
ret=root->child[i]->carrier;
break;
}
}
if (ret==0) return 0;
/* check if all children share the same carrier */
for (i=0; i<10; i++) {
if ((root->child[i] == NULL) || (root->child[i]->carrier != ret)) return 0;
}
return ret;
}
/*
Cuts off tree branches which share a common carrier id with a node located
more upwards in the tree. To do so, the subtree starting at root will be
traversed recursively and according leaf nodes deleted.
Nodes potentially eligible for removal will be marked by the special carrier
id `0' (also denoting a [non-mapping] intermediary node). In order to decide
whether a node is eligible or not, the lastly observed carrier id will be
maintained in lastcarrier and transferred over to each recursion.
*/
int dt_optimize_leaf(struct dt_node_t *root, carrier_t lastcarrier)
{
struct dt_node_t *node = root;
carrier_t currentcarrier;
int deleteret;
int delete;
carrier_t allcce;
int i;
if (node == NULL) return 0;
if (node->carrier == lastcarrier) {
node->carrier = 0; /* this is a node sharing carrier id */
}
if (node->carrier>0) currentcarrier=node->carrier; /* new common carrier id starts at this node */
else currentcarrier=lastcarrier; /* carry over common carrier id */
/*
Nodes with children sharing the same carrier may be generalized into a common node (prefix).
Note that the code in the following if-statement is the reason why dt_optimize() calls this function
multiple times.
*/
if ((allcce=dt_allcce(node))) {
/*
generalization requires having an intermediary parent node or a common carrier id between
all children and the current node
*/
if ((node->carrier == 0) || (node->carrier < 0 && allcce == currentcarrier)) {
currentcarrier=allcce;
node->carrier=currentcarrier;
for(i=0; i<10; i++) {
/*
Negative carrier ids mark children eligible for generalization into a parent
node. Carrier id 0 cannot be used because it could ambiguously refer to an
intermediary parent node, thereby rendering differentiation of such nodes and
generalized ones impossible and, in turn, overwriting mappings to existing
carrier ids.
When optimization completes, negative carrier ids will be modified to 0 to
make sure other functions operating on the tree do not get confused. (See
dt_clear_negatives() for details.)
*/
node->child[i]->carrier = -allcce;
}
}
}
/* preliminarily assume leaf nodes without carrier to be eligible for removal */
if (node->carrier <= 0) deleteret=1;
else deleteret=0;
/* optimize children */
for (i=0; i<10; i++) {
delete=dt_optimize_leaf(node->child[i], currentcarrier);
if (delete) {
dt_delete(node, node->child[i]);
node->child[i] = NULL;
}
/* this is no leaf node ==> revert removal assumption */
if (node->child[i]) deleteret = 0;
}
return deleteret;
}
/*
Replaces negative carrier ids in the given, root-based
subtree by zeros to comply with other functions which may
not expect negative carrier ids. (See also dt_optimize_leaf().)
*/
void dt_clear_negatives(struct dt_node_t *root)
{
struct dt_node_t *node = root;
int i;
if (node == NULL) return;
for (i=0; i<10; i++) {
dt_clear_negatives(node->child[i]);
}
if (node->carrier < 0) node->carrier = 0;
}
void dt_optimize(struct dt_node_t *root)
{
int size;
int oldsize = 0;
size=dt_size(root);
/*
optimization gradually trims leaf nodes in each invocation
of dt_optimize_leaf() ==> keep calling this function until
the size of the tree stabilizes
*/
while (size!=oldsize) {
dt_optimize_leaf(root, 0);
oldsize=size;
size=dt_size(root);
}
/* turn negative carrier ids into zero's */
dt_clear_negatives(root);
}