From 580928a3691b6b4d7661719393cda70754ef18da Mon Sep 17 00:00:00 2001 From: Vitali Lovich Date: Tue, 21 Jan 2025 10:16:48 -0800 Subject: [PATCH 1/2] Replace RawCString with a function call RawCString doesn't offer any additional smarts over lifetime ownserhip vs constructing a CString. --- crates/sherpa-rs/src/audio_tag.rs | 10 +++---- crates/sherpa-rs/src/diarize.rs | 8 +++--- crates/sherpa-rs/src/embedding_manager.rs | 4 +-- crates/sherpa-rs/src/keyword_spot.rs | 14 +++++----- crates/sherpa-rs/src/language_id.rs | 8 +++--- crates/sherpa-rs/src/moonshine.rs | 14 +++++----- crates/sherpa-rs/src/punctuate.rs | 8 +++--- crates/sherpa-rs/src/speaker_id.rs | 6 ++-- crates/sherpa-rs/src/tts/kokoro.rs | 12 ++++---- crates/sherpa-rs/src/tts/matcha.rs | 16 +++++------ crates/sherpa-rs/src/tts/mod.rs | 14 ++++++---- crates/sherpa-rs/src/tts/vits.rs | 14 +++++----- crates/sherpa-rs/src/utils.rs | 34 ++--------------------- crates/sherpa-rs/src/vad.rs | 6 ++-- crates/sherpa-rs/src/whisper.rs | 22 +++++++-------- crates/sherpa-rs/src/zipformer.rs | 14 +++++----- 16 files changed, 88 insertions(+), 116 deletions(-) diff --git a/crates/sherpa-rs/src/audio_tag.rs b/crates/sherpa-rs/src/audio_tag.rs index 8832ef7..aa32a3e 100644 --- a/crates/sherpa-rs/src/audio_tag.rs +++ b/crates/sherpa-rs/src/audio_tag.rs @@ -2,7 +2,7 @@ use eyre::{bail, Result}; use crate::{ get_default_provider, - utils::{cstr_to_string, RawCStr}, + utils::{cstr_to_string, cstring_from_str}, }; #[derive(Debug, Default, Clone)] @@ -25,10 +25,10 @@ impl AudioTag { pub fn new(config: AudioTagConfig) -> Result { let config_clone = config.clone(); - let model = RawCStr::new(&config.model); - let ced = RawCStr::new(&config.ced.unwrap_or_default()); - let labels = RawCStr::new(&config.labels); - let provider = RawCStr::new(&config.provider.unwrap_or(get_default_provider())); + let model = cstring_from_str(&config.model); + let ced = cstring_from_str(&config.ced.unwrap_or_default()); + let labels = cstring_from_str(&config.labels); + let provider = cstring_from_str(&config.provider.unwrap_or(get_default_provider())); let sherpa_config = sherpa_rs_sys::SherpaOnnxAudioTaggingConfig { model: sherpa_rs_sys::SherpaOnnxAudioTaggingModelConfig { diff --git a/crates/sherpa-rs/src/diarize.rs b/crates/sherpa-rs/src/diarize.rs index 43336e3..74c30f1 100644 --- a/crates/sherpa-rs/src/diarize.rs +++ b/crates/sherpa-rs/src/diarize.rs @@ -1,4 +1,4 @@ -use crate::{get_default_provider, utils::RawCStr}; +use crate::{get_default_provider, utils::cstring_from_str}; use eyre::{bail, Result}; use std::{path::Path, ptr::null_mut}; @@ -58,9 +58,9 @@ impl Diarize { threshold: config.threshold.unwrap_or(0.5), }; - let embedding_model = RawCStr::new(embedding_model); - let provider = RawCStr::new(&provider.clone()); - let segmentation_model = RawCStr::new(segmentation_model); + let embedding_model = cstring_from_str(embedding_model); + let provider = cstring_from_str(&provider.clone()); + let segmentation_model = cstring_from_str(segmentation_model); let config = sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationConfig { embedding: sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingExtractorConfig { diff --git a/crates/sherpa-rs/src/embedding_manager.rs b/crates/sherpa-rs/src/embedding_manager.rs index e5a9df4..f4f67fb 100644 --- a/crates/sherpa-rs/src/embedding_manager.rs +++ b/crates/sherpa-rs/src/embedding_manager.rs @@ -1,4 +1,4 @@ -use crate::utils::{cstr_to_string, RawCStr}; +use crate::utils::{cstr_to_string, cstring_from_str}; use eyre::{bail, Result}; #[derive(Debug, Clone)] @@ -67,7 +67,7 @@ impl EmbeddingManager { } pub fn add(&mut self, name: String, embedding: &mut [f32]) -> Result<()> { - let name_c = RawCStr::new(&name.clone()); + let name_c = cstring_from_str(&name.clone()); unsafe { let status = sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingManagerAdd( self.manager, diff --git a/crates/sherpa-rs/src/keyword_spot.rs b/crates/sherpa-rs/src/keyword_spot.rs index 1dbab05..1e13a6d 100644 --- a/crates/sherpa-rs/src/keyword_spot.rs +++ b/crates/sherpa-rs/src/keyword_spot.rs @@ -2,7 +2,7 @@ use std::ptr::null; use crate::{ get_default_provider, - utils::{cstr_to_string, RawCStr}, + utils::{cstr_to_string, cstring_from_str}, }; use eyre::{bail, Result}; @@ -61,14 +61,14 @@ impl KeywordSpot { // Create new keyboard spotter along with stream // Ready for streaming or regular use pub fn new(config: KeywordSpotConfig) -> Result { - let provider = RawCStr::new(&config.provider.unwrap_or(get_default_provider())); + let provider = cstring_from_str(&config.provider.unwrap_or(get_default_provider())); - let zipformer_encoder = RawCStr::new(&config.zipformer_encoder); - let zipformer_decoder = RawCStr::new(&config.zipformer_decoder); - let zipformer_joiner = RawCStr::new(&config.zipformer_joiner); + let zipformer_encoder = cstring_from_str(&config.zipformer_encoder); + let zipformer_decoder = cstring_from_str(&config.zipformer_decoder); + let zipformer_joiner = cstring_from_str(&config.zipformer_joiner); - let tokens = RawCStr::new(&config.tokens); - let keywords = RawCStr::new(&config.keywords); + let tokens = cstring_from_str(&config.tokens); + let keywords = cstring_from_str(&config.keywords); let sherpa_config = sherpa_rs_sys::SherpaOnnxKeywordSpotterConfig { feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { diff --git a/crates/sherpa-rs/src/language_id.rs b/crates/sherpa-rs/src/language_id.rs index f60e3ca..0c7cd12 100644 --- a/crates/sherpa-rs/src/language_id.rs +++ b/crates/sherpa-rs/src/language_id.rs @@ -1,6 +1,6 @@ use crate::{ get_default_provider, - utils::{cstr_to_string, RawCStr}, + utils::{cstr_to_string, cstring_from_str}, }; use eyre::{bail, Result}; @@ -22,9 +22,9 @@ impl SpokenLanguageId { pub fn new(config: SpokenLanguageIdConfig) -> Self { let debug = config.debug.into(); - let decoder = RawCStr::new(&config.decoder); - let encoder = RawCStr::new(&config.encoder); - let provider = RawCStr::new(&config.provider.unwrap_or(get_default_provider())); + let decoder = cstring_from_str(&config.decoder); + let encoder = cstring_from_str(&config.encoder); + let provider = cstring_from_str(&config.provider.unwrap_or(get_default_provider())); let whisper = sherpa_rs_sys::SherpaOnnxSpokenLanguageIdentificationWhisperConfig { decoder: decoder.as_ptr(), diff --git a/crates/sherpa-rs/src/moonshine.rs b/crates/sherpa-rs/src/moonshine.rs index d8f1d76..7728d1c 100644 --- a/crates/sherpa-rs/src/moonshine.rs +++ b/crates/sherpa-rs/src/moonshine.rs @@ -1,4 +1,4 @@ -use crate::{get_default_provider, utils::RawCStr}; +use crate::{get_default_provider, utils::cstring_from_str}; use eyre::{bail, Result}; use std::ptr::null; @@ -46,15 +46,15 @@ impl MoonshineRecognizer { let provider = config.provider.unwrap_or(get_default_provider()); // Onnx - let provider_ptr = RawCStr::new(&provider); + let provider_ptr = cstring_from_str(&provider); let num_threads = config.num_threads.unwrap_or(2); // Moonshine - let preprocessor_ptr = RawCStr::new(&config.preprocessor); - let encoder_ptr = RawCStr::new(&config.encoder); - let cached_decoder_ptr = RawCStr::new(&config.cached_decoder); - let uncached_decoder_ptr = RawCStr::new(&config.uncached_decoder); - let tokens_ptr = RawCStr::new(&config.tokens); + let preprocessor_ptr = cstring_from_str(&config.preprocessor); + let encoder_ptr = cstring_from_str(&config.encoder); + let cached_decoder_ptr = cstring_from_str(&config.cached_decoder); + let uncached_decoder_ptr = cstring_from_str(&config.uncached_decoder); + let tokens_ptr = cstring_from_str(&config.tokens); let model_config = sherpa_rs_sys::SherpaOnnxOfflineModelConfig { bpe_vocab: null(), diff --git a/crates/sherpa-rs/src/punctuate.rs b/crates/sherpa-rs/src/punctuate.rs index b425a46..5045473 100644 --- a/crates/sherpa-rs/src/punctuate.rs +++ b/crates/sherpa-rs/src/punctuate.rs @@ -2,7 +2,7 @@ use eyre::{bail, Result}; use crate::{ get_default_provider, - utils::{cstr_to_string, RawCStr}, + utils::{cstr_to_string, cstring_from_str}, }; #[derive(Debug, Default, Clone)] @@ -19,8 +19,8 @@ pub struct Punctuation { impl Punctuation { pub fn new(config: PunctuationConfig) -> Result { - let model = RawCStr::new(&config.model); - let provider = RawCStr::new(&config.provider.unwrap_or(if cfg!(target_os = "macos") { + let model = cstring_from_str(&config.model); + let provider = cstring_from_str(&config.provider.unwrap_or(if cfg!(target_os = "macos") { // TODO: sherpa-onnx/issues/1448 "cpu".into() } else { @@ -45,7 +45,7 @@ impl Punctuation { } pub fn add_punctuation(&mut self, text: &str) -> String { - let text = RawCStr::new(text); + let text = cstring_from_str(text); unsafe { let text_with_punct_ptr = sherpa_rs_sys::SherpaOfflinePunctuationAddPunct( self.audio_punctuation, diff --git a/crates/sherpa-rs/src/speaker_id.rs b/crates/sherpa-rs/src/speaker_id.rs index ff8f15a..4f78fdd 100644 --- a/crates/sherpa-rs/src/speaker_id.rs +++ b/crates/sherpa-rs/src/speaker_id.rs @@ -1,7 +1,7 @@ use eyre::{bail, Result}; use std::path::PathBuf; -use crate::{get_default_provider, utils::RawCStr}; +use crate::{get_default_provider, utils::cstring_from_str}; /// If similarity is greater or equal to thresold than it's a match! pub const DEFAULT_SIMILARITY_THRESHOLD: f32 = 0.5; @@ -31,8 +31,8 @@ impl EmbeddingExtractor { if !model_path.exists() { bail!("model not found at {}", model_path.display()) } - let model = RawCStr::new(&config.model); - let provider = RawCStr::new(&provider); + let model = cstring_from_str(&config.model); + let provider = cstring_from_str(&provider); let extractor_config = sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingExtractorConfig { debug, diff --git a/crates/sherpa-rs/src/tts/kokoro.rs b/crates/sherpa-rs/src/tts/kokoro.rs index 24e44df..c30f8ed 100644 --- a/crates/sherpa-rs/src/tts/kokoro.rs +++ b/crates/sherpa-rs/src/tts/kokoro.rs @@ -1,6 +1,6 @@ use std::{mem, ptr::null}; -use crate::{utils::RawCStr, OnnxConfig}; +use crate::{utils::cstring_from_str, OnnxConfig}; use eyre::Result; use sherpa_rs_sys; @@ -24,12 +24,12 @@ pub struct KokoroTtsConfig { impl KokoroTts { pub fn new(config: KokoroTtsConfig) -> Self { let tts = unsafe { - let model = RawCStr::new(&config.model); - let voices = RawCStr::new(&config.voices); - let tokens = RawCStr::new(&config.tokens); - let data_dir = RawCStr::new(&config.data_dir); + let model = cstring_from_str(&config.model); + let voices = cstring_from_str(&config.voices); + let tokens = cstring_from_str(&config.tokens); + let data_dir = cstring_from_str(&config.data_dir); - let provider = RawCStr::new(&config.onnx_config.provider); + let provider = cstring_from_str(&config.onnx_config.provider); let tts_config = config.common_config.to_raw(); diff --git a/crates/sherpa-rs/src/tts/matcha.rs b/crates/sherpa-rs/src/tts/matcha.rs index 34ee3d8..75674bd 100644 --- a/crates/sherpa-rs/src/tts/matcha.rs +++ b/crates/sherpa-rs/src/tts/matcha.rs @@ -1,6 +1,6 @@ use std::{mem, ptr::null}; -use crate::{utils::RawCStr, OnnxConfig}; +use crate::{utils::cstring_from_str, OnnxConfig}; use eyre::Result; use sherpa_rs_sys; @@ -30,15 +30,15 @@ pub struct MatchaTtsConfig { impl MatchaTts { pub fn new(config: MatchaTtsConfig) -> Self { let tts = unsafe { - let tokens = RawCStr::new(&config.tokens); - let data_dir = RawCStr::new(&config.data_dir); - let lexicon = RawCStr::new(&config.lexicon); - let dict_dir = RawCStr::new(&config.dict_dir); + let tokens = cstring_from_str(&config.tokens); + let data_dir = cstring_from_str(&config.data_dir); + let lexicon = cstring_from_str(&config.lexicon); + let dict_dir = cstring_from_str(&config.dict_dir); - let vocoder = RawCStr::new(&config.vocoder); - let acoustic_model = RawCStr::new(&config.acoustic_model); + let vocoder = cstring_from_str(&config.vocoder); + let acoustic_model = cstring_from_str(&config.acoustic_model); - let provider = RawCStr::new(&config.onnx_config.provider); + let provider = cstring_from_str(&config.onnx_config.provider); let tts_config = config.common_config.to_raw(); diff --git a/crates/sherpa-rs/src/tts/mod.rs b/crates/sherpa-rs/src/tts/mod.rs index 63cbf86..623b642 100644 --- a/crates/sherpa-rs/src/tts/mod.rs +++ b/crates/sherpa-rs/src/tts/mod.rs @@ -2,13 +2,15 @@ mod kokoro; mod matcha; mod vits; +use std::ffi::CString; + use eyre::{bail, Result}; pub use kokoro::{KokoroTts, KokoroTtsConfig}; pub use matcha::{MatchaTts, MatchaTtsConfig}; pub use vits::{VitsTts, VitsTtsConfig}; -use crate::utils::RawCStr; +use crate::utils::cstring_from_str; #[derive(Debug)] pub struct TtsAudio { @@ -25,8 +27,8 @@ pub struct CommonTtsConfig { } pub struct CommonTtsRaw { - pub rule_fars: Option, - pub rule_fsts: Option, + pub rule_fars: Option, + pub rule_fsts: Option, pub max_num_sentences: i32, } @@ -35,13 +37,13 @@ impl CommonTtsConfig { let rule_fars = if self.rule_fars.is_empty() { None } else { - Some(RawCStr::new(&self.rule_fars)) + Some(cstring_from_str(&self.rule_fars)) }; let rule_fsts = if self.rule_fsts.is_empty() { None } else { - Some(RawCStr::new(&self.rule_fsts)) + Some(cstring_from_str(&self.rule_fsts)) }; CommonTtsRaw { @@ -61,7 +63,7 @@ pub unsafe fn create( sid: i32, speed: f32, ) -> Result { - let text = RawCStr::new(text); + let text = cstring_from_str(text); let audio_ptr = sherpa_rs_sys::SherpaOnnxOfflineTtsGenerate(tts, text.as_ptr(), sid, speed); if audio_ptr.is_null() { diff --git a/crates/sherpa-rs/src/tts/vits.rs b/crates/sherpa-rs/src/tts/vits.rs index 4e4f74f..7348a02 100644 --- a/crates/sherpa-rs/src/tts/vits.rs +++ b/crates/sherpa-rs/src/tts/vits.rs @@ -1,6 +1,6 @@ use std::{mem, ptr::null}; -use crate::{utils::RawCStr, OnnxConfig}; +use crate::{utils::cstring_from_str, OnnxConfig}; use eyre::Result; use sherpa_rs_sys; @@ -28,13 +28,13 @@ pub struct VitsTtsConfig { impl VitsTts { pub fn new(config: VitsTtsConfig) -> Self { let tts = unsafe { - let model = RawCStr::new(&config.model); - let tokens = RawCStr::new(&config.tokens); - let data_dir = RawCStr::new(&config.data_dir); - let lexicon = RawCStr::new(&config.lexicon); - let dict_dir = RawCStr::new(&config.dict_dir); + let model = cstring_from_str(&config.model); + let tokens = cstring_from_str(&config.tokens); + let data_dir = cstring_from_str(&config.data_dir); + let lexicon = cstring_from_str(&config.lexicon); + let dict_dir = cstring_from_str(&config.dict_dir); - let provider = RawCStr::new(&config.onnx_config.provider); + let provider = cstring_from_str(&config.onnx_config.provider); let tts_config = config.tts_config.to_raw(); diff --git a/crates/sherpa-rs/src/utils.rs b/crates/sherpa-rs/src/utils.rs index b33a0f5..2268a6f 100644 --- a/crates/sherpa-rs/src/utils.rs +++ b/crates/sherpa-rs/src/utils.rs @@ -1,37 +1,7 @@ use std::ffi::{c_char, CString}; -// Smart pointer for CString -pub struct RawCStr { - ptr: *mut std::ffi::c_char, -} - -impl RawCStr { - /// Creates a new `CStr` from a given Rust string slice. - pub fn new(s: &str) -> Self { - let cstr = CString::new(s).expect("CString::new failed"); - let ptr = cstr.into_raw(); - Self { ptr } - } - - /// Returns the raw pointer to the internal C string. - /// - /// # Safety - /// This function only returns the raw pointer and does not transfer ownership. - /// The pointer remains valid as long as the `CStr` instance exists. - /// Be cautious not to deallocate or modify the pointer after using `CStr::new`. - pub fn as_ptr(&self) -> *const c_char { - self.ptr - } -} - -impl Drop for RawCStr { - fn drop(&mut self) { - if !self.ptr.is_null() { - unsafe { - let _ = CString::from_raw(self.ptr); - } - } - } +pub fn cstring_from_str(s: &str) -> CString { + return CString::new(s).expect("CString::new failed"); } pub fn cstr_to_string(ptr: *const c_char) -> String { diff --git a/crates/sherpa-rs/src/vad.rs b/crates/sherpa-rs/src/vad.rs index 77937d6..53f7156 100644 --- a/crates/sherpa-rs/src/vad.rs +++ b/crates/sherpa-rs/src/vad.rs @@ -1,4 +1,4 @@ -use crate::{get_default_provider, utils::RawCStr}; +use crate::{get_default_provider, utils::cstring_from_str}; use eyre::Result; #[derive(Debug)] @@ -47,8 +47,8 @@ impl Vad { pub fn new(config: VadConfig, buffer_size_in_seconds: f32) -> Result { let provider = config.provider.unwrap_or(get_default_provider()); - let model = RawCStr::new(&config.model); - let provider = RawCStr::new(&provider); + let model = cstring_from_str(&config.model); + let provider = cstring_from_str(&provider); let silero_vad = sherpa_rs_sys::SherpaOnnxSileroVadModelConfig { model: model.as_ptr(), diff --git a/crates/sherpa-rs/src/whisper.rs b/crates/sherpa-rs/src/whisper.rs index 236879d..369beec 100644 --- a/crates/sherpa-rs/src/whisper.rs +++ b/crates/sherpa-rs/src/whisper.rs @@ -1,4 +1,4 @@ -use crate::{get_default_provider, utils::RawCStr}; +use crate::{get_default_provider, utils::cstring_from_str}; use eyre::{bail, Result}; use std::ptr::null; @@ -43,21 +43,21 @@ impl WhisperRecognizer { let provider = config.provider.unwrap_or(get_default_provider()); // Onnx - let provider_ptr = RawCStr::new(&provider); + let provider_ptr = cstring_from_str(&provider); let num_threads = config.num_threads.unwrap_or(2); // Whisper - let bpe_vocab_ptr = RawCStr::new(&config.bpe_vocab.unwrap_or("".into())); + let bpe_vocab_ptr = cstring_from_str(&config.bpe_vocab.unwrap_or("".into())); let tail_paddings = 0; - let decoder_ptr = RawCStr::new(&config.decoder); - let encoder_ptr = RawCStr::new(&config.encoder); - let language_ptr = RawCStr::new(&config.language); - let task_ptr = RawCStr::new("transcribe"); - let tokens_ptr = RawCStr::new(&config.tokens); - let decoding_method_ptr = RawCStr::new("greedy_search"); + let decoder_ptr = cstring_from_str(&config.decoder); + let encoder_ptr = cstring_from_str(&config.encoder); + let language_ptr = cstring_from_str(&config.language); + let task_ptr = cstring_from_str("transcribe"); + let tokens_ptr = cstring_from_str(&config.tokens); + let decoding_method_ptr = cstring_from_str("greedy_search"); // Sense voice - let sense_voice_model_ptr = RawCStr::new(""); - let sense_voice_language_ptr = RawCStr::new(""); + let sense_voice_model_ptr = cstring_from_str(""); + let sense_voice_language_ptr = cstring_from_str(""); let whisper = sherpa_rs_sys::SherpaOnnxOfflineWhisperModelConfig { decoder: decoder_ptr.as_ptr(), diff --git a/crates/sherpa-rs/src/zipformer.rs b/crates/sherpa-rs/src/zipformer.rs index 372d9c0..328f3b1 100644 --- a/crates/sherpa-rs/src/zipformer.rs +++ b/crates/sherpa-rs/src/zipformer.rs @@ -1,6 +1,6 @@ use crate::{ get_default_provider, - utils::{cstr_to_string, RawCStr}, + utils::{cstr_to_string, cstring_from_str}, }; use eyre::{bail, Result}; use std::ptr::null; @@ -24,12 +24,12 @@ pub struct ZipFormer { impl ZipFormer { pub fn new(config: ZipFormerConfig) -> Result { // Zipformer config - let decoder_ptr = RawCStr::new(&config.decoder); - let encoder_ptr = RawCStr::new(&config.encoder); - let joiner_ptr = RawCStr::new(&config.joiner); - let provider_ptr = RawCStr::new(&config.provider.unwrap_or(get_default_provider())); - let tokens_ptr = RawCStr::new(&config.tokens); - let decoding_method_ptr = RawCStr::new("greedy_search"); + let decoder_ptr = cstring_from_str(&config.decoder); + let encoder_ptr = cstring_from_str(&config.encoder); + let joiner_ptr = cstring_from_str(&config.joiner); + let provider_ptr = cstring_from_str(&config.provider.unwrap_or(get_default_provider())); + let tokens_ptr = cstring_from_str(&config.tokens); + let decoding_method_ptr = cstring_from_str("greedy_search"); let transcuder_config = sherpa_rs_sys::SherpaOnnxOfflineTransducerModelConfig { decoder: decoder_ptr.as_ptr(), From 2717e6ff50e12dcb10c2b9e65cc77b8a496b18aa Mon Sep 17 00:00:00 2001 From: Vitali Lovich Date: Tue, 21 Jan 2025 10:51:53 -0800 Subject: [PATCH 2/2] Mark cstr_to_string as unsafe In Rust, having an unsafe interior conventionally means that you are guaranteeing safety at the boundary. However, that's not the case for cstr_to_string which is taking a random pointer as input. Thankfully, this doesn't functionally change anything anyway since it was already always called from an unsafe context. --- crates/sherpa-rs/src/lib.rs | 4 ++-- crates/sherpa-rs/src/utils.rs | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/sherpa-rs/src/lib.rs b/crates/sherpa-rs/src/lib.rs index 6d48090..4354118 100644 --- a/crates/sherpa-rs/src/lib.rs +++ b/crates/sherpa-rs/src/lib.rs @@ -94,8 +94,8 @@ pub struct OfflineRecognizerResult { impl OfflineRecognizerResult { fn new(result: &sherpa_rs_sys::SherpaOnnxOfflineRecognizerResult) -> Self { - let lang = cstr_to_string(result.lang); - let text = cstr_to_string(result.text); + let lang = unsafe { cstr_to_string(result.lang) }; + let text = unsafe { cstr_to_string(result.text) }; let count = result.count.try_into().unwrap(); let timestamps = if result.timestamps.is_null() { Vec::new() diff --git a/crates/sherpa-rs/src/utils.rs b/crates/sherpa-rs/src/utils.rs index 2268a6f..8d4c494 100644 --- a/crates/sherpa-rs/src/utils.rs +++ b/crates/sherpa-rs/src/utils.rs @@ -4,12 +4,10 @@ pub fn cstring_from_str(s: &str) -> CString { return CString::new(s).expect("CString::new failed"); } -pub fn cstr_to_string(ptr: *const c_char) -> String { - unsafe { - if ptr.is_null() { - String::new() - } else { - std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned() - } +pub unsafe fn cstr_to_string(ptr: *const c_char) -> String { + if ptr.is_null() { + String::new() + } else { + std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned() } }