Skip to content

Commit

Permalink
README: update algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
billziss-gh committed Jun 19, 2023
1 parent 2b08de6 commit 5369135
Showing 1 changed file with 73 additions and 75 deletions.
148 changes: 73 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,68 +141,75 @@ The _assign_ algorithm finds the slot that is mapped to an _x_ value, or inserts
```C
imap_slot_t *imap_assign(imap_node_t *tree, imap_u64_t x)
{
imap_slot_t *slotstack[16 + 1];
imap_u32_t posnstack[16 + 1];
imap_u32_t stackp, stacki;
imap_node_t *newnode, *node = tree; // (1)
imap_slot_t *slot;
imap_u32_t newmark, sval, diff, posn = 16, dirn = 0; // (1)
imap_u64_t prfx;
stackp = 0;
for (;;)
{
slot = &node->vec32[dirn]; // (2)
sval = *slot; // (2)
slotstack[stackp] = slot, posnstack[stackp++] = posn; // (2)
if (!(sval & imap__slot_node__)) // (3)
{
if (0 == posn)
{ // (3.1)
IMAP_ASSERT(imap__node_prefix__(node) == (x & ~0xfull));
return slot; // (3.1)
prfx = imap__node_prefix__(node); // (3.1)
if (0 == posn && prfx == (x & ~0xfull)) // (3.2)
return slot; // (3.2)
diff = imap__xpos__(prfx ^ x); // (3.3)
for (stacki = stackp; diff > posn;) // (3.4)
posn = posnstack[--stacki]; // (3.4)
if (stacki != stackp) // (3.5)
{
slot = slotstack[stacki];
sval = *slot;
IMAP_ASSERT(sval & imap__slot_node__);
newmark = imap__alloc_node__(tree); // (3.5.1)
*slot = (*slot & imap__slot_pmask__) | imap__slot_node__ | newmark; // (3.5.1)
newnode = imap__node__(tree, newmark);
*newnode = imap__node_zero__;
newmark = imap__alloc_node__(tree); // (3.5.2)
newnode->vec32[imap__xdir__(prfx, diff)] = sval; // (3.5.3)
newnode->vec32[imap__xdir__(x, diff)] = imap__slot_node__ | newmark;// (3.5.3)
imap__node_setprefix__(newnode, imap__xpfx__(prfx, diff) | diff); // (3.5.4)
}
newmark = imap__alloc_node__(tree); // (3.2)
*slot = (*slot & imap__slot_pmask__) | imap__slot_node__ | newmark; // (3.2)
break; // (3.3)
else
{
newmark = imap__alloc_node__(tree); // (3.6)
*slot = (*slot & imap__slot_pmask__) | imap__slot_node__ | newmark; // (3.6)
}
newnode = imap__node__(tree, newmark); // (3.7)
*newnode = imap__node_zero__; // (3.7)
imap__node_setprefix__(newnode, x & ~0xfull); // (3.7)
return &newnode->vec32[x & 0xfull]; // (3.8)
}
node = imap__node__(tree, sval & imap__slot_value__); // (4)
posn = imap__node_pos__(node); // (4)
dirn = imap__xdir__(x, posn); // (5)
prfx = imap__node_prefix__(node); // (6)
diff = imap__xpos__(prfx ^ x); // (6)
if (diff > posn) // (7)
{
newmark = imap__alloc_node__(tree); // (7.1)
*slot = (*slot & imap__slot_pmask__) | imap__slot_node__ | newmark; // (7.1)
newnode = imap__node__(tree, newmark);
newmark = imap__alloc_node__(tree); // (7.2)
*newnode = imap__node_zero__;
newnode->vec32[imap__xdir__(prfx, diff)] = sval; // (7.3)
newnode->vec32[imap__xdir__(x, diff)] = imap__slot_node__ | newmark;// (7.3)
imap__node_setprefix__(newnode, imap__xpfx__(prfx, diff) | diff); // (7.4)
break; // (7.5)
}
} // (8)
newnode = imap__node__(tree, newmark); // (9)
*newnode = imap__node_zero__; // (9)
imap__node_setprefix__(newnode, x & ~0xfull); // (9)
return &newnode->vec32[x & 0xfull]; // (10)
}
}
```

