use std::ffi::{c_char, c_double, c_long, c_void, CString};
use std::mem;
use std::ptr;
use std::slice;
use crate::{
error::check, raw, util, MSBitFieldFlags, MSControlFlags, MSDataEncoding, MSError, MSRecord,
MSResult, MSSampleType, MSTraceList,
};
use raw::MS3Record;
#[derive(Debug, Clone)]
pub struct TlPackInfo {
pub encoding: MSDataEncoding,
pub rec_len: i32,
pub extra_headers: Option<CString>,
}
impl Default for TlPackInfo {
fn default() -> Self {
Self {
encoding: MSDataEncoding::Steim2,
rec_len: 4096,
extra_headers: None,
}
}
}
pub fn pack_trace_list<F>(
mstl: &mut MSTraceList,
mut record_handler: F,
info: &TlPackInfo,
flags: MSControlFlags,
) -> MSResult<(usize, usize)>
where
F: FnMut(&[u8]),
{
let mut extra_ptr = ptr::null_mut();
if let Some(extra_headers) = &info.extra_headers {
let cloned = extra_headers.clone();
extra_ptr = cloned.into_raw();
}
let mut cnt_samples: i64 = 0;
let cnt_samples_ptr: *mut i64 = &mut cnt_samples;
let cnt_records = unsafe {
check(raw::mstl3_pack(
mstl.get_raw_mut(),
Some(rh_wrapper::<F>),
(&mut record_handler) as *mut _ as *mut c_void,
info.rec_len,
info.encoding as _,
cnt_samples_ptr,
flags.bits(),
0,
extra_ptr,
))?
};
if !extra_ptr.is_null() {
unsafe {
let _ = CString::from_raw(extra_ptr);
}
}
Ok((cnt_records as usize, cnt_samples as usize))
}
#[derive(Debug, Clone)]
pub struct PackInfo {
sid: CString,
pub sample_rate: c_double,
pub format_version: u8,
pub pub_version: u8,
pub flags: MSBitFieldFlags,
pub encoding: MSDataEncoding,
pub rec_len: i32,
pub extra_headers: Option<CString>,
}
impl PackInfo {
pub fn new<T>(sid: T) -> MSResult<Self>
where
T: Into<Vec<u8>>,
{
Ok(Self {
sid: sid_as_cstring(sid)?,
sample_rate: 1.0,
format_version: 3,
pub_version: 1,
flags: MSBitFieldFlags::empty(),
encoding: MSDataEncoding::Steim2,
rec_len: 4096,
extra_headers: None,
})
}
pub fn with_sample_rate<T>(sid: T, sample_rate: c_double) -> MSResult<Self>
where
T: Into<Vec<u8>>,
{
let mut rv = Self::new(sid)?;
rv.sample_rate = sample_rate;
Ok(rv)
}
pub fn sid(&self) -> &CString {
&self.sid
}
pub fn set_sid<T>(&mut self, sid: T) -> MSResult<()>
where
T: Into<Vec<u8>>,
{
self.sid = sid_as_cstring(sid)?;
Ok(())
}
}
fn sid_as_cstring<T>(sid: T) -> MSResult<CString>
where
T: Into<Vec<u8>>,
{
let sid = CString::new(sid).map_err(|e| MSError::from_str(&e.to_string()))?;
if sid.as_bytes_with_nul().len() > raw::LM_SIDLEN as usize {
return Err(MSError::from_str("sid too large"));
}
Ok(sid)
}
pub fn pack_raw<T, F>(
data_samples: &mut [T],
start_time: &time::OffsetDateTime,
mut record_handler: F,
info: &PackInfo,
flags: MSControlFlags,
) -> MSResult<(usize, usize)>
where
F: FnMut(&[u8]),
{
let msr: *mut MS3Record = ptr::null_mut();
let mut msr = unsafe { raw::msr3_init(msr) };
if msr.is_null() {
return Err(MSError::from_str("failed to initialize record"));
}
unsafe {
let sid_len = info.sid().as_bytes_with_nul().len();
ptr::copy_nonoverlapping(info.sid().as_ptr(), (*msr).sid.as_mut_ptr(), sid_len);
(*msr).encoding = info.encoding as _;
(*msr).sampletype = {
use MSDataEncoding::*;
match info.encoding {
Text => MSSampleType::Text,
Integer16 | Integer32 | Steim1 | Steim2 => MSSampleType::Integer32,
Float32 => MSSampleType::Float32,
Float64 => MSSampleType::Float64,
_ => MSSampleType::Unknown,
}
} as c_char;
(*msr).reclen = info.rec_len;
(*msr).starttime = util::time_to_nstime(start_time);
(*msr).samprate = info.sample_rate;
(*msr).pubversion = info.pub_version;
(*msr).formatversion = info.format_version;
(*msr).flags = info.flags.bits();
(*msr).numsamples = c_long::try_from(data_samples.len())
.map_err(|e| MSError::from_str(&format!("invalid data sample length ({})", e)))?
as _;
(*msr).datasamples = data_samples.as_mut_ptr() as *mut _ as *mut c_void;
(*msr).datasize = mem::size_of_val(data_samples) as u64;
(*msr).extralength = 0;
(*msr).extra = ptr::null_mut();
}
if let Some(extra_headers) = &info.extra_headers {
let cloned = extra_headers.clone();
unsafe {
(*msr).extralength = u16::try_from(cloned.as_bytes_with_nul().len())
.map_err(|e| MSError::from_str(&format!("invalid extra header length ({})", e)))?;
(*msr).extra = cloned.into_raw();
}
}
let mut cnt_samples: i64 = 0;
let cnt_samples_ptr: *mut i64 = &mut cnt_samples;
let cnt_records = unsafe {
check(raw::msr3_pack(
msr,
Some(rh_wrapper::<F>),
(&mut record_handler) as *mut _ as *mut c_void,
cnt_samples_ptr,
flags.bits(),
0,
))?
};
unsafe {
let extra_ptr = (*msr).extra;
if !extra_ptr.is_null() {
let _ = CString::from_raw(extra_ptr);
(*msr).extra = ptr::null_mut();
}
(*msr).datasamples = ptr::null_mut();
(*msr).numsamples = 0;
(*msr).datasize = 0;
raw::msr3_free((&mut msr) as *mut *mut _);
}
Ok((cnt_records as usize, cnt_samples as usize))
}
extern "C" fn rh_wrapper<F>(rec: *mut c_char, rec_len: i32, out: *mut c_void)
where
F: FnMut(&[u8]),
{
let rec = unsafe { slice::from_raw_parts(rec as *mut u8, rec_len as usize) };
let callback = unsafe { &mut *(out as *mut F) };
callback(rec);
}
#[allow(dead_code)]
pub fn pack_record<F>(
msr: &MSRecord,
mut record_handler: F,
flags: MSControlFlags,
) -> MSResult<(usize, usize)>
where
F: FnMut(&[u8]),
{
let mut cnt_samples: i64 = 0;
let cnt_samples_ptr: *mut i64 = &mut cnt_samples;
let cnt_records = unsafe {
check(raw::msr3_pack(
msr.get_raw(),
Some(rh_wrapper::<F>),
(&mut record_handler) as *mut _ as *mut c_void,
cnt_samples_ptr,
flags.bits(),
0,
))?
};
Ok((cnt_records as usize, cnt_samples as usize))
}
#[allow(dead_code)]
pub fn repack_mseed3(msr: &MSRecord, buf: &mut [u8]) -> MSResult<usize> {
let buflen: u32 = match buf.len().try_into() {
Ok(reclen) => reclen,
Err(err) => return Err(MSError::from_str(err.to_string().as_str())),
};
Ok(unsafe {
check(raw::msr3_repack_mseed3(
msr.get_raw(),
buf.as_mut_ptr() as *mut _,
buflen,
0,
))? as usize
})
}
#[allow(dead_code)]
pub fn pack_header3(msr: &MSRecord, buf: &mut [u8]) -> MSResult<usize> {
let buflen: u32 = match buf.len().try_into() {
Ok(reclen) => reclen,
Err(err) => return Err(MSError::from_str(err.to_string().as_str())),
};
Ok(unsafe {
check(raw::msr3_pack_header3(
msr.get_raw(),
buf.as_mut_ptr() as *mut _,
buflen,
0,
))? as usize
})
}
#[allow(dead_code)]
pub fn pack_header2(msr: &MSRecord, buf: &mut [u8]) -> MSResult<usize> {
let buflen: u32 = match buf.len().try_into() {
Ok(reclen) => reclen,
Err(err) => return Err(MSError::from_str(err.to_string().as_str())),
};
Ok(unsafe {
check(raw::msr3_pack_header2(
msr.get_raw(),
buf.as_mut_ptr() as *mut _,
buflen,
0,
))? as usize
})
}