Library / SDK
SoftbearStudios/bitcode avatar
SoftbearStudios/bitcode

bitcode is a Rust binary serializer that trades format stability for small size

A binary encoder/decoder for Rust

676 stars62 forksRustMIT

At a glance

What is it?
bitcode is a bitwise binary encoder and decoder for Rust whose stated goals are speed, small output and easy compression with Deflate, LZ4 and Zstd, at the cost of a format that is explicitly not stable across major versions and not readable by other languages. It ships as version 0.6.9 with derive macros, an optional serde integration and a no_std build.
Who is it for?
Engineers shipping Rust services that serialise large amounts of structured data to a private consumer, where both the wire size and the compression ratio matter, are the audience this crate fits, and the field-grouping plus sub-byte integer design is the reason it exists.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 116 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The goals are size and compression, not compatibility

bitcode is a binary encoder and decoder for Rust with an unusual profile: the project lists what it optimises for and, just as prominently, what it refuses to do. The goals are speed, a small serialized size, and being highly compressible by Deflate, LZ4 and Zstd. The non-goals are a stable format across major versions, a self describing format, and compatibility with languages other than Rust. That framing matters more than any benchmark, because it tells you what kind of data this is for. bitcode is a wire format between Rust programs, most likely a service and its own components, where you control both ends of the pipe and can deploy the encoder and decoder together. It is not an interchange format, and the crate does not pretend otherwise. The development dependencies confirm the intent: `flate2`, `lz4_flex` and `zstd` are in the tree, so the compression paths are exercised rather than hypothetical.

Field grouping is what makes the output compress well

The implementation notes explain the mechanism behind the size goal in four points. All instances of each field are grouped together, which makes compression easier because similar values end up adjacent in the byte stream rather than interleaved record by record. Smaller integers are used where possible, all the way down to one bit, so a boolean or a small enum does not cost a whole byte. Validation is performed up front on typed vectors before deserialization, which means malformed input is rejected before it is turned into structures. And the code is designed to be auto-vectorized by LLVM, which is the stated route to the speed goal rather than hand written SIMD. The inspiration is tree-buf, and the crate links to the rust_serialization_benchmark repository for benchmark comparisons. The trade-off hidden in the grouping decision is that a decoder cannot stream a record at a time, because fields of the same name are far apart in the stream.

Derive macros do the boilerplate, serde is opt-in

The main API is a pair of traits and two free functions. The README example derives both traits on a struct with a `u32` and a `&str`, encodes it with `bitcode::encode` and decodes it with `bitcode::decode`:

rust
use bitcode::{Encode, Decode};

#[derive(Encode, Decode, PartialEq, Debug)]
struct Foo<'a> {
    x: u32,
    y: &'a str,
}

let original = Foo {
    x: 10,
    y: "abc",
};

let encoded: Vec<u8> = bitcode::encode(&original); // No error
let decoded: Foo<'_> = bitcode::decode(&encoded).unwrap();
assert_eq!(original, decoded);

Note what the example does not have: no error return on encode, since encoding a fixed set of Rust types cannot fail, while decoding returns a Result and the example unwraps it. The `serde` integration exists but is gated behind a `serde` feature flag, so the crate does not pull in a serde implementation unless you ask for it. Support for third party types is added by following the instructions in the project's wiki page on adding library support, which is the path for types you do not own.

no_std is supported, alloc is not optional

The crate is `#![no_std]` capable, with all `std` only functionality behind the default `std` feature, and `alloc` required. The feature table in the manifest makes the shape of that clear:

toml
[features]
derive = [ "dep:bitcode_derive" ]
std = [ "serde?/std", "glam?/std", "arrayvec?/std" ]
default = [ "derive", "std" ]

The `std` feature propagates to the optional dependencies that care, using the question mark syntax to only enable it when those dependencies are themselves enabled. The docs.rs metadata then asks for `derive`, `serde` and `std` together, which is why the published documentation shows the full surface. For an embedded or kernel-adjacent target, disabling default features and enabling `alloc` is the configuration, and it is worth reading the feature list rather than assuming the default build is what the docs describe.

