From c42305c8d25669ef081934892f66eb1c30546a4c Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 18 Jan 2026 20:39:47 +0800 Subject: [PATCH] feat: finish sonnet tas file rw functions --- BallanceTasSonnet/Cargo.lock | 2 +- BallanceTasSonnet/Cargo.toml | 4 +- BallanceTasSonnet/src/lib.rs | 6 +- BallanceTasSonnet/src/tasfile.rs | 254 +------------ BallanceTasSonnet/src/utils.rs | 2 - BallanceTasSonnet/src/utils/fps_converter.rs | 27 -- BallanceTasSonnet/src/utils/zlib.rs | 36 -- BallanceTasSonnet/src/wrapped.rs | 1 + BallanceTasSonnet/src/wrapped/tasfile.rs | 374 +++++++++++++++++++ 9 files changed, 386 insertions(+), 320 deletions(-) delete mode 100644 BallanceTasSonnet/src/utils.rs delete mode 100644 BallanceTasSonnet/src/utils/fps_converter.rs delete mode 100644 BallanceTasSonnet/src/utils/zlib.rs create mode 100644 BallanceTasSonnet/src/wrapped.rs create mode 100644 BallanceTasSonnet/src/wrapped/tasfile.rs diff --git a/BallanceTasSonnet/Cargo.lock b/BallanceTasSonnet/Cargo.lock index ac5dabe..574449e 100644 --- a/BallanceTasSonnet/Cargo.lock +++ b/BallanceTasSonnet/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "BallanceTasSonnet" -version = "0.1.0" +version = "1.0.0" dependencies = [ "byteorder", "libz-sys", diff --git a/BallanceTasSonnet/Cargo.toml b/BallanceTasSonnet/Cargo.toml index e064afc..76f2054 100644 --- a/BallanceTasSonnet/Cargo.toml +++ b/BallanceTasSonnet/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "BallanceTasSonnet" -version = "0.1.0" +version = "1.0.0" edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] -name = "BallanceTasSonnet" +name = "blctas" crate-type = ["cdylib"] [dependencies] diff --git a/BallanceTasSonnet/src/lib.rs b/BallanceTasSonnet/src/lib.rs index ee42f9c..4f7d2fd 100644 --- a/BallanceTasSonnet/src/lib.rs +++ b/BallanceTasSonnet/src/lib.rs @@ -1,11 +1,9 @@ use pyo3::prelude::*; -pub(crate) mod utils; -pub(crate) mod tasfile; +pub(crate) mod wrapped; -/// A Python module implemented in Rust. #[pymodule] -mod BallanceTasSonnet { +mod tasfile { use pyo3::prelude::*; /// Formats the sum of two numbers as string. diff --git a/BallanceTasSonnet/src/tasfile.rs b/BallanceTasSonnet/src/tasfile.rs index 748c799..87f88cc 100644 --- a/BallanceTasSonnet/src/tasfile.rs +++ b/BallanceTasSonnet/src/tasfile.rs @@ -1,249 +1,7 @@ -use crate::utils::fps_converter; -use thiserror::Error as TeError; +use pyo3::prelude::*; -#[derive(Debug, TeError)] -pub enum Error { - #[error("{0}")] - BadFpsConv(#[from] fps_converter::Error), - - #[error("given index is out of range")] - IndexOutOfRange, - #[error("arithmetic overflow")] - NumOverflow, -} - -type Result = std::result::Result; - -pub enum TasKey { - KeyUp, - KeyDown, - KeyLeft, - KeyRight, - KeyShift, - KeySpace, - KeyQ, - KeyEsc, - KeyEnter, -} - -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] -#[repr(C)] -pub struct TasFrame { - delta_time: f32, - key_flags: u32, -} - -impl TasFrame { - pub fn new(delta_time: f32, key_flags: u32) -> Self { - Self { - delta_time, - key_flags, - } - } - - pub fn with_fps(fps: f32) -> Result { - Ok(Self::new(fps_converter::to_delta(fps)?, 0u32)) - } -} - -impl TasFrame { - pub fn get_delta_time(&self) -> f32 { - self.delta_time - } - - pub fn set_delta_time(&mut self, delta_time: f32) { - self.delta_time = delta_time - } -} - -impl TasFrame { - fn get_key_flag(key: TasKey) -> u32 { - let bit = match key { - TasKey::KeyUp => 0, - TasKey::KeyDown => 1, - TasKey::KeyLeft => 2, - TasKey::KeyRight => 3, - TasKey::KeyShift => 4, - TasKey::KeySpace => 5, - TasKey::KeyQ => 6, - TasKey::KeyEsc => 7, - TasKey::KeyEnter => 8, - }; - 1u32 << bit - } - - pub fn is_key_pressed(&self, key: TasKey) -> bool { - (self.key_flags & Self::get_key_flag(key)) != 0u32 - } - - pub fn set_key_pressed(&mut self, key: TasKey, pressed: bool) { - if pressed { - self.key_flags |= Self::get_key_flag(key) - } else { - self.key_flags &= !(Self::get_key_flag(key)) - } - } - - pub fn flip_key_pressed(&mut self, key: TasKey) { - self.key_flags ^= Self::get_key_flag(key) - } - - pub fn get_key_up_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeyUp) - } - pub fn set_key_up_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeyUp, pressed) - } - pub fn get_key_down_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeyDown) - } - pub fn set_key_down_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeyDown, pressed) - } - pub fn get_key_left_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeyLeft) - } - pub fn set_key_left_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeyLeft, pressed) - } - pub fn get_key_right_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeyRight) - } - pub fn set_key_right_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeyRight, pressed) - } - pub fn get_key_shift_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeyShift) - } - pub fn set_key_shift_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeyShift, pressed) - } - pub fn get_key_space_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeySpace) - } - pub fn set_key_space_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeySpace, pressed) - } - pub fn get_key_q_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeyQ) - } - pub fn set_key_q_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeyQ, pressed) - } - pub fn get_key_esc_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeyEsc) - } - pub fn set_key_esc_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeyEsc, pressed) - } - pub fn get_key_enter_pressed(&self) -> bool { - self.is_key_pressed(TasKey::KeyEnter) - } - pub fn set_key_enter_pressed(&mut self, pressed: bool) { - self.set_key_pressed(TasKey::KeyEnter, pressed) - } -} - -#[derive(Debug)] -pub struct TasFile { - frames: Vec, -} - -impl TasFile { - pub fn new(frames: Vec) -> Self { - Self { frames } - } - - pub fn from_brandnew(count: usize, frame: TasFrame) -> Self { - Self::new(vec![frame; count]) - } - - pub fn from_brandnew_with_fps(count: usize, fps: f32) -> Result { - Ok(Self::from_brandnew(count, TasFrame::with_fps(fps)?)) - } - - pub fn load_file(reader: dyn) - - pub fn from_file(file: &str) -> Result { - todo!() - } - - pub fn save(&self, file: &str) -> Result { - todo!() - } -} - -impl TasFile { - /// 清空存储结构。 - pub fn clear(&mut self) { - self.frames.clear() - } - - /// 获取当前存储的TAS帧的个数。 - pub fn get_count(&self) -> usize { - self.frames.len() - } - - /// 获取当前存储结构是不是空的。 - pub fn is_empty(&self) -> bool { - self.frames.is_empty() - } -} - -impl TasFile { - /// 访问给定索引的帧。 - pub fn visit<'a>(&'a self, index: usize) -> Result<&'a TasFrame> { - self.frames.get(index).ok_or(Error::IndexOutOfRange) - } - - /// 以可变形式访问给定索引的值。 - pub fn visit_mut<'a>(&'a mut self, index: usize) -> Result<&'a mut TasFrame> { - self.frames.get_mut(index).ok_or(Error::IndexOutOfRange) - } - - /// 在给定的索引**之前**插入给定的项目。 - /// - /// 按照此函数约定,如果要在头部插入数据,则可以通过指定0来实现。 - /// 然而对于在尾部插入数据,或在空的存储中插入数据,可以指定存储结构的长度来实现。 - /// 即指定最大Index + 1的值来实现。 - /// - /// `index`为要在前方插入数据的元素的索引。`frames`为要插入的元素的切片。 - pub fn insert(&mut self, index: usize, frames: &[TasFrame]) -> Result<()> { - if index > self.frames.len() { - Err(Error::IndexOutOfRange) - } else if index == self.frames.len() { - // Insert at tail - self.frames.extend_from_slice(frames); - Ok(()) - } else { - // Insert at middle or head - self.frames.splice(index..index, frames.iter().copied()); - Ok(()) - } - } - - /// 从给定单元开始,移除给定个数的元素。 - /// - /// `index`为要开始移除的单元的索引。`count`为要移除的元素的个数。 - pub fn remove(&mut self, index: usize, count: usize) -> Result<()> { - // Check index - let index_from = index; - if index_from >= self.frames.len() { - return Err(Error::IndexOutOfRange); - } - // Check count - let count = if count == 0 { - // Count == 0 may cause "..=" buggy, so we return first. - return Ok(()); - } else { - count - 1 - }; - let index_to = index.checked_add(count).ok_or(Error::NumOverflow)?; - if index_to >= self.frames.len() { - return Err(Error::IndexOutOfRange); - } - // Perform remove - self.frames.drain(index_from..=index_to); - Ok(()) - } -} +/// Formats the sum of two numbers as string. +#[pyfunction] +fn sum_as_string(a: usize, b: usize) -> PyResult { + Ok((a + b).to_string()) +} \ No newline at end of file diff --git a/BallanceTasSonnet/src/utils.rs b/BallanceTasSonnet/src/utils.rs deleted file mode 100644 index 2093228..0000000 --- a/BallanceTasSonnet/src/utils.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod fps_converter; -pub(crate) mod zlib; diff --git a/BallanceTasSonnet/src/utils/fps_converter.rs b/BallanceTasSonnet/src/utils/fps_converter.rs deleted file mode 100644 index 141cbbb..0000000 --- a/BallanceTasSonnet/src/utils/fps_converter.rs +++ /dev/null @@ -1,27 +0,0 @@ -use thiserror::Error as TeError; - -#[derive(Debug, TeError)] -pub enum Error { - #[error("delta time should not be zero or negative value")] - BadDeltaTime, - #[error("fps should should not be zero or negative value")] - BadFps, -} - -type Result = std::result::Result; - -pub fn to_fps(delta: f32) -> Result { - if delta <= 0f32 { - Err(Error::BadDeltaTime) - } else { - Ok(1f32 / delta) - } -} - -pub fn to_delta(fps: f32) -> Result { - if fps <= 0f32 { - Err(Error::BadFps) - } else { - Ok(1f32 / fps) - } -} diff --git a/BallanceTasSonnet/src/utils/zlib.rs b/BallanceTasSonnet/src/utils/zlib.rs deleted file mode 100644 index db90077..0000000 --- a/BallanceTasSonnet/src/utils/zlib.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::{io::{Read, Write}, mem::MaybeUninit}; -use libz_sys; -use byteorder::{ReadBytesExt, WriteBytesExt, NativeEndian}; -use thiserror::Error as TeError; -use crate::tasfile::TasFrame; - -#[derive(Debug, TeError)] -pub enum Error { - #[error("fail to read or write file")] - Io(#[from] std::io::Error), - #[error("arithmetic overflow")] - NumOverflow, - #[error("fail to cast numeric value")] - BadNumCast, -} - -type Result = std::result::Result; - -pub fn compress(writer: &mut dyn Write, frames: &[TasFrame]) -> Result<()> { - // Get decompressed size. - let usize_decomp_size = size_of::().checked_mul(frames.len()).ok_or(Error::NumOverflow)?; - - // Write decompressed size. - let u32_decomp_size = u32::try_from(usize_decomp_size).map_err(|e| Error::BadNumCast)?; - writer.write_u32::(u32_decomp_size)?; - - let buffer: Box<[MaybeUninit]> = Box::new_uninit_slice(usize_decomp_size); - - - Ok(()) -} - -pub fn decompress(reader: &mut dyn Read) -> Result> { - -} - diff --git a/BallanceTasSonnet/src/wrapped.rs b/BallanceTasSonnet/src/wrapped.rs new file mode 100644 index 0000000..425fcfb --- /dev/null +++ b/BallanceTasSonnet/src/wrapped.rs @@ -0,0 +1 @@ +pub(crate) mod tasfile; \ No newline at end of file diff --git a/BallanceTasSonnet/src/wrapped/tasfile.rs b/BallanceTasSonnet/src/wrapped/tasfile.rs new file mode 100644 index 0000000..eb81e0a --- /dev/null +++ b/BallanceTasSonnet/src/wrapped/tasfile.rs @@ -0,0 +1,374 @@ +use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; +use libz_sys; +use std::ffi::{c_int, c_ulong}; +use std::fs::File; +use std::io::{Read, Write}; +use std::mem::MaybeUninit; +use thiserror::Error as TeError; + +#[derive(Debug, TeError)] +pub enum Error { + #[error("delta time should not be zero or negative value")] + BadDeltaTime, + #[error("fps should should not be zero or negative value")] + BadFps, + + #[error("given index is out of range")] + IndexOutOfRange, + #[error("arithmetic overflow")] + NumOverflow, + #[error("fail to cast numeric value")] + BadNumCast, + + #[error("fail to read or write file")] + Io(#[from] std::io::Error), + #[error("fail to call zlib function")] + ZlibCall, + #[error("given TAS file is wrong")] + BadTasFile, +} + +type Result = std::result::Result; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TasKey { + KeyUp, + KeyDown, + KeyLeft, + KeyRight, + KeyShift, + KeySpace, + KeyQ, + KeyEsc, + KeyEnter, +} + +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +#[repr(C)] +pub struct TasFrame { + delta_time: f32, + key_flags: u32, +} + +impl TasFrame { + pub fn new(delta_time: f32, key_flags: u32) -> Self { + Self { + delta_time, + key_flags, + } + } + + pub fn with_fps(fps: f32) -> Result { + Ok(Self::new(Self::to_delta_time(fps)?, 0u32)) + } +} + +impl TasFrame { + fn to_fps(delta_time: f32) -> Result { + if delta_time <= 0f32 { + Err(Error::BadDeltaTime) + } else { + Ok(1f32 / delta_time) + } + } + + fn to_delta_time(fps: f32) -> Result { + if fps <= 0f32 { + Err(Error::BadFps) + } else { + Ok(1f32 / fps) + } + } + + pub fn get_delta_time(&self) -> f32 { + self.delta_time + } + + pub fn get_fps(&self) -> Result { + Self::to_fps(self.get_delta_time()) + } + + pub fn set_delta_time(&mut self, delta_time: f32) -> () { + self.delta_time = delta_time + } + + pub fn set_fps(&mut self, fps: f32) -> Result<()> { + Ok(self.set_delta_time(Self::to_delta_time(fps)?)) + } +} + +impl TasFrame { + fn get_key_flag(key: TasKey) -> u32 { + let bit = match key { + TasKey::KeyUp => 0, + TasKey::KeyDown => 1, + TasKey::KeyLeft => 2, + TasKey::KeyRight => 3, + TasKey::KeyShift => 4, + TasKey::KeySpace => 5, + TasKey::KeyQ => 6, + TasKey::KeyEsc => 7, + TasKey::KeyEnter => 8, + }; + 1u32 << bit + } + + pub fn is_key_pressed(&self, key: TasKey) -> bool { + (self.key_flags & Self::get_key_flag(key)) != 0u32 + } + + pub fn set_key_pressed(&mut self, key: TasKey, pressed: bool) { + if pressed { + self.key_flags |= Self::get_key_flag(key) + } else { + self.key_flags &= !(Self::get_key_flag(key)) + } + } + + pub fn flip_key_pressed(&mut self, key: TasKey) { + self.key_flags ^= Self::get_key_flag(key) + } + + pub fn clear_key_pressed(&mut self) { + self.key_flags = 0 + } +} + +#[derive(Debug)] +pub struct TasFile { + frames: Vec, +} + +impl TasFile { + pub fn new(frames: Vec) -> Self { + Self { frames } + } + + pub fn from_brandnew(count: usize, frame: TasFrame) -> Self { + Self::new(vec![frame; count]) + } + + pub fn from_brandnew_with_fps(count: usize, fps: f32) -> Result { + Ok(Self::from_brandnew(count, TasFrame::with_fps(fps)?)) + } + + pub fn load(filename: &str) -> Result { + // Open file + let mut reader = File::open(filename)?; + + // Read decompressed size. + let u32_decomp_size = reader.read_u32::()?; + let usize_decomp_size = usize::try_from(u32_decomp_size).map_err(|_| Error::BadNumCast)?; + let culong_decomp_size = + c_ulong::try_from(usize_decomp_size).map_err(|_| Error::BadNumCast)?; + + // Check size and compute frame count. + let frame_size = size_of::(); + let frame_count = usize_decomp_size + .checked_div(frame_size) + .ok_or(Error::NumOverflow)?; + if !usize_decomp_size.is_multiple_of(frame_size) { + return Err(Error::BadTasFile); + } + + // Read all rest file into memory + let mut comp_buffer = Vec::new(); + reader.read_to_end(&mut comp_buffer)?; + // Get compressed buffer size. + let usize_comp_size = comp_buffer.len(); + let culong_comp_size = c_ulong::try_from(usize_comp_size).map_err(|_| Error::BadNumCast)?; + + // Create decompressed buffer with uninitialized memory + let mut decomp_buffer: Box<[MaybeUninit]> = Box::new_uninit_slice(frame_count); + + // Decompress data + let source = comp_buffer.as_ptr() as *const libz_sys::Bytef; + let source_len = culong_comp_size; + let dest = decomp_buffer.as_mut_ptr() as *mut libz_sys::Bytef; + let mut dest_len = culong_decomp_size; + let rv = unsafe { libz_sys::uncompress(dest, &mut dest_len, source, source_len) }; + if rv != libz_sys::Z_OK { + return Err(Error::ZlibCall); + } + + // Convert uninitialized buffer to initialized + // SAFETY: We've just initialized the buffer with uncompress + let frames = unsafe { + std::slice::from_raw_parts(decomp_buffer.as_ptr() as *const TasFrame, frame_count) + .to_vec() + }; + + // Okey + Ok(Self::new(frames)) + } + + pub fn save(&self, filename: &str) -> Result<()> { + // Open file + let mut writer = File::create(filename)?; + + // Get decompressed size. + let usize_decomp_size = size_of::() + .checked_mul(self.frames.len()) + .ok_or(Error::NumOverflow)?; + + // Write decompressed size. + let u32_decomp_size = u32::try_from(usize_decomp_size).map_err(|_| Error::BadNumCast)?; + writer.write_u32::(u32_decomp_size)?; + + // Get compressed buffer boundary + let culong_decomp_size = + c_ulong::try_from(usize_decomp_size).map_err(|_| Error::BadNumCast)?; + let culong_comp_bound_size: c_ulong = + unsafe { libz_sys::compressBound(culong_decomp_size) }; + + // Create buffer for it. + let usize_comp_bound_size = + usize::try_from(culong_comp_bound_size).map_err(|_| Error::BadNumCast)?; + let mut buffer: Box<[MaybeUninit]> = Box::new_uninit_slice(usize_comp_bound_size); + + // Compress data into buffer + let source = self.frames.as_ptr() as *const libz_sys::Bytef; + let source_len: c_ulong = culong_decomp_size; + let dest = buffer.as_mut_ptr() as *mut libz_sys::Bytef; + let mut dest_len: c_ulong = culong_comp_bound_size; + let level: c_int = 9; + let rv = unsafe { libz_sys::compress2(dest, &mut dest_len, source, source_len, level) }; + if rv != libz_sys::Z_OK { + return Err(Error::ZlibCall); + } + + // Fetch the final compressed length. + let culong_comp_size: c_ulong = dest_len; + let usize_comp_size: usize = + usize::try_from(culong_comp_size).map_err(|_| Error::BadNumCast)?; + + // Write compressed data + let buffer = unsafe { buffer.assume_init() }; + writer.write(&buffer[..usize_comp_size])?; + + // Okey + Ok(()) + } +} + +impl TasFile { + /// 清空存储结构。 + pub fn clear(&mut self) { + self.frames.clear() + } + + /// 获取当前存储的TAS帧的个数。 + pub fn get_count(&self) -> usize { + self.frames.len() + } + + /// 获取当前存储结构是不是空的。 + pub fn is_empty(&self) -> bool { + self.frames.is_empty() + } +} + +impl TasFile { + fn check_index(&self, index: usize) -> bool { + index < self.frames.len() + } + + fn check_index_range(&self, index_from: usize, index_to: usize) -> bool { + // Check index relation + if index_to < index_from { + return false; + } + // Check index range + if index_to >= self.frames.len() { + return false; + } + // Okey + return true; + } + + /// 访问给定索引的帧。 + pub fn visit<'a>(&'a self, index: usize) -> Result<&'a TasFrame> { + if self.check_index(index) { + Ok(&self.frames[index]) + } else { + Err(Error::IndexOutOfRange) + } + } + + /// 以可变形式访问给定索引的值。 + pub fn visit_mut<'a>(&'a mut self, index: usize) -> Result<&'a mut TasFrame> { + if self.check_index(index) { + Ok(&mut self.frames[index]) + } else { + Err(Error::IndexOutOfRange) + } + } + + /// 访问给定索引范围内的帧。 + pub fn batchly_visit<'a>(&'a self, index_from: usize, index_to: usize) -> Result<&'a [TasFrame]> { + if self.check_index_range(index_from, index_to) { + Ok(&self.frames[index_from..=index_to]) + } else { + Err(Error::IndexOutOfRange) + } + } + + /// 以可变形式访问给定索引范围内的帧。 + pub fn batchly_visit_mut<'a>(&'a mut self, index_from: usize, index_to: usize) -> Result<&'a mut[TasFrame]> { + if self.check_index_range(index_from, index_to) { + Ok(&mut self.frames[index_from..=index_to]) + } else { + Err(Error::IndexOutOfRange) + } + } +} + +impl TasFile { + /// 在结尾继续添加给的的帧序列。 + /// + /// `frames`为要插入的元素的切片。 + pub fn append(&mut self, frames: &[TasFrame]) { + self.frames.extend_from_slice(frames) + } + + /// 在给定的索引**之前**插入给定的帧序列。 + /// + /// 按照此函数约定,如果要在头部插入数据,则可以通过指定0来实现。 + /// 然而对于在尾部插入数据,或在空的存储中插入数据,可以指定存储结构的长度来实现。 + /// 即指定最大Index + 1的值来实现。 + /// + /// `index`为要在前方插入数据的元素的索引。`frames`为要插入的元素的切片。 + pub fn insert(&mut self, index: usize, frames: &[TasFrame]) -> Result<()> { + if index > self.frames.len() { + Err(Error::IndexOutOfRange) + } else if index == self.frames.len() { + // Insert at tail + self.frames.extend_from_slice(frames); + Ok(()) + } else { + // Insert at middle or head + self.frames.splice(index..index, frames.iter().copied()); + Ok(()) + } + } + + /// 将给定范围内的帧移除。 + /// + /// `index_from`为要开始移除的单元的索引。`index_to`为最后一个要移除的单元的索引。 + /// `index_from`和`index_to`指向的帧均会被删除(端点inclusive模式)。 + /// `index_to`不能小于`index_from`。 + /// `index_from`和`index_to`均不能超过最大索引。 + pub fn remove(&mut self, index_from: usize, index_to: usize) -> Result<()> { + // Check index relation + if index_to < index_from { + return Err(Error::IndexOutOfRange); + } + // Check index range + if index_to >= self.frames.len() { + return Err(Error::IndexOutOfRange); + } + // Perform remove + self.frames.drain(index_from..=index_to); + Ok(()) + } +}