248 lines
6.3 KiB
Markdown
248 lines
6.3 KiB
Markdown
# 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.
|
|
|
|
```rust
|
|
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.
|
|
|
|
```rust
|
|
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:
|
|
|
|
1. **Data Objects**: For memory buffer transfers
|
|
2. **File Objects**: For file transfers
|
|
3. **Stream Objects**: For continuous data streaming
|
|
|
|
```rust
|
|
// 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.
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
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`, and `Object` implement `Drop` to 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
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
unsafe {
|
|
instance.set_allocation_functions(
|
|
my_alloc_function,
|
|
my_free_function
|
|
);
|
|
}
|
|
```
|
|
|
|
### File Transfers
|
|
|
|
When receiving files, you must set a cache directory:
|
|
|
|
```rust
|
|
instance.set_cache_directory("/tmp/norm_files")?;
|
|
```
|
|
|
|
### Stream Management
|
|
|
|
For stream objects, additional methods are available on the `Object` type:
|
|
|
|
```rust
|
|
// 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
|
|
|
|
1. **Always check return values** - Use the `?` operator or explicitly handle errors
|
|
2. **Process all events** - Use the event iterator to process all events
|
|
3. **Close resources properly** - Let RAII handle cleanup or explicitly call `stop_sender()`/`stop_receiver()`
|
|
4. **Configure multicast correctly** - Use the ergonomic multicast API
|
|
5. **Use appropriate buffer sizes** - Match buffer sizes to your application's needs |