static inline int sp_iensure(spi *i) { if (splikely((i->icount + 1) < i->itop)) return 0; i->itop *= 2; i->i = sp_realloc(i->a, i->i, i->itop * sizeof(spipage*)); if (spunlikely(i->i == NULL)) return -1; return 0; }
static int sp_node_unsubscribe (struct sp_trie_node **self, const uint8_t *data, size_t size) { int i; int j; int index; int ptr_index; int new_min; struct sp_trie_node **ch; struct sp_trie_node *new_node; struct sp_trie_node *ch2; if (!size) goto found; /* If prefix does not match the data, return. */ if (sp_node_check_prefix (*self, data, size) != (*self)->prefix_len) return 0; /* Skip the prefix. */ data += (*self)->prefix_len; size -= (*self)->prefix_len; if (!size) goto found; /* Move to the next node. */ ch = sp_node_next (*self, *data); if (!ch) return 0; /* TODO: This should be an error. */ /* Recursive traversal of the trie happens here. If the subscription wasn't really removed, nothing have changed in the trie and no additional pruning is needed. */ if (sp_node_unsubscribe (ch, data + 1, size - 1) == 0) return 0; /* Subscription removal is already done. Now we are going to compact the trie. However, if the following node remains in place, there's nothing to compact here. */ if (*ch) return 1; /* Sparse array. */ if ((*self)->type < SP_TRIE_DENSE_TYPE) { /* Get the indices of the removed child. */ for (index = 0; index != (*self)->type; ++index) if ((*self)->sparse.children [index] == *data) break; assert (index != (*self)->type); ptr_index = (*self)->sparse.children [index]; /* Remove the destroyed child from both lists of children. */ memmove ( (*self)->sparse.children + index, (*self)->sparse.children + index + 1, (*self)->type - index - 1); memmove ( sp_node_child (*self, ptr_index), sp_node_child (*self, ptr_index + 1), ((*self)->type - index - 1) * sizeof (struct sp_trie_node*)); --(*self)->type; for (i = 0; i != (*self)->type; ++i) if ((*self)->sparse.children [i] >= ptr_index) --((*self)->sparse.children [i]); *self = sp_realloc (*self, sizeof (struct sp_trie_node) + ((*self)->type * sizeof (struct sp_trie_node*))); assert (*self); /* If there are no more children and no refcount, we can delete the node altogether. */ if (!(*self)->type && !sp_node_has_subscribers (*self)) { sp_free (*self); *self = NULL; return 1; } /* Try to merge the node with the following node. */ *self = sp_node_compact (*self); return 1; } /* Dense array. */ /* In this case the array stays dense. We have to adjust the limits of the array, if appropriate. */ if ((*self)->dense.nbr > SP_TRIE_SPARSE_MAX + 1) { /* If the removed item is the leftmost one, trim the array from the left side. */ if (*data == (*self)->dense.min) { for (i = 0; i != (*self)->dense.max - (*self)->dense.min + 1; ++i) if (*sp_node_child (*self, i)) break; new_min = i + (*self)->dense.min; memmove (sp_node_child (*self, 0), sp_node_child (*self, i), ((*self)->dense.max - new_min + 1) * sizeof (struct sp_trie_node*)); (*self)->dense.min = new_min; --(*self)->dense.nbr; *self = sp_realloc (*self, sizeof (struct sp_trie_node) + ((*self)->dense.max - new_min + 1) * sizeof (struct sp_trie_node*)); assert (*self); return 1; } /* If the removed item is the rightmost one, trim the array from the right side. */ if (*data == (*self)->dense.max) { for (i = (*self)->dense.max - (*self)->dense.min; i != 0; --i) if (*sp_node_child (*self, i)) break; (*self)->dense.max = i + (*self)->dense.min; --(*self)->dense.nbr; *self = sp_realloc (*self, sizeof (struct sp_trie_node) + ((*self)->dense.max - (*self)->dense.min + 1) * sizeof (struct sp_trie_node*)); assert (*self); return 1; } /* If the item is removed from the middle of the array, do nothing. */ --(*self)->dense.nbr; return 1; } /* Convert dense array into sparse array. */ { new_node = sp_alloc (sizeof (struct sp_trie_node) + SP_TRIE_SPARSE_MAX * sizeof (struct sp_trie_node*), "trie node"); assert (new_node); new_node->refcount = 0; new_node->prefix_len = (*self)->prefix_len; memcpy (new_node->prefix, (*self)->prefix, new_node->prefix_len); new_node->type = SP_TRIE_SPARSE_MAX; j = 0; for (i = 0; i != (*self)->dense.max - (*self)->dense.min + 1; ++i) { ch2 = *sp_node_child (*self, i); if (ch2) { new_node->sparse.children [j] = i + (*self)->dense.min; *sp_node_child (new_node, j) = ch2; ++j; } } assert (j == SP_TRIE_SPARSE_MAX); sp_free (*self); *self = new_node; return 1; } found: /* We are at the end of the subscription here. */ /* Subscription doesn't exist. */ if (sp_slow (!*self || !sp_node_has_subscribers (*self))) return -EINVAL; /* Subscription exists. Unsubscribe. */ --(*self)->refcount; /* If reference count has dropped to zero we can try to compact the node. */ if (!(*self)->refcount) { /* If there are no children, we can delete the node altogether. */ if (!(*self)->type) { sp_free (*self); *self = NULL; return 1; } /* Try to merge the node with the following node. */ *self = sp_node_compact (*self); return 1; } return 0; }
int sp_trie_subscribe (struct sp_trie *self, const uint8_t *data, size_t size) { int i; struct sp_trie_node **node; struct sp_trie_node **n; struct sp_trie_node *ch; struct sp_trie_node *old_node; int pos; uint8_t c; uint8_t c2; uint8_t new_min; uint8_t new_max; int old_children; int new_children; int inserted; int more_nodes; /* Step 1 -- Traverse the trie. */ node = &self->root; pos = 0; while (1) { /* If there are no more nodes on the path, go to step 4. */ if (!*node) goto step4; /* Check whether prefix matches the new subscription. */ pos = sp_node_check_prefix (*node, data, size); data += pos; size -= pos; /* If only part of the prefix matches, go to step 2. */ if (pos < (*node)->prefix_len) goto step2; /* Even if whole prefix matches and there's no more data to match, go directly to step 5. */ if (!size) goto step5; /* Move to the next node. If it is not present, go to step 3. */ n = sp_node_next (*node, *data); if (!n) goto step3; node = n; ++data; --size; } /* Step 2 -- Split the prefix into two parts if required. */ step2: ch = *node; *node = sp_alloc (sizeof (struct sp_trie_node) + sizeof (struct sp_trie_node*), "trie node"); assert (*node); (*node)->refcount = 0; (*node)->prefix_len = pos; (*node)->type = 1; memcpy ((*node)->prefix, ch->prefix, pos); (*node)->sparse.children [0] = ch->prefix [pos]; ch->prefix_len -= (pos + 1); memmove (ch->prefix, ch->prefix + pos + 1, ch->prefix_len); ch = sp_node_compact (ch); *sp_node_child (*node, 0) = ch; pos = (*node)->prefix_len; /* Step 3 -- Adjust the child array to accommodate the new character. */ step3: /* If there are no more data in the subscription, there's nothing to adjust in the child array. Proceed directly to the step 5. */ if (!size) goto step5; /* If the new branch fits into sparse array... */ if ((*node)->type < SP_TRIE_SPARSE_MAX) { *node = sp_realloc (*node, sizeof (struct sp_trie_node) + ((*node)->type + 1) * sizeof (struct sp_trie_node*)); assert (*node); (*node)->sparse.children [(*node)->type] = *data; ++(*node)->type; node = sp_node_child (*node, (*node)->type - 1); *node = NULL; ++data; --size; goto step4; } /* If the node is already a dense array, resize it to fit the next character. */ if ((*node)->type == SP_TRIE_DENSE_TYPE) { c = *data; if (c < (*node)->dense.min || c > (*node)->dense.max) { new_min = (*node)->dense.min < c ? (*node)->dense.min : c; new_max = (*node)->dense.max > c ? (*node)->dense.max : c; *node = sp_realloc (*node, sizeof (struct sp_trie_node) + (new_max - new_min + 1) * sizeof (struct sp_trie_node*)); assert (*node); old_children = (*node)->dense.max - (*node)->dense.min + 1; new_children = new_max - new_min + 1; if ((*node)->dense.min != new_min) { inserted = (*node)->dense.min - new_min; memmove (sp_node_child (*node, inserted), sp_node_child (*node, 0), old_children * sizeof (struct sp_trie_node*)); memset (sp_node_child (*node, 0), 0, inserted * sizeof (struct sp_trie_node*)); } else { memset (sp_node_child (*node, old_children), 0, (new_children - old_children) * sizeof (struct sp_trie_node*)); } (*node)->dense.min = new_min; (*node)->dense.max = new_max; ++(*node)->dense.nbr; } node = sp_node_child (*node, c - (*node)->dense.min); ++data; --size; goto step4; } /* This is a sparse array, but no more children can be added to it. We have to convert it into a dense array. */ { /* First, determine the range of children. */ new_min = 255; new_max = 0; for (i = 0; i != (*node)->type; ++i) { c2 = (*node)->sparse.children [i]; new_min = new_min < c2 ? new_min : c2; new_max = new_max > c2 ? new_max : c2; } new_min = new_min < *data ? new_min : *data; new_max = new_max > *data ? new_max : *data; /* Create a new mode, while keeping the old one for a while. */ old_node = *node; *node = (struct sp_trie_node*) sp_alloc (sizeof (struct sp_trie_node) + (new_max - new_min + 1) * sizeof (struct sp_trie_node*), "trie node"); assert (*node); /* Fill in the new node. */ (*node)->refcount = 0; (*node)->prefix_len = old_node->prefix_len; (*node)->type = SP_TRIE_DENSE_TYPE; memcpy ((*node)->prefix, old_node->prefix, old_node->prefix_len); (*node)->dense.min = new_min; (*node)->dense.max = new_max; (*node)->dense.nbr = old_node->type + 1; memset (*node + 1, 0, (new_max - new_min + 1) * sizeof (struct sp_trie_node*)); for (i = 0; i != old_node->type; ++i) *sp_node_child (*node, old_node->sparse.children [i] - new_min) = *sp_node_child (old_node, i); node = sp_node_next (*node, *data); ++data; --size; /* Get rid of the obsolete old node. */ sp_free (old_node); } /* Step 4 -- Create new nodes for remaining part of the subscription. */ step4: assert (!*node); while (1) { /* Create a new node to hold the next part of the subscription. */ more_nodes = size > SP_TRIE_PREFIX_MAX; *node = sp_alloc (sizeof (struct sp_trie_node) + (more_nodes ? sizeof (struct sp_trie_node*) : 0), "trie node"); assert (*node); /* Fill in the new node. */ (*node)->refcount = 0; (*node)->type = more_nodes ? 1 : 0; (*node)->prefix_len = size < (size_t) SP_TRIE_PREFIX_MAX ? size : (size_t) SP_TRIE_PREFIX_MAX; memcpy ((*node)->prefix, data, (*node)->prefix_len); data += (*node)->prefix_len; size -= (*node)->prefix_len; if (!more_nodes) break; (*node)->sparse.children [0] = *data; node = sp_node_child (*node, 0); ++data; --size; } /* Step 5 -- Create the subscription as such. */ step5: ++(*node)->refcount; /* Return 1 in case of a fresh subscription. */ return (*node)->refcount == 1 ? 1 : 0; }