6.3 KiB
NORM Rust Bindings API Guide
This guide provides a detailed overview of the main components of the NORM Rust bindings API.
Core Components
The NORM Rust API follows a hierarchical structure similar to the underlying C API but with Rust idioms and safety guarantees:
Instance
└── Session
├── Object (Data)
├── Object (File)
└── Object (Stream)
Instance
Instance is the top-level object that represents a NORM protocol instance. It's the starting point
for all NORM operations.
use norm::Instance;
// Create a new NORM instance
let instance = Instance::new(false)?; // false = no priority boost
// Set cache directory for receiving files
instance.set_cache_directory("/tmp/norm")?;
// Process events
for event in instance.events() {
// Handle events
}
Session
A Session represents a NORM protocol session, which can operate in sender mode, receiver mode, or both.
use norm::{Instance, Session};
let instance = Instance::new(false)?;
// Create a session with address, port, and node ID
let session = instance.create_session("224.1.2.3", 6003, 1)?;
// Configure session
session
.set_tx_rate(1_000_000.0) // 1 Mbps
.set_ttl(64)?
.set_loopback(true)?;
// Start sender
session.start_sender(
rand::random(), // Session ID
1024 * 1024, // Buffer space (1 MB)
1400, // Segment size
64, // Data segments per block
16, // Parity segments per block
None, // FEC ID (default = 0)
)?;
// Or start receiver
session.start_receiver(1024 * 1024)?;
Objects
NORM supports three types of objects for data transfer:
- Data Objects: For memory buffer transfers
- File Objects: For file transfers
- Stream Objects: For continuous data streaming
// Send data
let data = b"Hello, NORM!";
let data_obj = session.data_enqueue(data, Some(b"Info"))?;
// Send file
let file_obj = session.file_enqueue("/path/to/file.txt", Some(b"file.txt"))?;
// Open stream
let stream_obj = session.stream_open(64 * 1024, Some(b"Stream info"))?;
Events
NORM uses an event-based model for signaling state changes. The Rust bindings provide an iterator-based approach for event handling.
// Process events using iterator
for event in instance.events() {
match event.event_type {
EventType::RxObjectCompleted => {
let object = Object::from_handle_unowned(event.object);
// Handle based on object type
match object.get_type() {
ObjectType::Data => {
if let Ok(data) = object.access_data() {
println!("Received data: {:?}", data);
}
},
ObjectType::File => {
if let Ok(info) = object.get_info() {
println!("Received file: {}", String::from_utf8_lossy(&info));
}
},
_ => {}
}
},
EventType::TxObjectSent => println!("Object sent"),
_ => {} // Handle other events
}
}
Multicast Configuration
The Rust bindings provide an ergonomic API for configuring multicast:
use norm::{multicast, MulticastExt};
// Configure multicast with builder pattern
let config = multicast!("224.1.2.3", 6003, {
ttl: 64,
interface: "eth0",
loopback: true,
ssm_source: "192.168.1.1",
});
// Apply configuration to session
session.with_multicast(&config)?;
Error Handling
All operations that might fail return a Result<T, Error> type:
match instance.set_cache_directory("/nonexistent/path") {
Ok(()) => println!("Cache directory set"),
Err(e) => match e {
Error::FileError(msg) => println!("File error: {}", msg),
Error::InvalidParameter => println!("Invalid parameter"),
_ => println!("Other error: {:?}", e),
}
}
Ownership and Lifetimes
The Rust bindings use RAII (Resource Acquisition Is Initialization) to ensure proper resource management:
Instance,Session, andObjectimplementDropto automatically clean up resources- Objects created directly have ownership and will be freed when dropped
- Objects obtained from events are not owned and are marked as such
// Owned object from direct creation
let data_obj = session.data_enqueue(data, None)?;
// Will be released when data_obj goes out of scope
// Non-owned object from event
let object = Object::from_handle_unowned(event.object);
// Will NOT be released when object goes out of scope
Utility Functions
The API provides several utility functions:
// Check if an address is a multicast address
let is_mcast = is_multicast_address("224.1.2.3");
// Get NORM version
let (major, minor, patch) = norm::version();
Advanced Features
Custom Memory Allocation
unsafe {
instance.set_allocation_functions(
my_alloc_function,
my_free_function
);
}
File Transfers
When receiving files, you must set a cache directory:
instance.set_cache_directory("/tmp/norm_files")?;
Stream Management
For stream objects, additional methods are available on the Object type:
// Open a stream
let stream = session.stream_open(64 * 1024, Some(b"Stream info"))?;
// Write to stream
let bytes_written = stream.stream_write(b"Hello, stream!")?;
// Check if stream has space for more data
if stream.stream_has_vacancy()? {
stream.stream_write(b"More data")?;
}
// Mark end of message
stream.stream_mark_eom()?;
// Flush stream with end-of-message marker
stream.stream_flush(true, FlushMode::Passive)?;
// Read from stream (receiver side)
let mut buffer = vec![0u8; 1024];
let bytes_read = stream.stream_read(&mut buffer)?;
// Seek to next message start
if stream.stream_seek_msg_start()? {
println!("Found next message");
}
// Close stream gracefully
stream.stream_close(true)?;
Best Practices
- Always check return values - Use the
?operator or explicitly handle errors - Process all events - Use the event iterator to process all events
- Close resources properly - Let RAII handle cleanup or explicitly call
stop_sender()/stop_receiver() - Configure multicast correctly - Use the ergonomic multicast API
- Use appropriate buffer sizes - Match buffer sizes to your application's needs