begin_node: if (assoc_array_ptr_is_shortcut(cursor)) { /* Descend through a shortcut */
shortcut = assoc_array_ptr_to_shortcut(cursor);
cursor = READ_ONCE(shortcut->next_node); /* Address dependency. */
}
node = assoc_array_ptr_to_node(cursor);
slot = 0;
/* We perform two passes of each node. * *Thefirstpassdoesalltheleavesinthisnode.Thismeanswe *don'tmissanyleavesifthenodeissplitupbyinsertionwhilst *we'reiteratingoverthebranchesrootedhere(wemay,however,see *someleavestwice).
*/
has_meta = 0; for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
has_meta |= (unsignedlong)ptr; if (ptr && assoc_array_ptr_is_leaf(ptr)) { /* We need a barrier between the read of the pointer, *whichissuppliedbytheaboveREAD_ONCE().
*/ /* Invoke the callback */
ret = iterator(assoc_array_ptr_to_leaf(ptr),
iterator_data); if (ret) return ret;
}
}
/* The second pass attends to all the metadata pointers. If we follow *oneofthesewemayfindthatwedon'tcomebackhere,butrathergo *backtoareplacementnodewiththeleavesinadifferentlayout. * *Weareguaranteedtomakeprogress,however,astheslotnumberfor *aparticularportionofthekeyspacecannotchange-andwe *continueatthebackpointer+1.
*/ if (!(has_meta & ASSOC_ARRAY_PTR_META_TYPE)) goto finished_node;
slot = 0;
finished_node: /* Move up to the parent (may need to skip back over a shortcut) */
parent = READ_ONCE(node->back_pointer); /* Address dependency. */
slot = node->parent_slot; if (parent == stop) return0;
struct assoc_array_walk_result { struct { struct assoc_array_node *node; /* Node in which leaf might be found */ int level; int slot;
} terminal_node; struct { struct assoc_array_shortcut *shortcut; int level; int sc_level; unsignedlong sc_segments; unsignedlong dissimilarity;
} wrong_shortcut;
};
/* Use segments from the key for the new leaf to navigate through the *internaltree,skippingthroughnodesandshortcutsthatareon *routetothedestination.Eventuallywe'llcometoaslotthatis *eitheremptyorcontainsaleafatwhichpointwe'vefoundanodein *whichtheleafwe'relookingformightbefoundorintowhichit *shouldbeinserted.
*/
jumped:
segments = ops->get_key_chunk(index_key, level);
pr_devel("segments[%d]: %lx\n", level, segments);
if (assoc_array_ptr_is_shortcut(cursor)) goto follow_shortcut;
if (!assoc_array_ptr_is_meta(ptr)) { /* The node doesn't have a node/shortcut pointer in the slot *correspondingtotheindexkeythatwehavetofollow.
*/
result->terminal_node.node = node;
result->terminal_node.level = level;
result->terminal_node.slot = slot;
pr_devel("<--%s() = terminal_node\n", __func__); return assoc_array_walk_found_terminal_node;
}
if (assoc_array_ptr_is_node(ptr)) { /* There is a pointer to a node in the slot corresponding to *thisindexkeysegment,soweneedtofollowit.
*/
cursor = ptr;
level += ASSOC_ARRAY_LEVEL_STEP; if ((level & ASSOC_ARRAY_KEY_CHUNK_MASK) != 0) goto consider_node; goto jumped;
}
/* There is a shortcut in the slot corresponding to the index key *segment.Wefollowtheshortcutifitspartialindexkeymatches *thisleaf's.Otherwiseweneedtosplittheshortcut.
*/
cursor = ptr;
follow_shortcut:
shortcut = assoc_array_ptr_to_shortcut(cursor);
pr_devel("shortcut to %d\n", shortcut->skip_to_level);
sc_level = level + ASSOC_ARRAY_LEVEL_STEP;
BUG_ON(sc_level > shortcut->skip_to_level);
do { /* Check the leaf against the shortcut's index key a word at a *time,trimmingthefinalword(theshortcutstorestheindex *keycompletelyfromtheroottotheshortcut'starget).
*/ if ((sc_level & ASSOC_ARRAY_KEY_CHUNK_MASK) == 0)
segments = ops->get_key_chunk(index_key, sc_level);
if (assoc_array_walk(array, ops, index_key, &result) !=
assoc_array_walk_found_terminal_node) return NULL;
node = result.terminal_node.node;
/* If the target key is available to us, it's has to be pointed to by *theterminalnode.
*/ for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */ if (ptr && assoc_array_ptr_is_leaf(ptr)) { /* We need a barrier between the read of the pointer *anddereferencingthepointer-butonlyifweare *actuallygoingtodereferenceit.
*/
leaf = assoc_array_ptr_to_leaf(ptr); if (ops->compare_object(leaf, index_key)) return (void *)leaf;
}
}
/* Move back up to the parent (may need to free a shortcut on
* the way up) */ if (assoc_array_ptr_is_shortcut(parent)) {
shortcut = assoc_array_ptr_to_shortcut(parent);
BUG_ON(shortcut->next_node != cursor);
cursor = parent;
parent = shortcut->back_pointer;
slot = shortcut->parent_slot;
pr_devel("free shortcut\n");
kfree(shortcut); if (!parent) return;
BUG_ON(!assoc_array_ptr_is_node(parent));
}
/* Ascend to next slot in parent node */
pr_devel("ascend to %p[%d]\n", parent, slot);
cursor = parent;
node = assoc_array_ptr_to_node(cursor);
slot++; goto continue_node;
}
/* We arrived at a node which doesn't have an onward node or shortcut *pointerthatwehavetofollow.Thismeansthat(a)theleafwe *wantmustgohere(eitherbyinsertionorreplacement)or(b)we *needtosplitthisnodeandinsertinoneofthefragments.
*/
free_slot = -1;
/* Firstly, we have to check the leaves in this node to see if there's *amatchingoneweshouldreplaceinplace.
*/ for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
ptr = node->slots[i]; if (!ptr) {
free_slot = i; continue;
} if (assoc_array_ptr_is_leaf(ptr) &&
ops->compare_object(assoc_array_ptr_to_leaf(ptr),
index_key)) {
pr_devel("replace in slot %d\n", i);
edit->leaf_p = &node->slots[i];
edit->dead_leaf = node->slots[i];
pr_devel("<--%s() = ok [replace]\n", __func__); returntrue;
}
}
/* If there is a free slot in this node then we can just insert the *leafhere.
*/ if (free_slot >= 0) {
pr_devel("insert in free slot %d\n", free_slot);
edit->leaf_p = &node->slots[free_slot];
edit->adjust_count_on = node;
pr_devel("<--%s() = ok [insert]\n", __func__); returntrue;
}
/* The node has no spare slots - so we're either going to have to split *itorinsertanothernodebeforeit. * *Whatever,we'regoingtoneedatleasttwonewnodes-soallocate *thosenow.Wemayalsoneedanewshortcut,butwedealwiththat *whenweneedit.
*/
new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); if (!new_n0) returnfalse;
edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
new_n1 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); if (!new_n1) returnfalse;
edit->new_meta[1] = assoc_array_node_to_ptr(new_n1);
/* We need to find out how similar the leaves are. */
pr_devel("no spare slots\n");
have_meta = false; for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
ptr = node->slots[i]; if (assoc_array_ptr_is_meta(ptr)) {
edit->segment_cache[i] = 0xff;
have_meta = true; continue;
}
base_seg = ops->get_object_key_chunk(
assoc_array_ptr_to_leaf(ptr), level);
base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
}
if (have_meta) {
pr_devel("have meta\n"); goto split_node;
}
/* The node contains only leaves */
dissimilarity = 0;
base_seg = edit->segment_cache[0]; for (i = 1; i < ASSOC_ARRAY_FAN_OUT; i++)
dissimilarity |= edit->segment_cache[i] ^ base_seg;
if ((dissimilarity & ASSOC_ARRAY_FAN_MASK) == 0) { /* The old leaves all cluster in the same slot. We will need *toinsertashortcutifthenewnodewantstoclusterwiththem.
*/ if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0) goto all_leaves_cluster_together;
/* Otherwise all the old leaves cluster in the same slot, but *thenewleafwantstogointoadifferentslot-sowe *createanewnode(n0)toholdthenewleafandapointerto *anewnode(n1)holdingalltheoldleaves. * *Thiscanbedonebyfallingthroughtothenodesplitting *path.
*/
pr_devel("present leaves cluster but not new leaf\n");
}
split_node:
pr_devel("split node\n");
/* We need to split the current node. The node must contain anything *fromasingleleaf(intheoneleafcase,thisleafwillcluster *withthenewleaf)andtherestmeta-pointers,toallleaves,some *ofwhichmaycluster. * *Itwon'tcontainthecaseinwhichallthecurrentleavesplusthe *newleaveswanttoclusterinthesameslot. * *Weneedtoexpelatleasttwoleavesoutofasetconsistingofthe *leavesinthenodeandthenewleaf.Thecurrentmetapointerscan *justbecopiedastheyshouldn'tclusterwithanyoftheleaves. * *Weneedanewnode(n0)toreplacethecurrentoneandanewnodeto *taketheexpellednodes(n1).
*/
edit->set[0].to = assoc_array_node_to_ptr(new_n0);
new_n0->back_pointer = node->back_pointer;
new_n0->parent_slot = node->parent_slot;
new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
new_n1->parent_slot = -1; /* Need to calculate this */
/* Begin by finding two matching leaves. There have to be at least two *thatmatch-eveniftherearemetapointers-becauseanyleafthat *wouldmatchaslotwithametapointerinitmustbesomewhere *behindthatmetapointerandcannotbehere.Further,givenN *remainingleafslots,wenowhaveN+1leavestogointhem.
*/ for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
slot = edit->segment_cache[i]; if (slot != 0xff) for (j = i + 1; j < ASSOC_ARRAY_FAN_OUT + 1; j++) if (edit->segment_cache[j] == slot) goto found_slot_for_multiple_occupancy;
}
found_slot_for_multiple_occupancy:
pr_devel("same slot: %x %x [%02x]\n", i, j, slot);
BUG_ON(i >= ASSOC_ARRAY_FAN_OUT);
BUG_ON(j >= ASSOC_ARRAY_FAN_OUT + 1);
BUG_ON(slot >= ASSOC_ARRAY_FAN_OUT);
new_n1->parent_slot = slot;
/* Metadata pointers cannot change slot */ for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) if (assoc_array_ptr_is_meta(node->slots[i]))
new_n0->slots[i] = node->slots[i]; else
new_n0->slots[i] = NULL;
BUG_ON(new_n0->slots[slot] != NULL);
new_n0->slots[slot] = assoc_array_node_to_ptr(new_n1);
/* Filter the leaf pointers between the new nodes */
free_slot = -1;
next_slot = 0; for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { if (assoc_array_ptr_is_meta(node->slots[i])) continue; if (edit->segment_cache[i] == slot) {
new_n1->slots[next_slot++] = node->slots[i];
new_n1->nr_leaves_on_branch++;
} else { do {
free_slot++;
} while (new_n0->slots[free_slot] != NULL);
new_n0->slots[free_slot] = node->slots[i];
}
}
all_leaves_cluster_together: /* All the leaves, new and old, want to cluster together in this node *inthesameslot,sowehavetoreplacethisnodewithashortcutto *skipovertheidenticalpartsofthekeyandthenplaceapairof *nodes,oneinsidetheother,attheendoftheshortcutand *distributethekeysbetweenthem. * *Firstlyweneedtoworkoutwheretheleavesstartdivergingasa *bitpositionintotheirkeyssothatweknowhowbigtheshortcut *needstobe. * *WeonlyneedtomakeasinglepassofNoftheN+1leavesbecauseif *anykeysdifferbetweenthemselvesatbitXthenatleastoneof *themmustalsodifferwiththebasekeyatbitXorbefore.
*/
pr_devel("all leaves cluster together\n");
diff = INT_MAX; for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) { int x = ops->diff_objects(assoc_array_ptr_to_leaf(node->slots[i]),
index_key); if (x < diff) {
BUG_ON(x < 0);
diff = x;
}
}
BUG_ON(diff == INT_MAX);
BUG_ON(diff < level + ASSOC_ARRAY_LEVEL_STEP);
/* This now reduces to a node splitting exercise for which we'll need *toregeneratethedisparitytable.
*/ for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
ptr = node->slots[i];
base_seg = ops->get_object_key_chunk(assoc_array_ptr_to_leaf(ptr),
level);
base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
}
/* We need to split a shortcut and insert a node between the two *pieces.Zero-lengthpieceswillbedispensedwithentirely. * *Firstofall,weneedtofindoutinwhichlevelthefirst *differencewas.
*/
diff = __ffs(dissimilarity);
diff &= ~ASSOC_ARRAY_LEVEL_STEP_MASK;
diff += sc_level & ~ASSOC_ARRAY_KEY_CHUNK_MASK;
pr_devel("diff=%d\n", diff);
/* Create a new node now since we're going to need it anyway */
new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); if (!new_n0) returnfalse;
edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
edit->adjust_count_on = new_n0;
/* Insert a new shortcut before the new node if this segment isn't of *zerolength-otherwisewejustconnectthenewnodedirectlytothe *parent.
*/
level += ASSOC_ARRAY_LEVEL_STEP; if (diff > level) {
pr_devel("pre-shortcut %d...%d\n", level, diff);
keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
side = assoc_array_ptr_to_node(shortcut->next_node);
new_n0->nr_leaves_on_branch = side->nr_leaves_on_branch;
/* We need to know which slot in the new node is going to take a *metadatapointer.
*/
sc_slot = sc_segments >> (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
sc_slot &= ASSOC_ARRAY_FAN_MASK;
/* Determine whether we need to follow the new node with a replacement *forthecurrentshortcut.Wecouldintheoryreusethecurrent *shortcutifitsparentslotnumberdoesn'tchange-butthat'sa *1-in-16chancesonotworthexpendingthecodeupon.
*/
level = diff + ASSOC_ARRAY_LEVEL_STEP; if (level < shortcut->skip_to_level) {
pr_devel("post-shortcut %d...%d\n", level, shortcut->skip_to_level);
keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
/* We don't have to replace the pointed-to node as long as we *usememorybarrierstomakesuretheparentslotnumberis *changedbeforethebackpointer(theparentslotnumberis *irrelevanttotheoldparentshortcut).
*/
new_n0->slots[sc_slot] = shortcut->next_node;
edit->set_parent_slot[0].p = &side->parent_slot;
edit->set_parent_slot[0].to = sc_slot;
edit->set[1].ptr = &side->back_pointer;
edit->set[1].to = assoc_array_node_to_ptr(new_n0);
}
/* Install the new leaf in a spare slot in the new node. */ if (sc_slot == 0)
edit->leaf_p = &new_n0->slots[1]; else
edit->leaf_p = &new_n0->slots[0];
pr_devel("<--%s() = ok [split shortcut]\n", __func__); returntrue;
}
/* The leaf pointer we're given must not have the bottom bit set as we *usethosefortype-markingthepointer.NULLpointersarealsonot *allowedastheyindicateanemptyslotbutwehavetoallowthem *hereastheycanbeupdatedlater.
*/
BUG_ON(assoc_array_ptr_is_meta(object));
switch (assoc_array_walk(array, ops, index_key, &result)) { case assoc_array_walk_tree_empty: /* Allocate a root node if there isn't one yet */ if (!assoc_array_insert_in_empty_tree(edit)) goto enomem; return edit;
case assoc_array_walk_found_terminal_node: /* We found a node that doesn't have a node/shortcut pointer in *theslotcorrespondingtotheindexkeythatwehaveto *follow.
*/ if (!assoc_array_insert_into_terminal_node(edit, ops, index_key,
&result)) goto enomem; return edit;
case assoc_array_walk_found_wrong_shortcut: /* We found a shortcut that didn't match our key in a slot we *neededtofollow.
*/ if (!assoc_array_insert_mid_shortcut(edit, ops, &result)) goto enomem; return edit;
}
enomem: /* Clean up after an out of memory error */
pr_devel("enomem\n");
assoc_array_cancel_edit(edit); return ERR_PTR(-ENOMEM);
}
switch (assoc_array_walk(array, ops, index_key, &result)) { case assoc_array_walk_found_terminal_node: /* We found a node that should contain the leaf we've been *askedtoremove-*if*it'sinthetree.
*/
pr_devel("terminal_node\n");
node = result.terminal_node.node;
for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
ptr = node->slots[slot]; if (ptr &&
assoc_array_ptr_is_leaf(ptr) &&
ops->compare_object(assoc_array_ptr_to_leaf(ptr),
index_key)) goto found_leaf;
}
fallthrough; case assoc_array_walk_tree_empty: case assoc_array_walk_found_wrong_shortcut: default:
assoc_array_cancel_edit(edit);
pr_devel("not found\n"); return NULL;
}
/* In the simplest form of deletion we just clear the slot and release *theleafafterasuitableinterval.
*/
edit->dead_leaf = node->slots[slot];
edit->set[0].ptr = &node->slots[slot];
edit->set[0].to = NULL;
edit->adjust_count_on = node;
/* If that concludes erasure of the last leaf, then delete the entire *internalarray.
*/ if (array->nr_leaves_on_tree == 1) {
edit->set[1].ptr = &array->root;
edit->set[1].to = NULL;
edit->adjust_count_on = NULL;
edit->excised_subtree = array->root;
pr_devel("all gone\n"); return edit;
}
/* However, we'd also like to clear up some metadata blocks if we *possiblycan. * *Wegoforasimplealgorithmof:ifthisnodehasFAN_OUTorfewer *leavesinit,thenattempttocollapseit-andattemptto *recursivelycollapseupthetree. * *Wecouldalsotryandcollapseinpartiallyfilledsubtreestotake *upspaceinthisnode.
*/ if (node->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) { struct assoc_array_node *parent, *grandparent; struct assoc_array_ptr *ptr;
/* First of all, we need to know if this node has metadata so *thatwedon'ttrycollapsingifalltheleavesarealready *here.
*/
has_meta = false; for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
ptr = node->slots[i]; if (assoc_array_ptr_is_meta(ptr)) {
has_meta = true; break;
}
}
/* Look further up the tree to see if we can collapse this node *intoamoreproximalnodetoo.
*/
parent = node;
collapse_up:
pr_devel("collapse subtree: %ld\n", parent->nr_leaves_on_branch);
ptr = parent->back_pointer; if (!ptr) goto do_collapse; if (assoc_array_ptr_is_shortcut(ptr)) { struct assoc_array_shortcut *s = assoc_array_ptr_to_shortcut(ptr);
ptr = s->back_pointer; if (!ptr) goto do_collapse;
}
do_collapse: /* There's no point collapsing if the original node has no meta *pointerstodiscardandifwedidn'tmergeintooneofthat *node'sancestry.
*/ if (has_meta || parent != node) {
node = parent;
/* Create a new node to collapse into */
new_n0 = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); if (!new_n0) goto enomem;
edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
if (edit->dead_leaf)
edit->ops->free_object(assoc_array_ptr_to_leaf(edit->dead_leaf)); for (i = 0; i < ARRAY_SIZE(edit->excised_meta); i++) if (edit->excised_meta[i])
kfree(assoc_array_ptr_to_node(edit->excised_meta[i]));
smp_wmb(); if (edit->leaf_p)
*edit->leaf_p = edit->leaf;
smp_wmb(); for (i = 0; i < ARRAY_SIZE(edit->set_parent_slot); i++) if (edit->set_parent_slot[i].p)
*edit->set_parent_slot[i].p = edit->set_parent_slot[i].to;
smp_wmb(); for (i = 0; i < ARRAY_SIZE(edit->set_backpointers); i++) if (edit->set_backpointers[i])
*edit->set_backpointers[i] = edit->set_backpointers_to;
smp_wmb(); for (i = 0; i < ARRAY_SIZE(edit->set); i++) if (edit->set[i].ptr)
*edit->set[i].ptr = edit->set[i].to;
/* Clean up after an out of memory error */ for (i = 0; i < ARRAY_SIZE(edit->new_meta); i++) {
ptr = edit->new_meta[i]; if (ptr) { if (assoc_array_ptr_is_node(ptr))
kfree(assoc_array_ptr_to_node(ptr)); else
kfree(assoc_array_ptr_to_shortcut(ptr));
}
}
kfree(edit);
}
descend: /* If this point is a shortcut, then we need to duplicate it and *advancethetargetcursor.
*/ if (assoc_array_ptr_is_shortcut(cursor)) {
shortcut = assoc_array_ptr_to_shortcut(cursor);
keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
new_s = kmalloc(struct_size(new_s, index_key, keylen),
GFP_KERNEL); if (!new_s) goto enomem;
pr_devel("dup shortcut %p -> %p\n", shortcut, new_s);
memcpy(new_s, shortcut, struct_size(new_s, index_key, keylen));
new_s->back_pointer = new_parent;
new_s->parent_slot = shortcut->parent_slot;
*new_ptr_pp = new_parent = assoc_array_shortcut_to_ptr(new_s);
new_ptr_pp = &new_s->next_node;
cursor = shortcut->next_node;
}
/* Duplicate the node at this position */
node = assoc_array_ptr_to_node(cursor);
new_n = kzalloc(sizeof(struct assoc_array_node), GFP_KERNEL); if (!new_n) goto enomem;
pr_devel("dup node %p -> %p\n", node, new_n);
new_n->back_pointer = new_parent;
new_n->parent_slot = node->parent_slot;
*new_ptr_pp = new_parent = assoc_array_node_to_ptr(new_n);
new_ptr_pp = NULL;
slot = 0;
continue_node: /* Filter across any leaves and gc any subtrees */ for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
ptr = node->slots[slot]; if (!ptr) continue;
if (assoc_array_ptr_is_leaf(ptr)) { if (iterator(assoc_array_ptr_to_leaf(ptr),
iterator_data)) /* The iterator will have done any reference *countingontheobjectforus.
*/
new_n->slots[slot] = ptr; continue;
}
/* Count up the number of empty slots in this node and work out the *subtreeleafcount.
*/
new_n->nr_leaves_on_branch = 0;
nr_free = 0; for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
ptr = new_n->slots[slot]; if (!ptr)
nr_free++; elseif (assoc_array_ptr_is_leaf(ptr))
new_n->nr_leaves_on_branch++;
}
pr_devel("free=%d, leaves=%lu\n", nr_free, new_n->nr_leaves_on_branch);
/* See what we can fold in */
retained = false;
next_slot = 0; for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) { struct assoc_array_shortcut *s; struct assoc_array_node *child;
ptr = new_n->slots[slot]; if (!ptr || assoc_array_ptr_is_leaf(ptr)) continue;
s = NULL; if (assoc_array_ptr_is_shortcut(ptr)) {
s = assoc_array_ptr_to_shortcut(ptr);
ptr = s->next_node;
}
/* Excise this node if it is singly occupied by a shortcut */ if (nr_free == ASSOC_ARRAY_FAN_OUT - 1) { for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) if ((ptr = new_n->slots[slot])) break;
if (assoc_array_ptr_is_shortcut(new_parent)) { /* We can discard any preceding shortcut also */ struct assoc_array_shortcut *s =
assoc_array_ptr_to_shortcut(new_parent);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.567Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.
(vorverarbeitet am 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.