{"id":28508786,"url":"https://github.com/surrealdb/revision","last_synced_at":"2026-05-01T12:01:47.381Z","repository":{"id":187206268,"uuid":"676442736","full_name":"surrealdb/revision","owner":"surrealdb","description":"A Rust library for revision-tolerant serialisation and deserialisation, with support for schema evolution over time","archived":false,"fork":false,"pushed_at":"2026-04-30T19:16:01.000Z","size":250,"stargazers_count":42,"open_issues_count":2,"forks_count":15,"subscribers_count":11,"default_branch":"main","last_synced_at":"2026-04-30T19:21:50.609Z","etag":null,"topics":["bincode","deserialization","rust","schema-evolution","serialization"],"latest_commit_sha":null,"homepage":"https://docs.rs/revision","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/surrealdb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-08-09T07:58:33.000Z","updated_at":"2026-04-30T18:19:56.000Z","dependencies_parsed_at":"2024-02-10T14:28:05.816Z","dependency_job_id":"08e30551-ed27-487e-9c01-17b36b790010","html_url":"https://github.com/surrealdb/revision","commit_stats":null,"previous_names":["surrealdb/revision"],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/surrealdb/revision","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surrealdb%2Frevision","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surrealdb%2Frevision/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surrealdb%2Frevision/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surrealdb%2Frevision/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/surrealdb","download_url":"https://codeload.github.com/surrealdb/revision/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/surrealdb%2Frevision/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32495949,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["bincode","deserialization","rust","schema-evolution","serialization"],"created_at":"2025-06-08T21:39:41.375Z","updated_at":"2026-05-01T12:01:47.365Z","avatar_url":"https://github.com/surrealdb.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cbr\u003e\n\n\u003c!-- \u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/surrealdb/revision#gh-dark-mode-only\" target=\"_blank\"\u003e\n        \u003cimg width=\"200\" src=\"/img/white/logo.svg\" alt=\"Revision Logo\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/surrealdb/revision#gh-light-mode-only\" target=\"_blank\"\u003e\n        \u003cimg width=\"200\" src=\"/img/black/logo.svg\" alt=\"Revision Logo\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e --\u003e\n\n\u003cp align=\"center\"\u003eA framework for revision-tolerant serialization and deserialization,\nwith support for schema evolution over time, allowing for easy revisioning of structs and enums for data storage requirements which need to support backwards\ncompatibility, but where the design of the data format evolves over time.\u003c/p\u003e\n\n\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/surrealdb/revision\"\u003e\u003cimg src=\"https://img.shields.io/badge/status-beta-ff00bb.svg?style=flat-square\"\u003e\u003c/a\u003e\n    \u0026nbsp;\n    \u003ca href=\"https://docs.rs/revision/\"\u003e\u003cimg src=\"https://img.shields.io/docsrs/revision?style=flat-square\"\u003e\u003c/a\u003e\n    \u0026nbsp;\n    \u003ca href=\"https://crates.io/crates/revision\"\u003e\u003cimg src=\"https://img.shields.io/crates/v/revision?style=flat-square\"\u003e\u003c/a\u003e\n    \u0026nbsp;\n    \u003ca href=\"https://github.com/surrealdb/revision\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-Apache_License_2.0-00bfff.svg?style=flat-square\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Information\n\n`Revision` is a framework for revision-tolerant serialization and deserialization with support for schema evolution over time. It allows for easy revisioning of structs and enums for data storage requirements which need to support backwards compatibility, but where the design of the data structures evolve over time. Revision enables data that was serialized at older revisions to be seamlessly deserialized and converted into the latest data structures. It uses [bincode](https://crates.io/crates/bincode) for serialization and deserialization. \n\nThe `Revisioned` trait is automatically implemented for the following primitives: `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `f32`, `f64`, `char`, `String`, `Vec\u003cT\u003e`, Arrays up to 32 elements, `Option\u003cT\u003e`, `Box\u003cT\u003e`, `Bound\u003cT\u003e`, `Wrapping\u003cT\u003e`, `Reverse\u003cT\u003e`, `(A, B)`, `(A, B, C)`, `(A, B, C, D)`, `(A, B, C, D, E)`, `Duration`, `HashMap\u003cK, V\u003e`, `BTreeMap\u003cK, V\u003e`, `HashSet\u003cT\u003e`, `BTreeSet\u003cT\u003e`, `BinaryHeap\u003cT\u003e`, `Result\u003cT, E\u003e`, `Cow\u003c'_, T\u003e`, `Decimal`, `regex::Regex`, `uuid::Uuid`, `chrono::Duration`, `chrono::DateTime\u003cUtc\u003e`, `geo::Point`, `geo::LineString` `geo::Polygon`, `geo::MultiPoint`, `geo::MultiLineString`, `geo::MultiPolygon`, and `ordered_float::NotNan`.\n\n## Feature Flags\n\nRevision supports the following feature flags:\n\n- **`specialised-vectors`** (default): Enables specialised implementations for certain vector types that provide serialisation and deserialisation performance improvements.\n- **`fixed-width-encoding`**: Uses fixed-width encoding for integers instead of variable-length encoding. By default, Revision uses variable-length encoding which is more space-efficient for small values but has overhead for large values. With this feature enabled, all integers use their full size (2 bytes for `u16`/`i16`, 4 bytes for `u32`/`i32`, 8 bytes for `u64`/`i64`, 16 bytes for `u128`/`i128`), providing predictable serialization sizes, and improved serialisation and deserialisation performance.\n- **`skip`** (disabled by default): Enables `SkipRevisioned` / `SkipCheckRevisioned`, `skip_slice` / `skip_check_slice` (plus `skip_reader` / `skip_check_reader` aliases), slice fast paths, and matching derive output (`#[revisioned(..., skip = false)]` opts out per type). Library crates should forward `skip = [\"revision/skip\"]` and document `features = [\"skip\"]` for dependents; see **Skipping encoded values** below.\n\n### Integer Encoding Trade-offs\n\n**Variable-length encoding (default)**:\n- Small values (0-250) use only 1 byte\n- More compact for typical workloads with mostly small values\n- Variable serialization size based on value magnitude\n- Slight overhead for very large values\n\n**Fixed-width encoding (`fixed-width-encoding` feature)**:\n- Predictable, constant serialization size per type\n- No branching or size checks during encoding/decoding\n- Less compact for small values\n- More efficient for workloads with large values\n\n### Benchmarking\n\nTo compare variable-length vs fixed-width encoding performance:\n\n```bash\n# Benchmark with default variable-length encoding\ncargo bench --bench varint_comparison\n\n# Benchmark with fixed-width encoding\ncargo bench --bench varint_comparison --features fixed-width-encoding\n```\n\nThe `varint_comparison` benchmark tests serialization and deserialization performance across different data distributions (small values, large values, and mixed distributions) for all integer types.\n\n## Inspiration\n\nThis code takes inspiration from the [Versionize](https://github.com/firecracker-microvm/versionize) library developed for [Amazon Firecracker](https://github.com/firecracker-microvm/firecracker) snapshot-restore development previews.\n\n## Revision in action\n\n```rust\nuse revision::Error;\nuse revision::revisioned;\n\n// The test structure is at revision 3.\n#[revisioned(revision = 3)]\n#[derive(Debug, PartialEq)]\npub struct TestStruct {\n    a: u32,\n    #[revision(start = 2, end = 3, convert_fn = \"convert_b\")]\n    b: u8,\n    #[revision(start = 3)]\n    c: u64,\n    #[revision(start = 3, default_fn = \"default_c\")]\n    d: String,\n}\n\nimpl TestStruct {\n    // Used to set the default value for a newly added field.\n    fn default_c(_revision: u16) -\u003e Result\u003cString, Error\u003e {\n        Ok(\"test_string\".to_owned())\n    }\n    // Used to convert the field from an old revision to the latest revision\n    fn convert_b(\u0026mut self, _revision: u16, value: u8) -\u003e Result\u003c(), Error\u003e {\n        self.c = value as u64;\n        Ok(())\n    }\n}\n\n// The test structure is at revision 3.\n#[revisioned(revision = 3)]\n#[derive(Debug, PartialEq)]\npub enum TestEnum {\n    #[revision(end = 2, convert_fn = \"upgrade_zero\")]\n    Zero,\n    #[revision(end = 2, convert_fn = \"upgrade_one\")]\n    One(u32),\n    #[revision(start = 2)]\n    Two(u64),\n    #[revision(start = 2)]\n    Three {\n        a: i64,\n        #[revision(end = 3, convert_fn = \"upgrade_three_b\")]\n        b: f32,\n        #[revision(start = 2)]\n        c: rust_decimal::Decimal,\n        #[revision(start = 3)]\n        d: String,\n    },\n}\n\nimpl TestEnum {\n    // Used to convert an old enum variant into a new variant.\n    fn upgrade_zero(_: TestEnumZeroFields, _revision: u16) -\u003e Result\u003cTestEnum, Error\u003e {\n        Ok(Self::Two(0))\n    }\n    // Used to convert an old enum variant into a new variant.\n    fn upgrade_one(f: TestEnumOneFields, _revision: u16) -\u003e Result\u003cTestEnum, Error\u003e {\n        Ok(Self::Two(f.0 as u64))\n    }\n    // Used to convert the field from an old revision to the latest revision\n    fn upgrade_three_b(\n        res: \u0026mut TestEnumThreeFields,\n        _revision: u16,\n        value: f32,\n    ) -\u003e Result\u003c(), Error\u003e {\n        res.c = value.into();\n        Ok(())\n    }\n}\n```\n\n## Skipping encoded values\n\nUse the **`skip`** feature when you handle revisioned bytes but only need to extract certain fields from the binary data - without deserializing full structs or maps into memory.\n\n### Extracting one field from a struct\n\nA `#[revisioned]` struct is laid out as **struct revision (`u16`)**, then **fields in source order**. Read only what you need and call `SkipRevisioned::skip_revisioned` on `\u0026mut reader` for the rest (or use `skip_slice::\u003cT\u003e` to skip a whole nested value in one go when you have a sub-slice).\n\n```rust\nuse revision::{DeserializeRevisioned, Error, SkipRevisioned, revisioned, to_vec};\n\n#[revisioned(revision = 1)]\nstruct Row {\n    // Large field we do not want to allocate when we only need `id`.\n    blob: Vec\u003cu8\u003e,\n    id: u64,\n}\n\nfn read_row_id_only(mut reader: \u0026[u8]) -\u003e Result\u003cu64, Error\u003e {\n    let _struct_revision = u16::deserialize_revisioned(\u0026mut reader)?;\n    \u003cVec\u003cu8\u003e as SkipRevisioned\u003e::skip_revisioned(\u0026mut reader)?;\n    u64::deserialize_revisioned(\u0026mut reader)\n}\n\nlet row = Row {\n    blob: vec![1, 2, 3],\n    id: 42,\n};\nlet bytes = to_vec(\u0026row).unwrap();\nassert_eq!(read_row_id_only(\u0026bytes).unwrap(), 42);\n```\n\n### Extracting one entry from a `BTreeMap`\n\nMaps are encoded as **length (`usize`)**, then **key / value** pairs in sorted key order. Typical pattern: deserialize each key, compare, deserialize the value you care about, otherwise skip the value with the appropriate `skip_revisioned` call.\n\n```rust\nuse revision::{DeserializeRevisioned, Error, SkipRevisioned, revisioned, to_vec};\nuse std::collections::BTreeMap;\n\n#[revisioned(revision = 1)]\nstruct Config {\n    values: BTreeMap\u003cString, u64\u003e,\n}\n\nfn get_u64(mut reader: \u0026[u8], wanted: \u0026str) -\u003e Result\u003cu64, Error\u003e {\n    let _struct_revision = u16::deserialize_revisioned(\u0026mut reader)?;\n    let n = usize::deserialize_revisioned(\u0026mut reader)?;\n    for _ in 0..n {\n        let key = String::deserialize_revisioned(\u0026mut reader)?;\n        if key == wanted {\n            return u64::deserialize_revisioned(\u0026mut reader);\n        }\n        \u003cu64 as SkipRevisioned\u003e::skip_revisioned(\u0026mut reader)?;\n    }\n    Err(Error::Deserialize(format!(\"missing key `{wanted}`\")))\n}\n\nlet cfg = Config {\n    values: BTreeMap::from([\n        (\"noise\".into(), 0),\n        (\"answer\".into(), 99),\n    ]),\n};\nlet bytes = to_vec(\u0026cfg).unwrap();\nassert_eq!(get_u64(\u0026bytes, \"answer\").unwrap(), 99);\n```\n\nFor **map values that are themselves `#[revisioned]` enums or structs**, deserialize the discriminant / nested revision as you would when fully deserializing, and call `MyValue::skip_revisioned` on entries you discard (see `benches/skip_mixed_btreemap_nested.rs`).\n\nUse **`skip_check_*`** when you want validation that matches stricter deserialize checks (e.g. UTF-8 for `String`). Disable skip for a type with `#[revisioned(revision = N, skip = false)]`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsurrealdb%2Frevision","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsurrealdb%2Frevision","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsurrealdb%2Frevision/lists"}