Skip to content

(Server)DeviceFeature cleanup and optimization#852

Open
jsmnbom wants to merge 5 commits intobuttplugio:devfrom
jsmnbom:feature-cleanup
Open

(Server)DeviceFeature cleanup and optimization#852
jsmnbom wants to merge 5 commits intobuttplugio:devfrom
jsmnbom:feature-cleanup

Conversation

@jsmnbom
Copy link
Copy Markdown
Contributor

@jsmnbom jsmnbom commented Mar 31, 2026

Refactors DeviceFeature and ServerDeviceFeature to replace structs with many Option fields with SmallVecEnumMap — a compact, SmallVec-backed collection that serializes/deserializes as a JSON object. While memory optimization was the primary goal, I took the opportunity to do a general cleanup of the surrounding code, keeping the scope contained and small. This dramatically reduces struct sizes while preserving the existing wire format and config file format.

Key Changes

Memory & Performance

  • SmallVecEnumMap — New utility (buttplug_core::util::small_vec_enum_map) that stores enum variants in a SmallVec and round-trips through a JSON object ({"vibrate": {...}, "rotate": {...}}). Optimized for the common case of one output and maybe one input (N = 1 means zero heap allocations). Falls back to heap allocation when more entries are present. The serde code is large but straightforward overall.
  • Struct size reductions (per rust-analyzer):
    • DeviceFeature: 584 → 120 bytes
    • ServerDeviceFeature: 904 → 184 bytes
  • Custom RangeInclusive (buttplug_core::util::range) — Smaller than std::ops::RangeInclusive and serializes cleanly without scattered serde helpers. Removes range_serialize.rs and inline range_vec_serde modules.

API & Ergonomics

  • Strum EnumDiscriminants for OutputType/InputType — Derives ServerOutputType and ServerInputType internally. Adding a variant to one enum but not the other becomes a compile-time error.
  • Reduced getset usagegetset is retained in buttplug_core and buttplug_client where structs are exposed to end users, but reduced from internal structs where it is only used to preserve encapsulation and invariants. Each proc-macro adds compile time, so trimming it down helps.
  • API call-site cleanup across client/server code — Replaced nested Option/map access patterns with helper methods like contains_output, contains_input, get_output_limits, and get_input, reducing branching and repeated unwrap/lookup code.
  • Examples updated to the new API styleexamples/src/bin/device_control.rs and examples/src/bin/device_tester.rs were updated to use the new helper-driven access pattern.

Serialization

  • Wire format and config file format preservedSmallVecEnumMap serializes as a JSON object keyed by variant name, matching the previous struct-with-Option-fields format exactly.
  • RangeWithLimit now uses #[serde(transparent)] instead of custom Serialize/Deserialize implementations.
  • Input command representation updated — Internal command sets moved from HashSet<InputCommandType> to BitFlags<InputCommandType>, serialized via a dedicated helper as the same sequence-of-command-names shape as before.

Dependencies

  • Added smallvec (with serde and const_generics) in buttplug_core for compact inline storage.
  • Added enumflags2 in buttplug_core and buttplug_server_device_config for flag-style command storage.

Validation & Scope

  • Tests adjusted for the new feature access patterns — Updated crates/buttplug_tests/tests/util/device_test/client/client_v4/mod.rs.
  • Formatting-only follow-up commit included — Final commit applies rustfmt to changed files.

Potential Concerns

  • Duplicate variant construction — A feature could theoretically be constructed in code with two of the same OutputType. The serialization path prevents this from occurring in practice, but it's worth noting as a difference from the previous struct-based approach where each field was a distinct Option.

@jsmnbom
Copy link
Copy Markdown
Contributor Author

jsmnbom commented Mar 31, 2026

Once again part of #833, focused on memory improvements, but also took time to do general usability improvements.

@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Mar 31, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 57 complexity . -19 duplication

Metric Results
Complexity 57
Duplication -19

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

@jsmnbom jsmnbom changed the base branch from master to dev April 1, 2026 00:07
@qdot
Copy link
Copy Markdown
Member

qdot commented Apr 2, 2026

Heads up, I had to rebase dev to notch out the lovense remote/connect removals (which need to wait until a minor version update, and I need to do a bugfix version like, now). This makes your PR look very weird, but rest assured it's 100% my fault. Recommended steps here if you want to clean up (but read rest of message before starting):

  • Check out dev as is as a new branch
  • Cherry-pick your changes from this branch onto it.
  • Rename new branch to feature-cleanup
  • force push

This is really only if you've got more changes coming into this, otherwise after I get this next version released I plan on addressing this PR so I can handle the branch fuckery otherwise.

@jsmnbom
Copy link
Copy Markdown
Contributor Author

jsmnbom commented Apr 2, 2026

I don't think I got any more changes that should be in this PR so I'll let you handle that ^^

@jsmnbom
Copy link
Copy Markdown
Contributor Author

jsmnbom commented Apr 3, 2026

Actually, I think I have an improvement. Lemme try ^^

jsmnbom added 5 commits April 3, 2026 16:50
- Simplified device output filtering in ButtplugClientDevice by using `values()` and `contains_output()`.
- Updated input availability check to utilize `values()` and `contains_input()`.
- Refactored input feature retrieval to streamline the process using `values()` and `contains_input()`.
- Enhanced `DeviceFeature` to use `SmallVecEnumMap` for output and input features, improving memory efficiency and serialization.
- Introduced `RangeInclusive` utility for better range handling.
- Added custom serializers for `BitFlags<T>` to maintain wire format consistency.
- Implemented `SmallVecEnumMap` for compact enum storage and serialization.
- Updated dependencies in Cargo.toml to include `smallvec` and `enumflags2`.
… and performance

- Consolidated range handling in RangeWithLimit struct, removing unnecessary internal_base field.
- Simplified serialization and deserialization for RangeWithLimit.
- Replaced Option types with SmallVecEnumMap for output and input properties in ServerDeviceFeature for better performance and memory efficiency.
- Updated ServerDeviceFeatureOutput and ServerDeviceFeatureInput to use enum discriminants for cleaner variant handling.
- Enhanced methods for checking and retrieving outputs and inputs, improving usability.
- Removed redundant code and improved overall structure for better maintainability.
}
}

impl From<ServerDeviceFeatureOutput> for DeviceFeatureOutput {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the new logic of filtering disabled is present anymore after the cherry pick stuff - may need to double check

@jsmnbom
Copy link
Copy Markdown
Contributor Author

jsmnbom commented Apr 3, 2026

Nevermind, I thought I had a good idea where I would change SmallVecEnumMap to be NewtypeEnumMap, and use a LiteMap as the backing store instead - with the eventual hope of being able to databake, but i think 1) that is overcomplicating things and 2) i couldn't figure out a good way of storing the enum variant keys in the map effectively and 3) not sure it would even make using databake easier since bespoke static/slice-based types would still be needed for the config types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants