Skip to content

Commit bf36a8e

Browse files
committed
misc: Move utils and MP4 IO to another crate
1 parent 8625a12 commit bf36a8e

File tree

114 files changed

+1037
-748
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+1037
-748
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"lofty",
66
"lofty_attr",
77
"ogg_pager",
8+
"aud_io",
89
"fuzz",
910
"examples/custom_resolver",
1011
]
@@ -14,13 +15,16 @@ edition = "2024"
1415
rust-version = "1.85"
1516
repository = "https://github.com/Serial-ATA/lofty-rs"
1617
license = "MIT OR Apache-2.0"
18+
authors = ["Serial <69764315+Serial-ATA@users.noreply.github.com>"]
1719

1820
[workspace.dependencies]
1921
lofty = { version = "0.22.4", path = "lofty" }
2022
lofty_attr = { version = "0.11.1", path = "lofty_attr" }
2123
ogg_pager = { version = "0.7.0", path = "ogg_pager" }
24+
aud_io = { version = "0.1.0", path = "aud_io" }
2225

2326
byteorder = "1.5.0"
27+
log = "0.4.27"
2428

2529
[workspace.lints.rust]
2630
missing_docs = "deny"

aud_io/Cargo.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
name = "aud_io"
3+
version = "0.1.0"
4+
description = "" # TODO
5+
keywords = ["audio", "mp4"]
6+
categories = ["multimedia", "multimedia::audio", "parser-implementations"]
7+
readme = "" # TODO
8+
include = ["src", "../LICENSE-APACHE", "../LICENSE-MIT"]
9+
authors.workspace = true
10+
edition.workspace = true
11+
license.workspace = true
12+
repository.workspace = true
13+
14+
[dependencies]
15+
byteorder.workspace = true
16+
log.workspace = true
17+
18+
[dev-dependencies]
19+
tempfile = "3.15.0"
20+
test-log = "0.2.16"
21+
22+
#[lints]
23+
#workspace = true

lofty/src/util/alloc.rs renamed to aud_io/src/alloc.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::error::Result;
2-
use crate::macros::err;
3-
2+
use crate::err;
43
use crate::config::global_options;
54

65
/// Provides the `fallible_repeat` method on `Vec`
@@ -49,7 +48,8 @@ impl<T> VecFallibleRepeat<T> for Vec<T> {
4948
/// Creates a `Vec` of the specified length, containing copies of `element`.
5049
///
5150
/// This should be used through [`try_vec!`](crate::macros::try_vec)
52-
pub(crate) fn fallible_vec_from_element<T>(element: T, expected_size: usize) -> Result<Vec<T>>
51+
#[doc(hidden)]
52+
pub fn fallible_vec_from_element<T>(element: T, expected_size: usize) -> Result<Vec<T>>
5353
where
5454
T: Clone,
5555
{
@@ -59,7 +59,7 @@ where
5959
/// Provides the `try_with_capacity` method on `Vec`
6060
///
6161
/// This can be used directly.
62-
pub(crate) trait VecFallibleCapacity<T>: Sized {
62+
pub trait VecFallibleCapacity<T>: Sized {
6363
/// Same as `Vec::with_capacity`, but takes `GlobalOptions::allocation_limit` into account.
6464
///
6565
/// Named `try_with_capacity_stable` to avoid conflicts with the nightly `Vec::try_with_capacity`.
@@ -81,7 +81,7 @@ impl<T> VecFallibleCapacity<T> for Vec<T> {
8181

8282
#[cfg(test)]
8383
mod tests {
84-
use crate::util::alloc::fallible_vec_from_element;
84+
use super::fallible_vec_from_element;
8585

8686
#[test_log::test]
8787
fn vec_fallible_repeat() {

aud_io/src/config/global.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use std::cell::UnsafeCell;
2+
3+
thread_local! {
4+
static GLOBAL_OPTIONS: UnsafeCell<GlobalOptions> = UnsafeCell::new(GlobalOptions::default());
5+
}
6+
7+
pub(crate) unsafe fn global_options() -> &'static GlobalOptions {
8+
GLOBAL_OPTIONS.with(|global_options| unsafe { &*global_options.get() })
9+
}
10+
11+
/// Options that control all interactions with Lofty for the current thread
12+
///
13+
/// # Examples
14+
///
15+
/// ```rust
16+
/// use aud_io::config::{GlobalOptions, apply_global_options};
17+
///
18+
/// // I have a custom resolver that I need checked
19+
/// let global_options = GlobalOptions::new().use_custom_resolvers(true);
20+
/// apply_global_options(global_options);
21+
/// ```
22+
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
23+
#[non_exhaustive]
24+
pub struct GlobalOptions {
25+
pub(crate) allocation_limit: usize,
26+
}
27+
28+
impl GlobalOptions {
29+
/// Default allocation limit for any single allocation
30+
pub const DEFAULT_ALLOCATION_LIMIT: usize = 16 * 1024 * 1024;
31+
32+
/// Creates a new `GlobalOptions`, alias for `Default` implementation
33+
///
34+
/// See also: [`GlobalOptions::default`]
35+
///
36+
/// # Examples
37+
///
38+
/// ```rust
39+
/// use aud_io::config::GlobalOptions;
40+
///
41+
/// let global_options = GlobalOptions::new();
42+
/// ```
43+
#[must_use]
44+
pub const fn new() -> Self {
45+
Self {
46+
allocation_limit: Self::DEFAULT_ALLOCATION_LIMIT,
47+
}
48+
}
49+
50+
/// The maximum number of bytes to allocate for any single tag item
51+
///
52+
/// This is a safety measure to prevent allocating too much memory for a single tag item. If a tag item
53+
/// exceeds this limit, the allocator will return [`AudioError::TooMuchData`].
54+
///
55+
/// # Examples
56+
///
57+
/// ```rust
58+
/// use aud_io::config::{GlobalOptions, apply_global_options};
59+
///
60+
/// // I have files with gigantic images, I'll double the allocation limit!
61+
/// let global_options = GlobalOptions::new().allocation_limit(32 * 1024 * 1024);
62+
/// apply_global_options(global_options);
63+
/// ```
64+
pub fn allocation_limit(&mut self, allocation_limit: usize) -> Self {
65+
self.allocation_limit = allocation_limit;
66+
*self
67+
}
68+
}
69+
70+
impl Default for GlobalOptions {
71+
/// The default implementation for `GlobalOptions`
72+
///
73+
/// The defaults are as follows:
74+
///
75+
/// ```rust,ignore
76+
/// GlobalOptions {
77+
/// allocation_limit: Self::DEFAULT_ALLOCATION_LIMIT,
78+
/// }
79+
/// ```
80+
fn default() -> Self {
81+
Self::new()
82+
}
83+
}
84+
85+
/// Applies the given `GlobalOptions` to the current thread
86+
///
87+
/// # Examples
88+
///
89+
/// ```rust
90+
/// use aud_io::config::{GlobalOptions, apply_global_options};
91+
///
92+
/// // I have a custom resolver that I need checked
93+
/// let global_options = GlobalOptions::new().use_custom_resolvers(true);
94+
/// apply_global_options(global_options);
95+
/// ```
96+
pub fn apply_global_options(options: GlobalOptions) {
97+
GLOBAL_OPTIONS.with(|global_options| unsafe {
98+
*global_options.get() = options;
99+
});
100+
}

aud_io/src/config/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
mod global;
2+
pub use global::*;
3+
mod parse;
4+
pub use parse::*;

aud_io/src/config/parse.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/// The parsing strictness mode
2+
///
3+
/// This can be set with [`Probe::options`](crate::probe::Probe).
4+
///
5+
/// # Examples
6+
///
7+
/// ```rust,no_run
8+
/// use lofty::config::{ParseOptions, ParsingMode};
9+
/// use lofty::probe::Probe;
10+
///
11+
/// # fn main() -> lofty::error::Result<()> {
12+
/// // We only want to read spec-compliant inputs
13+
/// let parsing_options = ParseOptions::new().parsing_mode(ParsingMode::Strict);
14+
/// let tagged_file = Probe::open("foo.mp3")?.options(parsing_options).read()?;
15+
/// # Ok(()) }
16+
/// ```
17+
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Default)]
18+
#[non_exhaustive]
19+
pub enum ParsingMode {
20+
/// Will eagerly error on invalid input
21+
///
22+
/// This mode will eagerly error on any non spec-compliant input.
23+
///
24+
/// ## Examples of behavior
25+
///
26+
/// * Unable to decode text - The parser will error and the entire input is discarded
27+
/// * Unable to determine the sample rate - The parser will error and the entire input is discarded
28+
Strict,
29+
/// Default mode, less eager to error on recoverably malformed input
30+
///
31+
/// This mode will attempt to fill in any holes where possible in otherwise valid, spec-compliant input.
32+
///
33+
/// NOTE: A readable input does *not* necessarily make it writeable.
34+
///
35+
/// ## Examples of behavior
36+
///
37+
/// * Unable to decode text - If valid otherwise, the field will be replaced by an empty string and the parser moves on
38+
/// * Unable to determine the sample rate - The sample rate will be 0
39+
#[default]
40+
BestAttempt,
41+
/// Least eager to error, may produce invalid/partial output
42+
///
43+
/// This mode will discard any invalid fields, and ignore the majority of non-fatal errors.
44+
///
45+
/// If the input is malformed, the resulting tags may be incomplete, and the properties zeroed.
46+
///
47+
/// ## Examples of behavior
48+
///
49+
/// * Unable to decode text - The entire item is discarded and the parser moves on
50+
/// * Unable to determine the sample rate - The sample rate will be 0
51+
Relaxed,
52+
}

aud_io/src/error.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use std::collections::TryReserveError;
2+
use std::fmt::Display;
3+
4+
pub type Result<T> = std::result::Result<T, AudioError>;
5+
6+
#[derive(Debug)]
7+
pub enum AudioError {
8+
// File data related errors
9+
/// Attempting to read/write an abnormally large amount of data
10+
TooMuchData,
11+
/// Expected the data to be a different size than provided
12+
///
13+
/// This occurs when the size of an item is written as one value, but that size is either too
14+
/// big or small to be valid within the bounds of that item.
15+
// TODO: Should probably have context
16+
SizeMismatch,
17+
18+
/// Errors that arise while decoding text
19+
TextDecode(&'static str),
20+
/// Arises when an atom contains invalid data
21+
BadAtom(&'static str),
22+
23+
// Conversions for external errors
24+
/// Unable to convert bytes to a String
25+
StringFromUtf8(std::string::FromUtf8Error),
26+
/// Unable to convert bytes to a str
27+
StrFromUtf8(std::str::Utf8Error),
28+
/// Represents all cases of [`std::io::Error`].
29+
Io(std::io::Error),
30+
/// Represents all cases of [`std::fmt::Error`].
31+
Fmt(std::fmt::Error),
32+
/// Failure to allocate enough memory
33+
Alloc(TryReserveError),
34+
/// This should **never** be encountered
35+
Infallible(std::convert::Infallible),
36+
}
37+
38+
impl Display for AudioError {
39+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40+
match self {
41+
// Conversions
42+
AudioError::StringFromUtf8(err) => write!(f, "{err}"),
43+
AudioError::StrFromUtf8(err) => write!(f, "{err}"),
44+
AudioError::Io(err) => write!(f, "{err}"),
45+
AudioError::Fmt(err) => write!(f, "{err}"),
46+
AudioError::Alloc(err) => write!(f, "{err}"),
47+
AudioError::Infallible(_) => write!(f, "A expected condition was not upheld"),
48+
49+
AudioError::TextDecode(message) => write!(f, "Text decoding: {message}"),
50+
AudioError::BadAtom(message) => write!(f, "MP4 Atom: {message}"),
51+
52+
// Files
53+
AudioError::TooMuchData => write!(
54+
f,
55+
"Attempted to read/write an abnormally large amount of data"
56+
),
57+
AudioError::SizeMismatch => write!(
58+
f,
59+
"Encountered an invalid item size, either too big or too small to be valid"
60+
),
61+
}
62+
}
63+
}
64+
65+
impl core::error::Error for AudioError {}
66+
67+
impl From<std::io::Error> for AudioError {
68+
fn from(input: std::io::Error) -> Self {
69+
AudioError::Io(input)
70+
}
71+
}
72+
73+
impl From<std::fmt::Error> for AudioError {
74+
fn from(input: std::fmt::Error) -> Self {
75+
AudioError::Fmt(input)
76+
}
77+
}
78+
79+
impl From<std::string::FromUtf8Error> for AudioError {
80+
fn from(input: std::string::FromUtf8Error) -> Self {
81+
AudioError::StringFromUtf8(input)
82+
}
83+
}
84+
85+
impl From<std::str::Utf8Error> for AudioError {
86+
fn from(input: std::str::Utf8Error) -> Self {
87+
AudioError::StrFromUtf8(input)
88+
}
89+
}
90+
91+
impl From<TryReserveError> for AudioError {
92+
fn from(input: TryReserveError) -> Self {
93+
AudioError::Alloc(input)
94+
}
95+
}
96+
97+
impl From<std::convert::Infallible> for AudioError {
98+
fn from(input: std::convert::Infallible) -> Self {
99+
AudioError::Infallible(input)
100+
}
101+
}

0 commit comments

Comments
 (0)