{"id":21434199,"url":"https://github.com/sdleffler/qp-trie-rs","last_synced_at":"2025-04-05T10:08:48.999Z","repository":{"id":22039245,"uuid":"95947576","full_name":"sdleffler/qp-trie-rs","owner":"sdleffler","description":"An idiomatic and fast QP-trie implementation in pure Rust.","archived":false,"fork":false,"pushed_at":"2024-04-21T03:32:38.000Z","size":82,"stargazers_count":100,"open_issues_count":16,"forks_count":24,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-29T09:09:19.237Z","etag":null,"topics":["bytes","data-structures","qp-trie","radix","rust","text-processing","trie"],"latest_commit_sha":null,"homepage":"","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/sdleffler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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":"2017-07-01T06:52:15.000Z","updated_at":"2025-03-06T20:55:11.000Z","dependencies_parsed_at":"2024-06-21T05:54:32.589Z","dependency_job_id":null,"html_url":"https://github.com/sdleffler/qp-trie-rs","commit_stats":{"total_commits":67,"total_committers":12,"mean_commits":5.583333333333333,"dds":0.4328358208955224,"last_synced_commit":"98b199b1f427bcd049132c8f24f4b5b38303917c"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdleffler%2Fqp-trie-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdleffler%2Fqp-trie-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdleffler%2Fqp-trie-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdleffler%2Fqp-trie-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sdleffler","download_url":"https://codeload.github.com/sdleffler/qp-trie-rs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247318744,"owners_count":20919484,"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":["bytes","data-structures","qp-trie","radix","rust","text-processing","trie"],"created_at":"2024-11-22T23:33:54.054Z","updated_at":"2025-04-05T10:08:48.981Z","avatar_url":"https://github.com/sdleffler.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/sdleffler/qp-trie-rs.svg?branch=master)](https://travis-ci.org/sdleffler/qp-trie-rs)\n[![Docs Status](https://docs.rs/qp-trie/badge.svg)](https://docs.rs/qp-trie)\n[![On crates.io](https://img.shields.io/crates/v/qp-trie.svg)](https://crates.io/crates/qp-trie)\n\n# qp-trie-rs: A QP-trie implementation in pure Rust\n\nA QP-trie (\"Quelques-bits Popcount trie\" or \"Quad-bit Popcount trie\") is a\nradix trie for keys which can be interpreted as a string of nybbles (where a\nnybble is half a byte, or four bits.) QP-tries are essentially Patricia tries\nwhich branch on nybbles instead of individual bits; as such, a QP-trie has a\nbranching factor (and radix) of 16.\n\n## Serialization/deserialization through Serde\n\nOptionally, the `qp_trie::Trie` type supports (de-)serialization through\n[serde](https://github.com/serde-rs/serde). Enabling the `serde` feature will\nenable compilation of `Deserialize` and `Serialize` implementations for `Trie`.\n\n## When should I use a QP-trie?\n\nQP-tries as implemented in this crate are key-value maps for any keys which\nimplement `Borrow\u003c[u8]\u003e`. They are useful whenever you might need the same\noperations as a `HashMap` or `BTreeMap`, but need either a bit more speed\n(QP-tries are as fast or a bit faster as Rust's `HashMap` with the default\nhasher) and/or the ability to efficiently query for sets of elements with a\ngiven prefix.\n\nQP-tries support efficient lookup/insertion/removal of individual elements,\nlookup/removal of sets of values with keys with a given prefix.\n\n## Examples\n\nKeys can be any type which implements `Borrow\u003c[u8]\u003e`. Unfortunately at the\nmoment, this rules out `String` - while this trie can still be used to store\nstrings, it is necessary to manually convert them to byte slices and `Vec\u003cu8\u003e`s\nfor use as keys. Here's a naive, simple example of putting 9 2-element byte arrays\ninto the trie, and then removing all byte arrays which begin with \"1\":\n\n```rust\nuse qp_trie::Trie;\n\nlet mut trie = Trie::new();\n\nfor i in 0u8..3 {\n    for j in 0u8..3 {\n        trie.insert([i, j], i + j);\n    }\n}\n\nfor i in 0u8..3 {\n    trie.remove([1, i]);\n}\n\nassert!(trie.iter().all(|(\u0026key, _)| key[0] != 1));\n```\n\nHere's a slightly less naive method, which is actually vastly more efficient:\n\n```rust\nuse qp_trie::Trie;\n\nlet mut trie = Trie::new();\n\nfor i in 0u8..3 {\n    trie.extend((0u8..3).map(|j| ([i, j], i + j)));\n}\n\ntrie.remove_prefix([1]);\n\nassert!(trie.iter().all(|(\u0026key, _)| key[0] != 1));\n```\n\nAlthough the `extend` bit really isn't any more efficient (it's difficult to\npreallocate space for `n` elements in a trie) we're guaranteed that\n`trie.remove_prefix([1])` only actually removes a single node in the trie - the\nparent node of all nodes with the given prefix. QP-tries, like all radix tries,\nare extremely efficient when dealing with anything related to prefixes. This\nextends to iteration over prefixes:\n\n```rust\nuse qp_trie::Trie;\n\nlet mut trie = Trie::new();\n\nfor i in 0u8..3 {\n    trie.extend((0u8..3).map(|k| ([i, j], i + j)));\n}\n\nlet mut iter = trie.iter_prefix([1]);\n\nassert_eq!(iter.next(), Some((\u0026[1, 0], \u00261)));\nassert_eq!(iter.next(), Some((\u0026[1, 1], \u00262)));\nassert_eq!(iter.next(), Some((\u0026[1, 2], \u00263)));\nassert_eq!(iter.next(), None);\n```\n\n## Differences from the qptrie crate\n\nThis crate originally started as a fork of the `qptrie` crate; however, I found\nthe code difficult to work with and full of unsafe pointer manipulation which I\nfelt could be avoided. To avoid a pull request which would essentially rewrite\nthe entire library I decided to write my own instead.\n\nSeveral notable idiomatic features are provided which were missing from the `qptrie` crate:\n- `.iter()` and `.iter_mut()` for immutable and mutable iteration over the key/value pairs of the trie\n- `qp_trie::Trie` implements `Extend` and `IntoIterator`\n- `qp_trie::Trie` implements `Index` and `IndexMut`\n- `qp_trie::Trie` provides an \"Entry API\" with type signatures almost identical\n  to that provided by the `std::collections` `BTreeMap` and `HashMap`.\n\nIn addition to being written using safer code (failures which would otherwise\ncause undefined behavior will cause panics when compiled with debug assertions\nenabled) `qp_trie::Trie` is slightly faster than `qptrie::Trie` according to\nbenchmarks based on those shown in the `qptrie` repository.\n\n## Benchmarks\n\nBenchmarks are run against the `qptrie` crate, the Rust stdlib `BTreeMap`, and\nthe stdlib `HashMap` with both default and FNV hashing. `qp_trie::Trie`\nconsistently outperforms the `std::collections` `BTreeMap` and `HashMap`, as\nwell as the `qptrie` crate's implementation.\n\nBenchmarks named `exotrie` are using the `qptrie::Trie` implementation.\n\n```\ntest bench_btreemap_get      ... bench: 111,468,098 ns/iter (+/- 10,103,247)\ntest bench_btreemap_insert   ... bench: 112,124,846 ns/iter (+/- 14,296,195)\ntest bench_exotrie_get       ... bench:  46,195,582 ns/iter (+/- 16,943,561)\ntest bench_exotrie_insert    ... bench:  52,886,847 ns/iter (+/- 15,574,538)\ntest bench_fnvhashmap_get    ... bench:   9,530,109 ns/iter (+/- 820,763)\ntest bench_fnvhashmap_insert ... bench:  21,281,107 ns/iter (+/- 7,254,084)\ntest bench_hashmap_get       ... bench:  49,653,426 ns/iter (+/- 7,004,051)\ntest bench_hashmap_insert    ... bench:  47,771,824 ns/iter (+/- 4,979,606)\ntest bench_trie_get          ... bench:  40,898,914 ns/iter (+/- 13,400,062)\ntest bench_trie_insert       ... bench:  50,966,392 ns/iter (+/- 18,077,240)\n```\n\n## Future work\n\n- Add wrapper types for `String` and `str` to make working with strings easier.\n\n## License\n\nThe `qp-trie-rs` crate is licensed under the MPL v2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdleffler%2Fqp-trie-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsdleffler%2Fqp-trie-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdleffler%2Fqp-trie-rs/lists"}