1. Set the current node to the root of the tree.
2. Given a direction (`dirn`) access the slot value from the current node.
2. Given a direction (`dirn`) access the slot value from the current node and record it in a stack.
3. If the slot value is not a pointer to another node, then:
1. If the node position (`posn`) is zero, then return the found slot.
2. Otherwise we must insert a new node with position _0_ to point to the _y_ value. Allocate space for the node and update the current slot to point to it.
3. Break out of the loop to complete the initialization of the new node.
1. Retrieve the node's prefix (`prfx`).
2. If the node position (`posn`) is zero and if the node prefix matches the _x_ value then return the found slot.
3. Compute the XOR difference of the node's prefix and the _x_ value (`diff`).
4. Find the topmost (from the tree root) stack slot where the XOR difference is greater than the corresponding position.
5. If such a slot is found, then:
1. We must insert a new node with position equal to the XOR difference. Allocate space for the node and update the slot to point to it. Let us call this node N<sub>D</sub>.
2. We must also insert a new node with position _0_ to point to the _y_ value. Allocate space for that node as well. Let us call this node N<sub>0</sub>. Initialization of node N<sub>0</sub> will complete outside the if/else block.
3. Initialize node N<sub>D</sub> so that one slot points to the current node and one slot points to node N<sub>0</sub>.
4. Set the node N<sub>D</sub> prefix and position.
6. Otherwise, if no such slot is found, then insert a new node with position _0_ to point to the _y_ value.
7. Initialize new node with position _0_ and set its prefix from the _x_ value.
8. Return the correct slot from the new node.
4. Compute the new current node and retrieve its position (`posn`).
5. Compute the direction at the node's position from the _x_ value.
6. Retrieve the node's prefix (`prfx`) and compute the XOR difference of the node's prefix and the _x_ value (`diff`).
7. If the XOR difference is greater than the current node's position, then:
1. We must insert a new node **above** the current node with position equal to the XOR difference. Allocate space for the node and update the current slot to point to it. Let us call this node N<sub>D</sub>.
2. We must also insert a new node with position _0_ to point to the _y_ value. Allocate space for that node as well. Let us call this node N<sub>0</sub>.
3. Initialize node N<sub>D</sub> so that one slot points to the current node and one slot points to node N<sub>0</sub>.
4. Set the node N<sub>D</sub> prefix and position.
5. Break out of the loop to complete the initialization of node N<sub>0</sub>.
8. Loop back to (2).
9. Initialize new node with position _0_ and set its prefix from the _x_ value.
10. Return the correct slot from the new node.

The _assign_ algorithm may insert 0, 1 or 2 new nodes. It works similarly to _lookup_ and attempts to find the slot that should be mapped to the _x_ value. If the node that contains this slot already exists, then _assign_ simply returns the appropriate slot within that node so that it can be filled ("assigned") with the _y_ value.

Expand All @@ -229,67 +236,58 @@ The _remove_ algorithm deletes the _y_ value from the slot that is mapped to an
```C
void imap_remove(imap_node_t *tree, imap_u64_t x)
{
imap_iter_t iterdata, *iter = &iterdata;
imap_slot_t *slotstack[16 + 1];
imap_u32_t stackp;
imap_node_t *node = tree; // (1)
imap_slot_t *slot;
imap_u32_t sval, pval, posn = 16, dirn = 0; // (1)
iter->stackp = 0;
stackp = 0;
for (;;)
{
slot = &node->vec32[dirn]; // (2)
sval = *slot; // (2)
slotstack[stackp++] = slot; // (2)
if (!(sval & imap__slot_node__)) // (3)
{
if ((sval & imap__slot_value__) && imap__node_prefix__(node) == (x & ~0xfull))
{ // (3.1)
IMAP_ASSERT(0 == posn); // (3.1)
IMAP_ASSERT(0 == posn);
imap_delval(tree, slot); // (3.1)
}
break; // (3.2)
--stackp; // (3.2)
while (stackp) // (3.3)
{
slot = slotstack[--stackp]; // (3.3.1)
sval = *slot; // (3.3.1)
node = imap__node__(tree, sval & imap__slot_value__); // (3.3.2)
posn = imap__node_pos__(node); // (3.3.2)
if (!!posn != imap__node_popcnt__(node, &pval)) // (3.3.3)
break; // (3.3.3)
imap__free_node__(tree, sval & imap__slot_value__); // (3.3.4)
*slot = (sval & imap__slot_pmask__) | (pval & ~imap__slot_pmask__); // (3.3.5)
}
return;
}
node = imap__node__(tree, sval & imap__slot_value__); // (4)
posn = imap__node_pos__(node); // (4)
dirn = imap__xdir__(x, posn); // (5)
iter->stack[iter->stackp++] = (sval & imap__slot_value__) | dirn; // (6)
}
while (iter->stackp) // (7)
{
sval = iter->stack[--iter->stackp]; // (8)
node = imap__node__(tree, sval & imap__slot_value__); // (8)
posn = !!imap__node_pos__(node); // (9)
if (posn != imap__node_popcnt__(node, &pval)) // (10)
break; // (10)
imap__free_node__(tree, sval & imap__slot_value__); // (11)
if (!iter->stackp) // (12)
slot = &tree->vec32[0]; // (12)
else
{
sval = iter->stack[iter->stackp - 1]; // (13)
dirn = sval & 31; // (13)
node = imap__node__(tree, sval & imap__slot_value__); // (13)
slot = &node->vec32[dirn]; // (13)
}
*slot = (*slot & imap__slot_pmask__) | (pval & ~imap__slot_pmask__);// (14)
}
}
```
1. Set the current node to the root of the tree.
2. Given a direction (`dirn`) access the slot value from the current node.
2. Given a direction (`dirn`) access the slot value from the current node and record it in a stack.
3. If the slot value is not a pointer to another node, then:
1. If the slot has a value and if the node prefix matches the _x_ value then delete the _y_ value from the slot.
2. Break out of the loop to continue the removal of extraneous nodes.
2. Discard the latest stack entry that points to a value (rather than a node) slot.
3. Loop while the stack is not empty.
1. Pop a slot from the stack.
2. Compute a node from the slot and retrieve its position (`posn`).
3. Compare the node's "boolean" position (_0_ if the node's position is equal to _0_, _1_ if the node's position is not equal to _0_) to the node's population count (i.e. the number of non-empty slots). This test determines if a node with position _0_ has non-empty slots or if a node with position other than _0_ has two or more subnodes. In either case break the loop without removing any more nodes.
4. Deallocate the node.
5. Update the slot.
4. Compute the new current node and retrieve its position (`posn`).
5. Compute the direction at the node's position from the _x_ value.
6. Store the current node pointer and current direction in a stack and loop back to (2).
7. Loop while the stack is not empty.
8. Pop a node from the stack.
9. Compute the node's "boolean" position (_0_ if the node's position is equal to _0_, _1_ if the node's position is not equal to _0_).
10. Compare the node's "boolean" position to the node's population count (i.e. the number of non-empty slots). This test determines if a node with position _0_ has non-empty slots or if a node with position other than _0_ has two or more subnodes. In either case break the loop without removing any more nodes.
11. Deallocate the node.
12. If we are at the top of the stack we update the `slot` variable to point to the pointer to the root node in the header node so that we can update the tree root in the next executed statement.
13. Otherwise we peek at the top of the stack to determine the node that needs to be updated so that it no longer points to the removed node. We then update the `slot` variable to point to the appropriate slot within that node.
14. Update the slot and loop back to 7.
The _remove_ algorithm may remove 0, 1 or 2 nodes. It works similarly to _lookup_ and attempts to find the slot that should be mapped to the _x_ value. If it finds such a slot then it deletes the value in it. If the position _0_ node that contained the slot has other non-empty slots, then _remove_ completes.
Expand Down

0 comments on commit 5369135

Please sign in to comment.