diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30a71ea..ef931ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
## [Unreleased]
+## [0.5.0] - 2026-05-05
+
+This release is a minor bump under the
+[Rust 0.x convention](https://doc.rust-lang.org/cargo/reference/semver.html):
+the only API break is `#[non_exhaustive]` on `buffa_codegen::GeneratedFileKind`
+(see *Changed* below), which affects downstream code generators only — it does
+not change the runtime API. Everything else is additive.
+
+**Consumers with checked-in generated code must regenerate** with the 0.5.0
+toolchain before depending on the 0.5.0 runtime crates: generated code from
+0.5.0's `buffa-codegen` references `ViewReborrow`, `decode_bytes_to_bytes`,
+and `__private::arbitrary_bytes`, none of which exist in `buffa` 0.4.0.
+
+### Breaking changes
+
+- **`buffa_codegen::GeneratedFileKind` is now `#[non_exhaustive]`.** Match it
+ with a wildcard arm — future kinds can then be added without a major
+ version bump. Build integrations that compare with `==` (the common case,
+ including connect-rust) are unaffected.
+
### Added
- `buffa_codegen::GeneratedFileKind::Companion` and `apply_companions` let
@@ -62,12 +82,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
dropped. Consumers with checked-in generated code must regenerate to pick
this up. ([#53](https://github.com/anthropics/buffa/issues/53))
-### Changed
-
-- `buffa_codegen::GeneratedFileKind` is now `#[non_exhaustive]`. Match it
- with a wildcard arm — future kinds can then be added without a major
- version bump. (Build integrations that compare with `==` are unaffected.)
-
### Fixed
- `buffa-types --features arbitrary` now compiles. `Any.value` is
@@ -448,7 +462,9 @@ This release publishes:
MSRV: Rust 1.85.
-[Unreleased]: https://github.com/anthropics/buffa/compare/v0.3.0...HEAD
+[Unreleased]: https://github.com/anthropics/buffa/compare/v0.5.0...HEAD
+[0.5.0]: https://github.com/anthropics/buffa/compare/v0.4.0...v0.5.0
+[0.4.0]: https://github.com/anthropics/buffa/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/anthropics/buffa/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/anthropics/buffa/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/anthropics/buffa/releases/tag/v0.1.0
diff --git a/Cargo.lock b/Cargo.lock
index aad2d5f..3cddc62 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -58,7 +58,7 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "buffa"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"arbitrary",
"base64",
@@ -72,7 +72,7 @@ dependencies = [
[[package]]
name = "buffa-build"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-codegen",
@@ -81,7 +81,7 @@ dependencies = [
[[package]]
name = "buffa-codegen"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-descriptor",
@@ -95,14 +95,14 @@ dependencies = [
[[package]]
name = "buffa-descriptor"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
]
[[package]]
name = "buffa-test"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"arbitrary",
"buffa",
@@ -115,7 +115,7 @@ dependencies = [
[[package]]
name = "buffa-types"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"arbitrary",
"buffa",
@@ -520,7 +520,7 @@ dependencies = [
[[package]]
name = "protoc-gen-buffa"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-codegen",
@@ -528,7 +528,7 @@ dependencies = [
[[package]]
name = "protoc-gen-buffa-packaging"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-codegen",
diff --git a/Cargo.toml b/Cargo.toml
index e277349..b32da4d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,7 +24,7 @@ exclude = [
]
[workspace.package]
-version = "0.4.0"
+version = "0.5.0"
edition = "2021"
rust-version = "1.85"
license = "Apache-2.0"
@@ -33,12 +33,12 @@ keywords = ["protobuf", "protocol-buffers", "serialization", "no-std", "editions
categories = ["encoding", "no-std"]
[workspace.dependencies]
-buffa = { path = "buffa", version = "0.4.0", default-features = false }
-buffa-types = { path = "buffa-types", version = "0.4.0" }
-buffa-descriptor = { path = "buffa-descriptor", version = "0.4.0" }
-buffa-codegen = { path = "buffa-codegen", version = "0.4.0" }
-buffa-build = { path = "buffa-build", version = "0.4.0" }
-buffa-test = { path = "buffa-test", version = "0.4.0" }
+buffa = { path = "buffa", version = "0.5.0", default-features = false }
+buffa-types = { path = "buffa-types", version = "0.5.0" }
+buffa-descriptor = { path = "buffa-descriptor", version = "0.5.0" }
+buffa-codegen = { path = "buffa-codegen", version = "0.5.0" }
+buffa-build = { path = "buffa-build", version = "0.5.0" }
+buffa-test = { path = "buffa-test", version = "0.5.0" }
base64 = { version = "0.22", default-features = false, features = ["alloc"] }
bytes = { version = "1", default-features = false }
hashbrown = { version = "0.15", default-features = false, features = ["default-hasher"] }
diff --git a/README.md b/README.md
index 7bf6cdd..5e57e87 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ These are gaps we intend to address in future releases:
## Semver and API stability
-Buffa is pre-1.0. We follow the [Rust community convention](https://doc.rust-lang.org/cargo/reference/semver.html) for 0.x crates: breaking changes increment the **minor** version (0.1.x → 0.2.0), additive changes increment the **patch** version (0.1.0 → 0.1.1). Pin to a minor version (`buffa = "0.4"`) to avoid surprises.
+Buffa is pre-1.0. We follow the [Rust community convention](https://doc.rust-lang.org/cargo/reference/semver.html) for 0.x crates: breaking changes increment the **minor** version (0.1.x → 0.2.0), additive changes increment the **patch** version (0.1.0 → 0.1.1). Pin to a minor version (`buffa = "0.5"`) to avoid surprises.
The generated code API (struct shapes, `Message` trait, `MessageView` trait, `EnumValue`, `MessageField`) is considered the primary stability surface. Internal helper modules marked `#[doc(hidden)]` (`__private`, `__buffa_*` fields) may change at any time.
@@ -146,7 +146,7 @@ let decoded: MyMessage = serde_json::from_str(&json).unwrap();
## Performance
-Throughput comparison across five representative message types, measured on an Intel Xeon Platinum 8488C (x86_64). Cross-implementation benchmarks run in Docker for toolchain consistency (`task bench-cross`). Higher is better.
+Throughput comparison across five representative message types, measured on an Intel Xeon Platinum 8488C (x86_64) at buffa v0.5.0. Cross-implementation benchmarks run in Docker for toolchain consistency (`task bench-cross`). Higher is better.
### Binary decode
@@ -160,11 +160,11 @@ Throughput comparison across five representative message types, measured on an I
| Message | buffa | buffa (view) | prost | prost (bytes) | protobuf-v4 | Go |
|---------|------:|------:|------:|------:|------:|------:|
-| ApiResponse | 862 | 1,475 (+71%) | 756 (−12%) | 676 (−22%) | 695 (−19%) | 269 (−69%) |
-| LogRecord | 722 | 1,984 (+175%) | 712 (−1%) | 676 (−6%) | 857 (+19%) | 247 (−66%) |
-| AnalyticsEvent | 199 | 320 (+61%) | 254 (+28%) | 194 (−3%) | 361 (+82%) | 88 (−56%) |
-| GoogleMessage1 | 1,014 | 1,341 (+32%) | 956 (−6%) | 931 (−8%) | 639 (−37%) | 338 (−67%) |
-| MediaFrame | 16,816 | 73,004 (+334%) | 9,648 (−43%) | 23,516 (+40%) | 17,633 (+5%) | 1,241 (−93%) |
+| ApiResponse | 825 | 1,399 (+70%) | 756 (−8%) | 677 (−18%) | 689 (−16%) | 272 (−67%) |
+| LogRecord | 741 | 1,869 (+152%) | 735 (−1%) | 682 (−8%) | 867 (+17%) | 251 (−66%) |
+| AnalyticsEvent | 192 | 317 (+65%) | 254 (+32%) | 197 (+3%) | 359 (+87%) | 91 (−53%) |
+| GoogleMessage1 | 905 | 1,201 (+33%) | 989 (+9%) | 930 (+3%) | 643 (−29%) | 348 (−62%) |
+| MediaFrame | 17,682 | 71,426 (+304%) | 9,612 (−46%) | 23,577 (+33%) | 17,894 (+1%) | 1,250 (−93%) |
@@ -178,13 +178,39 @@ Throughput comparison across five representative message types, measured on an I
Raw data (MiB/s)
-| Message | buffa | prost | protobuf-v4 | Go |
-|---------|------:|------:|------:|------:|
-| ApiResponse | 2,543 | 1,810 (−29%) | 1,013 (−60%) | 560 (−78%) |
-| LogRecord | 4,018 | 3,093 (−23%) | 1,642 (−59%) | 303 (−92%) |
-| AnalyticsEvent | 656 | 357 (−46%) | 511 (−22%) | 160 (−76%) |
-| GoogleMessage1 | 2,594 | 1,808 (−30%) | 869 (−67%) | 360 (−86%) |
-| MediaFrame | 45,990 | 38,514 (−16%) | 10,463 (−77%) | 1,647 (−96%) |
+| Message | buffa | buffa (view) | prost | prost (bytes) | protobuf-v4 | Go |
+|---------|------:|------:|------:|------:|------:|------:|
+| ApiResponse | 2,566 | 2,537 (−1%) | 1,801 (−30%) | — | 1,033 (−60%) | 561 (−78%) |
+| LogRecord | 4,029 | 4,703 (+17%) | 3,116 (−23%) | — | 1,651 (−59%) | 305 (−92%) |
+| AnalyticsEvent | 582 | 623 (+7%) | 359 (−38%) | — | 509 (−13%) | 161 (−72%) |
+| GoogleMessage1 | 2,441 | 2,725 (+12%) | 1,817 (−26%) | — | 865 (−65%) | 362 (−85%) |
+| MediaFrame | 43,830 | 45,425 (+4%) | 38,652 (−12%) | — | 10,616 (−76%) | 1,673 (−96%) |
+
+
+
+### Build + binary encode
+
+The `build + encode` measure starts from raw field values rather than a pre-built
+message struct, so it counts struct construction. The `buffa (view)` path
+constructs a borrowed view directly over the input slices and never allocates an
+owned message at all, which is why it is consistently faster than building owned
+structs and then encoding them.
+
+
+
+
+
+
+
+Raw data (MiB/s)
+
+| Message | buffa | buffa (view) |
+|---------|------:|------:|
+| ApiResponse | 732 | 1,649 (+125%) |
+| LogRecord | 498 | 2,843 (+471%) |
+| AnalyticsEvent | 520 | 1,166 (+124%) |
+| GoogleMessage1 | 818 | 1,169 (+43%) |
+| MediaFrame | 20,893 | 52,910 (+153%) |
@@ -199,12 +225,12 @@ Throughput comparison across five representative message types, measured on an I
Raw data (MiB/s)
| Message | buffa | prost | Go |
-|---------|------:|------:|---:|
-| ApiResponse | 875 | 943 (+8%) | 114 (−87%) |
-| LogRecord | 1,294 | 1,407 (+9%) | 136 (−89%) |
-| AnalyticsEvent | 786 | 843 (+7%) | 51 (−93%) |
-| GoogleMessage1 | 961 | 1,007 (+5%) | 122 (−87%) |
-| MediaFrame | 1,423 | 1,449 (+2%) | 206 (−86%) |
+|---------|------:|------:|------:|
+| ApiResponse | 872 | 942 (+8%) | 115 (−87%) |
+| LogRecord | 1,332 | 1,401 (+5%) | 139 (−90%) |
+| AnalyticsEvent | 766 | 849 (+11%) | 52 (−93%) |
+| GoogleMessage1 | 968 | 1,033 (+7%) | 125 (−87%) |
+| MediaFrame | 1,460 | 1,445 (−1%) | 209 (−86%) |
@@ -219,12 +245,12 @@ Throughput comparison across five representative message types, measured on an I
Raw data (MiB/s)
| Message | buffa | prost | Go |
-|---------|------:|------:|---:|
-| ApiResponse | 706 | 303 (−57%) | 67 (−90%) |
-| LogRecord | 757 | 696 (−8%) | 107 (−86%) |
-| AnalyticsEvent | 268 | 233 (−13%) | 45 (−83%) |
-| GoogleMessage1 | 640 | 258 (−60%) | 70 (−89%) |
-| MediaFrame | 1,942 | 1,954 (+1%) | 262 (−87%) |
+|---------|------:|------:|------:|
+| ApiResponse | 680 | 299 (−56%) | 68 (−90%) |
+| LogRecord | 795 | 701 (−12%) | 108 (−86%) |
+| AnalyticsEvent | 268 | 239 (−11%) | 45 (−83%) |
+| GoogleMessage1 | 649 | 253 (−61%) | 71 (−89%) |
+| MediaFrame | 1,910 | 1,958 (+3%) | 264 (−86%) |
@@ -232,7 +258,7 @@ Throughput comparison across five representative message types, measured on an I
**Libraries:** prost 0.13 + pbjson 0.7, protobuf‑v4 (Google Rust/upb, v4.33.1), Go `google.golang.org/protobuf` v1.36.6. protobuf-v4 JSON is not included as it does not provide a JSON codec.
-**`prost (bytes)`** uses `prost-build`'s `.bytes(["."])` config so every proto `bytes` field is generated as `bytes::Bytes` instead of `Vec`, and decodes from a `bytes::Bytes` input to exercise `Bytes`' zero-copy `copy_to_bytes` slicing. The substitution only affects the decode path, so only decode numbers are reported — `prost (bytes)` encode tracks default `prost` by construction. On the four non-bytes messages, `prost (bytes)` tracks default `prost` within noise (and is slightly slower on `ApiResponse` where the per-message `Bytes::clone` refcount overhead isn't offset by any actual zero-copy). On `MediaFrame` it runs ~2.4× faster than default `prost` at decode, confirming that prost's feature does land when it has bytes fields to work with. buffa views are in a different regime again: they borrow directly from the input buffer for strings, bytes, and nested message bodies, so `buffa (view)` on `MediaFrame` is ~3× the `prost (bytes)` number and ~4.3× `buffa`'s own owned decode. Views also benefit on the four non-bytes messages, where prost's `bytes` feature is inert.
+**`prost (bytes)`** uses `prost-build`'s `.bytes(["."])` config so every proto `bytes` field is generated as `bytes::Bytes` instead of `Vec`, and decodes from a `bytes::Bytes` input to exercise `Bytes`' zero-copy `copy_to_bytes` slicing. The substitution only affects the decode path, so only decode numbers are reported — `prost (bytes)` encode tracks default `prost` by construction. On the four non-bytes messages, `prost (bytes)` tracks default `prost` within noise (and is slightly slower on `ApiResponse` where the per-message `Bytes::clone` refcount overhead isn't offset by any actual zero-copy). On `MediaFrame` it runs ~2.4× faster than default `prost` at decode, confirming that prost's feature does land when it has bytes fields to work with. buffa views are in a different regime again: they borrow directly from the input buffer for strings, bytes, and nested message bodies, so `buffa (view)` on `MediaFrame` is ~3× the `prost (bytes)` number and ~4× `buffa`'s own owned decode. Views also benefit on the four non-bytes messages, where prost's `bytes` feature is inert.
**Owned decode trade-offs:** buffa's owned decode is typically within ±10% of prost, trading a small throughput cost for features prost omits: unknown-field preservation by default, typed `EnumValue` wrappers (not raw `i32`), and a type-stable decode loop that supports recursive message types without manual boxing. The zero-copy view path (`MyMessageView::decode_view`) sidesteps allocation entirely and is the recommended fast decode path. protobuf-v4's decode advantage on deeply-nested messages comes from upb's arena allocator — all sub-messages are bump-allocated in one arena rather than individually boxed.
diff --git a/benchmarks/buffa/Cargo.lock b/benchmarks/buffa/Cargo.lock
index 5321c20..72fd72d 100644
--- a/benchmarks/buffa/Cargo.lock
+++ b/benchmarks/buffa/Cargo.lock
@@ -60,7 +60,7 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "buffa"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"base64",
"bytes",
@@ -73,7 +73,7 @@ dependencies = [
[[package]]
name = "buffa-build"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-codegen",
@@ -82,7 +82,7 @@ dependencies = [
[[package]]
name = "buffa-codegen"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-descriptor",
@@ -95,7 +95,7 @@ dependencies = [
[[package]]
name = "buffa-descriptor"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
]
diff --git a/benchmarks/charts/binary-decode-analytics_event.svg b/benchmarks/charts/binary-decode-analytics_event.svg
index be2ea2a..08b4393 100644
--- a/benchmarks/charts/binary-decode-analytics_event.svg
+++ b/benchmarks/charts/binary-decode-analytics_event.svg
@@ -36,16 +36,16 @@
400
MiB/s
AnalyticsEvent
-
- 198
-
- 320
-
- 253
-
- 193
-
- 361
-
- 88
+
+ 192
+
+ 316
+
+ 253
+
+ 197
+
+ 359
+
+ 91
diff --git a/benchmarks/charts/binary-decode-api_response.svg b/benchmarks/charts/binary-decode-api_response.svg
index 0876c97..bc9021e 100644
--- a/benchmarks/charts/binary-decode-api_response.svg
+++ b/benchmarks/charts/binary-decode-api_response.svg
@@ -36,16 +36,16 @@
2,000
MiB/s
ApiResponse
-
- 862
-
- 1,474
-
- 756
-
- 676
-
- 695
-
- 268
+
+ 825
+
+ 1,398
+
+ 756
+
+ 676
+
+ 689
+
+ 272
diff --git a/benchmarks/charts/binary-decode-google_message1_proto3.svg b/benchmarks/charts/binary-decode-google_message1_proto3.svg
index 9bc0e07..4682121 100644
--- a/benchmarks/charts/binary-decode-google_message1_proto3.svg
+++ b/benchmarks/charts/binary-decode-google_message1_proto3.svg
@@ -36,16 +36,16 @@
2,000
MiB/s
GoogleMessage1
-
- 1,013
-
- 1,341
-
- 956
-
- 930
-
- 638
-
- 338
+
+ 904
+
+ 1,200
+
+ 989
+
+ 930
+
+ 642
+
+ 347
diff --git a/benchmarks/charts/binary-decode-log_record.svg b/benchmarks/charts/binary-decode-log_record.svg
index 2ff3bc6..80e00e0 100644
--- a/benchmarks/charts/binary-decode-log_record.svg
+++ b/benchmarks/charts/binary-decode-log_record.svg
@@ -36,16 +36,16 @@
2,000
MiB/s
LogRecord
-
- 722
-
- 1,983
-
- 712
-
- 676
-
- 857
-
- 247
+
+ 740
+
+ 1,869
+
+ 735
+
+ 682
+
+ 866
+
+ 251
diff --git a/benchmarks/charts/binary-decode-media_frame.svg b/benchmarks/charts/binary-decode-media_frame.svg
index 8377d2a..6b82e88 100644
--- a/benchmarks/charts/binary-decode-media_frame.svg
+++ b/benchmarks/charts/binary-decode-media_frame.svg
@@ -25,27 +25,27 @@
0.0
- 16.0
+ 14.0
- 32.0
+ 28.0
- 48.0
+ 42.0
- 64.0
+ 56.0
- 80.0
+ 70.0
GiB/s
MediaFrame
-
- 16.42
-
- 71.29
-
- 9.42
-
- 22.96
-
- 17.22
-
- 1.21
+
+ 17.27
+
+ 69.75
+
+ 9.39
+
+ 23.02
+
+ 17.47
+
+ 1.22
diff --git a/benchmarks/charts/binary-encode-analytics_event.svg b/benchmarks/charts/binary-encode-analytics_event.svg
index ffa5b88..8820cbf 100644
--- a/benchmarks/charts/binary-encode-analytics_event.svg
+++ b/benchmarks/charts/binary-encode-analytics_event.svg
@@ -1,4 +1,4 @@
-
diff --git a/benchmarks/charts/json-decode-api_response.svg b/benchmarks/charts/json-decode-api_response.svg
index d9beb8a..0246814 100644
--- a/benchmarks/charts/json-decode-api_response.svg
+++ b/benchmarks/charts/json-decode-api_response.svg
@@ -19,21 +19,21 @@
0
- 160
+ 140
- 320
+ 280
- 480
+ 420
- 640
+ 560
- 800
+ 700
MiB/s
ApiResponse
-
- 705
-
- 302
-
- 67
+
+ 680
+
+ 298
+
+ 68
diff --git a/benchmarks/charts/json-decode-google_message1_proto3.svg b/benchmarks/charts/json-decode-google_message1_proto3.svg
index 40fdcd8..453be1d 100644
--- a/benchmarks/charts/json-decode-google_message1_proto3.svg
+++ b/benchmarks/charts/json-decode-google_message1_proto3.svg
@@ -30,10 +30,10 @@
700
MiB/s
GoogleMessage1
-
- 639
-
- 258
-
- 70
+
+ 648
+
+ 253
+
+ 70
diff --git a/benchmarks/charts/json-decode-log_record.svg b/benchmarks/charts/json-decode-log_record.svg
index ec5cdd4..592c4dc 100644
--- a/benchmarks/charts/json-decode-log_record.svg
+++ b/benchmarks/charts/json-decode-log_record.svg
@@ -30,10 +30,10 @@
800
MiB/s
LogRecord
-
- 756
-
- 695
-
- 107
+
+ 794
+
+ 700
+
+ 107
diff --git a/benchmarks/charts/json-decode-media_frame.svg b/benchmarks/charts/json-decode-media_frame.svg
index 0bfc0f0..40c7c36 100644
--- a/benchmarks/charts/json-decode-media_frame.svg
+++ b/benchmarks/charts/json-decode-media_frame.svg
@@ -30,10 +30,10 @@
2,000
MiB/s
MediaFrame
-
- 1,942
-
- 1,954
-
- 261
+
+ 1,909
+
+ 1,958
+
+ 263
diff --git a/benchmarks/charts/json-encode-analytics_event.svg b/benchmarks/charts/json-encode-analytics_event.svg
index 5ae0ce4..3690f6f 100644
--- a/benchmarks/charts/json-encode-analytics_event.svg
+++ b/benchmarks/charts/json-encode-analytics_event.svg
@@ -30,10 +30,10 @@
900
MiB/s
AnalyticsEvent
-
- 785
-
- 843
-
- 51
+
+ 766
+
+ 849
+
+ 51
diff --git a/benchmarks/charts/json-encode-api_response.svg b/benchmarks/charts/json-encode-api_response.svg
index d83c3aa..a2ac8e8 100644
--- a/benchmarks/charts/json-encode-api_response.svg
+++ b/benchmarks/charts/json-encode-api_response.svg
@@ -30,10 +30,10 @@
1,000
MiB/s
ApiResponse
-
- 874
-
- 942
-
- 113
+
+ 871
+
+ 941
+
+ 114
diff --git a/benchmarks/charts/json-encode-google_message1_proto3.svg b/benchmarks/charts/json-encode-google_message1_proto3.svg
index 4041322..41a3ab4 100644
--- a/benchmarks/charts/json-encode-google_message1_proto3.svg
+++ b/benchmarks/charts/json-encode-google_message1_proto3.svg
@@ -30,10 +30,10 @@
2,000
MiB/s
GoogleMessage1
-
- 961
-
- 1,007
-
- 121
+
+ 968
+
+ 1,032
+
+ 125
diff --git a/benchmarks/charts/json-encode-log_record.svg b/benchmarks/charts/json-encode-log_record.svg
index 82a16c5..fadb975 100644
--- a/benchmarks/charts/json-encode-log_record.svg
+++ b/benchmarks/charts/json-encode-log_record.svg
@@ -30,10 +30,10 @@
2,000
MiB/s
LogRecord
-
- 1,293
-
- 1,407
-
- 136
+
+ 1,331
+
+ 1,401
+
+ 138
diff --git a/benchmarks/charts/json-encode-media_frame.svg b/benchmarks/charts/json-encode-media_frame.svg
index a96f115..dd76944 100644
--- a/benchmarks/charts/json-encode-media_frame.svg
+++ b/benchmarks/charts/json-encode-media_frame.svg
@@ -30,10 +30,10 @@
2,000
MiB/s
MediaFrame
-
- 1,422
-
- 1,449
-
- 206
+
+ 1,460
+
+ 1,444
+
+ 208
diff --git a/benchmarks/charts/tables.md b/benchmarks/charts/tables.md
index bc3ed34..da542f8 100644
--- a/benchmarks/charts/tables.md
+++ b/benchmarks/charts/tables.md
@@ -2,47 +2,48 @@
| Message | buffa | buffa (view) | prost | prost (bytes) | protobuf-v4 | Go |
|---------|------:|------:|------:|------:|------:|------:|
-| ApiResponse | 862 | 1,475 (+71%) | 756 (−12%) | 676 (−22%) | 695 (−19%) | 269 (−69%) |
-| LogRecord | 722 | 1,984 (+175%) | 712 (−1%) | 676 (−6%) | 857 (+19%) | 247 (−66%) |
-| AnalyticsEvent | 199 | 320 (+61%) | 254 (+28%) | 194 (−3%) | 361 (+82%) | 88 (−56%) |
-| GoogleMessage1 | 1,014 | 1,341 (+32%) | 956 (−6%) | 931 (−8%) | 639 (−37%) | 338 (−67%) |
-| MediaFrame | 16,816 | 73,004 (+334%) | 9,648 (−43%) | 23,516 (+40%) | 17,633 (+5%) | 1,241 (−93%) |
+| ApiResponse | 825 | 1,399 (+70%) | 756 (−8%) | 677 (−18%) | 689 (−16%) | 272 (−67%) |
+| LogRecord | 741 | 1,869 (+152%) | 735 (−1%) | 682 (−8%) | 867 (+17%) | 251 (−66%) |
+| AnalyticsEvent | 192 | 317 (+65%) | 254 (+32%) | 197 (+3%) | 359 (+87%) | 91 (−53%) |
+| GoogleMessage1 | 905 | 1,201 (+33%) | 989 (+9%) | 930 (+3%) | 643 (−29%) | 348 (−62%) |
+| MediaFrame | 17,682 | 71,426 (+304%) | 9,612 (−46%) | 23,577 (+33%) | 17,894 (+1%) | 1,250 (−93%) |
### Binary encode
-| Message | buffa | prost | prost (bytes) | protobuf-v4 | Go |
-|---------|------:|------:|------:|------:|------:|
-| ApiResponse | 2,543 | 1,810 (−29%) | — | 1,013 (−60%) | 560 (−78%) |
-| LogRecord | 4,018 | 3,093 (−23%) | — | 1,642 (−59%) | 303 (−92%) |
-| AnalyticsEvent | 656 | 357 (−46%) | — | 511 (−22%) | 160 (−76%) |
-| GoogleMessage1 | 2,594 | 1,808 (−30%) | — | 869 (−67%) | 360 (−86%) |
-| MediaFrame | 45,990 | 38,514 (−16%) | — | 10,463 (−77%) | 1,647 (−96%) |
+| Message | buffa | buffa (view) | prost | prost (bytes) | protobuf-v4 | Go |
+|---------|------:|------:|------:|------:|------:|------:|
+| ApiResponse | 2,566 | 2,537 (−1%) | 1,801 (−30%) | — | 1,033 (−60%) | 561 (−78%) |
+| LogRecord | 4,029 | 4,703 (+17%) | 3,116 (−23%) | — | 1,651 (−59%) | 305 (−92%) |
+| AnalyticsEvent | 582 | 623 (+7%) | 359 (−38%) | — | 509 (−13%) | 161 (−72%) |
+| GoogleMessage1 | 2,441 | 2,725 (+12%) | 1,817 (−26%) | — | 865 (−65%) | 362 (−85%) |
+| MediaFrame | 43,830 | 45,425 (+4%) | 38,652 (−12%) | — | 10,616 (−76%) | 1,673 (−96%) |
### Build + binary encode
| Message | buffa | buffa (view) |
|---------|------:|------:|
-| ApiResponse | 790 | 1,799 (+128%) |
-| LogRecord | 531 | 3,080 (+480%) |
-| AnalyticsEvent | 408 | 1,178 (+189%) |
-| GoogleMessage1 | 935 | 1,251 (+34%) |
+| ApiResponse | 732 | 1,649 (+125%) |
+| LogRecord | 498 | 2,843 (+471%) |
+| AnalyticsEvent | 520 | 1,166 (+124%) |
+| GoogleMessage1 | 818 | 1,169 (+43%) |
+| MediaFrame | 20,893 | 52,910 (+153%) |
### JSON encode
| Message | buffa | prost | Go |
|---------|------:|------:|------:|
-| ApiResponse | 875 | 943 (+8%) | 114 (−87%) |
-| LogRecord | 1,294 | 1,407 (+9%) | 136 (−89%) |
-| AnalyticsEvent | 786 | 843 (+7%) | 51 (−93%) |
-| GoogleMessage1 | 961 | 1,007 (+5%) | 122 (−87%) |
-| MediaFrame | 1,423 | 1,449 (+2%) | 206 (−86%) |
+| ApiResponse | 872 | 942 (+8%) | 115 (−87%) |
+| LogRecord | 1,332 | 1,401 (+5%) | 139 (−90%) |
+| AnalyticsEvent | 766 | 849 (+11%) | 52 (−93%) |
+| GoogleMessage1 | 968 | 1,033 (+7%) | 125 (−87%) |
+| MediaFrame | 1,460 | 1,445 (−1%) | 209 (−86%) |
### JSON decode
| Message | buffa | prost | Go |
|---------|------:|------:|------:|
-| ApiResponse | 706 | 303 (−57%) | 67 (−90%) |
-| LogRecord | 757 | 696 (−8%) | 107 (−86%) |
-| AnalyticsEvent | 268 | 233 (−13%) | 45 (−83%) |
-| GoogleMessage1 | 640 | 258 (−60%) | 70 (−89%) |
-| MediaFrame | 1,942 | 1,954 (+1%) | 262 (−87%) |
+| ApiResponse | 680 | 299 (−56%) | 68 (−90%) |
+| LogRecord | 795 | 701 (−12%) | 108 (−86%) |
+| AnalyticsEvent | 268 | 239 (−11%) | 45 (−83%) |
+| GoogleMessage1 | 649 | 253 (−61%) | 71 (−89%) |
+| MediaFrame | 1,910 | 1,958 (+3%) | 264 (−86%) |
diff --git a/benchmarks/gen-datasets/Cargo.lock b/benchmarks/gen-datasets/Cargo.lock
index 6b96b4f..8e8705e 100644
--- a/benchmarks/gen-datasets/Cargo.lock
+++ b/benchmarks/gen-datasets/Cargo.lock
@@ -16,7 +16,7 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "buffa"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"bytes",
"hashbrown 0.15.5",
@@ -28,7 +28,7 @@ dependencies = [
[[package]]
name = "buffa-build"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-codegen",
@@ -37,7 +37,7 @@ dependencies = [
[[package]]
name = "buffa-codegen"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-descriptor",
@@ -50,7 +50,7 @@ dependencies = [
[[package]]
name = "buffa-descriptor"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
]
diff --git a/buffa-build/Cargo.toml b/buffa-build/Cargo.toml
index 6a4dea3..0af6468 100644
--- a/buffa-build/Cargo.toml
+++ b/buffa-build/Cargo.toml
@@ -13,6 +13,6 @@ categories = ["development-tools::build-utils"]
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
-buffa = { path = "../buffa", version = "0.4.0" }
+buffa = { path = "../buffa", version = "0.5.0" }
buffa-codegen = { workspace = true }
tempfile = "3"
diff --git a/buffa-codegen/Cargo.toml b/buffa-codegen/Cargo.toml
index a091661..c0b39e7 100644
--- a/buffa-codegen/Cargo.toml
+++ b/buffa-codegen/Cargo.toml
@@ -11,7 +11,7 @@ categories = ["development-tools::build-utils"]
[dependencies]
# `path` wins for local workspace development; `version` is used on publish.
-buffa = { path = "../buffa", version = "0.4.0" }
+buffa = { path = "../buffa", version = "0.5.0" }
buffa-descriptor = { workspace = true }
prettyplease = { workspace = true }
proc-macro2 = { workspace = true }
diff --git a/buffa/src/lib.rs b/buffa/src/lib.rs
index 5884fc7..77e888e 100644
--- a/buffa/src/lib.rs
+++ b/buffa/src/lib.rs
@@ -96,7 +96,7 @@
//! # `no_std`
//!
//! ```toml
-//! buffa = { version = "0.4", default-features = false }
+//! buffa = { version = "0.5", default-features = false }
//! ```
//!
//! Generated code uses `::buffa::alloc::string::String` etc. rather than
diff --git a/docs/guide.md b/docs/guide.md
index bd11893..30e3bcd 100644
--- a/docs/guide.md
+++ b/docs/guide.md
@@ -9,11 +9,11 @@ Add buffa to your project:
```toml
# Cargo.toml
[dependencies]
-buffa = "0.4"
-buffa-types = "0.4" # well-known types (Timestamp, Duration, Any, etc.)
+buffa = "0.5"
+buffa-types = "0.5" # well-known types (Timestamp, Duration, Any, etc.)
[build-dependencies]
-buffa-build = "0.4"
+buffa-build = "0.5"
```
### Feature flags
@@ -28,8 +28,8 @@ Both `buffa` and `buffa-types` share the same feature flag names:
```toml
# Enable JSON support
-buffa = { version = "0.4", features = ["json"] }
-buffa-types = { version = "0.4", features = ["json"] }
+buffa = { version = "0.5", features = ["json"] }
+buffa-types = { version = "0.5", features = ["json"] }
```
## Prerequisites
@@ -165,7 +165,7 @@ This requires `buffa-types` as a dependency in your `Cargo.toml`:
```toml
[dependencies]
-buffa-types = "0.4"
+buffa-types = "0.5"
```
`buffa-types` is a pure source crate — it does **not** run `protoc` or any code generation at build time. If your protos use WKTs but you generate your own Rust code ahead-of-time (via `buf generate` or a `protoc` script), then `buffa` + `buffa-types` is your entire runtime dependency surface.
@@ -295,25 +295,25 @@ Download the binaries for your platform from the [releases page](https://github.
```sh
# Download binaries + cosign signatures + certificates (both plugins match)
-gh release download v0.4.0 --repo anthropics/buffa \
+gh release download v0.5.0 --repo anthropics/buffa \
--pattern 'protoc-gen-buffa*-linux-x86_64*'
# Verify with GitHub attestations (requires gh CLI ≥ 2.49)
-gh attestation verify protoc-gen-buffa-v0.4.0-linux-x86_64 --repo anthropics/buffa
-gh attestation verify protoc-gen-buffa-packaging-v0.4.0-linux-x86_64 --repo anthropics/buffa
+gh attestation verify protoc-gen-buffa-v0.5.0-linux-x86_64 --repo anthropics/buffa
+gh attestation verify protoc-gen-buffa-packaging-v0.5.0-linux-x86_64 --repo anthropics/buffa
# Or with cosign (standalone, no gh required) — shown for one binary
cosign verify-blob \
- --signature protoc-gen-buffa-v0.4.0-linux-x86_64.sig \
- --certificate protoc-gen-buffa-v0.4.0-linux-x86_64.pem \
+ --signature protoc-gen-buffa-v0.5.0-linux-x86_64.sig \
+ --certificate protoc-gen-buffa-v0.5.0-linux-x86_64.pem \
--certificate-identity-regexp "github.com/anthropics/buffa" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
- protoc-gen-buffa-v0.4.0-linux-x86_64
+ protoc-gen-buffa-v0.5.0-linux-x86_64
# Install both
-chmod +x protoc-gen-buffa-v0.4.0-linux-x86_64 protoc-gen-buffa-packaging-v0.4.0-linux-x86_64
-mv protoc-gen-buffa-v0.4.0-linux-x86_64 ~/.local/bin/protoc-gen-buffa
-mv protoc-gen-buffa-packaging-v0.4.0-linux-x86_64 ~/.local/bin/protoc-gen-buffa-packaging
+chmod +x protoc-gen-buffa-v0.5.0-linux-x86_64 protoc-gen-buffa-packaging-v0.5.0-linux-x86_64
+mv protoc-gen-buffa-v0.5.0-linux-x86_64 ~/.local/bin/protoc-gen-buffa
+mv protoc-gen-buffa-packaging-v0.5.0-linux-x86_64 ~/.local/bin/protoc-gen-buffa-packaging
```
Available platforms: `linux-x86_64`, `linux-aarch64`, `darwin-x86_64`, `darwin-aarch64`, `windows-x86_64` (`.exe`). All releases include SHA-256 checksums, Sigstore cosign signatures, and signed SLSA build provenance for supply chain verification.
@@ -369,7 +369,7 @@ Plugin options (passed via `opt:`):
```yaml
version: v2
plugins:
- - remote: buf.build/anthropic/buffa:v0.4.0
+ - remote: buf.build/anthropic/buffa:v0.5.0
out: src/generated
opt: [views=true]
```
@@ -965,7 +965,7 @@ Enable the `json` feature and `generate_json(true)` in your build config:
```toml
# Cargo.toml
[dependencies]
-buffa = { version = "0.4", features = ["json"] }
+buffa = { version = "0.5", features = ["json"] }
serde_json = "1"
```
@@ -1021,7 +1021,7 @@ Enable the `text` feature and `generate_text(true)`:
```toml
# Cargo.toml
[dependencies]
-buffa = { version = "0.4", features = ["text"] }
+buffa = { version = "0.5", features = ["text"] }
```
```rust,ignore
@@ -1152,8 +1152,8 @@ let obj = Struct::from_fields([
Buffa works without `std` (requires `alloc`):
```toml
-buffa = { version = "0.4", default-features = false }
-buffa-types = { version = "0.4", default-features = false }
+buffa = { version = "0.5", default-features = false }
+buffa-types = { version = "0.5", default-features = false }
```
In `no_std` mode:
diff --git a/docs/migration-from-prost.md b/docs/migration-from-prost.md
index aaf0f2e..247e86e 100644
--- a/docs/migration-from-prost.md
+++ b/docs/migration-from-prost.md
@@ -9,12 +9,12 @@ A step-by-step guide for migrating an existing prost-based project to buffa.
[dependencies]
-prost = "0.13"
-prost-types = "0.13"
-+buffa = "0.4"
-+buffa-types = "0.4"
++buffa = "0.5"
++buffa-types = "0.5"
[build-dependencies]
-prost-build = "0.13"
-+buffa-build = "0.4"
++buffa-build = "0.5"
```
If you use JSON serialization via `pbjson`:
@@ -23,8 +23,8 @@ If you use JSON serialization via `pbjson`:
[dependencies]
-pbjson = "0.7"
-pbjson-types = "0.7"
-+buffa = { version = "0.4", features = ["json"] }
-+buffa-types = { version = "0.4", features = ["json"] }
++buffa = { version = "0.5", features = ["json"] }
++buffa-types = { version = "0.5", features = ["json"] }
serde_json = "1"
-
-[build-dependencies]
diff --git a/docs/migration-from-protobuf.md b/docs/migration-from-protobuf.md
index 463814f..0a26275 100644
--- a/docs/migration-from-protobuf.md
+++ b/docs/migration-from-protobuf.md
@@ -12,13 +12,13 @@ This guide covers migration from both versions of the `protobuf` crate:
```diff
[dependencies]
-protobuf = "3"
-+buffa = "0.4"
-+buffa-types = "0.4"
++buffa = "0.5"
++buffa-types = "0.5"
[build-dependencies]
-protobuf-codegen = "3"
-protoc-bin-vendored = "3" # if using vendored protoc
-+buffa-build = "0.4"
++buffa-build = "0.5"
```
### From Google v4
@@ -26,12 +26,12 @@ This guide covers migration from both versions of the `protobuf` crate:
```diff
[dependencies]
-protobuf = "=4.33.1-release"
-+buffa = "0.4"
-+buffa-types = "0.4"
++buffa = "0.5"
++buffa-types = "0.5"
[build-dependencies]
-protobuf-codegen = "=4.33.1-release"
-+buffa-build = "0.4"
++buffa-build = "0.5"
```
## 2. Rewrite `build.rs`
diff --git a/examples/addressbook/Cargo.lock b/examples/addressbook/Cargo.lock
index 463fb18..5086f23 100644
--- a/examples/addressbook/Cargo.lock
+++ b/examples/addressbook/Cargo.lock
@@ -16,7 +16,7 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "buffa"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"bytes",
"hashbrown 0.15.5",
@@ -28,7 +28,7 @@ dependencies = [
[[package]]
name = "buffa-build"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-codegen",
@@ -37,7 +37,7 @@ dependencies = [
[[package]]
name = "buffa-codegen"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-descriptor",
@@ -50,14 +50,14 @@ dependencies = [
[[package]]
name = "buffa-descriptor"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
]
[[package]]
name = "buffa-types"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"bytes",
diff --git a/examples/conflicts/Cargo.lock b/examples/conflicts/Cargo.lock
index fbe1dc0..34ba50a 100644
--- a/examples/conflicts/Cargo.lock
+++ b/examples/conflicts/Cargo.lock
@@ -16,7 +16,7 @@ checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "buffa"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"bytes",
"hashbrown 0.15.5",
@@ -28,7 +28,7 @@ dependencies = [
[[package]]
name = "buffa-build"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-codegen",
@@ -37,7 +37,7 @@ dependencies = [
[[package]]
name = "buffa-codegen"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-descriptor",
@@ -50,7 +50,7 @@ dependencies = [
[[package]]
name = "buffa-descriptor"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"buffa",
]
diff --git a/examples/envelope/Cargo.lock b/examples/envelope/Cargo.lock
index 9e7cf05..432d841 100644
--- a/examples/envelope/Cargo.lock
+++ b/examples/envelope/Cargo.lock
@@ -22,7 +22,7 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "buffa"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"base64",
"bytes",
@@ -35,7 +35,7 @@ dependencies = [
[[package]]
name = "buffa-build"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-codegen",
@@ -44,7 +44,7 @@ dependencies = [
[[package]]
name = "buffa-codegen"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
"buffa-descriptor",
@@ -57,7 +57,7 @@ dependencies = [
[[package]]
name = "buffa-descriptor"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
]
diff --git a/examples/logging/Cargo.lock b/examples/logging/Cargo.lock
index b319be1..e273801 100644
--- a/examples/logging/Cargo.lock
+++ b/examples/logging/Cargo.lock
@@ -4,7 +4,7 @@ version = 4
[[package]]
name = "buffa"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"bytes",
"hashbrown",
@@ -16,7 +16,7 @@ dependencies = [
[[package]]
name = "buffa-types"
-version = "0.3.0"
+version = "0.5.0"
dependencies = [
"buffa",
"bytes",
diff --git a/protoc-gen-buffa-packaging/Cargo.toml b/protoc-gen-buffa-packaging/Cargo.toml
index 2f0e6d1..fed2306 100644
--- a/protoc-gen-buffa-packaging/Cargo.toml
+++ b/protoc-gen-buffa-packaging/Cargo.toml
@@ -10,5 +10,5 @@ keywords = ["protobuf", "protocol-buffers", "protoc", "plugin", "codegen"]
categories = ["development-tools::build-utils", "command-line-utilities"]
[dependencies]
-buffa = { path = "../buffa", version = "0.4.0" }
+buffa = { path = "../buffa", version = "0.5.0" }
buffa-codegen = { workspace = true }
diff --git a/protoc-gen-buffa/Cargo.toml b/protoc-gen-buffa/Cargo.toml
index 78ce186..9bfa17d 100644
--- a/protoc-gen-buffa/Cargo.toml
+++ b/protoc-gen-buffa/Cargo.toml
@@ -10,5 +10,5 @@ keywords = ["protobuf", "protocol-buffers", "protoc", "plugin", "codegen"]
categories = ["development-tools::build-utils", "command-line-utilities"]
[dependencies]
-buffa = { path = "../buffa", version = "0.4.0" }
+buffa = { path = "../buffa", version = "0.5.0" }
buffa-codegen = { workspace = true }