use std::convert::TryInto;
use std::ffi::CStr;
use std::io;
use std::os::raw::c_char;
use std::slice;
use crate::errors::SourmashError;
use crate::encodings::HashFunctions;
use crate::signature::Signature;
use crate::sketch::Sketch;
use crate::ffi::cmd::compute::SourmashComputeParameters;
use crate::ffi::minhash::SourmashKmerMinHash;
use crate::ffi::utils::{ForeignObject, SourmashStr};
use crate::prelude::ToWriter;
pub struct SourmashSignature;
impl ForeignObject for SourmashSignature {
type RustObject = Signature;
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn signature_new() -> *mut SourmashSignature {
unsafe { SourmashSignature::from_rust(Signature::default()) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn signature_from_params(
ptr: *const SourmashComputeParameters,
) -> *mut SourmashSignature {
unsafe {
let params = SourmashComputeParameters::as_rust(ptr);
SourmashSignature::from_rust(Signature::from_params(params))
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn signature_free(ptr: *mut SourmashSignature) {
unsafe { SourmashSignature::drop(ptr) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn signature_len(ptr: *const SourmashSignature) -> usize {
let sig = unsafe { SourmashSignature::as_rust(ptr) };
sig.size()
}
ffi_fn! {
unsafe fn signature_add_sequence(ptr: *mut SourmashSignature, sequence: *const c_char, force: bool) ->
Result<()> {
let sig = SourmashSignature::as_rust_mut(ptr);
let c_str = {
assert!(!sequence.is_null());
CStr::from_ptr(sequence)
};
sig.add_sequence(c_str.to_bytes(), force)
}
}
ffi_fn! {
unsafe fn signature_add_protein(ptr: *mut SourmashSignature, sequence: *const c_char) ->
Result<()> {
let sig = SourmashSignature::as_rust_mut(ptr);
let c_str = {
assert!(!sequence.is_null());
CStr::from_ptr(sequence)
};
sig.add_protein(c_str.to_bytes())
}
}
ffi_fn! {
unsafe fn signature_set_name(ptr: *mut SourmashSignature, name: *const c_char) ->
Result<()> {
let sig = SourmashSignature::as_rust_mut(ptr);
let c_str = {
assert!(!name.is_null());
CStr::from_ptr(name)
};
if let Ok(name) = c_str.to_str() {
sig.set_name(name)
}
Ok(())
}
}
ffi_fn! {
unsafe fn signature_set_filename(ptr: *mut SourmashSignature, name: *const c_char) ->
Result<()> {
let sig = SourmashSignature::as_rust_mut(ptr);
let c_str = {
assert!(!name.is_null());
CStr::from_ptr(name)
};
if let Ok(name) = c_str.to_str() {
sig.set_filename(name)
}
Ok(())
}
}
ffi_fn! {
unsafe fn signature_push_mh(ptr: *mut SourmashSignature, other: *const SourmashKmerMinHash) ->
Result<()> {
let sig = SourmashSignature::as_rust_mut(ptr);
let mh = SourmashKmerMinHash::as_rust(other);
sig.push(Sketch::MinHash(mh.clone()));
Ok(())
}
}
ffi_fn! {
unsafe fn signature_set_mh(ptr: *mut SourmashSignature, other: *const SourmashKmerMinHash) ->
Result<()> {
let sig = SourmashSignature::as_rust_mut(ptr);
let mh = SourmashKmerMinHash::as_rust(other);
sig.reset_sketches();
sig.push(Sketch::MinHash(mh.clone()));
Ok(())
}
}
ffi_fn! {
unsafe fn signature_get_name(ptr: *const SourmashSignature) -> Result<SourmashStr> {
let sig = SourmashSignature::as_rust(ptr);
if let Some(ref name) = sig.name {
Ok(name.clone().into())
} else {
Ok("".into())
}
}
}
ffi_fn! {
unsafe fn signature_get_filename(ptr: *const SourmashSignature) -> Result<SourmashStr> {
let sig = SourmashSignature::as_rust(ptr);
Ok(sig.filename().into())
}
}
ffi_fn! {
unsafe fn signature_get_license(ptr: *const SourmashSignature) -> Result<SourmashStr> {
let sig = SourmashSignature::as_rust(ptr);
Ok(sig.license().into())
}
}
ffi_fn! {
unsafe fn signature_first_mh(ptr: *const SourmashSignature) -> Result<*mut SourmashKmerMinHash> {
let sig = SourmashSignature::as_rust(ptr);
match sig.signatures.first() {
Some(Sketch::MinHash(mh)) => {
Ok(SourmashKmerMinHash::from_rust(mh.clone()))
},
Some(Sketch::LargeMinHash(mh_btree)) => {
Ok(SourmashKmerMinHash::from_rust(mh_btree.into()))
},
_ => Err(SourmashError::Internal {
message: "found unsupported sketch type".to_string()
}),
}
}
}
ffi_fn! {
unsafe fn signature_eq(ptr: *const SourmashSignature, other: *const SourmashSignature) -> Result<bool> {
let sig = SourmashSignature::as_rust(ptr);
let other_sig = SourmashSignature::as_rust(other);
Ok(sig == other_sig)
}
}
ffi_fn! {
unsafe fn signature_save_json(ptr: *const SourmashSignature) -> Result<SourmashStr> {
let sig = SourmashSignature::as_rust(ptr);
let mut st: Vec<u8> = vec![];
sig.to_writer(&mut st)?;
Ok(SourmashStr::from_string(String::from_utf8_unchecked(st)))
}
}
ffi_fn! {
unsafe fn signature_get_mhs(ptr: *const SourmashSignature, size: *mut usize) -> Result<*mut *mut SourmashKmerMinHash> {
let sig = SourmashSignature::as_rust(ptr);
let output = sig.sketches();
let ptr_sigs: Vec<*mut Signature> = output.into_iter().map(|x| {
Box::into_raw(Box::new(x)) as *mut Signature
}).collect();
let b = ptr_sigs.into_boxed_slice();
*size = b.len();
Ok(Box::into_raw(b) as *mut *mut SourmashKmerMinHash)
}
}
ffi_fn! {
unsafe fn signatures_save_buffer(ptr: *const *const SourmashSignature, size: usize, compression: u8, osize: *mut usize) -> Result<*const u8> {
let sigs = {
assert!(!ptr.is_null());
slice::from_raw_parts(ptr, size)
};
let rsigs: Vec<&Signature> = sigs.iter().map(|x| SourmashSignature::as_rust(*x)).collect();
let mut buffer = vec![];
{
let mut writer = if compression > 0 {
let level = match compression {
1 => niffler::compression::Level::One,
2 => niffler::compression::Level::Two,
3 => niffler::compression::Level::Three,
4 => niffler::compression::Level::Four,
5 => niffler::compression::Level::Five,
6 => niffler::compression::Level::Six,
7 => niffler::compression::Level::Seven,
8 => niffler::compression::Level::Eight,
_ => niffler::compression::Level::Nine,
};
niffler::get_writer(Box::new(&mut buffer),
niffler::compression::Format::Gzip,
level)?
} else {
Box::new(&mut buffer)
};
rsigs.to_writer(&mut writer)?;
}
let b = buffer.into_boxed_slice();
*osize = b.len();
Ok(Box::into_raw(b) as *const u8)
}
}
ffi_fn! {
unsafe fn signatures_load_path(ptr: *const c_char,
_ignore_md5sum: bool,
ksize: usize,
select_moltype: *const c_char,
size: *mut usize) -> Result<*mut *mut SourmashSignature> {
let buf = {
assert!(!ptr.is_null());
CStr::from_ptr(ptr)
};
let moltype: Option<HashFunctions> = if select_moltype.is_null() {
None
} else {
let mol = CStr::from_ptr(select_moltype).to_str()?;
Some(mol.try_into()?)
};
let k = match ksize {
0 => None,
x => Some(x)
};
let (mut input, _) = niffler::from_path(buf.to_str()?)?;
let filtered_sigs = Signature::load_signatures(&mut input, k, moltype, None)?;
let ptr_sigs: Vec<*mut SourmashSignature> = filtered_sigs.into_iter().map(|x| {
Box::into_raw(Box::new(x)) as *mut SourmashSignature
}).collect();
let b = ptr_sigs.into_boxed_slice();
*size = b.len();
Ok(Box::into_raw(b) as *mut *mut SourmashSignature)
}
}
ffi_fn! {
unsafe fn signatures_load_buffer(ptr: *const c_char,
insize: usize,
_ignore_md5sum: bool,
ksize: usize,
select_moltype: *const c_char,
size: *mut usize) -> Result<*mut *mut SourmashSignature> {
let buf = {
assert!(!ptr.is_null());
slice::from_raw_parts(ptr as *mut u8, insize)
};
let moltype: Option<HashFunctions> = if select_moltype.is_null() {
None
} else {
let mol = CStr::from_ptr(select_moltype).to_str()?;
Some(mol.try_into()?)
};
let k = match ksize {
0 => None,
x => Some(x)
};
let mut reader = io::BufReader::new(buf);
let filtered_sigs = Signature::load_signatures(&mut reader, k, moltype, None)?;
let ptr_sigs: Vec<*mut SourmashSignature> = filtered_sigs.into_iter().map(|x| {
Box::into_raw(Box::new(x)) as *mut SourmashSignature
}).collect();
let b = ptr_sigs.into_boxed_slice();
*size = b.len();
Ok(Box::into_raw(b) as *mut *mut SourmashSignature)
}
}