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.
11 #include "fetchmail.h"
17 #include <strings.h> /* ffs() lives here - needs #define on Solaris. */
25 MIN_RECORDS = 16 /* arbitrary */
30 struct pat_node *ptrs_[3];
33 The bit mask is stored in the nodes (as opposed to the
34 offset, which is (re-)calculated on demand) because
35 calculating the mask is a non-trivial operation (at
38 unsigned bit_ndx, bit_mask;
40 struct uid_db_record *rec;
44 The idea behind this is that the 'left' pointer of
45 a node is accessible as ptrs(np)[-1] and the right
46 one a ptrs(np)[1]. This implies that no separate codepaths
47 for 'symmetric left- and right-cases' are needed.
49 #define ptrs(np) ((np)->ptrs_ + 1)
52 /** various helpers */
53 static inline unsigned bit_ofs(unsigned bit_ndx)
58 static inline unsigned bit_mask(unsigned bit_ndx)
60 return 1 << (bit_ndx & 7);
63 /** PATRICIA trie insertion */
65 static struct pat_node *walk_down(struct uid_db *db, struct uid_db_record *rec,
66 struct pat_node ***edgep, struct pat_node **parentp)
69 Find the pat node whose id is 'most similar' to the id
70 stored in rec->id. Return a pointer to this node.
71 'parentp' and 'edgep' are output-only parameters which
72 will point to the parent of returned node and to the edge
73 pointer going from the parent to the returned node after
74 the call has returned.
76 This routine is intended for inserts only.
78 struct pat_node *cur, **edge;
79 unsigned bit_ndx, v = 0, ofs;
84 bit_ndx = cur->bit_ndx;
86 if (bit_ofs(bit_ndx) != ofs) {
87 ofs = bit_ofs(bit_ndx);
88 v = ofs < rec->id_len ? rec->id[ofs] : 0;
91 edge = ptrs(cur) + (v & cur->bit_mask ? 1 : -1);
92 } while ((cur = *edge) && cur->bit_ndx > bit_ndx);
96 ((unsigned char *)edge - (v & bit_mask(bit_ndx) ?
97 offsetof(struct pat_node, ptrs_) + 2 * sizeof(struct pat_node *)
98 : offsetof(struct pat_node, ptrs_)));
103 static inline struct pat_node *walk_up(unsigned diff_ndx, struct pat_node **parent)
106 Walk the chain of parent pointers starting with *parent until a node
107 is found whose parent has a bit_ndx smaller than diff_ndx. Return
108 a pointer to this node and update *parent to point to its parent.
110 struct pat_node *p, *np;
114 while ((p = *ptrs(np)) && p->bit_ndx > diff_ndx)
122 static inline unsigned first_set_bit_in_char(unsigned v)
127 static int find_first_diff_bit(struct uid_db_record const *r0,
128 struct uid_db_record const *r1)
131 Return the bit_ndx of the first differing bit in
132 r0->id and r1->id or -1 if the strings are identical.
134 struct uid_db_record const *long_id;
139 if (max > r1->id_len) {
147 v = r0->id[ofs] ^ r1->id[ofs];
148 while (!v && ++ofs < max);
151 if (r0->id_len == r1->id_len) return -1;
152 v = long_id->id[ofs];
155 return first_set_bit_in_char(v) + ofs * 8;
158 static inline unsigned bit_set(unsigned bit_ndx, struct uid_db_record const *rec)
161 Return non-zero if the bit corresponding with bit_ndx is set
166 ofs = bit_ofs(bit_ndx);
167 if (ofs >= rec->id_len) return 0;
168 return rec->id[ofs] & bit_mask(bit_ndx);
171 /*** node allocation */
172 static struct pat_node *get_pat_node(struct uid_db_record *rec)
175 Allocate a pat_node, set its rec pointer to rec and the
176 next pointer of rec to NULL. Return pointer to the pat_node.
180 np = (struct pat_node *)xmalloc(sizeof(*np));
186 static struct pat_node *get_standalone_node(struct uid_db_record *rec)
189 Return a pat_node suitable for being inserted on the 'left edge'
190 of the trie, ie either linked to a node whose left pointer was zero
191 or being inserted as root node into an empty trie. The bit_ndx of
192 the pat_node is set to the index corresponding with the highest
195 NB: This is a bad choice when UIDs share a common prefix because
196 this implies that the root node will cause a bit to be tested which
197 is non-zero in all other nodes, adding a theoretically redundant
198 level to the trie. This is (to the best of my knowledge) un-
199 fortunately unavoidable if nodes with different key lengths need
204 np = get_pat_node(rec);
205 np->bit_ndx = first_set_bit_in_char(*rec->id);
206 np->bit_mask = bit_mask(np->bit_ndx);
210 /*** various helpers */
212 static inline int record_id_equal(struct uid_db_record const *r0,
213 struct uid_db_record const *r1)
216 r0->id_len == r1->id_len
217 && memcmp(r0->id, r1->id, r0->id_len) == 0;
221 static struct uid_db_record *append_to_list(struct uid_db_record **recp,
222 struct uid_db_record *rec)
225 Append the uid_db_record pointed to by rec to the uid_db_record
226 list accessible as *recp and return rec.
228 while (*recp) recp = &(*recp)->next;
235 /*** insert routine */
236 static struct uid_db_record *pat_insert(struct uid_db *db,
237 struct uid_db_record *rec)
240 Insert the record pointed to by rec in the (potentially empty)
241 PATRICIA trie pointed to by db->pat_root and return rec.
243 struct pat_node *np, *closest, *parent, **edge;
247 np = get_standalone_node(rec);
248 ptrs(np)[-1] = *ptrs(np) = NULL;
255 closest = walk_down(db, rec, &edge, &parent);
258 bit_ndx = find_first_diff_bit(closest->rec, rec);
260 return append_to_list(&closest->rec->next, rec);
262 np = get_pat_node(rec);
263 np->bit_ndx = bit_ndx;
264 np->bit_mask = bit_mask(bit_ndx);
266 np = get_standalone_node(rec);
268 if (parent->bit_ndx > np->bit_ndx) {
269 closest = walk_up(np->bit_ndx, &parent);
271 if (!parent) edge = &db->pat_root;
272 else edge = ptrs(parent)[-1] == closest ?
273 ptrs(parent) - 1 : ptrs(parent) + 1;
280 me = bit_set(np->bit_ndx, rec) ? 1 : -1;
282 ptrs(np)[-me] = closest;
287 /** general db insertion */
288 static struct uid_db_record *get_uid_db_record(char const *id, unsigned status)
291 Allocate a uid_db_record structure and set its id pointer to a
292 dynamically allocated copy of id. The status member of the
293 new record is set to status and its message number to zero (invalid).
294 A pointer to it is then returned.
296 struct uid_db_record *rec;
299 rec = (struct uid_db_record *)xmalloc(sizeof(*rec));
302 rec->id = (char *)memcpy(xmalloc(id_len + 1), id, id_len + 1);
303 rec->id_len = id_len;
304 rec->status = status;
310 static void insert_into_records(struct uid_db *db,
311 struct uid_db_record *rec)
314 Insert rec into the records array of the uid_db pointed
315 to by db. The array is grown as necessary and the
316 corresponding state variables of the db are updated
317 accordingly. The pos member of rec is set to its position
322 next = db->records_next;
324 if (next == db->records_max) {
325 want = db->records_max *= 2;
326 db->records = (struct uid_db_record **)xrealloc(db->records, want * sizeof(rec));
330 db->records[next] = rec;
331 db->records_next = next + 1;
334 struct uid_db_record *uid_db_insert(struct uid_db *db,
335 char const *id, unsigned status)
338 Create an uid_db_record whose id is id and whose status is
339 status and insert it into the uid_db pointed to by db.
340 Return a pointer to the newly created record.
342 struct uid_db_record *rec;
344 rec = get_uid_db_record(id, status);
345 insert_into_records(db, rec);
346 return pat_insert(db, rec);
349 /** message number index */
350 void set_uid_db_num(struct uid_db *db, struct uid_db_record *rec,
354 Set the message number of the record pointed to by rec to num
355 and insert it into the num_ndx of the uid_db pointed to by db
356 at position corresponding with num. The num_ndx lookup array
357 is grown as needed. Message numbers are expected to 'generally'
358 be recorded in ascending order and hence, no provisions are
359 made to deal with the potentially quadratic complexity of
360 inserting a sequence of numbers into an array such that it
361 needs to be grown continuously.
363 struct num_ndx *num_ndx;
366 num_ndx = &db->num_ndx;
368 if (num_ndx->end_value > num) {
369 have = num_ndx->pos_0_value - num_ndx->end_value + 1;
370 want = num_ndx->pos_0_value - num + 1;
371 num_ndx->end_value = num;
373 num_ndx->records = (struct uid_db_record **)xrealloc(num_ndx->records, want * sizeof(rec));
374 do num_ndx->records[--want] = NULL; while (want > have);
377 num_ndx->records[uid_db_num_ofs(num_ndx, num)] = rec;
380 void reset_uid_db_nums(struct uid_db *db)
383 Reset the message numbers of all uid_db_records stored
384 in the uid_db pointed to by db. The corresponding num_ndx
385 lookup array is afterwards freed and the num_ndx end_value
386 adjusted in order to indicate an 'empty' message number
389 struct uid_db_record **rec;
390 struct num_ndx *num_ndx;
393 num_ndx = &db->num_ndx;
395 if (num_ndx->end_value < num_ndx->pos_0_value) {
396 ndx = num_ndx->pos_0_value - num_ndx->end_value;
398 rec = num_ndx->records + --ndx;
399 if (*rec) (*rec)->num = 0;
402 num_ndx->end_value = num_ndx->pos_0_value + 1;
404 free(num_ndx->records);
405 num_ndx->records = NULL;
409 /** search routines */
410 struct uid_db_record *find_uid_by_id(struct uid_db *db, char const *id)
413 Search for an uid_db_record whose id is id in the uid_db pointed
414 to by db and return a pointer to it or NULL if no such record was
418 struct uid_db_record *rec;
419 unsigned v = 0, bit_ndx, ofs;
427 bit_ndx = np->bit_ndx;
429 if (bit_ofs(bit_ndx) != ofs) {
430 ofs = bit_ofs(bit_ndx);
431 v = ofs < len ? id[ofs] : 0;
434 np = ptrs(np)[v & np->bit_mask ? 1 : -1];
435 } while (np && np->bit_ndx > bit_ndx);
437 if (!np) return NULL;
440 return rec->id_len == len && memcmp(id, rec->id, len) == 0 ?
447 struct uid_db_record *last_uid_in_db(struct uid_db *db, char const *id)
450 Return a pointer to the 'last' (insert order) uid_db_record
451 contained in the uid_db pointed to by db whose id is id or
452 NULL if no such record exists.
454 struct uid_db_record *rec;
456 rec = find_uid_by_id(db, id);
457 if (!rec) return NULL;
459 while (rec->next) rec = rec->next;
464 static void free_uid_list(struct uid_db_record *rec)
469 Free the list of uid_db_records starting with
470 the record pointed to by rec.
472 if (rec->next) free_uid_list(rec->next);
478 static void free_pat_trie(struct pat_node *np)
481 Free the PATRCIA-trie pointed to by np and all
482 uid_db_records contained in it.
484 The algorithm implemented below is:
486 1. Load the left pointer of the node pointed to by
489 2. If the result is not NULL,
490 2a) Set the left pointer to NULL.
491 2b) Goto 1 if next points to a child of np.
493 3. Load the right pointer of the node pointed to by
496 4. If the result is not NULL,
497 4a) Set the right pointer to NULL.
498 4b) Goto 1 id next points to a child of np.
500 5. Load next with the parent pointer of np.
502 6. Free np->rec and np.
504 7. Set np to next and goto 1 if it is not null.
506 struct pat_node *next;
512 if (next->bit_ndx > np->bit_ndx) continue;
518 if (next->bit_ndx > np->bit_ndx) continue;
523 free_uid_list(np->rec);
525 } while ((np = next));
528 void free_uid_db(struct uid_db *db)
531 Free all dynamically allocated memory of the uid_db
532 pointed to by db. The structure is not reinitialized.
534 if (db->pat_root) free_pat_trie(db->pat_root);
537 xfree(db->num_ndx.records);
540 /** various public interfaces */
541 void init_uid_db(struct uid_db *db)
544 Initialize the uid_db structure pointed to by db 'properly'
545 such that it represents an empty database. An array of
546 size MIN_RECORDS is allocated and assigned to db->records.
548 struct num_ndx *num_ndx;
552 db->records = (struct uid_db_record **)xmalloc(MIN_RECORDS * sizeof(*db->records));
553 db->records_max = MIN_RECORDS;
554 db->records_next = 0;
556 num_ndx = &db->num_ndx;
557 num_ndx->pos_0_value = num_ndx->end_value = -1;
558 num_ndx->records = NULL;
561 void swap_uid_db_data(struct uid_db *db_0, struct uid_db *db_1)
570 int traverse_uid_db(struct uid_db *db,
571 uid_db_traversal_routine *r, void *arg)
574 Traverses the struct uid_db records array in insert order,
575 invoking the subroutine pointed to by r with a pointer to
576 each record and the arg pointer as arguments. If the return
577 value of that is non-zero, traverse_uid_db immediately returns
578 with this value. Otherwise, zero is returned after the last
581 The uid_db_traversal_routine must not modify the uid_db during
584 struct uid_db_record **recs;
590 max = db->records_next;
592 while (ndx < max && (rc = r(recs[ndx], arg)) == 0)