Skip to content

Commit 4a1e228

Browse files
branchseerclaude
andauthored
refactor: migrate from bincode to wincode (#334)
## Why bincode is unmaintained. The author ceased development and published a [final 3.0.0 release](https://docs.rs/crate/bincode/3.0.0) containing only a compiler error to inform users. wincode is the recommended bincode-compatible drop-in replacement. ## Summary - Replace bincode 2.x with wincode across all 14 crates for in-place initialization and direct memory writes without intermediate staging buffers - Use wincode's foreign type adoption for `Arc<str>` and `Duration` (manual `SchemaWrite`/`SchemaRead` impls) - Borrowed zero-copy deserialization preserved for `&NativeStr`, `&NativePath`, `PathAccess<'a>`, `Payload<'a>` - Bump cache version from 10 → 11 ## Test plan - [x] `cargo check` passes - [x] `cargo clippy` passes (no warnings) - [x] All 23 workspace test suites pass (excluding 3 pre-existing fspy oxlint failures unrelated to this change) - [x] E2E snapshot tests pass - [x] Plan snapshot tests pass - [x] `fspy_shared` IPC channel tests pass (encoding/decoding round-trips correctly) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 52cd0f6 commit 4a1e228

File tree

40 files changed

+488
-322
lines changed

40 files changed

+488
-322
lines changed

Cargo.lock

Lines changed: 117 additions & 83 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ members = ["crates/*"]
66
authors = ["Vite+ Authors"]
77
edition = "2024"
88
license = "MIT"
9-
rust-version = "1.88.0"
9+
rust-version = "1.89.0"
1010

1111
[workspace.lints.rust]
1212
absolute_paths_not_starting_with_crate = "warn"
@@ -44,7 +44,7 @@ assert2 = "0.3.16"
4444
assertables = "9.8.1"
4545
async-trait = "0.1.89"
4646
base64 = "0.22.1"
47-
bincode = "2.0.1"
47+
wincode = "0.5.2"
4848
bindgen = "0.72.1"
4949
bitflags = "2.10.0"
5050
# The newest released version (0.3.0) of brush-parser has a bug that reports incorrect locations for some ast nodes.

crates/fspy/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ publish = false
66

77
[dependencies]
88
allocator-api2 = { workspace = true, features = ["alloc"] }
9-
bincode = { workspace = true }
9+
wincode = { workspace = true }
1010
bstr = { workspace = true, default-features = false }
1111
bumpalo = { workspace = true }
1212
const_format = { workspace = true, features = ["fmt"] }

crates/fspy/src/ipc.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use std::io;
22

3-
use bincode::borrow_decode_from_slice;
43
use fspy_shared::ipc::{
5-
BINCODE_CONFIG, PathAccess,
4+
PathAccess,
65
channel::{Receiver, ReceiverLockGuard},
76
};
87
use tokio::task::spawn_blocking;
@@ -32,11 +31,8 @@ impl OwnedReceiverLockGuard {
3231
}
3332

3433
pub fn iter_path_accesses(&self) -> impl Iterator<Item = PathAccess<'_>> {
35-
self.borrow_lock_guard().iter_frames().map(|frame| {
36-
let (path_access, decoded_size) =
37-
borrow_decode_from_slice::<PathAccess<'_>, _>(frame, BINCODE_CONFIG).unwrap();
38-
assert_eq!(decoded_size, frame.len());
39-
path_access
40-
})
34+
self.borrow_lock_guard()
35+
.iter_frames()
36+
.map(|frame| wincode::deserialize_exact(frame).unwrap())
4137
}
4238
}

crates/fspy/src/windows/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
use const_format::formatcp;
1010
use fspy_detours_sys::{DetourCopyPayloadToProcess, DetourUpdateProcessWithDll};
1111
use fspy_shared::{
12-
ipc::{BINCODE_CONFIG, PathAccess, channel::channel},
12+
ipc::{PathAccess, channel::channel},
1313
windows::{PAYLOAD_ID, Payload},
1414
};
1515
use futures_util::FutureExt;
@@ -109,7 +109,7 @@ impl SpyImpl {
109109
channel_conf: channel_conf.clone(),
110110
ansi_dll_path_with_nul: ansi_dll_path_with_nul.to_bytes(),
111111
};
112-
let payload_bytes = bincode::encode_to_vec(payload, BINCODE_CONFIG).unwrap();
112+
let payload_bytes = wincode::serialize(&payload).unwrap();
113113
// SAFETY: process_handle is valid, PAYLOAD_ID is a static GUID,
114114
// payload_bytes is a valid buffer with correct length
115115
let success = unsafe {

crates/fspy_preload_unix/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ crate-type = ["cdylib"]
88

99
[target.'cfg(unix)'.dependencies]
1010
anyhow = { workspace = true }
11-
bincode = { workspace = true }
11+
wincode = { workspace = true }
1212
bstr = { workspace = true, default-features = false }
1313
ctor = { workspace = true }
1414
fspy_shared = { workspace = true }

crates/fspy_preload_unix/src/client/mod.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ use std::{
66
sync::OnceLock,
77
};
88

9-
use bincode::{enc::write::SizeWriter, encode_into_slice, encode_into_writer};
109
use convert::{ToAbsolutePath, ToAccessMode};
11-
use fspy_shared::ipc::{BINCODE_CONFIG, PathAccess, channel::Sender};
10+
use fspy_shared::ipc::{PathAccess, channel::Sender};
1211
use fspy_shared_unix::{
1312
exec::ExecResolveConfig,
1413
payload::EncodedPayload,
1514
spawn::{PreExec, handle_exec},
1615
};
1716
use raw_exec::RawExec;
17+
use wincode::Serialize as _;
1818

1919
pub struct Client {
2020
encoded_payload: EncodedPayload,
@@ -72,17 +72,18 @@ impl Client {
7272
return Ok(());
7373
}
7474
let path_access = PathAccess { mode, path: path.into() };
75-
let mut size_writer = SizeWriter::default();
76-
encode_into_writer(path_access, &mut size_writer, BINCODE_CONFIG)?;
75+
let serialized_size = usize::try_from(PathAccess::serialized_size(&path_access)?)
76+
.expect("serialized size exceeds usize");
7777

78-
let frame_size = NonZeroUsize::new(size_writer.bytes_written)
78+
let frame_size = NonZeroUsize::new(serialized_size)
7979
.expect("fspy: encoded PathAccess should never be empty");
8080

8181
let mut frame = ipc_sender
8282
.claim_frame(frame_size)
8383
.expect("fspy: failed to claim frame in shared memory");
84-
let written_size = encode_into_slice(path_access, &mut frame, BINCODE_CONFIG)?;
85-
assert_eq!(written_size, size_writer.bytes_written);
84+
let mut writer: &mut [u8] = &mut frame;
85+
PathAccess::serialize_into(&mut writer, &path_access)?;
86+
assert_eq!(writer.len(), 0);
8687

8788
Ok(())
8889
}

crates/fspy_preload_windows/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition.workspace = true
77
crate-type = ["cdylib"]
88

99
[target.'cfg(target_os = "windows")'.dependencies]
10-
bincode = { workspace = true }
10+
wincode = { workspace = true }
1111
constcat = { workspace = true }
1212
fspy_detours_sys = { workspace = true }
1313
fspy_shared = { workspace = true }

crates/fspy_preload_windows/src/windows/client.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use std::{cell::SyncUnsafeCell, ffi::CStr, mem::MaybeUninit};
22

3-
use bincode::{borrow_decode_from_slice, encode_to_vec};
43
use fspy_detours_sys::DetourCopyPayloadToProcess;
54
use fspy_shared::{
6-
ipc::{BINCODE_CONFIG, PathAccess, channel::Sender},
5+
ipc::{PathAccess, channel::Sender},
76
windows::{PAYLOAD_ID, Payload},
87
};
98
use winapi::{shared::minwindef::BOOL, um::winnt::HANDLE};
@@ -15,9 +14,7 @@ pub struct Client<'a> {
1514

1615
impl<'a> Client<'a> {
1716
pub fn from_payload_bytes(payload_bytes: &'a [u8]) -> Self {
18-
let (payload, decoded_len) =
19-
borrow_decode_from_slice::<'a, Payload, _>(payload_bytes, BINCODE_CONFIG).unwrap();
20-
assert_eq!(decoded_len, payload_bytes.len());
17+
let payload: Payload<'a> = wincode::deserialize_exact(payload_bytes).unwrap();
2118

2219
let ipc_sender = match payload.channel_conf.sender() {
2320
Ok(sender) => Some(sender),
@@ -43,11 +40,11 @@ impl<'a> Client<'a> {
4340
let Some(sender) = &self.ipc_sender else {
4441
return;
4542
};
46-
sender.write_encoded(&access, BINCODE_CONFIG).expect("failed to send path access");
43+
sender.write_encoded(&access).expect("failed to send path access");
4744
}
4845

4946
pub unsafe fn prepare_child_process(&self, child_handle: HANDLE) -> BOOL {
50-
let payload_bytes = encode_to_vec(&self.payload, BINCODE_CONFIG).unwrap();
47+
let payload_bytes = wincode::serialize(&self.payload).unwrap();
5148
// SAFETY: FFI call to DetourCopyPayloadToProcess with valid handle and payload buffer
5249
unsafe {
5350
DetourCopyPayloadToProcess(

crates/fspy_seccomp_unotify/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2024"
55
publish = false
66

77
[target.'cfg(target_os = "linux")'.dependencies]
8-
bincode = { workspace = true }
8+
wincode = { workspace = true, features = ["derive"] }
99
libc = { workspace = true }
1010
nix = { workspace = true, features = ["process", "fs", "poll", "socket", "uio"] }
1111
passfd = { workspace = true, default-features = false, optional = true }

0 commit comments

Comments
 (0)