dbc
A clean, zero-dependency DBC (CAN Database) file parser and editor for Rust.
Minimum Supported Rust Version (MSRV)
The minimum supported Rust version is 1.85.0. The crate is tested against this version and may use features available in it or later.
Features
- ✅ Zero dependencies - Pure Rust implementation
- ✅ no_std + alloc support - Works on embedded targets without the standard library
- ✅ Full editing & writing - Modify and save DBC files with the same structs
- ✅ Feature flag control - Optional
stdfeature for desktop conveniences - ✅ Well tested - Tested with real-world DBC files
Quick Start
use Dbc;
// Parse a DBC file
let content = read_to_string.unwrap;
let dbc = parse.expect;
// Access messages and signals (read-only)
if let Some = dbc.messages.iter.find
Feature Flags
The crate is #![no_std] + alloc friendly. The following features are available:
| Feature | Default | Description |
|---|---|---|
std |
✅ | Enables standard library integration (I/O helpers, tests). Disable it for pure no_std builds. |
Example (no_std build):
[]
= { = "1", = false }
Example (force std explicitly):
[]
= { = "1", = ["std"] }
Internationalization (i18n)
Error messages can be localized at build time using feature flags. The language is selected during compilation and cannot be changed at runtime.
Supported Languages
The following languages are currently supported:
| Language | Feature Flag | Code |
|---|---|---|
| English (default) | (none) | en |
| French | lang-fr |
fr |
| Spanish | lang-es |
es |
| German | lang-de |
de |
| Japanese | lang-ja |
ja |
Selecting a Language
To use a specific language, enable the corresponding feature flag:
Example (French):
[]
= { = "1", = ["lang-fr"] }
Example (German):
[]
= { = "1", = ["lang-de"] }
Example (Command line):
Note: Language features are mutually exclusive. If multiple language features are enabled, the last one in the feature list will be used. The default language (English) is used when no language feature is specified.
Translation Status
⚠️ Warning: The translations have been generated and may contain errors or inaccuracies. They have not been fully verified by native speakers. If you encounter translation issues, please report them or contribute improvements (see Contributing section below).
Contributing Translations
Contributions to improve existing translations or add new languages are welcome!
Updating Existing Translations
- Navigate to
src/error/lang/directory - Open the language file you want to update (e.g.,
fr.rsfor French) - Edit the string constants with improved translations
- Submit a pull request with your changes
Adding a New Language
- Create a new file in
src/error/lang/(e.g.,it.rsfor Italian) - Copy the structure from
en.rsand translate all constants - Add the language module to
src/error/lang/mod.rs:use it as lang; - Add the feature flag to
Cargo.toml:= [] # Italian - Update this README with the new language in the Supported Languages table
- Submit a pull request
Translation Guidelines:
- Maintain the same constant names across all language files
- Keep format placeholders (
{}) in the same positions - Ensure technical terms are accurately translated
- Test that error messages display correctly with the new language
DBC Format Feature Support
This table shows which DBC file format features are currently implemented:
Core Features
| Feature | Statement | Parse | Write | Notes |
|---|---|---|---|---|
| Version | VERSION |
✅ | ✅ | Database version string |
| New Symbols | NS_ |
⚠️ | ❌ | Parsed but ignored (no errors) |
| Bit Timing | BS_ |
⚠️ | ❌ | Parsed but ignored (no errors) |
| Bus Nodes | BU_ |
✅ | ✅ | List of ECUs on the bus |
| Messages | BO_ |
✅ | ✅ | CAN message definitions |
| Signals | SG_ |
✅ | ✅ | Signal definitions (see limitations below) |
| Comments | // |
✅ | ❌ | Single-line comments parsed but not preserved |
Signal Features (SG_)
| Feature | Parse | Write | Notes |
|---|---|---|---|
| Signal name | ✅ | ✅ | |
| Start bit | ✅ | ✅ | |
| Length (bits) | ✅ | ✅ | |
Byte order (@0/@1) |
⚠️ | ❌ | Parsed but not stored; write always uses @1+ |
Sign (+/-) |
⚠️ | ❌ | Parsed but not stored; write always uses + |
| Factor (scaling) | ✅ | ✅ | |
| Offset | ✅ | ✅ | |
| Min value | ✅ | ✅ | |
| Max value | ✅ | ✅ | |
| Unit | ✅ | ✅ | |
| Receivers | ❌ | ❌ | Receiver nodes not parsed or written |
Extended Features
| Feature | Statement | Parse | Write | Notes |
|---|---|---|---|---|
| Value Tables | VAL_TABLE_ |
❌ | ❌ | Named enumeration tables |
| Value Descriptions | VAL_ |
❌ | ❌ | Enum values for signals |
| Comments | CM_ |
❌ | ❌ | Structured comments for messages/signals/nodes |
| Attribute Definitions | BA_DEF_ |
❌ | ❌ | Custom attribute definitions |
| Default Attributes | BA_DEF_DEF_ |
❌ | ❌ | Default values for attributes |
| Attributes | BA_ |
❌ | ❌ | Attribute assignments |
| Signal Groups | SIG_GROUP_ |
❌ | ❌ | Group related signals |
| Signal Value Types | SIG_VALTYPE_ |
❌ | ❌ | Link signals to value tables |
| Environment Variables | EV_ |
❌ | ❌ | Environment variable definitions |
| Signal Type References | SIG_TYPE_REF_ |
❌ | ❌ | Reference signal types |
| Signal Multiplexing | SG_MUL_VAL_ |
❌ | ❌ | Multiplexed signals |
| Message Transmitters | BO_TX_BU_ |
❌ | ❌ | Multiple transmitters per message |
| Node Relations | BU_SG_REL_, BU_EV_REL_, BU_BO_REL_ |
❌ | ❌ | Node relationship definitions |
Advanced Features
| Feature | Parse | Write | Notes |
|---|---|---|---|
| 29-bit Extended CAN IDs | ✅ | ✅ | Parsed as u32 but not validated |
| Signal multiplexing | ❌ | ❌ | |
| Signal type definitions | ❌ | ❌ | |
| Category definitions | ❌ | ❌ | |
| Filters | ❌ | ❌ |
Implementation Status Summary
✅ Fully Implemented
- Basic DBC file structure (VERSION, BU_, BO_, SG_)
- Signal parsing with scaling, offset, min/max, units
- Message and signal editing
- Round-trip save/load for basic features
⚠️ Partially Implemented
- Signal byte order and sign: Parsed but not stored; always written as
@1+ - Comments: Single-line
//comments are ignored during parsing
❌ Not Yet Implemented
- Value tables and enumerations (VAL_TABLE_, VAL_)
- Structured comments (CM_)
- Attributes (BA_DEF_, BA_DEF_DEF_, BA_)
- Signal groups (SIG_GROUP_)
- Environment variables (EV_)
- Signal receivers in signal definitions
- Signal multiplexing
- Advanced node relationships
Example DBC File
VERSION "1.0"
BU_: ECM TCM
BO_ 256 EngineData : 8 ECM
SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C"
BO_ 512 BrakeData : 4 TCM
SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar"
Examples
Basic Parsing
use Dbc;
let content = read_to_string?;
let dbc = parse?;
println!;
println!;
println!;
Error Handling
use ;
match parse
Round-Trip: Parse, Modify, Save
use ;
// Parse existing DBC
let dbc = parse?;
// Create new signal using builder
let new_signal = builder
.name
.start_bit
.length
.byte_order
.unsigned
.factor
.offset
.min
.max
.unit
.receivers
.build?;
// Create new message with the signal using builder
let new_message = builder
.id
.name
.dlc
.sender
.add_signal
.build?;
// Note: Dbc doesn't have a mutable API yet, so you'd need to:
// 1. Extract all data via getters
// 2. Create new Dbc with modified data
let version = dbc.version;
let nodes = dbc.nodes;
let mut messages: = dbc.messages.to_vec;
messages.push;
let modified_dbc = builder
.version
.nodes
.messages
.build?;
// Save back to DBC format
let saved_content = modified_dbc.save;
write?;
Finding Messages and Signals
use Dbc;
let dbc = parse?;
// Find message by ID
let engine_msg = dbc.messages.iter.find;
// Find signal by name in a specific message
if let Some = engine_msg
// Iterate all messages and signals
for message in dbc.messages
Validation Example
use ;
// This will fail validation - signal overlaps
let signal1 = builder
.name
.start_bit
.length
.byte_order
.unsigned
.min
.max
.build?;
let signal2 = builder
.name
.start_bit
.length
.byte_order
.unsigned
.min
.max
.build?;
// This will return an error due to signal overlap
match builder
.id
.name
.dlc
.sender
.add_signal
.add_signal
.build
no_std Usage
// In a no_std environment (with alloc)
use Dbc;
// Parse from a byte slice (no file I/O needed)
let dbc_bytes = b"VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM\n SG_ RPM : 0|16@1+ (0.25,0) [0|8000] \"rpm\"";
let dbc = parse_bytes?;
// Access data
let version = dbc.version;
let messages = dbc.messages;
// Save to string (can be written to storage later)
let saved = dbc.save;
Working with Different Input Sources
use Dbc;
// From string slice
let dbc1 = parse?;
// From bytes
let bytes = b"VERSION \"1.0\"...";
let dbc2 = parse_bytes?;
// From String
let string = Stringfrom;
let dbc3 = parse_from?;
// From std::io::Read (requires std feature)
Limitations
-
Byte Order & Sign: Currently, all signals are written with
@1+(big-endian, unsigned) regardless of how they were parsed. The parser extracts these values but doesn't store them. -
Signal Receivers: Receiver nodes in signal definitions are not parsed or preserved.
-
Extended Features: Many advanced DBC features (attributes, value tables, comments, etc.) are not yet supported. Files containing these features will parse successfully but the extended data will be lost on save.
-
Validation: Limited validation of CAN IDs, DLC values, and signal bit ranges.
Usage Patterns
Common Workflows
Parse and Inspect
use Dbc;
let dbc = parse?;
// Get version information
let version = dbc.version;
println!;
// List all nodes
let nodes = dbc.nodes;
println!;
// Iterate messages
for message in dbc.messages
Find Specific Data
use Dbc;
let dbc = parse?;
// Find message by ID
let engine_msg = dbc.messages.iter.find;
// Find signal by name
if let Some = engine_msg
Create and Modify DBC Files
use ;
// Create from scratch
let version = builder.major.minor.build?;
let nodes = builder
.add_node
.add_node
.add_node
.build;
let signal = builder
.name
.start_bit
.length
.byte_order
.unsigned
.factor
.offset
.min
.max
.unit
.receivers
.build?;
let message = builder
.id
.name
.dlc
.sender
.add_signal
.build?;
let dbc = builder
.version
.nodes
.add_message
.build?;
// Save to string
let dbc_string = dbc.save;
Modify Existing DBC
use ;
let dbc = parse?;
// Extract current data
let version = dbc.version;
let nodes = dbc.nodes;
let mut messages: = dbc.messages.to_vec;
// Add new message using builder
let new_signal = builder
.name
.start_bit
.length
.byte_order
.unsigned
.factor
.offset
.min
.max
.receivers
.build?;
let new_message = builder
.id
.name
.dlc
.sender
.add_signal
.build?;
messages.push;
// Create modified DBC
let modified_dbc = builder
.version
.nodes
.messages
.build?;
let saved = modified_dbc.save;
Best Practices
- Always handle errors: Use
?operator ormatchto handle parsing errors gracefully - Validate before creating: Use builders (
.builder()) which include validation - Use getters: Access data through getter methods, not direct field access
- Clone when needed: Clone
VersionandNodeswhen creating newDbcinstances - Check signal ranges: Verify signal min/max values match your application requirements
Anti-Patterns to Avoid
❌ Don't: Access fields directly (they're private)
// This won't compile
let id = message.id; // Error: field is private
✅ Do: Use getter methods
let id = message.id; // Correct
❌ Don't: Ignore validation errors
// This might panic or create invalid data
let signal = builder
.name
.start_bit
.length
.build
.unwrap; // Don't use unwrap() in production!
✅ Do: Handle errors properly
match builder
.name
.start_bit
.length
.build
Performance Notes
Memory Usage
- String Storage: Uses
Box<str>for strings to reduce memory overhead compared toString - Pre-allocation: Vectors are pre-allocated with estimated capacity to reduce reallocations
- No String Interning: Each string is stored independently (no deduplication)
Parsing Performance
- Linear Complexity: Parsing is O(n) where n is the file size
- No Streaming: Entire file is parsed into memory (suitable for typical DBC file sizes)
- Validation: Validation occurs during parsing and construction, adding minimal overhead
Recommendations
- For very large DBC files (>10MB), consider splitting into multiple files
- If parsing many files, reuse
Dbcinstances when possible - Use
parse_bytes()instead ofparse()if you already have bytes to avoid UTF-8 validation
Troubleshooting
Common Issues
"Message ID out of valid range"
Problem: CAN ID exceeds maximum allowed value (0x1FFFFFFF for extended IDs)
Solution:
- Standard 11-bit IDs: Use 0-0x7FF (0-2047)
- Extended 29-bit IDs: Use 0x800-0x1FFFFFFF (2048-536870911)
// ❌ Invalid
let msg = builder
.id // Too large
.name
.dlc
.sender
.build?;
// ✅ Valid
let msg = builder
.id // Max extended ID
.name
.dlc
.sender
.build?;
"Signal extends beyond CAN message"
Problem: Signal start_bit + length exceeds message size (DLC * 8 bits)
Solution: Ensure signals fit within message boundaries
// ❌ Invalid: Signal extends to bit 64, but DLC=8 means max bit is 63
let sig = builder
.name
.start_bit
.length // 56 + 16 = 72 > 64
.build?;
let msg = builder
.id
.name
.dlc
.sender
.add_signal
.build?;
// ✅ Valid: Signal fits within 8-byte message (0-63)
let sig = builder
.name
.start_bit
.length // 0 + 64 = 64 (fits in 8 bytes)
.build?;
let msg = builder
.id
.name
.dlc
.sender
.add_signal
.build?;
"Signal overlap detected"
Problem: Multiple signals occupy overlapping bit ranges
Solution: Ensure signals don't overlap
// ❌ Invalid: Signals overlap at bits 8-15
let sig1 = builder
.name
.start_bit
.length // Bits 0-15
.build?;
let sig2 = builder
.name
.start_bit
.length // Bits 8-23 (overlaps!)
.build?;
// ✅ Valid: Signals don't overlap
let sig1 = builder
.name
.start_bit
.length // Bits 0-15
.build?;
let sig2 = builder
.name
.start_bit
.length // Bits 16-31 (no overlap)
.build?;
"Sender not in nodes"
Problem: Message sender is not listed in the nodes
Solution: Add the sender to the nodes list
// ❌ Invalid: "ECM" not in nodes
let nodes = builder.add_node.build?;
let msg = builder
.id
.name
.dlc
.sender
.build?;
let dbc = builder
.version
.nodes
.add_message
.build; // Error!
// ✅ Valid: "ECM" is in nodes
let nodes = builder.add_node.add_node.build?;
let msg = builder
.id
.name
.dlc
.sender
.build?;
let dbc = builder
.version
.nodes
.add_message
.build?; // OK
"Duplicate message ID"
Problem: Multiple messages have the same CAN ID
Solution: Use unique CAN IDs for each message
// ❌ Invalid: Duplicate IDs
let msg1 = builder
.id
.name
.dlc
.sender
.build?;
let msg2 = builder
.id
.name
.dlc
.sender
.build?; // Same ID!
// ✅ Valid: Unique IDs
let msg1 = builder
.id
.name
.dlc
.sender
.build?;
let msg2 = builder
.id
.name
.dlc
.sender
.build?; // Different IDs
Debugging Tips
- Enable verbose error messages: Error messages include context about what failed
- Check validation: Use builders (
.builder()) which validate input - Verify DBC format: Ensure your DBC file follows the correct format
- Test with minimal examples: Start with simple DBC files to isolate issues
Architecture & Design
Design Principles
- Immutability: All data structures are immutable after creation (read-only access)
- Validation: Input validation occurs at construction time, not at use time
- no_std First: Designed to work in
no_stdenvironments withalloc - Zero Dependencies: No external dependencies to keep the library lightweight
- Memory Efficiency: Uses
Box<str>and pre-allocated vectors to minimize memory usage
Module Structure
dbc-rs/
├── lib.rs # Main library entry point, re-exports
├── dbc.rs # DBC file structure and parsing
├── message.rs # CAN message definitions
├── signal.rs # Signal definitions with validation
├── nodes.rs # Node/ECU management
├── version.rs # Version string parsing
└── error/
├── mod.rs # Error types
├── messages.rs # Error message formatting
└── lang/ # Internationalized error messages
├── en.rs # English (default)
├── fr.rs # French
├── es.rs # Spanish
├── de.rs # German
└── ja.rs # Japanese
Data Flow
- Parsing:
Dbc::parse()→ tokenizes input → validates → creates structures - Construction:
new()methods → validate input → create immutable structures - Access: Getter methods provide read-only access to internal data
- Serialization:
save()/to_dbc_string()→ convert structures back to DBC format
Validation Strategy
- Parse-time validation: Basic format validation during parsing
- Construction-time validation: Comprehensive validation in
new()methods - Shared validation: Same validation logic used in both parsing and construction
- Early failure: Validation errors are returned immediately, not deferred
Error Handling
- Result-based: All fallible operations return
Result<T, Error> - Categorized errors: Errors are categorized by type:
Error::Signal- Signal-specific validation errorsError::Message- Message-specific validation errorsError::Dbc- DBC file-level errorsError::Version- Version parsing errorsError::Nodes- Node-related errorsError::InvalidData- General parsing/data errors
- Internationalized: Error messages can be localized at build time
- Descriptive: Error messages include context about what failed and why
Contributing
Contributions are welcome! Areas that need work:
- Full byte order and sign support in signals
- Signal receiver parsing
- Value tables and enumerations (VAL_TABLE_, VAL_)
- Structured comments (CM_)
- Attributes (BA_DEF_, BA_, etc.)
- Environment variables (EV_)
- Signal multiplexing support
- Translation improvements - Help verify and improve error message translations (see Internationalization section above)
License
dbc-rs is available under a dual-licensing model:
- Open Source: MIT OR Apache-2.0 (default) - See LICENSING.md for details
- Commercial: Available for proprietary use - See LICENSING.md for terms
For most users, the open-source license (MIT OR Apache-2.0) is sufficient. Commercial licenses are available for organizations that need additional flexibility or legal protection.
References
- DBC Format Specification - Detailed format documentation
- Vector Informatik: "DBC File Format Documentation Version 01/2007"
- CAN Specification (ISO 11898)
Test DBC Files
The test suite includes several DBC files in tests/data/:
complete.dbc- Comprehensive test file with multiple messages, signals, and extended DBC features (attributes, value tables, etc.). Created for testing parser robustness with real-world-like complexity.simple.dbc- Basic 2-message file for testing core parsing functionality.multiplexed.dbc- Tests multiple sensors and actuators with various signal types.minimal.dbc- Minimal valid DBC file for testing edge cases.extended_ids.dbc- Tests higher message IDs and small signal sizes.broadcast_signals.dbc- Tests broadcast receivers (*) and specific receiver nodes.
All test files are custom-created for this project to ensure comprehensive test coverage. For additional DBC files, consider:
- commaai/opendbc - Open-source collection of DBC files for various vehicle models
- CSS Electronics DBC Editor - Sample DBC files and documentation