From b1cff0c70842f385814beba8fa2157a4e03ddfce Mon Sep 17 00:00:00 2001 From: Can Gokmen Date: Tue, 21 Oct 2025 15:46:01 -0400 Subject: [PATCH 1/5] Implemented QuART_stail_reset --- trees/QuART_stail_reset.h | 445 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 trees/QuART_stail_reset.h diff --git a/trees/QuART_stail_reset.h b/trees/QuART_stail_reset.h new file mode 100644 index 0000000..9fb6b9e --- /dev/null +++ b/trees/QuART_stail_reset.h @@ -0,0 +1,445 @@ +#pragma once + +#include "../ART.h" +#include "../ArtNode.h" + +namespace ART { + +class QuART_stail_reset : public ART { + private: + int reset_counter; + + public: + QuART_stail_reset() : ART(), reset_counter(300) {} + + void insert(uint8_t key[], uintptr_t value) { + /* Check if we can tail insert */ + + ArtNode* root = this->root; + + // If the root is null (i = 0), we will insert and change fp since + // keys[0] = 1 in all cases + if (root == nullptr) { + QuART_stail_reset::insert_recursive_change_fp( + this->root, &this->root, key, 0, value, maxPrefixLength); + return; + } + + // Store leafValue, it will be used a lot + int leafValue = getLeafValue(this->fp_leaf); + + // For each byte in the key excluding the last byte, + // check if it matches the corresponding byte in the leaf value + for (size_t i = 0; i < maxPrefixLength - 1; i++) { + // Find the byte of leaf in the i'th position (i = [0, 1, 2]) + uint8_t leafByte = + (leafValue >> (8 * (maxPrefixLength - 1 - i))) & 0xFF; + + // If the bytes match, continue to next byte + if (key[i] == leafByte) { + continue; + } + // If the key byte is less than the leaf value, we do insert without + // tracking the path, as this will never be the new fp path. We + // only update the current fp information if it changes. + else if (key[i] < leafByte) { + QuART_stail_reset::insert_recursive_preserve_fp( + this->root, &this->root, key, 0, value, maxPrefixLength); + return; + } + // Key byte is greater than leaf byte + else { + // If we are at the first byte + if (i == 0) { + // If the key is a bridge value, change fp + if ((key[0] == leafByte + 1) && (key[1] == 0) && + (key[2] == 0) && ((leafValue >> 8 * 2) & 0xFF) == 255 && + ((leafValue >> 8) & 0xFF) == 255) { + this->fp_path = {this->root}; + this->fp_path_length = 1; + QuART_stail_reset::insert_recursive_change_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + // If it is not a bridge value and counter ended, force fp change + else if (this->reset_counter == 0) { + this->reset_counter = 300; // reset counter + this->insert_recursive_change_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + // If it is not a bridge value, insert without changing + else { + QuART_stail_reset::insert_recursive_preserve_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + } + // If we are at the second byte + else if (i == 1) { + // If the key is a bridge value, change fp + if ((key[1] == leafByte + 1) && (key[2] == 0) && + (key[0] == ((leafValue >> 8 * 3) & 0xFF)) && + ((leafValue >> 8) & 0xFF) == 255) { + this->fp_path = {this->root}; + this->fp_path_length = 1; + QuART_stail_reset::insert_recursive_change_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + // If it is not a bridge value and counter ended, force fp change + else if (this->reset_counter == 0) { + this->reset_counter = 300; // reset counter + this->insert_recursive_change_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + // If it is not a bridge value, insert without changing + else { + QuART_stail_reset::insert_recursive_preserve_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + } + // If we are at the third byte + else { + // If the key is a bridge value, change fp + if ((key[2] == leafByte + 1) && + (key[0] == ((leafValue >> 8 * 3) & 0xFF)) && + (key[1] == ((leafValue >> 8 * 2) & 0xFF))) { + this->fp_path = {this->root}; + this->fp_path_length = 1; + QuART_stail_reset::insert_recursive_change_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + // If it is not a bridge value and counter ended, force fp change + else if (this->reset_counter == 0) { + this->reset_counter = 300; // reset counter + this->insert_recursive_change_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + // If it is not a bridge value, insert without changing + else { + QuART_stail_reset::insert_recursive_preserve_fp( + this->root, &this->root, key, 0, value, + maxPrefixLength); + return; + } + } + } + } + + /* If the algorithm reaches here, it means that fp insert will happen */ + + // If depth is at maxPrefixLength - 1, we do not need to worry about + // leaf expansion of prefix mismatch, we can directly insert the new + // leaf into fp node + if (this->fp_depth == maxPrefixLength - 1) { + // Insert leaf into fp + ArtNode* newNode = makeLeaf(value); + switch (this->fp->type) { + case NodeType4: + static_cast(this->fp)->stailInsertNode4PreserveFp( + this, this->fp_ref, key[fp_depth], newNode); + break; + case NodeType16: + static_cast(this->fp)->stailInsertNode16PreserveFp( + this, this->fp_ref, key[fp_depth], newNode); + break; + case NodeType48: + static_cast(this->fp)->stailInsertNode48PreserveFp( + this, this->fp_ref, key[fp_depth], newNode); + break; + case NodeType256: + static_cast(this->fp)->insertNode256( + this, this->fp_ref, key[fp_depth], newNode); + break; + } + return; + } + // Else, we call the recursive function and let it handle leaf expansion + // or prefix mismatch if there is one. If not, it will directly insert + // into the fp + else { + QuART_stail_reset::insert_recursive_preserve_fp( + this->fp, this->fp_ref, key, fp_depth, value, maxPrefixLength); + return; + } + } + + private: + /* Recursive insert function that does NOT change fp_leaf value */ + void insert_recursive_preserve_fp(ArtNode* node, ArtNode** nodeRef, + uint8_t key[], unsigned depth, + uintptr_t value, unsigned maxKeyLength) { + // If leaf expansion is needed + if (isLeaf(node)) { + // Replace leaf with Node4 and store both leaves in it + uint8_t existingKey[maxKeyLength]; + loadKey(getLeafValue(node), existingKey); + unsigned newPrefixLength = 0; + while (existingKey[depth + newPrefixLength] == + key[depth + newPrefixLength]) + newPrefixLength++; + + Node4* newNode = new Node4(); + newNode->prefixLength = newPrefixLength; + memcpy(newNode->prefix, key + depth, + min(newPrefixLength, maxPrefixLength)); + *nodeRef = newNode; + + // If the changing node was the fp + if (this->fp_leaf == node) { + // If fp is not null (used only to avoid second insert) + if (!isLeaf(this->fp)) { + this->fp_depth += fp->prefixLength; + this->fp_depth++; + } + // Adjust fp parameters + this->fp_path[this->fp_path_length] = newNode; + this->fp_path_length++; + this->fp = newNode; + this->fp_ref = nodeRef; + } + + newNode->insertNode4(this, nodeRef, + existingKey[depth + newPrefixLength], node); + newNode->insertNode4(this, nodeRef, key[depth + newPrefixLength], + makeLeaf(value)); + return; + } + + // Handle prefix of inner node + if (node->prefixLength) { + unsigned mismatchPos = + prefixMismatch(node, key, depth, maxKeyLength); + if (mismatchPos != node->prefixLength) { + // Prefix differs, create new node + Node4* newNode = new Node4(); + *nodeRef = newNode; + newNode->prefixLength = mismatchPos; + memcpy(newNode->prefix, node->prefix, + min(mismatchPos, maxPrefixLength)); + // Break up prefix + if (node->prefixLength < maxPrefixLength) { + // If the nodes that being changed is in fp_path + auto it = std::find(fp_path.begin(), + fp_path.begin() + fp_path_length, node); + if (it != fp_path.begin() + fp_path_length) { + // Find the position of node in fp_path + size_t pos = std::distance(fp_path.begin(), it); + // Shift the elements to the right to make space for + std::copy_backward( + fp_path.begin() + pos, + fp_path.begin() + fp_path_length, + fp_path.begin() + fp_path_length + 1); + // Insert newNode in the position of node + fp_path[pos] = newNode; + fp_path_length++; + // If the changing node was the fp + if (node == this->fp) { + // Adjust fp_depth + this->fp_depth += newNode->prefixLength; + this->fp_depth++; + } + } + newNode->stailInsertNode4PreserveFpPrefixExpansion( + this, nodeRef, node->prefix[mismatchPos], node); + node->prefixLength -= (mismatchPos + 1); + memmove(node->prefix, node->prefix + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } else { + node->prefixLength -= (mismatchPos + 1); + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + // If the nodes that being changed is in fp_path + auto it = std::find(fp_path.begin(), + fp_path.begin() + fp_path_length, node); + if (it != fp_path.begin() + fp_path_length) { + // Find the position of node in fp_path + size_t pos = std::distance(fp_path.begin(), it); + // Shift the elements to the right to make space for + std::copy_backward( + fp_path.begin() + pos, + fp_path.begin() + fp_path_length, + fp_path.begin() + fp_path_length + 1); + // Insert newNode in the position of node + fp_path[pos] = newNode; + fp_path_length++; + // If the changing node was the fp + if (node == this->fp) { + // Adjust fp_depth + this->fp_depth += newNode->prefixLength; + this->fp_depth++; + } + } + newNode->stailInsertNode4PreserveFpPrefixExpansion( + this, nodeRef, minKey[depth + mismatchPos], node); + memmove(node->prefix, minKey + depth + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } + newNode->insertNode4(this, nodeRef, key[depth + mismatchPos], + makeLeaf(value)); + return; + } + depth += node->prefixLength; + } + + // Recurse + ArtNode** child = findChild(node, key[depth]); + if (*child) { + insert_recursive_preserve_fp(*child, child, key, depth + 1, value, + maxKeyLength); + return; + } + + // Insert leaf into inner node + ArtNode* newNode = makeLeaf(value); + switch (node->type) { + case NodeType4: + static_cast(node)->stailInsertNode4PreserveFp( + this, nodeRef, key[depth], newNode); + break; + case NodeType16: + static_cast(node)->stailInsertNode16PreserveFp( + this, nodeRef, key[depth], newNode); + break; + case NodeType48: + static_cast(node)->stailInsertNode48PreserveFp( + this, nodeRef, key[depth], newNode); + break; + case NodeType256: + static_cast(node)->insertNode256(this, nodeRef, + key[depth], newNode); + break; + } + } + + /* Recursive insert function that changes fp_leaf value */ + void insert_recursive_change_fp(ArtNode* node, ArtNode** nodeRef, + uint8_t key[], unsigned depth, + uintptr_t value, unsigned maxKeyLength) { + // Insert the leaf + if (node == NULL) { + *nodeRef = makeLeaf(value); + // Adjust fp parameters + this->fp_leaf = *nodeRef; + this->fp = *nodeRef; + this->fp_ref = nodeRef; + this->fp_depth = 0; + return; + } + + // If leaf expansion is needed + if (isLeaf(node)) { + // Replace leaf with Node4 and store both leaves in it + uint8_t existingKey[maxKeyLength]; + loadKey(getLeafValue(node), existingKey); + unsigned newPrefixLength = 0; + while (existingKey[depth + newPrefixLength] == + key[depth + newPrefixLength]) + newPrefixLength++; + + Node4* newNode = new Node4(); + newNode->prefixLength = newPrefixLength; + memcpy(newNode->prefix, key + depth, + min(newPrefixLength, maxPrefixLength)); + *nodeRef = newNode; + + // Adjust fp parameters + this->fp_path[this->fp_path_length - 1] = newNode; + this->fp_depth = depth + newPrefixLength; + + newNode->insertNode4(this, nodeRef, + existingKey[depth + newPrefixLength], node); + newNode->stailInsertNode4ChangeFp( + this, nodeRef, key[depth + newPrefixLength], makeLeaf(value)); + return; + } + + // Handle prefix of inner node + if (node->prefixLength) { + unsigned mismatchPos = + prefixMismatch(node, key, depth, maxKeyLength); + if (mismatchPos != node->prefixLength) { + // Prefix differs, create new node + Node4* newNode = new Node4(); + *nodeRef = newNode; + newNode->prefixLength = mismatchPos; + memcpy(newNode->prefix, node->prefix, + min(mismatchPos, maxPrefixLength)); + // Break up prefix + if (node->prefixLength < maxPrefixLength) { + // In all cases, newNode should be added to fp_path + fp_path[fp_path_length - 1] = newNode; + newNode->insertNode4(this, nodeRef, + node->prefix[mismatchPos], node); + node->prefixLength -= (mismatchPos + 1); + memmove(node->prefix, node->prefix + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } else { + node->prefixLength -= (mismatchPos + 1); + uint8_t minKey[maxKeyLength]; + loadKey(getLeafValue(minimum(node)), minKey); + // In all cases, newNode should be added to fp_path + fp_path[fp_path_length - 1] = newNode; + newNode->insertNode4(this, nodeRef, + minKey[depth + mismatchPos], node); + memmove(node->prefix, minKey + depth + mismatchPos + 1, + min(node->prefixLength, maxPrefixLength)); + } + // Adjust fp_depth + this->fp_depth = depth; + newNode->stailInsertNode4ChangeFp( + this, nodeRef, key[depth + mismatchPos], makeLeaf(value)); + return; + } + depth += node->prefixLength; + } + + // Recurse + ArtNode** child = findChild(node, key[depth]); + if (*child) { + fp_path[fp_path_length] = + *child; // add the node to the array before recursion + fp_path_length++; // increase the size of the array + insert_recursive_change_fp(*child, child, key, depth + 1, value, + maxKeyLength); + return; + } + + // Insert leaf into inner node + ArtNode* newNode = makeLeaf(value); + this->fp_depth = depth - node->prefixLength; + switch (node->type) { + case NodeType4: + static_cast(node)->stailInsertNode4ChangeFp( + this, nodeRef, key[depth], newNode); + break; + case NodeType16: + static_cast(node)->stailInsertNode16ChangeFp( + this, nodeRef, key[depth], newNode); + break; + case NodeType48: + static_cast(node)->stailInsertNode48ChangeFp( + this, nodeRef, key[depth], newNode); + break; + case NodeType256: + static_cast(node)->stailInsertNode256ChangeFp( + this, nodeRef, key[depth], newNode); + break; + } + } +}; + +} // namespace ART \ No newline at end of file From 2ed0cc9cfca880aeba20729d2819db5fafe13d44 Mon Sep 17 00:00:00 2001 From: Can Gokmen Date: Tue, 21 Oct 2025 15:50:14 -0400 Subject: [PATCH 2/5] Made stail_reset a child of stail --- trees/QuART_stail.h | 2 +- trees/QuART_stail_reset.h | 287 ++------------------------------------ 2 files changed, 13 insertions(+), 276 deletions(-) diff --git a/trees/QuART_stail.h b/trees/QuART_stail.h index 3047e99..b4de200 100644 --- a/trees/QuART_stail.h +++ b/trees/QuART_stail.h @@ -150,7 +150,7 @@ class QuART_stail : public ART { } } - private: + protected: /* Recursive insert function that does NOT change fp_leaf value */ void insert_recursive_preserve_fp(ArtNode* node, ArtNode** nodeRef, uint8_t key[], unsigned depth, diff --git a/trees/QuART_stail_reset.h b/trees/QuART_stail_reset.h index 9fb6b9e..69f878c 100644 --- a/trees/QuART_stail_reset.h +++ b/trees/QuART_stail_reset.h @@ -2,15 +2,16 @@ #include "../ART.h" #include "../ArtNode.h" +#include "trees/QuART_stail.h" namespace ART { -class QuART_stail_reset : public ART { +class QuART_stail_reset : public QuART_stail { private: int reset_counter; public: - QuART_stail_reset() : ART(), reset_counter(300) {} + QuART_stail_reset() : QuART_stail(), reset_counter(300) {} void insert(uint8_t key[], uintptr_t value) { /* Check if we can tail insert */ @@ -20,7 +21,7 @@ class QuART_stail_reset : public ART { // If the root is null (i = 0), we will insert and change fp since // keys[0] = 1 in all cases if (root == nullptr) { - QuART_stail_reset::insert_recursive_change_fp( + QuART_stail::insert_recursive_change_fp( this->root, &this->root, key, 0, value, maxPrefixLength); return; } @@ -43,7 +44,7 @@ class QuART_stail_reset : public ART { // tracking the path, as this will never be the new fp path. We // only update the current fp information if it changes. else if (key[i] < leafByte) { - QuART_stail_reset::insert_recursive_preserve_fp( + QuART_stail::insert_recursive_preserve_fp( this->root, &this->root, key, 0, value, maxPrefixLength); return; } @@ -57,7 +58,7 @@ class QuART_stail_reset : public ART { ((leafValue >> 8) & 0xFF) == 255) { this->fp_path = {this->root}; this->fp_path_length = 1; - QuART_stail_reset::insert_recursive_change_fp( + QuART_stail::insert_recursive_change_fp( this->root, &this->root, key, 0, value, maxPrefixLength); return; @@ -72,7 +73,7 @@ class QuART_stail_reset : public ART { } // If it is not a bridge value, insert without changing else { - QuART_stail_reset::insert_recursive_preserve_fp( + QuART_stail::insert_recursive_preserve_fp( this->root, &this->root, key, 0, value, maxPrefixLength); return; @@ -86,7 +87,7 @@ class QuART_stail_reset : public ART { ((leafValue >> 8) & 0xFF) == 255) { this->fp_path = {this->root}; this->fp_path_length = 1; - QuART_stail_reset::insert_recursive_change_fp( + QuART_stail::insert_recursive_change_fp( this->root, &this->root, key, 0, value, maxPrefixLength); return; @@ -101,7 +102,7 @@ class QuART_stail_reset : public ART { } // If it is not a bridge value, insert without changing else { - QuART_stail_reset::insert_recursive_preserve_fp( + QuART_stail::insert_recursive_preserve_fp( this->root, &this->root, key, 0, value, maxPrefixLength); return; @@ -115,7 +116,7 @@ class QuART_stail_reset : public ART { (key[1] == ((leafValue >> 8 * 2) & 0xFF))) { this->fp_path = {this->root}; this->fp_path_length = 1; - QuART_stail_reset::insert_recursive_change_fp( + QuART_stail::insert_recursive_change_fp( this->root, &this->root, key, 0, value, maxPrefixLength); return; @@ -130,7 +131,7 @@ class QuART_stail_reset : public ART { } // If it is not a bridge value, insert without changing else { - QuART_stail_reset::insert_recursive_preserve_fp( + QuART_stail::insert_recursive_preserve_fp( this->root, &this->root, key, 0, value, maxPrefixLength); return; @@ -171,275 +172,11 @@ class QuART_stail_reset : public ART { // or prefix mismatch if there is one. If not, it will directly insert // into the fp else { - QuART_stail_reset::insert_recursive_preserve_fp( + QuART_stail::insert_recursive_preserve_fp( this->fp, this->fp_ref, key, fp_depth, value, maxPrefixLength); return; } } - - private: - /* Recursive insert function that does NOT change fp_leaf value */ - void insert_recursive_preserve_fp(ArtNode* node, ArtNode** nodeRef, - uint8_t key[], unsigned depth, - uintptr_t value, unsigned maxKeyLength) { - // If leaf expansion is needed - if (isLeaf(node)) { - // Replace leaf with Node4 and store both leaves in it - uint8_t existingKey[maxKeyLength]; - loadKey(getLeafValue(node), existingKey); - unsigned newPrefixLength = 0; - while (existingKey[depth + newPrefixLength] == - key[depth + newPrefixLength]) - newPrefixLength++; - - Node4* newNode = new Node4(); - newNode->prefixLength = newPrefixLength; - memcpy(newNode->prefix, key + depth, - min(newPrefixLength, maxPrefixLength)); - *nodeRef = newNode; - - // If the changing node was the fp - if (this->fp_leaf == node) { - // If fp is not null (used only to avoid second insert) - if (!isLeaf(this->fp)) { - this->fp_depth += fp->prefixLength; - this->fp_depth++; - } - // Adjust fp parameters - this->fp_path[this->fp_path_length] = newNode; - this->fp_path_length++; - this->fp = newNode; - this->fp_ref = nodeRef; - } - - newNode->insertNode4(this, nodeRef, - existingKey[depth + newPrefixLength], node); - newNode->insertNode4(this, nodeRef, key[depth + newPrefixLength], - makeLeaf(value)); - return; - } - - // Handle prefix of inner node - if (node->prefixLength) { - unsigned mismatchPos = - prefixMismatch(node, key, depth, maxKeyLength); - if (mismatchPos != node->prefixLength) { - // Prefix differs, create new node - Node4* newNode = new Node4(); - *nodeRef = newNode; - newNode->prefixLength = mismatchPos; - memcpy(newNode->prefix, node->prefix, - min(mismatchPos, maxPrefixLength)); - // Break up prefix - if (node->prefixLength < maxPrefixLength) { - // If the nodes that being changed is in fp_path - auto it = std::find(fp_path.begin(), - fp_path.begin() + fp_path_length, node); - if (it != fp_path.begin() + fp_path_length) { - // Find the position of node in fp_path - size_t pos = std::distance(fp_path.begin(), it); - // Shift the elements to the right to make space for - std::copy_backward( - fp_path.begin() + pos, - fp_path.begin() + fp_path_length, - fp_path.begin() + fp_path_length + 1); - // Insert newNode in the position of node - fp_path[pos] = newNode; - fp_path_length++; - // If the changing node was the fp - if (node == this->fp) { - // Adjust fp_depth - this->fp_depth += newNode->prefixLength; - this->fp_depth++; - } - } - newNode->stailInsertNode4PreserveFpPrefixExpansion( - this, nodeRef, node->prefix[mismatchPos], node); - node->prefixLength -= (mismatchPos + 1); - memmove(node->prefix, node->prefix + mismatchPos + 1, - min(node->prefixLength, maxPrefixLength)); - } else { - node->prefixLength -= (mismatchPos + 1); - uint8_t minKey[maxKeyLength]; - loadKey(getLeafValue(minimum(node)), minKey); - // If the nodes that being changed is in fp_path - auto it = std::find(fp_path.begin(), - fp_path.begin() + fp_path_length, node); - if (it != fp_path.begin() + fp_path_length) { - // Find the position of node in fp_path - size_t pos = std::distance(fp_path.begin(), it); - // Shift the elements to the right to make space for - std::copy_backward( - fp_path.begin() + pos, - fp_path.begin() + fp_path_length, - fp_path.begin() + fp_path_length + 1); - // Insert newNode in the position of node - fp_path[pos] = newNode; - fp_path_length++; - // If the changing node was the fp - if (node == this->fp) { - // Adjust fp_depth - this->fp_depth += newNode->prefixLength; - this->fp_depth++; - } - } - newNode->stailInsertNode4PreserveFpPrefixExpansion( - this, nodeRef, minKey[depth + mismatchPos], node); - memmove(node->prefix, minKey + depth + mismatchPos + 1, - min(node->prefixLength, maxPrefixLength)); - } - newNode->insertNode4(this, nodeRef, key[depth + mismatchPos], - makeLeaf(value)); - return; - } - depth += node->prefixLength; - } - - // Recurse - ArtNode** child = findChild(node, key[depth]); - if (*child) { - insert_recursive_preserve_fp(*child, child, key, depth + 1, value, - maxKeyLength); - return; - } - - // Insert leaf into inner node - ArtNode* newNode = makeLeaf(value); - switch (node->type) { - case NodeType4: - static_cast(node)->stailInsertNode4PreserveFp( - this, nodeRef, key[depth], newNode); - break; - case NodeType16: - static_cast(node)->stailInsertNode16PreserveFp( - this, nodeRef, key[depth], newNode); - break; - case NodeType48: - static_cast(node)->stailInsertNode48PreserveFp( - this, nodeRef, key[depth], newNode); - break; - case NodeType256: - static_cast(node)->insertNode256(this, nodeRef, - key[depth], newNode); - break; - } - } - - /* Recursive insert function that changes fp_leaf value */ - void insert_recursive_change_fp(ArtNode* node, ArtNode** nodeRef, - uint8_t key[], unsigned depth, - uintptr_t value, unsigned maxKeyLength) { - // Insert the leaf - if (node == NULL) { - *nodeRef = makeLeaf(value); - // Adjust fp parameters - this->fp_leaf = *nodeRef; - this->fp = *nodeRef; - this->fp_ref = nodeRef; - this->fp_depth = 0; - return; - } - - // If leaf expansion is needed - if (isLeaf(node)) { - // Replace leaf with Node4 and store both leaves in it - uint8_t existingKey[maxKeyLength]; - loadKey(getLeafValue(node), existingKey); - unsigned newPrefixLength = 0; - while (existingKey[depth + newPrefixLength] == - key[depth + newPrefixLength]) - newPrefixLength++; - - Node4* newNode = new Node4(); - newNode->prefixLength = newPrefixLength; - memcpy(newNode->prefix, key + depth, - min(newPrefixLength, maxPrefixLength)); - *nodeRef = newNode; - - // Adjust fp parameters - this->fp_path[this->fp_path_length - 1] = newNode; - this->fp_depth = depth + newPrefixLength; - - newNode->insertNode4(this, nodeRef, - existingKey[depth + newPrefixLength], node); - newNode->stailInsertNode4ChangeFp( - this, nodeRef, key[depth + newPrefixLength], makeLeaf(value)); - return; - } - - // Handle prefix of inner node - if (node->prefixLength) { - unsigned mismatchPos = - prefixMismatch(node, key, depth, maxKeyLength); - if (mismatchPos != node->prefixLength) { - // Prefix differs, create new node - Node4* newNode = new Node4(); - *nodeRef = newNode; - newNode->prefixLength = mismatchPos; - memcpy(newNode->prefix, node->prefix, - min(mismatchPos, maxPrefixLength)); - // Break up prefix - if (node->prefixLength < maxPrefixLength) { - // In all cases, newNode should be added to fp_path - fp_path[fp_path_length - 1] = newNode; - newNode->insertNode4(this, nodeRef, - node->prefix[mismatchPos], node); - node->prefixLength -= (mismatchPos + 1); - memmove(node->prefix, node->prefix + mismatchPos + 1, - min(node->prefixLength, maxPrefixLength)); - } else { - node->prefixLength -= (mismatchPos + 1); - uint8_t minKey[maxKeyLength]; - loadKey(getLeafValue(minimum(node)), minKey); - // In all cases, newNode should be added to fp_path - fp_path[fp_path_length - 1] = newNode; - newNode->insertNode4(this, nodeRef, - minKey[depth + mismatchPos], node); - memmove(node->prefix, minKey + depth + mismatchPos + 1, - min(node->prefixLength, maxPrefixLength)); - } - // Adjust fp_depth - this->fp_depth = depth; - newNode->stailInsertNode4ChangeFp( - this, nodeRef, key[depth + mismatchPos], makeLeaf(value)); - return; - } - depth += node->prefixLength; - } - - // Recurse - ArtNode** child = findChild(node, key[depth]); - if (*child) { - fp_path[fp_path_length] = - *child; // add the node to the array before recursion - fp_path_length++; // increase the size of the array - insert_recursive_change_fp(*child, child, key, depth + 1, value, - maxKeyLength); - return; - } - - // Insert leaf into inner node - ArtNode* newNode = makeLeaf(value); - this->fp_depth = depth - node->prefixLength; - switch (node->type) { - case NodeType4: - static_cast(node)->stailInsertNode4ChangeFp( - this, nodeRef, key[depth], newNode); - break; - case NodeType16: - static_cast(node)->stailInsertNode16ChangeFp( - this, nodeRef, key[depth], newNode); - break; - case NodeType48: - static_cast(node)->stailInsertNode48ChangeFp( - this, nodeRef, key[depth], newNode); - break; - case NodeType256: - static_cast(node)->stailInsertNode256ChangeFp( - this, nodeRef, key[depth], newNode); - break; - } - } }; } // namespace ART \ No newline at end of file From c04270686513770ff1e45689329bbc409044c822 Mon Sep 17 00:00:00 2001 From: Can Gokmen Date: Tue, 21 Oct 2025 15:57:11 -0400 Subject: [PATCH 3/5] Added stail_reset as a tree option in run.cpp --- run.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/run.cpp b/run.cpp index f7746a6..2f73b08 100644 --- a/run.cpp +++ b/run.cpp @@ -12,6 +12,7 @@ #include "trees/QuART_tail.h" #include "trees/QuART_stail.h" #include "trees/QuART_lil_can.h" +#include "trees/QuART_stail_reset.h" using namespace std; @@ -267,6 +268,48 @@ int main(int argc, char** argv) { cout << "Query time: " << query_time << " ns" << endl; } + // Output the times in csv format, including tree type + cout << insertion_time << "," << query_time << endl; + } else if (tree_type == "QuART_stail_reset") { + ART::QuART_stail_reset* tree = new ART::QuART_stail_reset(); + long long insertion_time = 0; + for (uint64_t i = 0; i < N; i++) { + uint8_t key[4]; + ART::loadKey(keys[i], key); + auto start = chrono::high_resolution_clock::now(); + tree->insert(key, keys[i]); + auto stop = chrono::high_resolution_clock::now(); + auto duration = + chrono::duration_cast(stop - start); + insertion_time += duration.count(); + } + + if (verbose) { + cout << "Tree type: " << tree_type << endl; + cout << "Insertion time: " << insertion_time << " ns" << endl; + } + + srand(time(0)); + + long long query_time = 0; + for (uint64_t i = 0; i < (N / 100); i++) { + int random = rand() % (maxval - minval + 1) + minval; + uint8_t key[4]; + ART::loadKey(keys[random], key); + auto start = chrono::high_resolution_clock::now(); + ART::ArtNode* leaf = tree->lookup(key); + auto stop = chrono::high_resolution_clock::now(); + auto duration = + chrono::duration_cast(stop - start); + query_time += duration.count(); + assert(ART::isLeaf(leaf) && + ART::getLeafValue(leaf) == keys[random]); + } + + if (verbose) { + cout << "Query time: " << query_time << " ns" << endl; + } + // Output the times in csv format, including tree type cout << insertion_time << "," << query_time << endl; } From f96e183513dacc8f4f69a8bde634205f689fd281 Mon Sep 17 00:00:00 2001 From: Can Gokmen Date: Tue, 21 Oct 2025 15:59:49 -0400 Subject: [PATCH 4/5] Resolved build error --- trees/QuART_stail_reset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trees/QuART_stail_reset.h b/trees/QuART_stail_reset.h index 69f878c..5e66889 100644 --- a/trees/QuART_stail_reset.h +++ b/trees/QuART_stail_reset.h @@ -2,7 +2,7 @@ #include "../ART.h" #include "../ArtNode.h" -#include "trees/QuART_stail.h" +#include "QuART_stail.h" namespace ART { From 21d2894f17be7d17a7c5a208008492198386d8ba Mon Sep 17 00:00:00 2001 From: Can Gokmen Date: Tue, 21 Oct 2025 16:06:56 -0400 Subject: [PATCH 5/5] Fixed error in counter implementation, wasn't decreasing --- trees/QuART_stail_reset.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/trees/QuART_stail_reset.h b/trees/QuART_stail_reset.h index 5e66889..27eb52f 100644 --- a/trees/QuART_stail_reset.h +++ b/trees/QuART_stail_reset.h @@ -73,6 +73,7 @@ class QuART_stail_reset : public QuART_stail { } // If it is not a bridge value, insert without changing else { + this->reset_counter--; // decrement counter QuART_stail::insert_recursive_preserve_fp( this->root, &this->root, key, 0, value, maxPrefixLength); @@ -102,6 +103,7 @@ class QuART_stail_reset : public QuART_stail { } // If it is not a bridge value, insert without changing else { + this->reset_counter--; // decrement counter QuART_stail::insert_recursive_preserve_fp( this->root, &this->root, key, 0, value, maxPrefixLength); @@ -131,6 +133,7 @@ class QuART_stail_reset : public QuART_stail { } // If it is not a bridge value, insert without changing else { + this->reset_counter--; // decrement counter QuART_stail::insert_recursive_preserve_fp( this->root, &this->root, key, 0, value, maxPrefixLength);