4 Copyright (c) 2010 MAD Partners, Ltd. (rweikusat@mssgmbh.com)
6 This file is being published in accordance with the GPLv2 terms
7 contained in the COPYING file being part of the fetchmail
8 6.3.17 release, including the OpenSSL exemption.
15 #include <strings.h> // ffs() lives here
22 MIN_RECORDS = 16 /* arbitrary */
27 struct pat_node *ptrs_[3];
30 The bit mask is stored in the nodes (as opposed to the
31 offset, which is (re-)calculated on demand) because
32 calculating the mask is a non-trivial operation (at
35 unsigned bit_ndx, bit_mask;
37 struct uid_db_record *rec;
41 The idea behind this is that the 'left' pointer of
42 a node is accessible as ptrs(np)[-1] and the right
43 one a ptrs(np)[1]. This implies that no separate codepaths
44 for 'symmetric left- and right-cases' are needed.
46 #define ptrs(np) ((np)->ptrs_ + 1)
49 /** various helpers */
50 static inline unsigned bit_ofs(unsigned bit_ndx)
55 static inline unsigned bit_mask(unsigned bit_ndx)
57 return 1 << (bit_ndx & 7);
60 /** PATRICIA trie insertion */
62 static struct pat_node *walk_down(struct uid_db *db, struct uid_db_record *rec,
63 struct pat_node ***edgep, struct pat_node **parentp)
66 Find the pat node whose id is 'most similar' to the id
67 stored in rec->id. Return a pointer to this node.
68 'parentp' and 'edgep' are output-only parameters which
69 will point to the parent of returned node and to the edge
70 pointer going from the parent to the returned node after
71 the call has returned.
73 This routine is intended for inserts only.
75 struct pat_node *cur, **edge;
76 unsigned bit_ndx, v = 0, ofs;
81 bit_ndx = cur->bit_ndx;
83 if (bit_ofs(bit_ndx) != ofs) {
84 ofs = bit_ofs(bit_ndx);
85 v = ofs < rec->id_len ? rec->id[ofs] : 0;
88 edge = ptrs(cur) + (v & cur->bit_mask ? 1 : -1);
89 } while ((cur = *edge) && cur->bit_ndx > bit_ndx);
93 ((unsigned char *)edge - (v & bit_mask(bit_ndx) ?
94 offsetof(struct pat_node, ptrs_[2])
95 : offsetof(struct pat_node, ptrs_[0])));
100 static inline struct pat_node *walk_up(unsigned diff_ndx, struct pat_node **parent)
103 Walk the chain of parent pointers starting with *parent until a node
104 is found whose parent has a bit_ndx smaller than diff_ndx. Return
105 a pointer to this node and update *parent to point to its parent.
107 struct pat_node *p, *np;
111 while ((p = *ptrs(np)) && p->bit_ndx > diff_ndx)
119 static inline unsigned first_set_bit_in_char(unsigned v)
124 static int find_first_diff_bit(struct uid_db_record const *r0,
125 struct uid_db_record const *r1)
128 Return the bit_ndx of the first differing bit in
129 r0->id and r1->id or -1 if the strings are identical.
131 struct uid_db_record const *long_id;
136 if (max > r1->id_len) {
144 v = r0->id[ofs] ^ r1->id[ofs];
145 while (!v && ++ofs < max);
148 if (r0->id_len == r1->id_len) return -1;
149 v = long_id->id[ofs];
152 return first_set_bit_in_char(v) + ofs * 8;
155 static inline unsigned bit_set(unsigned bit_ndx, struct uid_db_record const *rec)
158 Return non-zero if the bit corresponding with bit_ndx is set
163 ofs = bit_ofs(bit_ndx);
164 if (ofs >= rec->id_len) return 0;
165 return rec->id[ofs] & bit_mask(bit_ndx);
168 /*** node allocation */
169 static struct pat_node *get_pat_node(struct uid_db_record *rec)
172 Allocate a pat_node, set its rec pointer to rec and the
173 next pointer of rec to NULL. Return pointer to the pat_node.
177 np = (struct pat_node *)xmalloc(sizeof(*np));
183 static struct pat_node *get_standalone_node(struct uid_db_record *rec)
186 Return a pat_node suitable for being inserted on the 'left edge'
187 of the trie, ie either linked to a node whose left pointer was zero
188 or being inserted as root node into an empty trie. The bit_ndx of
189 the pat_node is set to the index corresponding with the highest
192 NB: This is a bad choice when UIDs share a common prefix because
193 this implies that the root node will cause a bit to be tested which
194 is non-zero in all other nodes, adding a theoretically redundant
195 level to the trie. This is (to the best of my knowledge) un-
196 fortunately unavoidable if nodes with different key lengths need
201 np = get_pat_node(rec);
202 np->bit_ndx = first_set_bit_in_char(*rec->id);
203 np->bit_mask = bit_mask(np->bit_ndx);
207 /*** various helpers */
208 static inline int record_id_equal(struct uid_db_record const *r0,
209 struct uid_db_record const *r1)
212 r0->id_len == r1->id_len
213 && memcmp(r0->id, r1->id, r0->id_len) == 0;
216 static struct uid_db_record *append_to_list(struct uid_db_record **recp,
217 struct uid_db_record *rec)
220 Append the uid_db_record pointed to by rec to the uid_db_record
221 list accessible as *recp and return rec.
223 while (*recp) recp = &(*recp)->next;
230 /*** insert routine */
231 static struct uid_db_record *pat_insert(struct uid_db *db,
232 struct uid_db_record *rec)
235 Insert the record pointed to by rec in the (potentially empty)
236 PATRICIA trie pointed to by db->pat_root and return rec.
238 struct pat_node *np, *closest, *parent, **edge;
242 np = get_standalone_node(rec);
243 ptrs(np)[-1] = *ptrs(np) = NULL;
250 closest = walk_down(db, rec, &edge, &parent);
253 bit_ndx = find_first_diff_bit(closest->rec, rec);
255 return append_to_list(&closest->rec->next, rec);
257 np = get_pat_node(rec);
258 np->bit_ndx = bit_ndx;
259 np->bit_mask = bit_mask(bit_ndx);
261 np = get_standalone_node(rec);
263 if (parent->bit_ndx > np->bit_ndx) {
264 closest = walk_up(np->bit_ndx, &parent);
266 if (!parent) edge = &db->pat_root;
267 else edge = ptrs(parent)[-1] == closest ?
268 ptrs(parent) - 1 : ptrs(parent) + 1;
275 me = bit_set(np->bit_ndx, rec) ? 1 : -1;
277 ptrs(np)[-me] = closest;
282 /** general db insertion */
283 static struct uid_db_record *get_uid_db_record(char const *id, unsigned status)
286 Allocate a uid_db_record structure and set its id pointer to a
287 dynamically allocated copy of id. The status member of the
288 new record is set to status and its message number to zero (invalid).
289 A pointer to it is then returned.
291 struct uid_db_record *rec;
294 rec = (struct uid_db_record *)xmalloc(sizeof(*rec));
297 rec->id = (char *)memcpy(xmalloc(id_len + 1), id, id_len + 1);
298 rec->id_len = id_len;
299 rec->status = status;
305 static void insert_into_records(struct uid_db *db,
306 struct uid_db_record *rec)
309 Insert rec into the records array of the uid_db pointed
310 to by db. The array is grown as necessary and the
311 corresponding state variables of the db are updated
312 accordingly. The pos member of rec is set to its position
317 next = db->records_next;
319 if (next == db->records_max) {
320 want = db->records_max *= 2;
321 db->records = (struct uid_db_record **)xrealloc(db->records, want * sizeof(rec));
325 db->records[next] = rec;
326 db->records_next = next + 1;
329 struct uid_db_record *uid_db_insert(struct uid_db *db,
330 char const *id, unsigned status)
333 Create an uid_db_record whose id is id and whose status is
334 status and insert it into the uid_db pointed to by db.
335 Return a pointer to the newly created record.
337 struct uid_db_record *rec;
339 rec = get_uid_db_record(id, status);
340 insert_into_records(db, rec);
341 return pat_insert(db, rec);
344 /** message number index */
345 void set_uid_db_num(struct uid_db *db, struct uid_db_record *rec,
349 Set the message number of the record pointed to by rec to num
350 and insert it into the num_ndx of the uid_db pointed to by db
351 at position corresponding with num. The num_ndx lookup array
352 is grown as needed. Message numbers are expected to 'generally'
353 be recorded in ascending order and hence, no provisions are
354 made to deal with the potentially quadratic complexity of
355 inserting a sequence of numbers into an array such that it
356 needs to be grown continuously.
358 struct num_ndx *num_ndx;
361 num_ndx = &db->num_ndx;
363 if (num_ndx->end_value > num) {
364 have = num_ndx->pos_0_value - num_ndx->end_value + 1;
365 want = num_ndx->pos_0_value - num + 1;
366 num_ndx->end_value = num;
368 num_ndx->records = (struct uid_db_record **)xrealloc(num_ndx->records, want * sizeof(rec));
369 do num_ndx->records[--want] = NULL; while (want > have);
372 num_ndx->records[uid_db_num_ofs(num_ndx, num)] = rec;
375 void reset_uid_db_nums(struct uid_db *db)
378 Reset the message numbers of all uid_db_records stored
379 in the uid_db pointed to by db. The corresponding num_ndx
380 lookup array is afterwards freed and the num_ndx end_value
381 adjusted in order to indicate an 'empty' message number
384 struct uid_db_record **rec;
385 struct num_ndx *num_ndx;
388 num_ndx = &db->num_ndx;
390 if (num_ndx->end_value < num_ndx->pos_0_value) {
391 ndx = num_ndx->pos_0_value - num_ndx->end_value;
393 rec = num_ndx->records + --ndx;
394 if (*rec) (*rec)->num = 0;
397 num_ndx->end_value = num_ndx->pos_0_value + 1;
399 free(num_ndx->records);
400 num_ndx->records = NULL;
404 /** search routines */
405 struct uid_db_record *find_uid_by_id(struct uid_db *db, char const *id)
408 Search for an uid_db_record whose id is id in the uid_db pointed
409 to by db and return a pointer to it or NULL if no such record was
413 struct uid_db_record *rec;
414 unsigned v = 0, bit_ndx, ofs;
422 bit_ndx = np->bit_ndx;
424 if (bit_ofs(bit_ndx) != ofs) {
425 ofs = bit_ofs(bit_ndx);
426 v = ofs < len ? id[ofs] : 0;
429 np = ptrs(np)[v & np->bit_mask ? 1 : -1];
430 } while (np && np->bit_ndx > bit_ndx);
432 if (!np) return NULL;
435 return rec->id_len == len && memcmp(id, rec->id, len) == 0 ?
442 struct uid_db_record *last_uid_in_db(struct uid_db *db, char const *id)
445 Return a pointer to the 'last' (insert order) uid_db_record
446 contained in the uid_db pointed to by db whose id is id or
447 NULL if no such record exists.
449 struct uid_db_record *rec;
451 rec = find_uid_by_id(db, id);
452 if (!rec) return NULL;
454 while (rec->next) rec = rec->next;
459 static void free_uid_list(struct uid_db_record *rec)
462 Free the list of uid_db_records starting with
463 the record pointed to by rec.
465 if (rec->next) free_uid_list(rec->next);
471 static void free_pat_trie(struct pat_node *np)
474 Free the PATRCIA-trie pointed to by np and all
475 uid_db_records contained in it.
477 The algorithm implemented below is:
479 1. Load the left pointer of the node pointed to by
482 2. If the result is not NULL,
483 2a) Set the left pointer to NULL.
484 2b) Goto 1 if next points to a child of np.
486 3. Load the right pointer of the node pointed to by
489 4. If the result is not NULL,
490 4a) Set the right pointer to NULL.
491 4b) Goto 1 id next points to a child of np.
493 5. Load next with the parent pointer of np.
495 6. Free np->rec and np.
497 7. Set np to next and goto 1 if it is not null.
499 struct pat_node *next;
505 if (next->bit_ndx > np->bit_ndx) continue;
511 if (next->bit_ndx > np->bit_ndx) continue;
516 free_uid_list(np->rec);
518 } while ((np = next));
521 void free_uid_db(struct uid_db *db)
524 Free all dynamically allocated memory of the uid_db
525 pointed to by db. The structure is not reinitialized.
527 if (db->pat_root) free_pat_trie(db->pat_root);
530 xfree(db->num_ndx.records);
533 /** various public interfaces */
534 void init_uid_db(struct uid_db *db)
537 Initialize the uid_db structure pointed to by db 'properly'
538 such that it represents an empty database. An array of
539 size MIN_RECORDS is allocated and assigned to db->records.
541 struct num_ndx *num_ndx;
545 db->records = (struct uid_db_record **)xmalloc(MIN_RECORDS * sizeof(*db->records));
546 db->records_max = MIN_RECORDS;
547 db->records_next = 0;
549 num_ndx = &db->num_ndx;
550 num_ndx->pos_0_value = num_ndx->end_value = -1;
551 num_ndx->records = NULL;
554 void swap_uid_db_data(struct uid_db *db_0, struct uid_db *db_1)
563 int traverse_uid_db(struct uid_db *db,
564 uid_db_traversal_routine *r, void *arg)
567 Traverses the struct uid_db records array in insert order,
568 invoking the subroutine pointed to by r with a pointer to
569 each record and the arg pointer as arguments. If the return
570 value of that is non-zero, traverse_uid_db immediately returns
571 with this value. Otherwise, zero is returned after the last
574 The uid_db_traversal_routine must not modify the uid_db during
577 struct uid_db_record **recs;
583 max = db->records_next;
585 while (ndx < max && (rc = r(recs[ndx], arg)) == 0)