The optional dependencies are the list of types you can encode

The dependency list is short and tells you what the crate can handle out of the box. `bytemuck` is required, with the `min_const_generics` and `must_cast` features, which is the mechanism behind the smaller integer claim, since it allows casting between primitive types at compile time. Everything else is optional and feature gated. `arrayvec` brings fixed capacity vectors. `glam` brings the vector and matrix math types. `rust_decimal` brings fixed point decimals, `time` brings date and time values, `uuid` brings identifiers, and `serde` brings the optional integration. The derive macro lives in a separate crate, `bitcode_derive`, which is a workspace member of this repository and is pinned to an exact version to match the parent crate. The package version is 0.6.9, the edition is 2021, and the minimum supported Rust version is 1.88.

Fuzzing and benchmarks are part of the repository

The repository layout shows a project that puts effort into checking itself. There is a `fuzz/` directory, which is excluded from the published package by the `exclude` list in the manifest so the fuzz targets are not shipped to users of the crate, and the manifest carries a lint configuration that checks the `cfg(fuzzing)` conditional compilation flag, which is how a fuzz build is detected. The dev dependencies include `bincode` as a comparison point, along with `rand` and `rand_chacha` for property testing, and the compression crates for round trip testing. The manifest also contains a commented out `lto` setting under the bench profile and a note that zstd does not compile with miri in big endian mode, which is the kind of detail that only appears in a repository where the test matrix is actually run. The `bitcode_derive` subdirectory is a separate published crate, so the derive macro has its own release and version line.

A 0.6 line with no tagged releases and a June push

The repository has no GitHub releases, so versions are taken from the manifest, where the current version is 0.6.9, and the crate is published on crates.io with documentation on docs.rs. The last push to the repository was on 2026-06-29, and the repository is not archived, so work is continuing even though the tagged release history is not visible on the repository page. The licence is dual, either the Apache License, Version 2.0 or the MIT licence at your option, and the project states that contributions are dual licensed the same way unless the author says otherwise, with LICENSE-APACHE and LICENSE-MIT in the repository root. For an engineering team, the practical questions are the ones the crate's own non-goals raise: what happens to stored data when the major version moves, and whether anything other than Rust will ever need to read it. On both counts the answer is written down in the README, and it is not the answer you get from a format like JSON or a stable binary standard.

Editorial conclusion

Engineers shipping Rust services that serialise large amounts of structured data to a private consumer, where both the wire size and the compression ratio matter, are the audience this crate fits, and the field-grouping plus sub-byte integer design is the reason it exists. It is the wrong choice for a public API contract, for a format that has to be read by anything other than Rust, or for data you expect to keep across a major version bump, because those are the crate's own stated non-goals. Before adopting it, read the non-goals list rather than the goals list, and check that the version you pin matches the decoder on the other end, since bitcode does not promise compatibility across major versions.

Frequently asked questions

Is the bitcode format stable across versions?

No, and the project states that as an explicit non-goal along with self describing data and compatibility with languages other than Rust. A stable format across major versions is listed among the non-goals, so data written by one major version is not promised to be readable by the next.

How do I use bitcode with serde?

The serde integration is gated behind a `serde` feature flag and is not enabled by default. The project links to a wiki page explaining the serde integration, and the docs.rs metadata requests the `derive`, `serde` and `std` features together.

Does bitcode work without the standard library?

Yes. The crate is `#![no_std]` capable, with all std only functionality behind the default `std` feature, and `alloc` is required. The feature table also propagates `std` to the optional dependencies that support it.

What types can bitcode encode out of the box?

Beyond the primitive types handled by the derive macros, the optional dependencies cover fixed capacity vectors through arrayvec, math types through glam, fixed point decimals through rust_decimal, date and time through time, identifiers through uuid, and the serde integration. Support for other libraries is added by following the project's wiki instructions.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. SoftbearStudios/bitcode on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/softbearstudios-bitcode.svg)](https://hysenlabs.com/projects/softbearstudios-bitcode)

Was this useful?