9 releases
0.4.0 | Mar 18, 2024 |
---|---|
0.3.0 | Feb 7, 2023 |
0.2.4 | Nov 15, 2022 |
0.2.0 | Aug 13, 2022 |
0.1.1 | Aug 13, 2022 |
#783 in Encoding
371 downloads per month
69KB
1K
SLoC
psrdada-rs
This is a rust library around the psrdada library commonly used in radio astronomy. Unfortunately, the C library is for the most part undocumented, so the behavior presented by this rust library is what the authors have been able to ascertain by reading the original example code. As such, this might not be a 1-to-1 implementation of the original use case and implements only a subset of the features available in the C library.
Installation
You need to build and install PSRDADA manually, following the installation guide found here. Alternatively, you can use the nix flake here to declaratively create environments (shells/docker containers/operating systems) with PSRDADA baked in (deterministically).
Safety
The original library is intrinsically unsafe as it is written in C, but also there are very few checks that the user uses it correctly. This library tries to ensure at compile time some of the things the C library checks at runtime. For example, If you try to write to buffer while something else is trying to read, this would usually fail a lock. Instead, in this library, we use Rust's borrowing system to ensure you can't build both at the same time. The same goes for read/write blocks. References to these cannot exist once you mark them as cleared.
This is a huge ergonomic improvement over the C library (and the C++ library to some extent, as they attempt to implement some RAII patterns).
Take the following code as an example
use psrdada::prelude::*;
use std::io::{Read, Write};
// Build the paired client
let key = 0xb0ba;
let mut client = DadaClientBuilder::new(key).build().unwrap();
// Split into individual clients
let (_, mut data_client) = client.split();
// Construct the writer (mutable borrow), panicking if a lock is not obtainable
let mut writer = data_client.writer().unwrap();
// Grab the next block in the ring (assuming we can)
let mut write_block = writer.next().unwrap();
// Write using std::io::Write so you can write chunks at a time
write_block.write_all(&[0u8; 10]).unwrap();
// Inform the backend that we've completed writing
write_block.commit();
// Drop the writer to unlock it (this would happen also when the writer leaves scope)
drop(writer);
// Construct the reader (mutable borrow), panicking if a lock is not obtainable
let mut reader = data_client.reader().unwrap();
// Grab the next read block in the ring
let mut read_block = reader.next().unwrap();
// Read using std::io::Read
let mut buf = [0u8; 10];
read_block.read_exact(&mut buf).unwrap();
without that write_block.commit()
line, this code would not compile as there still exists a write in progress.
Additionally, you can only ever split
once, so you'll only ever have a single reader and writer for each type.
Please see the examples for some more use cases.
Thanks
Much of the implementation is inspired by other "modern" wrappings of PSRDADA, especially PSRDADA_CPP.
License
psrdada-rs
is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
See LICENSE-APACHE and LICENSE-MIT for details.
Dependencies
~1.1–3.5MB
~61K SLoC