Last active
July 8, 2025 02:06
-
-
Save mistymntncop/3fd09e83d6df306c6a2a2bead05ab2cd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <stdbool.h> | |
| #include <malloc.h> | |
| #include <assert.h> | |
| //Partially Persistent Left-Leaning Red-Black Tree | |
| //Implemented using Zippers | |
| //https://sedgewick.io/wp-content/themes/sedgewick/papers/2008LLRB.pdf | |
| //Just pedagogical. No proper memory managagement here, would use Arenas in practice | |
| // cl -Zi -Od /INCREMENTAL:NO zipper.c | |
| enum { | |
| LEFT = 0, | |
| RIGHT = 1, | |
| NODE_CHILD_COUNT = 2, | |
| }; | |
| typedef enum Color Color; | |
| enum Color { | |
| RED = 1, | |
| BLACK = 0, | |
| }; | |
| typedef struct Node Node; | |
| struct Node { | |
| uint64_t key; | |
| uint64_t value; | |
| Color color; | |
| uint32_t ref_count; | |
| Node *children[NODE_CHILD_COUNT]; | |
| }; | |
| typedef struct Tree Tree; | |
| struct Tree { | |
| Node **roots; | |
| uint32_t roots_count; | |
| //Arena *roots_arena; | |
| //Arena *nodes_arena; | |
| //Node **free_list; | |
| }; | |
| typedef struct ZipperNode ZipperNode; | |
| struct ZipperNode { | |
| Node *parent; | |
| uint32_t child_index; | |
| bool dirty; | |
| bool dont_release; | |
| ZipperNode *prev; | |
| }; | |
| typedef struct Zipper Zipper; | |
| struct Zipper { | |
| bool dirty; | |
| bool dont_release; | |
| //context | |
| Node *focus; | |
| ZipperNode *path; | |
| uint32_t path_count; | |
| }; | |
| static Node *node_acquire(Node *node) { | |
| if(node == 0) return 0; | |
| node->ref_count++; | |
| return node; | |
| } | |
| static void node_release(Node *node) { | |
| if(node == 0) return; | |
| assert(node->ref_count != 0); | |
| if(--node->ref_count == 0) { | |
| for(uint32_t i = 0; i < NODE_CHILD_COUNT; i++) { | |
| node_release(node->children[i]); | |
| } | |
| free(node); | |
| } | |
| } | |
| static Node *node_clone(Node *src) { | |
| Node *node = calloc(1, sizeof(Node)); | |
| node->key = src->key; | |
| node->value = src->value; | |
| node->color = src->color; | |
| node->ref_count = 1; | |
| for(uint32_t i = 0; i < NODE_CHILD_COUNT; i++) { | |
| node->children[i] = node_acquire(src->children[i]); | |
| } | |
| return node; | |
| } | |
| static bool is_red(Node *node) { | |
| bool result = node && node->color == RED; | |
| return result; | |
| } | |
| static void go_down(Zipper *zipper, uint32_t child_index) { | |
| Node *current = zipper->focus; | |
| bool dirty = false; | |
| assert(child_index < NODE_CHILD_COUNT); | |
| Node *child = current ? current->children[child_index] : 0; | |
| if(zipper->dirty && child && child->ref_count == 1) { | |
| dirty = true; | |
| } | |
| ZipperNode *path_node = calloc(1, sizeof(ZipperNode)); | |
| path_node->parent = current; | |
| path_node->child_index = child_index; | |
| path_node->dirty = zipper->dirty; | |
| path_node->dont_release = zipper->dont_release; | |
| path_node->prev = zipper->path; | |
| zipper->path = path_node; | |
| zipper->path_count++; | |
| zipper->focus = child; | |
| zipper->dirty = dirty; | |
| zipper->dont_release = false; | |
| } | |
| static void go_up(Zipper *zipper) { | |
| if(zipper->path != 0) { | |
| ZipperNode *path_node = zipper->path; | |
| zipper->path = path_node->prev; | |
| zipper->path_count--; | |
| Node *current = zipper->focus; | |
| Node *parent = path_node->parent; | |
| Node *new_focus = parent; | |
| uint32_t child_index = path_node->child_index; | |
| bool dirty = path_node->dirty; | |
| Node *original_child = parent ? parent->children[child_index] : 0; | |
| if(original_child != current) { | |
| if(zipper->dirty && !dirty) { | |
| Node *new_parent = node_clone(parent); | |
| new_focus = new_parent; | |
| dirty = true; | |
| } | |
| new_focus->children[child_index] = current; | |
| if(!zipper->dont_release) { | |
| node_release(original_child); | |
| } | |
| } | |
| zipper->focus = new_focus; | |
| zipper->dirty = dirty; | |
| zipper->dont_release = path_node->dont_release; | |
| free(path_node); | |
| } | |
| } | |
| static Node *commit(Zipper *zipper) { | |
| while(zipper->path != 0) { | |
| go_up(zipper); | |
| } | |
| Node *root = zipper->focus; | |
| return root; | |
| } | |
| static Node *mutable_focus(Zipper *zipper) { | |
| Node *node = zipper->focus; | |
| if(!zipper->dirty) { | |
| node = node_clone(node); | |
| zipper->focus = node; | |
| zipper->dirty = true; | |
| } | |
| return node; | |
| } | |
| static void update_child(Zipper *zipper, uint32_t child_index, Node *child, bool dont_release) { | |
| Node *node = mutable_focus(zipper); | |
| Node *original_child = node->children[child_index]; | |
| node->children[child_index] = child; | |
| if(!dont_release) { | |
| node_release(original_child); | |
| } | |
| } | |
| static void update_color(Zipper *zipper, Color color) { | |
| Node *node = mutable_focus(zipper); | |
| node->color = color; | |
| } | |
| static void update(Zipper *zipper, uint64_t value) { | |
| Node *node = mutable_focus(zipper); | |
| node->value = value; | |
| } | |
| static void rotate_left(Zipper *zipper) { | |
| Node *h = mutable_focus(zipper); | |
| Color color = h->color; | |
| Node *x = h->children[RIGHT]; | |
| update_child(zipper, RIGHT, x->children[LEFT], true); | |
| update_color(zipper, RED); | |
| zipper->focus = x; | |
| zipper->dirty = x->ref_count == 1; | |
| zipper->dont_release = true; | |
| update_child(zipper, LEFT, h, true); | |
| update_color(zipper, color); | |
| } | |
| static void rotate_right(Zipper *zipper) { | |
| Node *h = mutable_focus(zipper); | |
| Color color = h->color; | |
| Node *x = h->children[LEFT]; | |
| update_child(zipper, LEFT, x->children[RIGHT], true); | |
| update_color(zipper, RED); | |
| zipper->focus = x; | |
| zipper->dirty = x->ref_count == 1; | |
| zipper->dont_release = true; | |
| update_child(zipper, RIGHT, h, true); | |
| update_color(zipper, color); | |
| } | |
| static void flip_color(Zipper *zipper) { | |
| go_down(zipper, LEFT); | |
| update_color(zipper, !zipper->focus->color); | |
| go_up(zipper); | |
| go_down(zipper, RIGHT); | |
| update_color(zipper, !zipper->focus->color); | |
| go_up(zipper); | |
| update_color(zipper, !zipper->focus->color); | |
| } | |
| static bool insert(Zipper *zipper, uint64_t key, uint64_t value) { | |
| bool insert_node = true; | |
| while(zipper->focus != 0) { | |
| Node *node = zipper->focus; | |
| if(is_red(node->children[LEFT]) && is_red(node->children[RIGHT])) { | |
| flip_color(zipper); | |
| } | |
| if(key < node->key) { | |
| go_down(zipper, LEFT); | |
| } else if(key > node->key) { | |
| go_down(zipper, RIGHT); | |
| } else { | |
| update(zipper, value); | |
| insert_node = false; | |
| break; | |
| } | |
| } | |
| if(insert_node) { | |
| Node *new_node = calloc(1, sizeof(Node)); | |
| new_node->key = key; | |
| new_node->value = value; | |
| new_node->color = RED; | |
| new_node->ref_count = 1; | |
| zipper->focus = new_node; | |
| zipper->dirty = true; | |
| } | |
| while(zipper->path != 0) { | |
| go_up(zipper); | |
| Node *node = zipper->focus; | |
| //balancing | |
| if(is_red(node->children[RIGHT]) && !is_red(node->children[LEFT])) { | |
| rotate_left(zipper); | |
| node = zipper->focus; | |
| } | |
| if(is_red(node->children[LEFT]) && is_red(node->children[LEFT]->children[LEFT])) { | |
| rotate_right(zipper); | |
| node = zipper->focus; | |
| } | |
| } | |
| update_color(zipper, BLACK); | |
| return insert_node; | |
| } | |
| static Zipper init_zipper(Node *root) { | |
| Zipper zipper = { .focus = root }; | |
| return zipper; | |
| } | |
| static void print_rb_tree_recurse(Node *node) { | |
| printf("\tn_%lld_%p [label=\"%lld\\nrc: %d\"];\n", node->key, node, node->key, node->ref_count); | |
| for(uint32_t i = 0; i < NODE_CHILD_COUNT; i++) { | |
| Node *child = node->children[i]; | |
| if(child) { | |
| char *color = child->color == RED ? "red" : "black"; | |
| uint32_t penwidth = child->color == RED ? 5 : 1; | |
| printf("\t" "n_%lld_%p -> n_%lld_%p [color=\"%s\", penwidth=%d, dir=none];\n", | |
| node->key, node, child->key, child, color, penwidth ); | |
| print_rb_tree_recurse(child); | |
| } | |
| } | |
| } | |
| static void print_rb_tree(Node *node) { | |
| printf("strict digraph BST {\n"); | |
| print_rb_tree_recurse(node); | |
| printf("}\n"); | |
| } | |
| int main() { | |
| Zipper zipper = init_zipper(0); | |
| for(uint32_t i = 0; i < 10; i++) { | |
| insert(&zipper, i, i); | |
| } | |
| Node *root = commit(&zipper); | |
| zipper = init_zipper(root); | |
| for(uint32_t i = 10; i < 20; i++) { | |
| insert(&zipper, i, i); | |
| } | |
| Node *root2 = commit(&zipper); | |
| printf("strict digraph BST {\n"); | |
| print_rb_tree_recurse(root); | |
| print_rb_tree_recurse(root2); | |
| printf("}\n"); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment