#[inline]
fn complement_base(base: u8) -> u8 {
if base < 4 {
3 - base
} else {
base }
}
#[no_mangle]
pub extern "C" fn ragc_reverse_complement_inplace(sequence: *mut u8, length: usize) {
if length == 0 {
return;
}
unsafe {
let seq = std::slice::from_raw_parts_mut(sequence, length);
let mut i = 0;
let mut j = length - 1;
while i < j {
let x = complement_base(seq[j]);
let y = complement_base(seq[i]);
seq[i] = x;
seq[j] = y;
i += 1;
j -= 1;
}
if i == j {
seq[i] = complement_base(seq[i]);
}
}
}
#[repr(C)]
pub struct Sequence {
pub data: *mut u8,
pub len: usize,
}
#[no_mangle]
pub extern "C" fn ragc_reverse_complement_copy(src: *const u8, src_len: usize) -> Sequence {
if src_len == 0 {
return Sequence {
data: std::ptr::null_mut(),
len: 0,
};
}
unsafe {
let src_slice = std::slice::from_raw_parts(src, src_len);
let mut dest: Vec<u8> = src_slice
.iter()
.rev()
.map(|&base| complement_base(base))
.collect();
let ptr = dest.as_mut_ptr();
let len = dest.len();
std::mem::forget(dest);
Sequence { data: ptr, len }
}
}
#[no_mangle]
pub extern "C" fn ragc_free_sequence(seq: Sequence) {
unsafe {
if !seq.data.is_null() && seq.len > 0 {
let _ = Vec::from_raw_parts(seq.data, seq.len, seq.len);
}
}
}
#[no_mangle]
pub extern "C" fn ragc_complement_base(base: u8) -> u8 {
complement_base(base)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_complement_base() {
assert_eq!(complement_base(0), 3); assert_eq!(complement_base(1), 2); assert_eq!(complement_base(2), 1); assert_eq!(complement_base(3), 0); assert_eq!(complement_base(4), 4); assert_eq!(complement_base(5), 5); }
#[test]
fn test_reverse_complement_inplace() {
let mut seq = vec![0, 1, 2]; ragc_reverse_complement_inplace(seq.as_mut_ptr(), seq.len());
assert_eq!(seq, vec![1, 2, 3]);
let mut seq2 = vec![0, 0, 0]; ragc_reverse_complement_inplace(seq2.as_mut_ptr(), seq2.len());
assert_eq!(seq2, vec![3, 3, 3]); }
#[test]
fn test_reverse_complement_copy() {
let src = vec![0, 1, 2]; let result = ragc_reverse_complement_copy(src.as_ptr(), src.len());
unsafe {
let dest = std::slice::from_raw_parts(result.data, result.len);
assert_eq!(dest, &[1, 2, 3]); }
ragc_free_sequence(result);
}
#[test]
fn test_reverse_complement_with_n() {
let src = vec![0, 1, 4, 2]; let result = ragc_reverse_complement_copy(src.as_ptr(), src.len());
unsafe {
let dest = std::slice::from_raw_parts(result.data, result.len);
assert_eq!(dest, &[1, 4, 2, 3]); }
ragc_free_sequence(result);
}
#[test]
fn test_empty_sequence() {
let empty: Vec<u8> = vec![];
let result = ragc_reverse_complement_copy(empty.as_ptr(), 0);
assert_eq!(result.len, 0);
assert!(result.data.is_null());
}
}