{"id":18016899,"url":"https://github.com/fitzgen/intrusive_splay_tree","last_synced_at":"2025-04-05T07:05:24.710Z","repository":{"id":31272768,"uuid":"127422562","full_name":"fitzgen/intrusive_splay_tree","owner":"fitzgen","description":"An intrusive splay tree implementation that is no-std compatible and free from allocation and moves","archived":false,"fork":false,"pushed_at":"2025-01-14T01:14:10.000Z","size":41,"stargazers_count":67,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-29T06:07:27.449Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fitzgen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-30T11:51:30.000Z","updated_at":"2025-01-14T01:14:14.000Z","dependencies_parsed_at":"2025-01-31T20:30:25.552Z","dependency_job_id":null,"html_url":"https://github.com/fitzgen/intrusive_splay_tree","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitzgen%2Fintrusive_splay_tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitzgen%2Fintrusive_splay_tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitzgen%2Fintrusive_splay_tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitzgen%2Fintrusive_splay_tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fitzgen","download_url":"https://codeload.github.com/fitzgen/intrusive_splay_tree/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247299832,"owners_count":20916190,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-30T04:19:38.353Z","updated_at":"2025-04-05T07:05:24.681Z","avatar_url":"https://github.com/fitzgen.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e\u003ccode\u003eintrusive_splay_tree\u003c/code\u003e\u003c/h1\u003e\n  \u003cp\u003e\u003cstrong\u003eAn intrusive, allocation-free splay tree implementation.\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003e\n    \u003ca href=\"https://docs.rs/intrusive_splay_tree/\"\u003e\u003cimg src=\"https://docs.rs/intrusive_splay_tree/badge.svg\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/intrusive_splay_tree\"\u003e\u003cimg src=\"https://img.shields.io/crates/v/intrusive_splay_tree.svg\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/intrusive_splay_tree\"\u003e\u003cimg src=\"https://img.shields.io/crates/d/intrusive_splay_tree.svg\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/fitzgen/intrusive_splay_tree/actions/workflows/rust.yml\"\u003e\u003cimg src=\"https://github.com/fitzgen/intrusive_splay_tree/actions/workflows/rust.yml/badge.svg\" /\u003e\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n[Splay trees] are self-adjusting, meaning that operating on an element (for\nexample, doing a `find` or an `insert`) rebalances the tree in such a way\nthat the element becomes the root. This means that subsequent operations on\nthat element are *O(1)* as long as no other element is operated on in the\nmeantime.\n\n### Implementation and Goals\n\n* **Intrusive:** The space for the subtree pointers is stored *inside* the\nelement type. In non-intrusive trees, we would have a node type that\ncontains the subtree pointers and either a pointer to the element or we\nwould move the element into the node. The intrusive design inverts the\nrelationship, so that the elements hold the subtree pointers within\nthemselves.\n\n* **Freedom from allocations and moves:** The intrusive design enables this\nimplementation to fully avoid both allocations and moving elements in\nmemory. Since the space for subtree pointers already exists in the element,\nno allocation is necessary, just a handful of pointer writes. Therefore,\nthis implementation can be used in constrained environments that don't have\naccess to an allocator (e.g. some embedded devices or within a signal\nhandler) and with types that can't move in memory (e.g. `pthread_mutex_t`).\n\n* **Small code size:** This implementation is geared towards small code\nsize, and uses trait objects internally to avoid the code bloat induced by\nmonomorphization. This implementation is suitable for targeting WebAssembly,\nwhere code is downloaded over the network, and code bloat delays Web page\nloading.\n\n* **Nodes do not have parent pointers**: An intrusive node is only two words\nin size: left and right sub tree pointers. There are no parent pointers,\nwhich would require another word of overhead. To meet this goal, the\nimplementation uses the \"top-down\" variant of splay trees.\n\n[Splay trees]: https://en.wikipedia.org/wiki/Splay_tree\n[paper]: http://www.cs.cmu.edu/~sleator/papers/self-adjusting.pdf\n\n### Constraints\n\n* **Elements within a tree must all have the same lifetime.** This means\nthat you must use something like the [`bumpalo`][arena] crate for\nallocation, or be working with static data, etc.\n\n* **Elements in an intrusive collections are inherently shared.** They are\nalways potentially aliased by the collection(s) they are in. In the other\ndirection, a particular intrusive collection only has a shared reference to\nthe element, since elements can both be in many intrusive collections at the\nsame time. Therefore, you cannot get a unique, mutable reference to an\nelement out of an intrusive splay tree. To work around this, you may need to\nliberally use interior mutability, for example by leveraging `Cell`,\n`RefCell`, and `Mutex`.\n\n[arena]: https://crates.io/crates/bumpalo\n\n### Example\n\nThis example defines a `Monster` type, where each of its instances live\nwithin two intrusive trees: one ordering monsters by their name, and the\nother ordering them by their health.\n\n```rust\nuse intrusive_splay_tree::{impl_intrusive_node, SplayTree};\n\nuse std::cmp::Ordering;\nuse std::marker::PhantomData;\n\n// We have a monster type, and we want to query monsters by both name and\n// health.\n#[derive(Debug)]\nstruct Monster\u003c'a\u003e {\n    name: String,\n    health: u64,\n\n    // An intrusive node so we can put monsters in a tree to query by name.\n    by_name_node: intrusive_splay_tree::Node\u003c'a\u003e,\n\n    // Another intrusive node so we can put monsters in a second tree (at\n    // the same time!) and query them by health.\n    by_health_node: intrusive_splay_tree::Node\u003c'a\u003e,\n}\n\n// Define a type for trees where monsters are ordered by name.\nstruct MonstersByName;\n\n// Implement `IntrusiveNode` for the `MonstersByName` tree, where the\n// element type is `Monster` and the field in `Monster` that has this tree's\n// intrusive node is `by_name`.\nimpl_intrusive_node! {\n    impl\u003c'a\u003e IntrusiveNode\u003c'a\u003e for MonstersByName\n    where\n        type Elem = Monster\u003c'a\u003e,\n        node = by_name_node;\n}\n\n// Define how to order `Monster`s within the `MonstersByName` tree by\n// implementing `TreeOrd`.\nimpl\u003c'a\u003e intrusive_splay_tree::TreeOrd\u003c'a, MonstersByName\u003e for Monster\u003c'a\u003e {\n    fn tree_cmp(\u0026self, rhs: \u0026Monster\u003c'a\u003e) -\u003e Ordering {\n        self.name.cmp(\u0026rhs.name)\n    }\n}\n\n// And do all the same things for trees where monsters are ordered by health...\nstruct MonstersByHealth;\nimpl_intrusive_node! {\n    impl\u003c'a\u003e IntrusiveNode\u003c'a\u003e for MonstersByHealth\n    where\n        type Elem = Monster\u003c'a\u003e,\n        node = by_health_node;\n}\nimpl\u003c'a\u003e intrusive_splay_tree::TreeOrd\u003c'a, MonstersByHealth\u003e for Monster\u003c'a\u003e {\n    fn tree_cmp(\u0026self, rhs: \u0026Monster\u003c'a\u003e) -\u003e Ordering {\n        self.health.cmp(\u0026rhs.health)\n    }\n}\n\n// We can also implement `TreeOrd` for other types, so that we can query the\n// tree by these types. For example, we want to query the `MonstersByHealth`\n// tree by some `u64` health value, and we want to query the `MonstersByName`\n// tree by some `\u0026str` name value.\n\nimpl\u003c'a\u003e intrusive_splay_tree::TreeOrd\u003c'a, MonstersByHealth\u003e for u64 {\n    fn tree_cmp(\u0026self, rhs: \u0026Monster\u003c'a\u003e) -\u003e Ordering {\n        self.cmp(\u0026rhs.health)\n    }\n}\n\nimpl\u003c'a\u003e intrusive_splay_tree::TreeOrd\u003c'a, MonstersByName\u003e for str {\n    fn tree_cmp(\u0026self, rhs: \u0026Monster\u003c'a\u003e) -\u003e Ordering {\n        self.cmp(\u0026rhs.name)\n    }\n}\n\nimpl\u003c'a\u003e Monster\u003c'a\u003e {\n    /// The `Monster` constructor allocates `Monster`s in a bump arena, and\n    /// inserts the new `Monster` in both trees.\n    pub fn new(\n        arena: \u0026'a bumpalo::Bump,\n        name: String,\n        health: u64,\n        by_name_tree: \u0026mut SplayTree\u003c'a, MonstersByName\u003e,\n        by_health_tree: \u0026mut SplayTree\u003c'a, MonstersByHealth\u003e\n    ) -\u003e \u0026'a Monster\u003c'a\u003e {\n        let monster = arena.alloc(Monster {\n            name,\n            health,\n            by_name_node: Default::default(),\n            by_health_node: Default::default(),\n        });\n\n        by_name_tree.insert(monster);\n        by_health_tree.insert(monster);\n\n        monster\n    }\n}\n\nfn main() {\n    // The arena that the monsters will live within.\n    let mut arena = bumpalo::Bump::new();\n\n    // The splay trees ordered by name and health respectively.\n    let mut by_name_tree = SplayTree::default();\n    let mut by_health_tree = SplayTree::default();\n\n    // Now let's create some monsters, inserting them into the trees!\n\n    Monster::new(\n        \u0026arena,\n        \"Frankenstein's Monster\".into(),\n        99,\n        \u0026mut by_name_tree,\n        \u0026mut by_health_tree,\n    );\n\n    Monster::new(\n        \u0026arena,\n        \"Godzilla\".into(),\n        2000,\n        \u0026mut by_name_tree,\n        \u0026mut by_health_tree,\n    );\n\n    Monster::new(\n        \u0026arena,\n        \"Vegeta\".into(),\n        9001,\n        \u0026mut by_name_tree,\n        \u0026mut by_health_tree,\n    );\n\n    // Query the `MonstersByName` tree by a name.\n\n    let godzilla = by_name_tree.find(\"Godzilla\").unwrap();\n    assert_eq!(godzilla.name, \"Godzilla\");\n\n    assert!(by_name_tree.find(\"Gill-Man\").is_none());\n\n    // Query the `MonstersByHealth` tree by a health.\n\n    let vegeta = by_health_tree.find(\u00269001).unwrap();\n    assert_eq!(vegeta.name, \"Vegeta\");\n\n    assert!(by_health_tree.find(\u00260).is_none());\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffitzgen%2Fintrusive_splay_tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffitzgen%2Fintrusive_splay_tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffitzgen%2Fintrusive_splay_tree/lists"}