From 194f964e91c3e08dff07d2ff0cf22bf7637df813 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 26 May 2022 11:16:29 +0000 Subject: [PATCH] Update --- Cargo.lock | 28 ++++++ Cargo.toml | 1 + src/downloader/pd_file/enums.rs | 46 ++++++++++ src/downloader/pd_file/error.rs | 8 ++ src/downloader/pd_file/file.rs | 18 ++++ src/downloader/pd_file/mod.rs | 7 +- src/downloader/pd_file/part_status.rs | 126 ++++++++++++++++++++++++++ src/main.rs | 1 + 8 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 src/downloader/pd_file/part_status.rs diff --git a/Cargo.lock b/Cargo.lock index ac945a6..fbfb0cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -935,6 +935,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "native-tls" version = "0.2.8" @@ -1142,6 +1163,7 @@ dependencies = [ "json", "lazy_static", "link-cplusplus", + "modular-bitfield", "proc_macros", "regex", "reqwest", @@ -1470,6 +1492,12 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 8666034..d574139 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ indicatif = "0.17.0-rc.11" int-enum = "0.4" json = "0.12" lazy_static = "1.4" +modular-bitfield = "0.11" proc_macros = { path = "proc_macros" } regex = "1" reqwest = { version = "0.11", features = ["brotli", "deflate", "gzip", "rustls-tls", "socks", "stream"] } diff --git a/src/downloader/pd_file/enums.rs b/src/downloader/pd_file/enums.rs index c5da1a9..08ad67f 100644 --- a/src/downloader/pd_file/enums.rs +++ b/src/downloader/pd_file/enums.rs @@ -1,5 +1,7 @@ use crate::downloader::pd_file::file::PdFile; use int_enum::IntEnum; +use modular_bitfield::BitfieldSpecifier; +use std::fmt::Display; /// The status of the downloaded file. #[repr(u8)] @@ -59,8 +61,52 @@ impl PdFileType { } } +#[repr(u8)] +#[derive(BitfieldSpecifier, Clone, Copy, Debug, Eq, PartialEq, IntEnum)] +#[bits = 2] +/// The status of the each part in pd file. +pub enum PdFilePartStatus { + /// The download of this part is waited. + Waited = 0, + /// The download of this part is started. + Downloading = 1, + /// The download of this part is completed. + Downloaded = 2, +} + +impl PdFilePartStatus { + #[inline] + /// Returns true if the download is waited + pub fn is_waited(&self) -> bool { + *self == Self::Waited + } + + #[inline] + /// Returns true if the download is started + pub fn is_downloading(&self) -> bool { + *self == Self::Downloading + } + + #[inline] + /// Returns true if the download is completed. + pub fn is_downloaded(&self) -> bool { + *self == Self::Downloaded + } +} + +impl Display for PdFilePartStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Waited => { f.write_str("PdFilePartStatus::Waited") } + Self::Downloading => { f.write_str("PdFilePartStatus::Downloading") } + Self::Downloaded => { f.write_str("PdFilePartStatus::Downloaded") } + } + } +} + #[test] fn test_enums() { assert_eq!(PdFileStatus::Downloading.int_value().to_le_bytes(), [1]); assert_eq!(PdFileType::MultiThread.int_value().to_le_bytes(), [1]); + assert_eq!(PdFilePartStatus::Downloaded.int_value().to_le_bytes(), [2]); } diff --git a/src/downloader/pd_file/error.rs b/src/downloader/pd_file/error.rs index 15c2ada..76bce55 100644 --- a/src/downloader/pd_file/error.rs +++ b/src/downloader/pd_file/error.rs @@ -1,3 +1,4 @@ +use super::part_status::OutOfBoundsError; use crate::gettext; use int_enum::IntEnum; use int_enum::IntEnumError; @@ -5,6 +6,7 @@ use std::convert::From; use std::fmt::Display; use std::string::FromUtf8Error; +/// Pd file's error #[derive(Debug, derive_more::From)] pub enum PdFileError { IoError(std::io::Error), @@ -50,3 +52,9 @@ impl From> for PdFileError { Self::String(format!("{} {}", gettext("Invalid pd file: "), e)) } } + +impl From> for PdFileError { + fn from(e: OutOfBoundsError) -> Self { + Self::String(format!("{}", e)) + } +} diff --git a/src/downloader/pd_file/file.rs b/src/downloader/pd_file/file.rs index 535c572..6af2122 100644 --- a/src/downloader/pd_file/file.rs +++ b/src/downloader/pd_file/file.rs @@ -2,6 +2,7 @@ use crate::downloader::pd_file::error::PdFileError; use crate::downloader::pd_file::enums::PdFileResult; use crate::downloader::pd_file::enums::PdFileStatus; use crate::downloader::pd_file::enums::PdFileType; +use crate::downloader::pd_file::part_status::PdFilePartStatus; use crate::downloader::pd_file::version::PdFileVersion; use crate::ext::io::StructRead; use crate::ext::replace::ReplaceWith2; @@ -24,6 +25,7 @@ use std::io::Write; use std::ops::Drop; use std::path::Path; use std::path::PathBuf; +use std::sync::Arc; use std::sync::RwLock; use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicU32; @@ -60,6 +62,8 @@ pub struct PdFile { part_size: AtomicU32, /// Only stored in memory. mem_only: AtomicBool, + /// The status of the each part. + part_datas: RwLock>>, } impl PdFile { @@ -77,6 +81,7 @@ impl PdFile { downloaded_file_size: AtomicU64::new(0), part_size: AtomicU32::new(0), mem_only: AtomicBool::new(true), + part_datas: RwLock::new(Vec::new()), } } @@ -106,6 +111,17 @@ impl PdFile { self.downloaded_file_size.load(Ordering::Relaxed) } + /// Return status data of a part + /// * `index` - The part index + pub fn get_part_data(&self, index: usize) -> Option> { + let datas = self.part_datas.get_ref(); + if index < datas.len() { + Some(Arc::clone(&datas[index])) + } else { + None + } + } + #[inline] /// Returns true if the download is completed. pub fn is_completed(&self) -> bool { @@ -198,6 +214,8 @@ impl PdFile { downloaded_file_size: AtomicU64::new(downloaded_file_size), part_size: AtomicU32::new(part_size), mem_only: AtomicBool::new(false), + // #TODO + part_datas: RwLock::new(Vec::new()), }) } diff --git a/src/downloader/pd_file/mod.rs b/src/downloader/pd_file/mod.rs index ff1b856..a80f528 100644 --- a/src/downloader/pd_file/mod.rs +++ b/src/downloader/pd_file/mod.rs @@ -1,8 +1,11 @@ +/// The enums of the pd file. +pub mod enums; /// The error type of the pd file. pub mod error; /// The pd file pub mod file; -/// The enums of the pd file. -pub mod enums; +/// The status of the each part. +#[allow(dead_code)] +pub mod part_status; /// Version of the pd file pub mod version; diff --git a/src/downloader/pd_file/part_status.rs b/src/downloader/pd_file/part_status.rs new file mode 100644 index 0000000..2d9fbe3 --- /dev/null +++ b/src/downloader/pd_file/part_status.rs @@ -0,0 +1,126 @@ +use crate::downloader::pd_file::enums::PdFilePartStatus as PdFilePartStatus2; +use crate::downloader::pd_file::error::PdFileError; +use crate::ext::rw_lock::GetRwLock; +use crate::gettext; +use int_enum::IntEnum; +use modular_bitfield::bitfield; +use modular_bitfield::prelude::B30; +use std::fmt::Debug; +use std::fmt::Display; +use std::sync::RwLock; + +/// The data is out of bounds. +#[derive(Clone, Debug)] +pub struct OutOfBoundsError { + /// Type name + t: String, + /// the value + v: T, +} + +impl OutOfBoundsError { + pub fn new + ?Sized>(t: &S, v: T) -> Self { + Self { t: String::from(t.as_ref()), v: v } + } +} + +impl Display for OutOfBoundsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Failed to set type {} with value {}", self.t.as_str(), self.v)) + } +} + +#[bitfield(bits = 32)] +#[derive(Clone)] +/// The status of the each part in pd file (For internal usage) +struct PdFilePartStatusInternal { + #[bits = 2] + status: PdFilePartStatus2, + downloaded_size: B30, +} + +impl Debug for PdFilePartStatusInternal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{{ status: {:?}, downloaded_size: {:?} }}", self.status(), self.downloaded_size())) + } +} + +#[derive(Debug)] +/// The status of the each part in pd file +pub struct PdFilePartStatus { + status: RwLock, +} + +impl PdFilePartStatus { + /// Create a new [PdFilePartStatus] + /// # paincs + /// Will panic if internal errors happened. + pub fn new() -> Self { + let mut status = PdFilePartStatusInternal::new(); + status.set_status(PdFilePartStatus2::Waited); + status.set_downloaded_size(0); + Self { status: RwLock::new(status) } + } + + /// Returns the status of this part + pub fn status(&self) -> PdFilePartStatus2 { + self.status.get_ref().status() + } + + /// Set the status of this part + pub fn set_status(&self, status: PdFilePartStatus2) -> Result<(), PdFileError> { + match self.status.get_mut().set_status_checked(status) { + Ok(_) => { Ok(()) } + Err(_) => { + Err(PdFileError::from(OutOfBoundsError::new("PdFilePartStatus", status))) + } + } + } + + #[inline] + /// Returns true if the download is waited + pub fn is_waited(&self) -> bool { + self.status().is_waited() + } + + #[inline] + /// Returns true if the download is started + pub fn is_downloading(&self) -> bool { + self.status().is_downloading() + } + + #[inline] + /// Returns true if the download is completed. + pub fn is_downloaded(&self) -> bool { + self.status().is_downloaded() + } + + /// Create a new instance of the [PdFilePartStatus] from bytes. + /// * `bytes` - The data + /// * `offset` - The offset of the needed data + /// + /// Returns a new instance if succeed otherwise a Error because the data is less than 4 bytes. + /// # Panics + /// Will panic if unwanted error occured + pub fn from_bytes + ?Sized>(bytes: &T, offset: usize) -> Result { + let value = bytes.as_ref(); + if (value.len() - offset) < 4 { + Err(gettext("At least 4 bytes is needed."))?; + } + let st = (value[offset] & 0xC0) / 0x20; + let mut status = PdFilePartStatusInternal::new(); + status.set_status(PdFilePartStatus2::from_int(st)?); + // #TODO + Ok(Self { status: RwLock::new(status) }) + } +} + +#[test] +fn test_part_status() { + assert_eq!(std::mem::size_of::(), 4); + let status = PdFilePartStatus::new(); + assert_eq!(status.status(), PdFilePartStatus2::Waited); + assert_eq!(status.is_waited(), true); + let status = PdFilePartStatus::from_bytes(&[80u8, 0, 0, 0], 0).unwrap(); + assert_eq!(status.is_downloaded(), true); +} diff --git a/src/main.rs b/src/main.rs index 9e60cab..64db2c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ extern crate int_enum; extern crate lazy_static; #[cfg(all(feature = "link-cplusplus", target_env = "gnu"))] extern crate link_cplusplus; +extern crate modular_bitfield; extern crate proc_macros; extern crate tokio; extern crate regex;