Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
189f4b824d | ||
|
|
79703a59cd | ||
|
|
339be16385 | ||
|
|
afa74cb65b | ||
|
|
7dd236d849 | ||
|
|
28087b861d |
37
Cargo.toml
37
Cargo.toml
@ -3,12 +3,45 @@ members = ["cli", "common", "mp4san", "mp4san-derive", "mp4san-test", "mp4san-te
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
edition = "2021"
|
||||
|
||||
rust-version = "1.61.0"
|
||||
rust-version = "1.66.0"
|
||||
|
||||
repository = "https://github.com/privacyresearchgroup/mp4san"
|
||||
license = "MIT"
|
||||
|
||||
exclude = ["/.github", ".*", "/test-data/"]
|
||||
|
||||
[workspace.dependencies]
|
||||
ac-ffmpeg = "0.18.1"
|
||||
afl = "0.12.16"
|
||||
anyhow = "1.0.68"
|
||||
assert_matches = "1.5.0"
|
||||
bindgen = "0.69.4"
|
||||
bitflags = "2.4.0"
|
||||
bitstream-io = "1.7.0"
|
||||
bytes = "1.3.0"
|
||||
cc = "1.0.79"
|
||||
clap = "4.0.32"
|
||||
criterion = "0.5.1"
|
||||
derive-where = "1.1.0"
|
||||
derive_builder = "0.20.2"
|
||||
derive_more = "2.0.0"
|
||||
downcast-rs = "2.0.1"
|
||||
dyn-clonable = "0.9.0"
|
||||
env_logger = "0.11.3"
|
||||
ffmpeg-sys-next = { version = "7.0.0", default-features = false }
|
||||
futures-util = { version = "0.3.28", default-features = false }
|
||||
libflate = "2.1.0"
|
||||
libwebp-sys = "0.9.4"
|
||||
log = "0.4.17"
|
||||
num-integer = { version = "0.1.45", default-features = false }
|
||||
num-traits = { version = "0.2.16", default-features = false }
|
||||
paste = "1.0.14"
|
||||
pkg-config = "0.3.26"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = "2.0"
|
||||
thiserror = "2.0.12"
|
||||
uuid = "1.3"
|
||||
|
||||
@ -6,9 +6,9 @@ version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
clap = { version = "4.0.32", features = ["derive"] }
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
mp4san = { path = "../mp4san" }
|
||||
webpsan = { path = "../webpsan" }
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.17"
|
||||
env_logger = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
@ -6,7 +6,7 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::{Parser as _, ValueEnum};
|
||||
use mp4san::SanitizedMetadata;
|
||||
use mp4san::{Config, SanitizedMetadata};
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
struct Args {
|
||||
@ -20,6 +20,9 @@ struct Args {
|
||||
#[clap(long, short = 'o')]
|
||||
output: Option<PathBuf>,
|
||||
|
||||
#[clap(long, short = 'c')]
|
||||
cumulative_mdat_box_size: Option<u32>,
|
||||
|
||||
/// Path to the file to test sanitization on.
|
||||
file: PathBuf,
|
||||
}
|
||||
@ -51,23 +54,26 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
let mut infile = File::open(&args.file).context("Error opening file")?;
|
||||
|
||||
match format {
|
||||
Format::Mp4 => match mp4san::sanitize(&mut infile).context("Error parsing mp4 file")? {
|
||||
SanitizedMetadata { metadata: Some(metadata), data } => {
|
||||
if let Some(output_path) = args.output {
|
||||
let mut outfile = File::create(output_path).context("Error opening output file")?;
|
||||
outfile.write(&metadata).context("Error writing output")?;
|
||||
infile
|
||||
.seek(io::SeekFrom::Start(data.offset))
|
||||
.context("Error seeking input")?;
|
||||
io::copy(&mut infile.take(data.len), &mut outfile).context("Error copying input to output")?;
|
||||
Format::Mp4 => {
|
||||
let config = Config { cumulative_mdat_box_size: args.cumulative_mdat_box_size, ..Default::default() };
|
||||
match mp4san::sanitize_with_config(&mut infile, config).context("Error parsing mp4 file")? {
|
||||
SanitizedMetadata { metadata: Some(metadata), data } => {
|
||||
if let Some(output_path) = args.output {
|
||||
let mut outfile = File::create(output_path).context("Error opening output file")?;
|
||||
outfile.write(&metadata).context("Error writing output")?;
|
||||
infile
|
||||
.seek(io::SeekFrom::Start(data.offset))
|
||||
.context("Error seeking input")?;
|
||||
io::copy(&mut infile.take(data.len), &mut outfile).context("Error copying input to output")?;
|
||||
}
|
||||
}
|
||||
SanitizedMetadata { metadata: None, .. } => {
|
||||
if let Some(output_path) = args.output {
|
||||
fs::copy(&args.file, output_path).context("Error writing output")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
SanitizedMetadata { metadata: None, .. } => {
|
||||
if let Some(output_path) = args.output {
|
||||
fs::copy(&args.file, output_path).context("Error writing output")?;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Format::Webp => {
|
||||
webpsan::sanitize(infile).context("Error parsing webp file")?;
|
||||
if let Some(output_path) = args.output {
|
||||
|
||||
@ -6,6 +6,6 @@ version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.11.3"
|
||||
libflate = "2.1.0"
|
||||
log = "0.4.17"
|
||||
env_logger = { workspace = true }
|
||||
libflate = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
@ -15,7 +15,7 @@ readme = "../README.md"
|
||||
exclude.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.3.0"
|
||||
derive_more = "0.99.17"
|
||||
futures-util = { version = "0.3.28", default-features = false, features = ["io"] }
|
||||
thiserror = "1.0.38"
|
||||
bytes = { workspace = true }
|
||||
derive_more = { workspace = true, features = ["deref", "deref_mut", "display"] }
|
||||
futures-util = { workspace = true, features = ["io"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@ -40,12 +40,12 @@ pub struct Report<E: ReportableError> {
|
||||
|
||||
/// A [`Display`]-able indicating there was extra trailing input after parsing.
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "extra unparsed input")]
|
||||
#[display("extra unparsed input")]
|
||||
pub struct ExtraUnparsedInput;
|
||||
|
||||
/// A [`Display`]-able indicating an error occurred while parsing a certain type.
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "while parsing value of type `{}`", _0)]
|
||||
#[display("while parsing value of type `{}`", _0)]
|
||||
pub struct WhileParsingType(&'static str);
|
||||
|
||||
/// A convenience type alias for a [`Result`](std::result::Result) containing an error wrapped by a [`Report`].
|
||||
@ -70,7 +70,7 @@ pub struct ReportStack {
|
||||
|
||||
/// A null error stack which ignores all data attached to it.
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "")]
|
||||
#[display("")]
|
||||
pub struct NullReportStack;
|
||||
|
||||
/// A trait for error types which can be used in a [`Report`].
|
||||
@ -95,7 +95,7 @@ pub trait ReportableErrorStack: Display {
|
||||
//
|
||||
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{message} at {location}")]
|
||||
#[display("{message} at {location}")]
|
||||
struct ReportEntry {
|
||||
message: Box<dyn Display + Send + Sync + 'static>,
|
||||
location: &'static Location<'static>,
|
||||
|
||||
@ -18,7 +18,7 @@ exclude.workspace = true
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
syn = "2.0"
|
||||
quote = "1.0"
|
||||
uuid = "1.3"
|
||||
proc-macro2 = { workspace = true }
|
||||
syn = { workspace = true }
|
||||
quote = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
|
||||
@ -6,9 +6,9 @@ version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
clap = { version = "4.0.32", features = ["derive"] }
|
||||
env_logger = "0.11.3"
|
||||
libflate = "2.1.0"
|
||||
log = "0.4.17"
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
env_logger = { workspace = true }
|
||||
libflate = { workspace = true }
|
||||
log = { workspace = true }
|
||||
mp4san = { path = "../mp4san" }
|
||||
|
||||
@ -11,12 +11,12 @@ ffmpeg = ["dep:ac-ffmpeg", "dep:bindgen", "dep:cc", "dep:ffmpeg-sys-next"]
|
||||
gpac = ["dep:bindgen", "dep:cc", "dep:pkg-config"]
|
||||
|
||||
[dependencies]
|
||||
ac-ffmpeg = { version = "0.18.1", optional = true }
|
||||
ffmpeg-sys-next = { version = "7.0.0", default-features = false, features = ["avformat"], optional = true }
|
||||
log = "0.4.17"
|
||||
thiserror = "1.0.40"
|
||||
ac-ffmpeg = { workspace = true, optional = true }
|
||||
ffmpeg-sys-next = { workspace = true, default-features = false, features = ["avformat"], optional = true }
|
||||
log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = { version = "0.69.4", optional = true }
|
||||
cc = { version = "1.0.79", optional = true }
|
||||
pkg-config = { version = "0.3.26", optional = true }
|
||||
bindgen = { workspace = true, optional = true }
|
||||
cc = { workspace = true, optional = true }
|
||||
pkg-config = { workspace = true, optional = true }
|
||||
|
||||
@ -15,20 +15,20 @@ readme = "README.md"
|
||||
exclude.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.3.0"
|
||||
derive-where = "1.1.0"
|
||||
derive_builder = "0.20.0"
|
||||
derive_more = "0.99.17"
|
||||
downcast-rs = "1.2.0"
|
||||
dyn-clonable = "0.9.0"
|
||||
futures-util = { version = "0.3.28", default-features = false, features = ["io"] }
|
||||
log = "0.4.17"
|
||||
mediasan-common = { path = "../common", version = "=0.5.2" }
|
||||
mp4san-derive = { path = "../mp4san-derive", version = "=0.5.2" }
|
||||
paste = "1.0.14"
|
||||
thiserror = "1.0.38"
|
||||
bytes = { workspace = true }
|
||||
derive-where = { workspace = true }
|
||||
derive_builder = { workspace = true }
|
||||
derive_more = { workspace = true, features = ["display", "from"] }
|
||||
downcast-rs = { workspace = true }
|
||||
dyn-clonable = { workspace = true }
|
||||
futures-util = { workspace = true, features = ["io"] }
|
||||
log = { workspace = true }
|
||||
mediasan-common = { path = "../common", version = "=0.5.3" }
|
||||
mp4san-derive = { path = "../mp4san-derive", version = "=0.5.3" }
|
||||
paste = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
assert_matches = { workspace = true }
|
||||
mediasan-common-test = { path = "../common-test" }
|
||||
mp4san-test = { path = "../mp4san-test" }
|
||||
|
||||
@ -90,6 +90,29 @@ pub struct Config {
|
||||
/// The default is 1 GiB.
|
||||
#[builder(default = "1024 * 1024 * 1024")]
|
||||
pub max_metadata_size: u64,
|
||||
/// The cumulative MDAT box size
|
||||
///
|
||||
/// The value is tightly associated with a specific
|
||||
/// use case scenario in which the transcoder internally
|
||||
/// generates a sequence of MDAT boxes, but delivers
|
||||
/// them compounded as a single monolythic MDAT box.
|
||||
///
|
||||
/// In order to avoid constantly updating the single
|
||||
/// MDAT box size (which may be impossible when the
|
||||
/// payload is continually encrypted and written to
|
||||
/// the file), the transcoder instead does the following:
|
||||
/// a) writes the MDAT box size to be equal to the
|
||||
/// fixed zero value
|
||||
/// b) keeps accumulating the box size and passes
|
||||
/// it as config argument to mp4sanitizer
|
||||
///
|
||||
/// IMPORTANT: given the special circumstances of
|
||||
/// the use case scenario of transcoding on mobile
|
||||
/// devices, the MDAT box size is expected to not
|
||||
/// exceed the 32-bit max bytes limit. Hence the
|
||||
/// cumulative_mdat_box_size is a 32-bit value
|
||||
#[builder(default = None)]
|
||||
pub cumulative_mdat_box_size: Option<u32>,
|
||||
}
|
||||
|
||||
/// Sanitized metadata returned by the sanitizer.
|
||||
@ -120,7 +143,7 @@ pub const COMPATIBLE_BRAND: FourCC = FourCC { value: *b"isom" };
|
||||
//
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "box data too large: {} > {}", _0, _1)]
|
||||
#[display("box data too large: {} > {}", _0, _1)]
|
||||
struct BoxDataTooLarge(u64, u64);
|
||||
|
||||
const MAX_FTYP_SIZE: u64 = 1024;
|
||||
@ -260,7 +283,7 @@ pub async fn sanitize_async_with_config<R: AsyncRead + AsyncSkip>(
|
||||
while !reader.as_mut().fill_buf().await?.is_empty() {
|
||||
let start_pos = reader.as_mut().stream_position().await?;
|
||||
|
||||
let header = BoxHeader::read(&mut reader)
|
||||
let mut header = BoxHeader::read(&mut reader)
|
||||
.await
|
||||
.map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedBox, "while parsing box header")))?;
|
||||
|
||||
@ -306,6 +329,12 @@ pub async fn sanitize_async_with_config<R: AsyncRead + AsyncSkip>(
|
||||
}
|
||||
|
||||
BoxType::MDAT => {
|
||||
if let Ok(None) = header.box_data_size() {
|
||||
if let Some(t) = config.cumulative_mdat_box_size {
|
||||
header.overwrite_size(t);
|
||||
}
|
||||
}
|
||||
|
||||
let box_size = skip_box(reader.as_mut(), &header).await? + header.encoded_len();
|
||||
log::info!("mdat @ 0x{start_pos:08x}: {box_size} bytes");
|
||||
|
||||
@ -803,4 +832,22 @@ mod test {
|
||||
assert_matches!(err.into_inner(), ParseError::InvalidBoxLayout);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cumulative_mdat_box_size() {
|
||||
let test_spec = test_mp4().mdat_data_until_eof().build_spec().unwrap();
|
||||
let test_1 = test_spec.build();
|
||||
let mdat_box_length = test_1.mdat.len as u32;
|
||||
|
||||
let config_bad = Config::builder().build();
|
||||
assert_matches!(sanitize_with_config(test_1, config_bad).unwrap_err(), Error::Parse(err) => {
|
||||
assert_matches!(err.into_inner(), ParseError::MissingRequiredBox(_));
|
||||
});
|
||||
|
||||
let test_2 = test_spec.build();
|
||||
let config_good = Config::builder()
|
||||
.cumulative_mdat_box_size(Some(mdat_box_length))
|
||||
.build();
|
||||
test_2.sanitize_ok_with_config(config_good);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,23 +77,23 @@ pub trait __ParseResultExt: ResultExt + Sized {
|
||||
pub(crate) use self::__ParseResultExt as ParseResultExt;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "multiple `{}` boxes", _0)]
|
||||
#[display("multiple `{}` boxes", _0)]
|
||||
pub(crate) struct MultipleBoxes(pub(crate) BoxType);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "while parsing `{}` box", _0)]
|
||||
#[display("while parsing `{}` box", _0)]
|
||||
pub(crate) struct WhileParsingBox(pub(crate) BoxType);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "while parsing `{}` box field `{}`", _0, _1)]
|
||||
#[display("while parsing `{}` box field `{}`", _0, _1)]
|
||||
pub(crate) struct WhileParsingField<T>(pub(crate) BoxType, pub(crate) T);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "while parsing `{}` box child `{}`", _0, _1)]
|
||||
#[display("while parsing `{}` box child `{}`", _0, _1)]
|
||||
pub(crate) struct WhileParsingChild(pub(crate) BoxType, pub(crate) BoxType);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "where `{} = {}`", _0, _1)]
|
||||
#[display("where `{} = {}`", _0, _1)]
|
||||
pub(crate) struct WhereEq<T, U>(pub(crate) T, pub(crate) U);
|
||||
|
||||
impl ReportableError for ParseError {
|
||||
|
||||
@ -131,6 +131,11 @@ impl BoxHeader {
|
||||
Ok(Self { box_type: name, box_size: size })
|
||||
}
|
||||
|
||||
pub fn overwrite_size(&mut self, actual_box_size: u32) {
|
||||
assert_eq!(self.box_size, BoxSize::UntilEof);
|
||||
self.box_size = BoxSize::Size(actual_box_size);
|
||||
}
|
||||
|
||||
pub const fn encoded_len(&self) -> u64 {
|
||||
let mut size = FourCC::size() + size_of::<u32>() as u64;
|
||||
if let BoxSize::Ext(_) = self.box_size {
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit b9f4d3f3273819ce50170357098a56113eb088b2
|
||||
Subproject commit b2121e38ec04025fdb62306b4863566fa993c8cb
|
||||
@ -10,6 +10,6 @@ default = []
|
||||
libwebp = ["dep:libwebp-sys"]
|
||||
|
||||
[dependencies]
|
||||
libwebp-sys = { version = "0.9.4", optional = true }
|
||||
log = "0.4.17"
|
||||
thiserror = "1.0.40"
|
||||
libwebp-sys = { workspace = true, optional = true }
|
||||
log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@ -19,21 +19,21 @@ default = ["error-detail"]
|
||||
error-detail = []
|
||||
|
||||
[dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
bitflags = "2.4.0"
|
||||
bitstream-io = "1.7.0"
|
||||
bytes = "1.3.0"
|
||||
derive_builder = "0.20.0"
|
||||
derive_more = "0.99.17"
|
||||
log = "0.4.17"
|
||||
mediasan-common = { path = "../common", version = "=0.5.2" }
|
||||
num-integer = { version = "0.1.45", default-features = false }
|
||||
num-traits = { version = "0.2.16", default-features = false }
|
||||
thiserror = "1.0.38"
|
||||
assert_matches = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
bitstream-io = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
derive_builder = { workspace = true }
|
||||
derive_more = { workspace = true, features = ["display"] }
|
||||
log = { workspace = true }
|
||||
mediasan-common = { path = "../common", version = "=0.5.3" }
|
||||
num-integer = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
criterion = { version = "0.5.1", features = ["async_futures"] }
|
||||
assert_matches = { workspace = true }
|
||||
criterion = { workspace = true, features = ["async_futures"] }
|
||||
mediasan-common-test = { path = "../common-test" }
|
||||
webpsan-test = { path = "../webpsan-test" }
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ trait ReadSkip: Read + Skip {}
|
||||
type DynChunkReader<'a> = ChunkReader<dyn ReadSkip + 'a>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "frame dimensions `{_0}`x`{_1}` do not match canvas dimensions `{_2}`x`{_3}`")]
|
||||
#[display("frame dimensions `{_0}`x`{_1}` do not match canvas dimensions `{_2}`x`{_3}`")]
|
||||
struct FrameDimensionsMismatch(NonZeroU16, NonZeroU16, NonZeroU32, NonZeroU32);
|
||||
|
||||
//
|
||||
|
||||
@ -26,7 +26,7 @@ pub struct CanonicalHuffmanTree<E: Endianness, S: Clone> {
|
||||
}
|
||||
|
||||
#[derive(Display)]
|
||||
#[display(fmt = "invalid lz77 prefix code `{_0}`")]
|
||||
#[display("invalid lz77 prefix code `{_0}`")]
|
||||
struct InvalidLz77PrefixCode(u16);
|
||||
|
||||
pub const LZ77_MAX_LEN: u16 = (LZ77_MAX_SYMBOL - 2) >> 1;
|
||||
|
||||
@ -58,19 +58,19 @@ pub(crate) trait ParseResultExt: ResultExt + Sized {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "multiple `{}` chunks", _0)]
|
||||
#[display("multiple `{}` chunks", _0)]
|
||||
pub(crate) struct MultipleChunks(pub(crate) FourCC);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "expected `{}` chunk", _0)]
|
||||
#[display("expected `{}` chunk", _0)]
|
||||
pub(crate) struct ExpectedChunk(pub(crate) FourCC);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "while parsing `{}` chunk", _0)]
|
||||
#[display("while parsing `{}` chunk", _0)]
|
||||
pub(crate) struct WhileParsingChunk(pub(crate) FourCC);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "while parsing `{}` chunk field `{}`", _0, _1)]
|
||||
#[display("while parsing `{}` chunk field `{}`", _0, _1)]
|
||||
pub(crate) struct WhileParsingField<T>(pub(crate) FourCC, pub(crate) T);
|
||||
|
||||
impl ReportableError for ParseError {
|
||||
|
||||
@ -26,26 +26,26 @@ pub struct LosslessImage {
|
||||
|
||||
#[derive(Clone, Display, PartialEq, Eq)]
|
||||
enum Transform {
|
||||
#[display(fmt = "predictor transform: block size {block_size}")]
|
||||
#[display("predictor transform: block size {block_size}")]
|
||||
Predictor { block_size: u16, _image: EntropyCodedImage },
|
||||
#[display(fmt = "color transform: block size {block_size}")]
|
||||
#[display("color transform: block size {block_size}")]
|
||||
Color { block_size: u16, _image: EntropyCodedImage },
|
||||
#[display(fmt = "subtract green transform")]
|
||||
#[display("subtract green transform")]
|
||||
SubtractGreen,
|
||||
#[display(fmt = "color indexing transform: {} colors", "image.width")]
|
||||
#[display("color indexing transform: {} colors", "image.width")]
|
||||
ColorIndexing { image: EntropyCodedImage },
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum TransformType {
|
||||
#[display(fmt = "predictor")]
|
||||
#[display("predictor")]
|
||||
Predictor = 0b00,
|
||||
#[display(fmt = "color")]
|
||||
#[display("color")]
|
||||
Color = 0b01,
|
||||
#[display(fmt = "subtract green")]
|
||||
#[display("subtract green")]
|
||||
SubtractGreen = 0b10,
|
||||
#[display(fmt = "color indexing")]
|
||||
#[display("color indexing")]
|
||||
ColorIndexing = 0b11,
|
||||
}
|
||||
|
||||
@ -59,14 +59,14 @@ struct EntropyCodedImage {
|
||||
struct SpatiallyCodedImage;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq)]
|
||||
#[display(fmt = "distance {dist} length {len}")]
|
||||
#[display("distance {dist} length {len}")]
|
||||
struct BackReference {
|
||||
dist: NonZeroU32,
|
||||
len: NonZeroU32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Display, PartialEq, Eq)]
|
||||
#[display(fmt = "({alpha}, {red}, {green}, {blue})")]
|
||||
#[display("({alpha}, {red}, {green}, {blue})")]
|
||||
struct Color {
|
||||
alpha: u8,
|
||||
red: u8,
|
||||
@ -81,9 +81,9 @@ struct ColorCache {
|
||||
|
||||
#[derive(Clone, Display, PartialEq, Eq)]
|
||||
enum MetaPrefixCodes {
|
||||
#[display(fmt = "single meta prefix code")]
|
||||
#[display("single meta prefix code")]
|
||||
Single,
|
||||
#[display(fmt = "multiple meta prefix codes: max code group {max_code_group}, block size {block_size}")]
|
||||
#[display("multiple meta prefix codes: max code group {max_code_group}, block size {block_size}")]
|
||||
Multiple {
|
||||
block_size: u16,
|
||||
max_code_group: u16,
|
||||
@ -124,39 +124,39 @@ struct DistancePrefixCode {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "out-of-bounds color cache index `{_0}` >= `{_1}`")]
|
||||
#[display("out-of-bounds color cache index `{_0}` >= `{_1}`")]
|
||||
struct ColorCacheIndexOutOfBounds(u16, u16);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "invalid back-reference distance `{_0}` at pixel `{_1}`")]
|
||||
#[display("invalid back-reference distance `{_0}` at pixel `{_1}`")]
|
||||
struct InvalidBackRefDistance(NonZeroU32, u32);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "invalid back-reference length `{_0}` at pixel `{_1}` with image length `{_2}`")]
|
||||
#[display("invalid back-reference length `{_0}` at pixel `{_1}` with image length `{_2}`")]
|
||||
struct InvalidBackRefLength(NonZeroU32, u32, u32);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "invalid code length repetition `{_0}` at `{_1}` with max symbols `{_2}`")]
|
||||
#[display("invalid code length repetition `{_0}` at `{_1}` with max symbols `{_2}`")]
|
||||
struct InvalidCodeLengthRepetition(u8, usize, u16);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "invalid color cache size `{_0}`")]
|
||||
#[display("invalid color cache size `{_0}`")]
|
||||
struct InvalidColorCacheSize(u8);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "invalid duplicate {_0} transform")]
|
||||
#[display("invalid duplicate {_0} transform")]
|
||||
struct InvalidDuplicateTransform(TransformType);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "invalid predictor `{_0}`")]
|
||||
#[display("invalid predictor `{_0}`")]
|
||||
struct InvalidPredictor(u8);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "invalid symbol count `{_0}` >= `{_1}`")]
|
||||
#[display("invalid symbol count `{_0}` >= `{_1}`")]
|
||||
struct InvalidSymbolCount(u16, u16);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "while parsing {_0} transform")]
|
||||
#[display("while parsing {_0} transform")]
|
||||
struct WhileParsingTransform(TransformType);
|
||||
|
||||
//
|
||||
|
||||
@ -31,7 +31,7 @@ pub struct Vp8lChunk {
|
||||
//
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display)]
|
||||
#[display(fmt = "invalid VP8L signature `0x{_0:x}` != `0x{}`", Vp8lChunk::SIGNATURE)]
|
||||
#[display("invalid VP8L signature `0x{_0:x}` != `0x{}`", Vp8lChunk::SIGNATURE)]
|
||||
struct InvalidSignature(u8);
|
||||
|
||||
//
|
||||
|
||||
Loading…
Reference in New Issue
Block a user