From 08734c6ef7b4c6eceeadb7950de91f8d48cb3289 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Wed, 1 Apr 2026 13:33:30 +0800 Subject: [PATCH] feat: introduce batchly visit in tas sequence --- .../BallanceTasEditor/Backend/TasOperation.cs | 30 +++++----- .../BallanceTasEditor/Backend/TasSequence.cs | 58 +++++++++++++++++++ 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/BallanceTasEditor/BallanceTasEditor/Backend/TasOperation.cs b/BallanceTasEditor/BallanceTasEditor/Backend/TasOperation.cs index 535400e..ced2fdb 100644 --- a/BallanceTasEditor/BallanceTasEditor/Backend/TasOperation.cs +++ b/BallanceTasEditor/BallanceTasEditor/Backend/TasOperation.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -108,14 +109,13 @@ namespace BallanceTasEditor.Backend { ArgumentOutOfRangeException.ThrowIfLessThan(m_StartIndex, 0); // Do backup and set values at the same time + int backupIndex = 0; var backups = new RawTasFrame[m_EndIndex - m_StartIndex + 1]; // Pre-build key list for fast fetching. var keys = Enumerable.Range(m_StartKey.ToIndex(), m_EndKey.ToIndex() - m_StartKey.ToIndex() + 1).Select((i) => TasKey.FromIndex(i)).ToArray(); - for (int index = m_StartIndex; index <= m_EndIndex; index++) { - // Fetch frame - var frame = seq.Visit(index); + foreach (var frame in seq.BatchlyVisit(m_StartIndex, m_EndIndex)) { // Do backup - frame.ToRawImplace(ref backups[index - m_StartIndex]); + frame.ToRawImplace(ref backups[backupIndex++]); // Modify keys foreach (var key in keys) { switch (m_Kind) { @@ -143,8 +143,9 @@ namespace BallanceTasEditor.Backend { // Index range is checked, // so we directly restore backup. - for (int index = m_StartIndex; index <= m_EndIndex; index++) { - seq.Visit(index).FromRawImplace(m_FramesBackup[index - m_StartIndex]); + int backupIndex = 0; + foreach (var frame in seq.BatchlyVisit(m_StartIndex, m_EndIndex)) { + frame.FromRawImplace(m_FramesBackup[backupIndex++]); } // Clear backups @@ -197,12 +198,11 @@ namespace BallanceTasEditor.Backend { ArgumentOutOfRangeException.ThrowIfLessThan(m_StartIndex, 0); // Do backup and set values at the same time + int backupIndex = 0; var backups = new float[m_EndIndex - m_StartIndex + 1]; - for (int index = m_StartIndex; index <= m_EndIndex; index++) { - // Fetch frame - var frame = seq.Visit(index); + foreach (var frame in seq.BatchlyVisit(m_StartIndex, m_EndIndex)) { // Do backup - backups[index - m_StartIndex] = frame.GetTimeDelta(); + backups[backupIndex++] = frame.GetTimeDelta(); // Modify delta time frame.SetTimeDelta(m_DeltaTime); } @@ -218,8 +218,9 @@ namespace BallanceTasEditor.Backend { // Index range is checked, // so we directly restore backup. - for (int index = m_StartIndex; index <= m_EndIndex; index++) { - seq.Visit(index).SetTimeDelta(m_DeltaTimesBackup[index - m_StartIndex]); + int backupIndex = 0; + foreach (var frame in seq.BatchlyVisit(m_StartIndex, m_EndIndex)) { + frame.SetTimeDelta(m_DeltaTimesBackup[backupIndex++]); } // Clear backups @@ -259,9 +260,10 @@ namespace BallanceTasEditor.Backend { ArgumentOutOfRangeException.ThrowIfLessThan(m_StartIndex, 0); // Do backups + int backupIndex = 0; var backups = new RawTasFrame[m_EndIndex - m_StartIndex + 1]; - for (int index = m_StartIndex; index <= m_EndIndex; index++) { - seq.Visit(index).ToRawImplace(ref backups[index - m_StartIndex]); + foreach (var frame in seq.BatchlyVisit(m_StartIndex, m_EndIndex)) { + frame.ToRawImplace(ref backups[backupIndex++]); } // Do remove seq.Remove(m_StartIndex, m_EndIndex); diff --git a/BallanceTasEditor/BallanceTasEditor/Backend/TasSequence.cs b/BallanceTasEditor/BallanceTasEditor/Backend/TasSequence.cs index 6ed4971..101bcb3 100644 --- a/BallanceTasEditor/BallanceTasEditor/Backend/TasSequence.cs +++ b/BallanceTasEditor/BallanceTasEditor/Backend/TasSequence.cs @@ -30,6 +30,21 @@ namespace BallanceTasEditor.Backend { /// 给定的索引无效。 TasFrame Visit(int index); /// + /// 按顺序访问给定索引区间内的帧的值。 + /// + /// + /// 实现此函数时需要格外注意以下事项: + /// + /// 该函数如果可以进行顺序访问优化,则应当优化。 + /// 即使用此函数可以获得等于或大于单独一次使用函数。 + /// + /// 该函数理论上的复杂度应为O(1)。 + /// + /// 要访问的帧区间的起始索引(包含)。 + /// 要访问的帧区间的终止索引(包含) + /// 给定的索引无效。 + IExactSizeEnumerable BatchlyVisit(int startIndex, int endIndex); + /// /// 在给定的帧索引之前插入给定的项目。 /// /// @@ -93,6 +108,10 @@ namespace BallanceTasEditor.Backend { throw new NotImplementedException(); } + public IExactSizeEnumerable BatchlyVisit(int startIndex, int endIndex) { + throw new NotImplementedException(); + } + public void Insert(int index, IExactSizeEnumerable items) { throw new NotImplementedException(); } @@ -120,6 +139,7 @@ namespace BallanceTasEditor.Backend { IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + } /// @@ -143,6 +163,21 @@ namespace BallanceTasEditor.Backend { } } + private IEnumerable BatchlyVisitEx(int startIndex, int endIndex) { + if (endIndex < startIndex || startIndex < 0 || endIndex >= m_Container.Count) { + throw new IndexOutOfRangeException("Invalid index for frame."); + } + + // Iterate items one by one. + for (int i = startIndex; i <= endIndex; ++i) { + yield return m_Container[i]; + } + } + + public IExactSizeEnumerable BatchlyVisit(int startIndex, int endIndex) { + return new ExactSizeEnumerableAdapter(BatchlyVisitEx(startIndex, endIndex), endIndex - startIndex + 1); + } + public void Insert(int index, IExactSizeEnumerable items) { if (index == m_Container.Count) { m_Container.AddRange(items); @@ -276,6 +311,29 @@ namespace BallanceTasEditor.Backend { } } + private IEnumerable BatchlyVisitEx(int startIndex, int endIndex) { + if (endIndex < startIndex || startIndex < 0 || endIndex >= m_Container.Count) { + throw new IndexOutOfRangeException("Invalid index for frame."); + } + + // We move to start index first. + MoveToIndex(startIndex); + // Then we copy its reference + LinkedListNode? node = m_Cursor.Node; + // Then compute count + var count = endIndex - startIndex + 1; + // Now we can iterate items one by one. + for (int i = 0; i < count; ++i) { + node = node.Unwrap(); + yield return node.Unwrap().Value; + node = node.Next; + } + } + + public IExactSizeEnumerable BatchlyVisit(int startIndex, int endIndex) { + return new ExactSizeEnumerableAdapter(BatchlyVisitEx(startIndex, endIndex), endIndex - startIndex + 1); + } + public void Insert(int index, IExactSizeEnumerable items) { // YYC MARK: // We must test the equal first, to handle back appending properly.