Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: C APIの#[repr(Rust)]なものへのアクセスをすべて安全にする #849

Merged
merged 9 commits into from
Oct 8, 2024
24 changes: 23 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ ndarray-stats = "0.5.1"
octocrab = { version = "0.19.0", default-features = false }
once_cell = "1.20.1"
ouroboros = "0.18.4"
parking_lot = "0.12.1"
parse-display = "0.8.2"
pollster = "0.3.0"
predicates = "3.1.2"
pretty_assertions = "1.4.1"
proc-macro2 = "1.0.86"
pyo3 = "0.20.3"
Expand Down
6 changes: 5 additions & 1 deletion crates/voicevox_core_c_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ chrono = { workspace = true, default-features = false, features = ["clock"] }
colorchoice.workspace = true
const_format.workspace = true
cstr.workspace = true
derive-getters.workspace = true
duplicate.workspace = true
easy-ext.workspace = true
educe.workspace = true
itertools.workspace = true
libc.workspace = true
parking_lot = { workspace = true, features = ["arc_lock"] }
process_path.workspace = true
ref-cast.workspace = true
serde_json = { workspace = true, features = ["preserve_order"] }
Expand All @@ -45,10 +47,12 @@ clap = { workspace = true, features = ["derive"] }
duct.workspace = true
easy-ext.workspace = true
inventory.workspace = true
indexmap = { workspace = true, features = ["serde"] }
libloading.workspace = true
libtest-mimic.workspace = true
ndarray.workspace = true
ndarray-stats.workspace = true
predicates.workspace = true
regex.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_with.workspace = true
Expand Down
85 changes: 16 additions & 69 deletions crates/voicevox_core_c_api/include/voicevox_core.h

Large diffs are not rendered by default.

88 changes: 65 additions & 23 deletions crates/voicevox_core_c_api/src/c_impls.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
use std::{ffi::CString, path::Path};
use std::{
collections::HashMap,
ffi::CString,
path::Path,
ptr::NonNull,
sync::{Arc, LazyLock},
};

use camino::Utf8Path;
use duplicate::duplicate_item;
use easy_ext::ext;
use ref_cast::ref_cast_custom;
use voicevox_core::{InitializeOptions, Result, SpeakerMeta, VoiceModelId};

use crate::{
helpers::CApiResult, OpenJtalkRc, VoicevoxOnnxruntime, VoicevoxSynthesizer,
helpers::CApiResult,
object::{CApiObject, CApiObjectPtrExt as _},
OpenJtalkRc, VoicevoxOnnxruntime, VoicevoxSynthesizer, VoicevoxUserDict,
VoicevoxVoiceModelFile,
};

Expand Down Expand Up @@ -61,61 +71,93 @@ macro_rules! to_cstr {
use to_cstr;

impl OpenJtalkRc {
pub(crate) fn new(open_jtalk_dic_dir: impl AsRef<Utf8Path>) -> Result<Self> {
Ok(Self {
open_jtalk: voicevox_core::blocking::OpenJtalk::new(open_jtalk_dic_dir)?,
})
pub(crate) fn new(open_jtalk_dic_dir: impl AsRef<Utf8Path>) -> Result<NonNull<Self>> {
let body = voicevox_core::blocking::OpenJtalk::new(open_jtalk_dic_dir)?;
Ok(<Self as CApiObject>::new(body))
}
}

impl VoicevoxSynthesizer {
pub(crate) fn new(
onnxruntime: &'static VoicevoxOnnxruntime,
open_jtalk: &OpenJtalkRc,
open_jtalk: *const OpenJtalkRc,
options: &InitializeOptions,
) -> Result<Self> {
let synthesizer = voicevox_core::blocking::Synthesizer::new(
) -> Result<NonNull<Self>> {
let body = voicevox_core::blocking::Synthesizer::new(
&onnxruntime.0,
open_jtalk.open_jtalk.clone(),
open_jtalk.body().clone(),
options,
)?;
Ok(Self { synthesizer })
Ok(<Self as CApiObject>::new(body))
}
}

pub(crate) fn onnxruntime(&self) -> &'static VoicevoxOnnxruntime {
VoicevoxOnnxruntime::new(self.synthesizer.onnxruntime())
#[ext(VoicevoxSynthesizerPtrExt)]
impl *const VoicevoxSynthesizer {
pub(crate) fn onnxruntime(self) -> &'static VoicevoxOnnxruntime {
VoicevoxOnnxruntime::new(self.body().onnxruntime())
}

pub(crate) fn load_voice_model(
&self,
self,
model: &voicevox_core::blocking::VoiceModelFile,
) -> CApiResult<()> {
self.synthesizer.load_voice_model(model)?;
self.body().load_voice_model(model)?;
Ok(())
}

pub(crate) fn unload_voice_model(&self, model_id: VoiceModelId) -> Result<()> {
self.synthesizer.unload_voice_model(model_id)?;
pub(crate) fn unload_voice_model(self, model_id: VoiceModelId) -> Result<()> {
self.body().unload_voice_model(model_id)?;
Ok(())
}

pub(crate) fn metas(&self) -> CString {
metas_to_json(&self.synthesizer.metas())
pub(crate) fn metas(self) -> CString {
metas_to_json(&self.body().metas())
}
}

impl VoicevoxVoiceModelFile {
pub(crate) fn open(path: impl AsRef<Path>) -> Result<Self> {
pub(crate) fn open(path: impl AsRef<Path>) -> Result<NonNull<Self>> {
let model = voicevox_core::blocking::VoiceModelFile::open(path)?;
Ok(Self { model })
Ok(Self::new(model))
}
}

pub(crate) fn metas(&self) -> CString {
metas_to_json(self.model.metas())
#[ext(VoicevoxVoiceModelFilePtrExt)]
impl *const VoicevoxVoiceModelFile {
pub(crate) fn metas(self) -> CString {
metas_to_json(self.body().metas())
}
}

fn metas_to_json(metas: &[SpeakerMeta]) -> CString {
let metas = serde_json::to_string(metas).expect("should not fail");
CString::new(metas).expect("should not contain NUL")
}

#[duplicate_item(
H B;
[ OpenJtalkRc ] [ voicevox_core::blocking::OpenJtalk ];
[ VoicevoxUserDict ] [ voicevox_core::blocking::UserDict ];
[ VoicevoxSynthesizer ] [ voicevox_core::blocking::Synthesizer<voicevox_core::blocking::OpenJtalk> ];
[ VoicevoxVoiceModelFile ] [ voicevox_core::blocking::VoiceModelFile ];
)]
impl CApiObject for H {
type RustApiObject = B;

fn heads() -> &'static std::sync::Mutex<Vec<Self>> {
static HEADS: std::sync::Mutex<Vec<H>> = std::sync::Mutex::new(vec![]);
&HEADS
}

fn bodies() -> &'static std::sync::Mutex<
HashMap<usize, Arc<parking_lot::RwLock<Option<Self::RustApiObject>>>>,
> {
#[expect(clippy::type_complexity, reason = "`CApiObject::bodies`と同様")]
static BODIES: LazyLock<
std::sync::Mutex<HashMap<usize, Arc<parking_lot::RwLock<Option<B>>>>>,
> = LazyLock::new(Default::default);

&BODIES
}
}
Loading
Loading