diff --git a/src/codec/codec.rs b/src/codec/codec.rs index bba7d04..61fad53 100644 --- a/src/codec/codec.rs +++ b/src/codec/codec.rs @@ -1,9 +1,6 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use super::{Audio, Capabilities, Id, Profile, Video}; use crate::ffi::*; -use crate::{media, Error}; +use crate::{media, utils, Error}; #[derive(PartialEq, Eq, Copy, Clone)] pub struct Codec { @@ -37,18 +34,11 @@ impl Codec { } pub fn name(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) } } pub fn description(&self) -> &str { - unsafe { - let long_name = (*self.as_ptr()).long_name; - if long_name.is_null() { - "" - } else { - from_utf8_unchecked(CStr::from_ptr(long_name).to_bytes()) - } - } + unsafe { utils::optional_str_from_c_ptr((*self.as_ptr()).long_name).unwrap_or("") } } pub fn medium(&self) -> media::Type { diff --git a/src/codec/id.rs b/src/codec/id.rs index 8863c14..ddc0915 100644 --- a/src/codec/id.rs +++ b/src/codec/id.rs @@ -1,9 +1,7 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::AVCodecID::*; use crate::ffi::*; use crate::util::media; +use crate::utils; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -661,7 +659,7 @@ impl Id { } pub fn name(&self) -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avcodec_get_name((*self).into())).to_bytes()) } + unsafe { utils::str_from_c_ptr(avcodec_get_name((*self).into())) } } } diff --git a/src/codec/mod.rs b/src/codec/mod.rs index 3c19383..11084a6 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -48,19 +48,17 @@ pub mod decoder; pub mod encoder; pub mod traits; -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::*; +use crate::utils; pub fn version() -> u32 { unsafe { avcodec_version() } } pub fn configuration() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avcodec_configuration()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avcodec_configuration()) } } pub fn license() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avcodec_license()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avcodec_license()) } } diff --git a/src/codec/subtitle/rect.rs b/src/codec/subtitle/rect.rs index c003a20..296ef66 100644 --- a/src/codec/subtitle/rect.rs +++ b/src/codec/subtitle/rect.rs @@ -1,9 +1,8 @@ -use std::ffi::CStr; use std::marker::PhantomData; -use std::str::from_utf8_unchecked; use super::{Flags, Type}; use crate::ffi::*; +use crate::utils; #[cfg(not(feature = "ffmpeg_5_0"))] use crate::{format, Picture}; @@ -122,7 +121,7 @@ impl<'a> Text<'a> { impl<'a> Text<'a> { pub fn get(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).text).to_bytes()) } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).text) } } } @@ -147,6 +146,6 @@ impl<'a> Ass<'a> { impl<'a> Ass<'a> { pub fn get(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).ass).to_bytes()) } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).ass) } } } diff --git a/src/device/mod.rs b/src/device/mod.rs index 51f9932..0e0e1dc 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -2,11 +2,10 @@ pub mod extensions; pub mod input; pub mod output; -use std::ffi::CStr; use std::marker::PhantomData; -use std::str::from_utf8_unchecked; use crate::ffi::*; +use crate::utils; pub struct Info<'a> { ptr: *mut AVDeviceInfo, @@ -33,13 +32,11 @@ impl<'a> Info<'a> { impl<'a> Info<'a> { pub fn name(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).device_name).to_bytes()) } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).device_name) } } pub fn description(&self) -> &str { - unsafe { - from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).device_description).to_bytes()) - } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).device_description) } } } @@ -54,9 +51,9 @@ pub fn version() -> u32 { } pub fn configuration() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avdevice_configuration()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avdevice_configuration()) } } pub fn license() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avdevice_license()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avdevice_license()) } } diff --git a/src/filter/filter.rs b/src/filter/filter.rs index 681f707..8823bc3 100644 --- a/src/filter/filter.rs +++ b/src/filter/filter.rs @@ -1,9 +1,8 @@ -use std::ffi::CStr; use std::marker::PhantomData; -use std::str::from_utf8_unchecked; use super::{Flags, Pad}; use crate::ffi::*; +use crate::utils; pub struct Filter { ptr: *mut AVFilter, @@ -25,19 +24,11 @@ impl Filter { impl Filter { pub fn name(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) } } pub fn description(&self) -> Option<&str> { - unsafe { - let ptr = (*self.as_ptr()).description; - - if ptr.is_null() { - None - } else { - Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) - } - } + unsafe { utils::optional_str_from_c_ptr((*self.as_ptr()).description) } } pub fn inputs(&self) -> Option { diff --git a/src/filter/mod.rs b/src/filter/mod.rs index ee6dee5..c195be0 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -13,10 +13,10 @@ pub use self::context::{Context, Sink, Source}; pub mod graph; pub use self::graph::Graph; -use std::ffi::{CStr, CString}; -use std::str::from_utf8_unchecked; +use std::ffi::CString; use crate::ffi::*; +use crate::utils; #[cfg(not(feature = "ffmpeg_5_0"))] use crate::Error; @@ -42,11 +42,11 @@ pub fn version() -> u32 { } pub fn configuration() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avfilter_configuration()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avfilter_configuration()) } } pub fn license() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avfilter_license()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avfilter_license()) } } pub fn find(name: &str) -> Option { diff --git a/src/filter/pad.rs b/src/filter/pad.rs index 28ec642..7aec626 100644 --- a/src/filter/pad.rs +++ b/src/filter/pad.rs @@ -1,9 +1,8 @@ -use std::ffi::CStr; use std::marker::PhantomData; -use std::str::from_utf8_unchecked; use crate::ffi::*; use crate::media; +use crate::utils; pub struct Pad<'a> { ptr: *const AVFilterPad, @@ -34,12 +33,7 @@ impl<'a> Pad<'a> { pub fn name(&self) -> Option<&str> { unsafe { let ptr = avfilter_pad_get_name(self.ptr, self.idx as i32); - - if ptr.is_null() { - None - } else { - Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) - } + utils::optional_str_from_c_ptr(ptr) } } diff --git a/src/format/format/input.rs b/src/format/format/input.rs index aee359b..b646b96 100644 --- a/src/format/format/input.rs +++ b/src/format/format/input.rs @@ -1,7 +1,5 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::*; +use crate::utils; pub struct Input { ptr: *mut AVInputFormat, @@ -23,19 +21,11 @@ impl Input { impl Input { pub fn name(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) } } pub fn description(&self) -> &str { - unsafe { - let long_name = (*self.as_ptr()).long_name; - - if long_name.is_null() { - "" - } else { - from_utf8_unchecked(CStr::from_ptr(long_name).to_bytes()) - } - } + unsafe { utils::optional_str_from_c_ptr((*self.as_ptr()).long_name).unwrap_or("") } } pub fn extensions(&self) -> Vec<&str> { @@ -45,9 +35,7 @@ impl Input { if ptr.is_null() { Vec::new() } else { - from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) - .split(',') - .collect() + utils::str_from_c_ptr(ptr).split(',').collect() } } } @@ -59,9 +47,7 @@ impl Input { if ptr.is_null() { Vec::new() } else { - from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) - .split(',') - .collect() + utils::str_from_c_ptr(ptr).split(',').collect() } } } diff --git a/src/format/format/output.rs b/src/format/format/output.rs index cdd7147..4bffbe6 100644 --- a/src/format/format/output.rs +++ b/src/format/format/output.rs @@ -1,12 +1,11 @@ use std::path::Path; -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::ptr; -use std::str::from_utf8_unchecked; use super::Flags; use crate::ffi::*; -use crate::{codec, media}; +use crate::{codec, media, utils}; pub struct Output { ptr: *mut AVOutputFormat, @@ -28,19 +27,11 @@ impl Output { impl Output { pub fn name(&self) -> &str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) } } pub fn description(&self) -> &str { - unsafe { - let long_name = (*self.as_ptr()).long_name; - - if long_name.is_null() { - "" - } else { - from_utf8_unchecked(CStr::from_ptr(long_name).to_bytes()) - } - } + unsafe { utils::optional_str_from_c_ptr((*self.as_ptr()).long_name).unwrap_or("") } } pub fn extensions(&self) -> Vec<&str> { @@ -50,9 +41,7 @@ impl Output { if ptr.is_null() { Vec::new() } else { - from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) - .split(',') - .collect() + utils::str_from_c_ptr(ptr).split(',').collect() } } } @@ -64,9 +53,7 @@ impl Output { if ptr.is_null() { Vec::new() } else { - from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) - .split(',') - .collect() + utils::str_from_c_ptr(ptr).split(',').collect() } } } diff --git a/src/format/mod.rs b/src/format/mod.rs index ff6c1ac..4a83d05 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -15,12 +15,12 @@ pub use self::format::{Input, Output}; pub mod network; -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::path::Path; use std::ptr; -use std::str::from_utf8_unchecked; use crate::ffi::*; +use crate::utils; use crate::{Dictionary, Error}; #[cfg(not(feature = "ffmpeg_5_0"))] @@ -49,11 +49,11 @@ pub fn version() -> u32 { } pub fn configuration() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avformat_configuration()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avformat_configuration()) } } pub fn license() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avformat_license()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avformat_license()) } } // XXX: use to_cstring when stable diff --git a/src/lib.rs b/src/lib.rs index 71cfddb..7e1f041 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,8 @@ pub use crate::filter::Filter; pub mod software; +pub(crate) mod utils; + fn init_error() { util::error::register_all(); } diff --git a/src/software/resampling/mod.rs b/src/software/resampling/mod.rs index 368cf8c..4785331 100644 --- a/src/software/resampling/mod.rs +++ b/src/software/resampling/mod.rs @@ -18,19 +18,17 @@ pub use self::context::Context; mod extensions; -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::*; +use crate::utils; pub fn version() -> u32 { unsafe { swresample_version() } } pub fn configuration() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(swresample_configuration()).to_bytes()) } + unsafe { utils::str_from_c_ptr(swresample_configuration()) } } pub fn license() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(swresample_license()).to_bytes()) } + unsafe { utils::str_from_c_ptr(swresample_license()) } } diff --git a/src/software/scaling/mod.rs b/src/software/scaling/mod.rs index 3476b93..0d06461 100644 --- a/src/software/scaling/mod.rs +++ b/src/software/scaling/mod.rs @@ -17,19 +17,17 @@ pub use self::context::Context; mod extensions; -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::*; +use crate::utils; pub fn version() -> u32 { unsafe { swscale_version() } } pub fn configuration() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(swscale_configuration()).to_bytes()) } + unsafe { utils::str_from_c_ptr(swscale_configuration()) } } pub fn license() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(swscale_license()).to_bytes()) } + unsafe { utils::str_from_c_ptr(swscale_license()) } } diff --git a/src/util/color/primaries.rs b/src/util/color/primaries.rs index c4b1969..1f11643 100644 --- a/src/util/color/primaries.rs +++ b/src/util/color/primaries.rs @@ -1,8 +1,6 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::AVColorPrimaries::*; use crate::ffi::*; +use crate::utils; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -40,8 +38,7 @@ impl Primaries { } unsafe { let ptr = av_color_primaries_name((*self).into()); - ptr.as_ref() - .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + utils::optional_str_from_c_ptr(ptr) } } } diff --git a/src/util/color/range.rs b/src/util/color/range.rs index 9f6a972..c461880 100644 --- a/src/util/color/range.rs +++ b/src/util/color/range.rs @@ -1,8 +1,6 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::AVColorRange::*; use crate::ffi::*; +use crate::utils; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -21,8 +19,7 @@ impl Range { } unsafe { let ptr = av_color_range_name((*self).into()); - ptr.as_ref() - .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + utils::optional_str_from_c_ptr(ptr) } } } diff --git a/src/util/color/space.rs b/src/util/color/space.rs index eaceca1..25cbbcc 100644 --- a/src/util/color/space.rs +++ b/src/util/color/space.rs @@ -1,8 +1,6 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::AVColorSpace::*; use crate::ffi::*; +use crate::utils; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -43,8 +41,7 @@ impl Space { } unsafe { let ptr = av_color_space_name((*self).into()); - ptr.as_ref() - .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + utils::optional_str_from_c_ptr(ptr) } } } diff --git a/src/util/color/transfer_characteristic.rs b/src/util/color/transfer_characteristic.rs index eb15863..f5499a2 100644 --- a/src/util/color/transfer_characteristic.rs +++ b/src/util/color/transfer_characteristic.rs @@ -1,8 +1,6 @@ -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::AVColorTransferCharacteristic::*; use crate::ffi::*; +use crate::utils; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -37,8 +35,7 @@ impl TransferCharacteristic { } unsafe { let ptr = av_color_transfer_name((*self).into()); - ptr.as_ref() - .map(|ptr| from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + utils::optional_str_from_c_ptr(ptr) } } } diff --git a/src/util/dictionary/iter.rs b/src/util/dictionary/iter.rs index 119347b..cfe6a6b 100644 --- a/src/util/dictionary/iter.rs +++ b/src/util/dictionary/iter.rs @@ -1,9 +1,9 @@ -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::marker::PhantomData; use std::ptr; -use std::str::from_utf8_unchecked; use crate::ffi::*; +use crate::utils; pub struct Iter<'a> { ptr: *const AVDictionary, @@ -32,8 +32,8 @@ impl<'a> Iterator for Iter<'a> { let entry = av_dict_get(self.ptr, empty.as_ptr(), self.cur, AV_DICT_IGNORE_SUFFIX); if !entry.is_null() { - let key = from_utf8_unchecked(CStr::from_ptr((*entry).key).to_bytes()); - let val = from_utf8_unchecked(CStr::from_ptr((*entry).value).to_bytes()); + let key = utils::str_from_c_ptr((*entry).key); + let val = utils::str_from_c_ptr((*entry).value); self.cur = entry; diff --git a/src/util/format/pixel.rs b/src/util/format/pixel.rs index 76ae6f5..36bed78 100644 --- a/src/util/format/pixel.rs +++ b/src/util/format/pixel.rs @@ -1,10 +1,11 @@ use std::error; -use std::ffi::{CStr, CString, NulError}; +use std::ffi::{CString, NulError}; use std::fmt; -use std::str::{from_utf8_unchecked, FromStr}; +use std::str::FromStr; use crate::ffi::AVPixelFormat::*; use crate::ffi::*; +use crate::utils; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -448,7 +449,7 @@ impl Descriptor { } pub fn name(self) -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) } + unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) } } pub fn nb_components(self) -> u8 { diff --git a/src/util/format/sample.rs b/src/util/format/sample.rs index 311f060..a6552f4 100644 --- a/src/util/format/sample.rs +++ b/src/util/format/sample.rs @@ -1,11 +1,11 @@ -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::ops::Index; use std::ptr; use std::slice; -use std::str::from_utf8_unchecked; use crate::ffi::AVSampleFormat::*; use crate::ffi::*; +use crate::utils; use libc::{c_int, c_void}; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -33,9 +33,7 @@ pub enum Type { impl Sample { #[inline] pub fn name(&self) -> &'static str { - unsafe { - from_utf8_unchecked(CStr::from_ptr(av_get_sample_fmt_name((*self).into())).to_bytes()) - } + unsafe { utils::str_from_c_ptr(av_get_sample_fmt_name((*self).into())) } } #[inline] diff --git a/src/util/frame/side_data.rs b/src/util/frame/side_data.rs index 2413221..81d89a4 100644 --- a/src/util/frame/side_data.rs +++ b/src/util/frame/side_data.rs @@ -1,11 +1,10 @@ -use std::ffi::CStr; use std::marker::PhantomData; use std::slice; -use std::str::from_utf8_unchecked; use super::Frame; use crate::ffi::AVFrameSideDataType::*; use crate::ffi::*; +use crate::utils; use crate::DictionaryRef; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -74,9 +73,7 @@ pub enum Type { impl Type { #[inline] pub fn name(&self) -> &'static str { - unsafe { - from_utf8_unchecked(CStr::from_ptr(av_frame_side_data_name((*self).into())).to_bytes()) - } + unsafe { utils::str_from_c_ptr(av_frame_side_data_name((*self).into())) } } } diff --git a/src/util/mod.rs b/src/util/mod.rs index 899492c..2937dde 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -16,10 +16,8 @@ pub mod range; pub mod rational; pub mod time; -use std::ffi::CStr; -use std::str::from_utf8_unchecked; - use crate::ffi::*; +use crate::utils; #[inline(always)] pub fn version() -> u32 { @@ -28,10 +26,10 @@ pub fn version() -> u32 { #[inline(always)] pub fn configuration() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avutil_configuration()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avutil_configuration()) } } #[inline(always)] pub fn license() -> &'static str { - unsafe { from_utf8_unchecked(CStr::from_ptr(avutil_license()).to_bytes()) } + unsafe { utils::str_from_c_ptr(avutil_license()) } } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..c0751cc --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,21 @@ +//! Internal utils, not related to `avutil` + +use std::ffi::CStr; + +/// `ptr` must be non-null and valid. +/// Ensure that the returned lifetime is correctly bounded. +#[inline] +pub unsafe fn str_from_c_ptr<'s>(ptr: *const libc::c_char) -> &'s str { + unsafe { std::str::from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes()) } +} + +/// `ptr` must be null or valid. +/// Ensure that the returned lifetime is correctly bounded. +#[inline] +pub unsafe fn optional_str_from_c_ptr<'s>(ptr: *const libc::c_char) -> Option<&'s str> { + if ptr.is_null() { + None + } else { + Some(str_from_c_ptr(ptr)) + } +}