Line | Count | Source |
1 | | /* |
2 | | * Copyright Supranational LLC |
3 | | * Licensed under the Apache License, Version 2.0, see LICENSE for details. |
4 | | * SPDX-License-Identifier: Apache-2.0 |
5 | | */ |
6 | | |
7 | | #include <stddef.h> |
8 | | |
9 | | /* |
10 | | * Red-black tree tailored for uniqueness test. Amount of messages to be |
11 | | * checked is known prior context initialization, implementation is |
12 | | * insert-only, failure is returned if message is already in the tree. |
13 | | */ |
14 | | |
15 | | struct node { |
16 | | struct node *leafs[2]; |
17 | | const void *data; |
18 | | size_t len_n_colour; /* len<<1 | colour */ |
19 | | }; |
20 | | |
21 | | struct rb_tree { |
22 | | struct node *root; |
23 | | size_t n_nodes; |
24 | | struct node nodes[1]; |
25 | | }; |
26 | | |
27 | | static long bytes_compare(const unsigned char *ptr0, size_t len0, |
28 | | const unsigned char *ptr1, size_t len1) |
29 | 0 | { |
30 | 0 | size_t i, len = len0<len1 ? len0 : len1; |
31 | 0 | long a, b; |
32 | |
|
33 | 0 | for (i=0; i<len; i++) { |
34 | 0 | if ((a = ptr0[i]) != (b = ptr1[i])) |
35 | 0 | return a - b; |
36 | 0 | } |
37 | | |
38 | 0 | return (long)len0 - (long)len1; |
39 | 0 | } |
40 | | |
41 | 0 | #define PAINT_BLACK(p) ((p)->len_n_colour &= ~(size_t)1) |
42 | 0 | #define PAINT_RED(p) ((p)->len_n_colour |= 1) |
43 | 0 | #define IS_RED(p) ((p)->len_n_colour & 1) |
44 | | |
45 | | static int rb_tree_insert(struct rb_tree *tree, const void *data, size_t len) |
46 | 0 | { |
47 | 0 | struct node *nodes[8*sizeof(void *)]; /* visited nodes */ |
48 | 0 | unsigned char dirs[8*sizeof(void *)]; /* taken directions */ |
49 | 0 | size_t k = 0; /* walked distance */ |
50 | 0 | struct node *p, *y, *z; |
51 | |
|
52 | 0 | for (p = tree->root; p != NULL; k++) { |
53 | 0 | long cmp = bytes_compare(data, len, p->data, p->len_n_colour>>1); |
54 | |
|
55 | 0 | if (cmp == 0) |
56 | 0 | return 0; /* already in tree, no insertion */ |
57 | | |
58 | | /* record the step */ |
59 | 0 | nodes[k] = p; |
60 | 0 | p = p->leafs[(dirs[k] = cmp>0)]; |
61 | 0 | } |
62 | | |
63 | | /* allocate new node */ |
64 | 0 | z = &tree->nodes[tree->n_nodes++]; |
65 | 0 | z->leafs[0] = z->leafs[1] = NULL; |
66 | 0 | z->data = data; |
67 | 0 | z->len_n_colour = len<<1; |
68 | 0 | PAINT_RED(z); |
69 | | |
70 | | /* graft |z| */ |
71 | 0 | if (k > 0) |
72 | 0 | nodes[k-1]->leafs[dirs[k-1]] = z; |
73 | 0 | else |
74 | 0 | tree->root = z; |
75 | | |
76 | | /* re-balance |tree| */ |
77 | 0 | while (k >= 2 && IS_RED(y = nodes[k-1])) { |
78 | 0 | size_t ydir = dirs[k-2]; |
79 | 0 | struct node *x = nodes[k-2], /* |z|'s grandparent */ |
80 | 0 | *s = x->leafs[ydir^1]; /* |z|'s uncle */ |
81 | |
|
82 | 0 | if (s != NULL && IS_RED(s)) { |
83 | 0 | PAINT_RED(x); |
84 | 0 | PAINT_BLACK(y); |
85 | 0 | PAINT_BLACK(s); |
86 | 0 | k -= 2; |
87 | 0 | } else { |
88 | 0 | if (dirs[k-1] != ydir) { |
89 | | /* | | |
90 | | * x x |
91 | | * / \ \ |
92 | | * y s -> z s |
93 | | * \ / |
94 | | * z y |
95 | | * / \ |
96 | | * ? ? |
97 | | */ |
98 | 0 | struct node *t = y; |
99 | 0 | y = y->leafs[ydir^1]; |
100 | 0 | t->leafs[ydir^1] = y->leafs[ydir]; |
101 | 0 | y->leafs[ydir] = t; |
102 | 0 | } |
103 | | |
104 | | /* | | |
105 | | * x y |
106 | | * \ / \ |
107 | | * y s -> z x |
108 | | * / \ / \ |
109 | | * z ? ? s |
110 | | */ |
111 | 0 | x->leafs[ydir] = y->leafs[ydir^1]; |
112 | 0 | y->leafs[ydir^1] = x; |
113 | |
|
114 | 0 | PAINT_RED(x); |
115 | 0 | PAINT_BLACK(y); |
116 | |
|
117 | 0 | if (k > 2) |
118 | 0 | nodes[k-3]->leafs[dirs[k-3]] = y; |
119 | 0 | else |
120 | 0 | tree->root = y; |
121 | |
|
122 | 0 | break; |
123 | 0 | } |
124 | 0 | } |
125 | |
|
126 | 0 | PAINT_BLACK(tree->root); |
127 | |
|
128 | 0 | return 1; |
129 | 0 | } |
130 | | |
131 | | #undef IS_RED |
132 | | #undef PAINT_RED |
133 | | #undef PAINT_BLACK |
134 | | |
135 | | size_t blst_uniq_sizeof(size_t n_nodes) |
136 | 0 | { return sizeof(struct rb_tree) + sizeof(struct node)*(n_nodes-1); } |
137 | | |
138 | | void blst_uniq_init(struct rb_tree *tree) |
139 | 0 | { |
140 | 0 | tree->root = NULL; |
141 | 0 | tree->n_nodes = 0; |
142 | 0 | } |
143 | | |
144 | | int blst_uniq_test(struct rb_tree *tree, const void *data, size_t len) |
145 | 0 | { return (int)rb_tree_insert(tree, data, len); } |