musikr: improve lifecycles

This commit is contained in:
Alexander Capehart 2025-02-15 13:04:50 -07:00
parent f5de03dfee
commit 0f3bed413d
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
10 changed files with 89 additions and 85 deletions

View file

@ -8,6 +8,7 @@ mod taglib;
mod jstream;
use taglib::file_ref::FileRef;
use taglib::file::File;
use jstream::JInputStream;
type SharedEnv<'local> = Rc<RefCell<JNIEnv<'local>>>;

View file

@ -1,12 +1,12 @@
use super::bridge::CppAudioProperties;
use std::pin::Pin;
pub struct AudioProperties<'a> {
this: Pin<&'a CppAudioProperties>
pub struct AudioProperties<'file_ref> {
this: Pin<&'file_ref CppAudioProperties>
}
impl<'a> AudioProperties<'a> {
pub(super) fn new(this: Pin<&'a CppAudioProperties>) -> Self {
impl<'file_ref> AudioProperties<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref CppAudioProperties>) -> Self {
Self { this }
}

View file

@ -78,7 +78,7 @@ mod bridge_impl {
#[cxx_name = "XiphComment"]
type CPPXiphComment;
#[cxx_name = "fieldListMap"]
fn fieldListMap(self: Pin<&CPPXiphComment>) -> &CPPFieldListMap;
fn fieldListMap(self: Pin<& CPPXiphComment>) -> &CPPFieldListMap;
#[namespace = "TagLib"]
#[cxx_name = "SimplePropertyMap"]

View file

@ -5,17 +5,17 @@ use super::ogg::OpusFile;
use super::ogg::VorbisFile;
use super::flac::FLACFile;
pub struct File<'a> {
this: Pin<&'a mut CPPFile>
pub struct File<'file_ref> {
this: Pin<&'file_ref mut CPPFile>
}
impl<'a> File<'a> {
pub(super) fn new(this: Pin<&'a mut CPPFile>) -> Self {
impl<'file_ref> File<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref mut CPPFile>) -> Self {
Self { this }
}
pub fn audio_properties(&self) -> Option<AudioProperties<'a>> {
pub fn audio_properties(&self) -> Option<AudioProperties<'file_ref>> {
let props_ptr = self.this.as_ref().audioProperties();
let props_ref = unsafe {
// SAFETY:
@ -30,7 +30,7 @@ impl<'a> File<'a> {
props_pin.map(|props| AudioProperties::new(props))
}
pub fn as_opus(&mut self) -> Option<OpusFile<'a>> {
pub fn as_opus(&mut self) -> Option<OpusFile<'file_ref>> {
let opus_file = unsafe {
// SAFETY:
// This FFI function will be a simple C++ dynamic_cast, which checks if
@ -51,7 +51,7 @@ impl<'a> File<'a> {
opus_pin.map(|opus| OpusFile::new(opus))
}
pub fn as_vorbis(&mut self) -> Option<VorbisFile<'a>> {
pub fn as_vorbis(&mut self) -> Option<VorbisFile<'file_ref>> {
let vorbis_file = unsafe {
// SAFETY:
// This FFI function will be a simple C++ dynamic_cast, which checks if
@ -72,7 +72,7 @@ impl<'a> File<'a> {
vorbis_pin.map(|vorbis| VorbisFile::new(vorbis))
}
pub fn as_flac(&mut self) -> Option<FLACFile<'a>> {
pub fn as_flac(&mut self) -> Option<FLACFile<'file_ref>> {
let flac_file = unsafe {
// SAFETY:
// This FFI function will be a simple C++ dynamic_cast, which checks if
@ -94,7 +94,7 @@ impl<'a> File<'a> {
}
}
impl<'a> Drop for File<'a> {
impl<'file_ref> Drop for File<'file_ref> {
fn drop(&mut self) {
unsafe {
std::ptr::drop_in_place(&mut self.this);

View file

@ -4,13 +4,13 @@ use super::iostream::{BridgedIOStream, IOStream};
use cxx::UniquePtr;
use std::pin::Pin;
pub struct FileRef<'a> {
stream: BridgedIOStream<'a>,
pub struct FileRef<'io> {
stream: BridgedIOStream<'io>,
this: UniquePtr<CPPFileRef>,
}
impl<'a> FileRef<'a> {
pub fn new<T: IOStream + 'a>(stream: T) -> FileRef<'a> {
impl<'io> FileRef<'io> {
pub fn new<T: IOStream + 'io>(stream: T) -> FileRef<'io> {
let stream = BridgedIOStream::new(stream);
let cpp_stream = stream.cpp_stream().as_mut_ptr();
let file_ref = unsafe { bridge::new_FileRef(cpp_stream) };
@ -20,7 +20,7 @@ impl<'a> FileRef<'a> {
}
}
pub fn file(&self) -> Option<File<'a>> {
pub fn file<'file_ref>(&'file_ref self) -> Option<File<'file_ref>> {
let file_ptr = unsafe {
// SAFETY:
// - This pin is only used in this unsafe scope.

View file

@ -7,16 +7,16 @@ use std::marker::PhantomData;
use cxx::UniquePtr;
use std::pin::Pin;
pub struct FLACFile<'a> {
this: Pin<&'a mut CPPFLACFile>
pub struct FLACFile<'file_ref> {
this: Pin<&'file_ref mut CPPFLACFile>
}
impl<'a> FLACFile<'a> {
pub(super) fn new(this: Pin<&'a mut CPPFLACFile>) -> Self {
impl<'file_ref> FLACFile<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref mut CPPFLACFile>) -> Self {
Self { this }
}
pub fn xiph_comments(&mut self) -> Option<XiphComment> {
pub fn xiph_comments(&mut self) -> Option<XiphComment<'file_ref>> {
let this = self.this.as_mut();
let tag = this.xiphComment(false);
let tag_ref = unsafe {
@ -28,25 +28,26 @@ impl<'a> FLACFile<'a> {
tag_pin.map(|tag| XiphComment::new(tag))
}
pub fn picture_list(&mut self) -> PictureList {
pub fn picture_list(&mut self) -> PictureList<'file_ref> {
let pictures = FLACFile_pictureList(self.this.as_mut());
PictureList::new(pictures)
}
}
pub struct PictureList<'a> {
// PictureList is implicitly tied to the lifetime of the parent that owns it,
// so we need to track that lifetime.
_data: PhantomData<&'a CPPFLACPicture>,
pub struct PictureList<'file_ref> {
// PictureList is implicitly tied to the lifetime of the file_ref, despite us technically
// """""owning"""" it.
_data: PhantomData<&'file_ref CPPFLACPicture>,
// Only in a UniquePtr because we can't marshal over ownership of the PictureList by itself over cxx.
this: UniquePtr<CPPPictureList>,
}
impl<'a> PictureList<'a> {
impl<'file_ref> PictureList<'file_ref> {
pub(super) fn new(this: UniquePtr<CPPPictureList>) -> Self {
Self { _data: PhantomData, this }
}
pub fn to_vec(&self) -> Vec<Picture> {
pub fn to_vec(&self) -> Vec<Picture<'file_ref>> {
let pictures = PictureList_to_vector(unsafe {
// SAFETY: This pin is only used in this unsafe scope.
// The pin is used as a C++ this pointer in the ffi call, which does
@ -69,16 +70,16 @@ impl<'a> PictureList<'a> {
}
}
pub struct Picture<'a> {
this: Pin<&'a CPPFLACPicture>
pub struct Picture<'file_ref> {
this: Pin<&'file_ref CPPFLACPicture>
}
impl<'a> Picture<'a> {
pub(super) fn new(this: Pin<&'a CPPFLACPicture>) -> Self {
impl<'file_ref> Picture<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref CPPFLACPicture>) -> Self {
Self { this }
}
pub fn data(&self) -> ByteVector<'a> {
pub fn data(&self) -> ByteVector<'file_ref> {
let data = Picture_data(self.this);
ByteVector::new(data)
}

View file

@ -10,13 +10,13 @@ pub trait IOStream : Read + Write + Seek {
}
pub(super) struct BridgedIOStream<'a> {
rs_stream: Pin<Box<DynIOStream<'a>>>,
pub(super) struct BridgedIOStream<'io_stream> {
rs_stream: Pin<Box<DynIOStream<'io_stream>>>,
cpp_stream: UniquePtr<CPPIOStream>
}
impl<'a> BridgedIOStream<'a> {
pub fn new<T : IOStream + 'a>(stream: T) -> Self {
impl<'io_stream> BridgedIOStream<'io_stream> {
pub fn new<T : IOStream + 'io_stream>(stream: T) -> Self {
let mut rs_stream = Box::pin(DynIOStream(Box::new(stream)));
let cpp_stream = bridge::wrap_RsIOStream(rs_stream.as_mut());
BridgedIOStream {
@ -30,7 +30,7 @@ impl<'a> BridgedIOStream<'a> {
}
}
impl<'a> Drop for BridgedIOStream<'a> {
impl<'io_stream> Drop for BridgedIOStream<'io_stream> {
fn drop(&mut self) {
unsafe {
// CPP stream references the rust stream, so it must be dropped first
@ -41,9 +41,9 @@ impl<'a> Drop for BridgedIOStream<'a> {
}
#[repr(C)]
pub(super) struct DynIOStream<'a>(Box<dyn IOStream + 'a>);
pub(super) struct DynIOStream<'io_stream>(Box<dyn IOStream + 'io_stream>);
impl<'a> DynIOStream<'a> {
impl<'io_stream> DynIOStream<'io_stream> {
// Implement the exposed functions for cxx bridge
pub fn name(&mut self) -> String {

View file

@ -2,16 +2,16 @@ pub use super::bridge::{CPPOpusFile, CPPVorbisFile};
use super::xiph::XiphComment;
use std::pin::Pin;
pub struct VorbisFile<'a> {
this: Pin<&'a mut CPPVorbisFile>
pub struct VorbisFile<'file_ref> {
this: Pin<&'file_ref mut CPPVorbisFile>
}
impl<'a> VorbisFile<'a> {
pub(super) fn new(this: Pin<&'a mut CPPVorbisFile>) -> Self {
impl<'file_ref> VorbisFile<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref mut CPPVorbisFile>) -> Self {
Self { this }
}
pub fn xiph_comments(&self) -> Option<XiphComment> {
pub fn xiph_comments(&self) -> Option<XiphComment<'file_ref>> {
let this = self.this.as_ref();
let tag = this.vorbisTag();
let tag_ref = unsafe {
@ -24,25 +24,18 @@ impl<'a> VorbisFile<'a> {
}
}
pub struct OpusFile<'a> {
this: Pin<&'a mut CPPOpusFile>
pub struct OpusFile<'file_ref> {
this: Pin<&'file_ref mut CPPOpusFile>
}
impl <'a> OpusFile<'a> {
pub(super) fn new(this: Pin<&'a mut CPPOpusFile>) -> Self {
impl<'file_ref> OpusFile<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref mut CPPOpusFile>) -> Self {
Self { this }
}
pub fn xiph_comments(&self) -> Option<XiphComment<'a>> {
pub fn xiph_comments(&self) -> Option<XiphComment<'file_ref>> {
let this = self.this.as_ref();
let tag = unsafe {
// SAFETY:
// - This pin is only used in this unsafe scope.
// - The pin is used as a C++ this pointer in the ffi call, which does
// not change address by C++ semantics.
// - The value is a pointer that does not depend on the address of self.
this.opusTag()
};
let tag = this.opusTag();
let tag_ref = unsafe {
// SAFETY: This pointer is a valid type, and can only used and accessed
// via this function and thus cannot be mutated, satisfying the aliasing rules.

View file

@ -4,17 +4,17 @@ use std::marker::PhantomData;
use std::pin::Pin;
use std::{ffi::CStr, string::ToString};
pub struct String<'a> {
this: Pin<&'a CPPString>,
pub struct String<'file_ref> {
this: Pin<&'file_ref CPPString>,
}
impl<'a> String<'a> {
pub(super) fn new(this: Pin<&'a CPPString>) -> Self {
impl<'file_ref> String<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref CPPString>) -> Self {
Self { this }
}
}
impl<'a> ToString for String<'a> {
impl<'file_ref> ToString for String<'file_ref> {
fn to_string(&self) -> std::string::String {
let c_str = self.this.toCString(true);
unsafe {
@ -34,12 +34,12 @@ impl<'a> ToString for String<'a> {
}
}
pub struct StringList<'a> {
this: Pin<&'a CPPStringList>,
pub struct StringList<'file_ref> {
this: Pin<&'file_ref CPPStringList>,
}
impl<'a> StringList<'a> {
pub(super) fn new(this: Pin<&'a CPPStringList>) -> Self {
impl<'file_ref> StringList<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref CPPStringList>) -> Self {
Self { this }
}
@ -57,15 +57,15 @@ impl<'a> StringList<'a> {
}
}
pub struct ByteVector<'a> {
pub struct ByteVector<'file_ref> {
// ByteVector is implicitly tied to the lifetime of the parent that owns it,
// so we need to track that lifetime. Only reason why it's a UniquePtr is because
// we can't marshal over ownership of the ByteVector by itself over cxx.
_data: PhantomData<&'a CPPByteVector>,
_data: PhantomData<&'file_ref CPPByteVector>,
this: UniquePtr<CPPByteVector>,
}
impl<'a> ByteVector<'a> {
impl<'file_ref> ByteVector<'file_ref> {
pub(super) fn new(this: UniquePtr<CPPByteVector>) -> Self {
Self {
_data: PhantomData,

View file

@ -5,38 +5,47 @@ use super::tk;
use std::pin::Pin;
use std::collections::HashMap;
pub struct XiphComment<'a> {
this: Pin<&'a mut CPPXiphComment>
pub struct XiphComment<'file_ref> {
this: Pin<&'file_ref mut CPPXiphComment>
}
impl<'a> XiphComment<'a> {
pub(super) fn new(this: Pin<&'a mut CPPXiphComment>) -> Self {
impl<'file_ref> XiphComment<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref mut CPPXiphComment>) -> Self {
Self { this }
}
pub fn field_list_map(&'a self) -> FieldListMap<'a> {
let map = self.this.as_ref().fieldListMap();
pub fn field_list_map<'slf>(&'slf self) -> FieldListMap<'file_ref> {
// To call the method we need, we have to get our mut reference down to an immutable
// reference. The safe API can do this, but shortens the lifecycle to at most self, even
// though the reference really lives as long as file_ref. Sadly, this requires us to transmute
// to extend the lifecycle back. This new pointer is really unsafe (we now have both a mut
// and an immutable reference to the same object), but it's dropped after this call.
// The value returned is unable to actually mutate this object, so it's safe.
let this_ref: &'slf CPPXiphComment = self.this.as_ref().get_ref();
let extended_ref: &'file_ref CPPXiphComment = unsafe { std::mem::transmute(this_ref) };
let this: Pin<&'file_ref CPPXiphComment> = unsafe { Pin::new_unchecked(extended_ref) };
let map = this.fieldListMap();
let map_pin = unsafe { Pin::new_unchecked(map) };
FieldListMap::new(map_pin)
}
pub fn picture_list(&mut self) -> PictureList<'a> {
pub fn picture_list(&mut self) -> PictureList<'file_ref> {
let pictures = XiphComment_pictureList(self.this.as_mut());
PictureList::new(pictures)
}
}
pub struct FieldListMap<'a> {
this: Pin<&'a CPPFieldListMap>,
pub struct FieldListMap<'file_ref> {
this: Pin<&'file_ref CPPFieldListMap>,
}
impl<'a> FieldListMap<'a> {
pub(super) fn new(this: Pin<&'a CPPFieldListMap>) -> Self {
impl<'file_ref> FieldListMap<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref CPPFieldListMap>) -> Self {
Self { this }
}
}
impl<'a> FieldListMap<'a> {
impl<'file_ref> FieldListMap<'file_ref> {
pub fn to_hashmap(&self) -> HashMap<String, Vec<String>> {
let cxx_vec = FieldListMap_to_entries(self.this);
cxx_vec