diff --git a/VersionedMap.cpp b/VersionedMap.cpp index 17b6276..3229b78 100644 --- a/VersionedMap.cpp +++ b/VersionedMap.cpp @@ -615,9 +615,10 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { // If `val` is set, then this is a point mutation at `latestVersion`. // Otherwise it's the end of a range mutation at `latestVersion`. - void insert(Key key, std::optional val) { - Finger finger; + // `finger` becomes the search path of `key` + void insert(Key key, std::optional val, Finger &finger) { bool ignored; + finger.clear(); finger.push(latestRoot, ignored); bool inserted; @@ -674,7 +675,7 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { } // Prepare new node - uint32_t node = newNode( + const uint32_t node = newNode( pointVersion, rangeVersion, key.p, key.len, val->p, val->len, inserted ? gRandom.next() : mm.base[finger.backNode()].entry->priority); if (!inserted) { @@ -684,9 +685,34 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { n.pointer[1] = child(finger.backNode(), true, latestVersion); } + finger.backNodeRef() = node; + uint32_t oldSize = finger.searchPathSize(); - // Rotate and propagate up the search path + if (inserted) { + // Rotate + for (;;) { + const uint32_t node = finger.backNode(); + oldSize = finger.searchPathSize(); + if (finger.searchPathSize() == 1) { + // Made it to the root + latestRoot = node; + break; + } + const bool direction = finger.backDirection(); + finger.pop(); + auto &parent = finger.backNodeRef(); + parent = update(parent, direction, node, latestVersion); + if (mm.base[node].entry->priority > mm.base[parent].entry->priority) { + rotate(parent, latestVersion, !direction); + } else { + break; + } + } + } + + // Propagate for (;;) { + const uint32_t node = finger.backNode(); if (finger.searchPathSize() == 1) { // Made it to the root latestRoot = node; @@ -694,18 +720,24 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { } const bool direction = finger.backDirection(); finger.pop(); - auto parent = finger.backNode(); + const auto old = finger.backNode(); + auto &parent = finger.backNodeRef(); parent = update(parent, direction, node, latestVersion); - if (inserted && - mm.base[node].entry->priority > mm.base[parent].entry->priority) { - rotate(parent, latestVersion, !direction); - } else { - if (parent == finger.backNode()) { - break; - } + if (parent == old) { + break; } - node = parent; } + + finger.setSearchPathSizeUnsafe(oldSize); + +#ifndef NDEBUG + { + Finger expected; + search(key, latestRoot, latestVersion, + expected); + assert(finger == expected); + } +#endif } // Removes `finger` from the tree, and leaves `finger` pointing to insertion @@ -861,23 +893,23 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { // TODO Improve ILP? for (int i = 0; i < numMutations; ++i) { const auto &m = mutations[i]; + Finger iter; switch (m.type) { case Set: { - insert({m.param1, m.param1Len}, {{m.param2, m.param2Len}}); + insert({m.param1, m.param1Len}, {{m.param2, m.param2Len}}, iter); } break; case Clear: { - insert({m.param1, m.param1Len}, {{nullptr, -1}}); + insert({m.param1, m.param1Len}, {{nullptr, -1}}, iter); if (m.param2Len > 0) { - Finger iter; - search({m.param1, m.param1Len}, latestRoot, - latestVersion, iter); move(iter, latestVersion); while (iter.searchPathSize() > 0 && mm.base[iter.backNode()] < Key{m.param2, m.param2Len}) { remove(iter); move(iter, latestVersion); } - insert({m.param2, m.param2Len}, {}); + // TODO reuse finger? It should be one rank away from its insertion + // point + insert({m.param2, m.param2Len}, {}, iter); } } break; default: // GCOVR_EXCL_LINE