| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 | 
							- /*
 
-  * RowVisitor.h
 
-  * ------------
 
-  * Purpose: Class for recording which rows of a song has already been visited, used for detecting when a module starts to loop.
 
-  * Notes  : See implementation file.
 
-  * Authors: OpenMPT Devs
 
-  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 
-  */
 
- #pragma once
 
- #include "openmpt/all/BuildSettings.hpp"
 
- #include "mpt/base/span.hpp"
 
- #include "Snd_defs.h"
 
- #include <map>
 
- OPENMPT_NAMESPACE_BEGIN
 
- #if defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_FUZZER)
 
- #define MPT_VERIFY_ROWVISITOR_LOOPSTATE
 
- #endif  // MPT_BUILD_DEBUG || MPT_BUILD_FUZZER
 
- class CSoundFile;
 
- class ModSequence;
 
- struct ModChannel;
 
- class RowVisitor
 
- {
 
- protected:
 
- 	using ChannelStates = mpt::span<const ModChannel>;
 
- 	class LoopState
 
- 	{
 
- 		static constexpr uint64 FNV1a_BASIS = 14695981039346656037ull;
 
- 		static constexpr uint64 FNV1a_PRIME = 1099511628211ull;
 
- 		uint64 m_hash = FNV1a_BASIS;
 
- #ifdef MPT_VERIFY_ROWVISITOR_LOOPSTATE
 
- 		std::vector<std::pair<uint8, uint8>> m_counts;  // Actual loop counts to verify equality of hash-based implementation
 
- #endif
 
- 	public:
 
- 		LoopState() = default;
 
- 		LoopState(const ChannelStates &chnState, const bool ignoreRow);
 
- 		LoopState(const LoopState &) = default;
 
- 		LoopState(LoopState&&) = default;
 
- 		LoopState &operator=(const LoopState &) = default;
 
- 		LoopState &operator=(LoopState&&) = default;
 
- 		[[nodiscard]] bool operator==(const LoopState &other) const noexcept
 
- 		{
 
- #ifdef MPT_VERIFY_ROWVISITOR_LOOPSTATE
 
- 			if((m_counts == other.m_counts) != (m_hash == other.m_hash))
 
- 				std::abort();
 
- #endif
 
- 			return m_hash == other.m_hash;
 
- 		}
 
- 		
 
- 		[[nodiscard]] bool HasLoops() const noexcept
 
- 		{
 
- #ifdef MPT_VERIFY_ROWVISITOR_LOOPSTATE
 
- 			if(m_counts.empty() != (m_hash == FNV1a_BASIS))
 
- 				std::abort();
 
- #endif
 
- 			return m_hash != FNV1a_BASIS;
 
- 		}
 
- 	};
 
- 	using LoopStateSet = std::vector<LoopState>;
 
- 	// Stores for every (order, row) combination in the sequence if it has been visited or not.
 
- 	std::vector<std::vector<bool>> m_visitedRows;
 
- 	// Map for each row that's part of a pattern loop which loop states have been visited. Held in a separate data structure because it is sparse data in typical modules.
 
- 	std::map<std::pair<ORDERINDEX, ROWINDEX>, LoopStateSet> m_visitedLoopStates;
 
- 	const CSoundFile &m_sndFile;
 
- 	ROWINDEX m_rowsSpentInLoops = 0;
 
- 	const SEQUENCEINDEX m_sequence;
 
- public:
 
- 	RowVisitor(const CSoundFile &sndFile, SEQUENCEINDEX sequence = SEQUENCEINDEX_INVALID);
 
- 	
 
- 	void MoveVisitedRowsFrom(RowVisitor &other) noexcept;
 
- 	// Resize / Clear the row vector.
 
- 	// If reset is true, the vector is not only resized to the required dimensions, but also completely cleared (i.e. all visited rows are unset).
 
- 	void Initialize(bool reset);
 
- 	// Mark an order/row combination as visited and returns true if it was visited before.
 
- 	bool Visit(ORDERINDEX ord, ROWINDEX row, const ChannelStates &chnState, bool ignoreRow);
 
- 	// Find the first row that has not been played yet.
 
- 	// The order and row is stored in the order and row variables on success, on failure they contain invalid values.
 
- 	// If onlyUnplayedPatterns is true (default), only completely unplayed patterns are considered, otherwise a song can start anywhere.
 
- 	// Function returns true on success.
 
- 	[[nodiscard]] bool GetFirstUnvisitedRow(ORDERINDEX &order, ROWINDEX &row, bool onlyUnplayedPatterns) const;
 
- 	// Pattern loops can stack up exponentially, which can cause an effectively infinite amount of time to be spent on evaluating them.
 
- 	// If this function returns true, module evaluation should be aborted because the pattern loops appear to be too complex.
 
- 	[[nodiscard]] bool ModuleTooComplex(ROWINDEX threshold) const noexcept { return m_rowsSpentInLoops >= threshold; }
 
- 	void ResetComplexity() { m_rowsSpentInLoops = 0; }
 
- protected:
 
- 	// Get the needed vector size for a given pattern.
 
- 	[[nodiscard]] ROWINDEX VisitedRowsVectorSize(PATTERNINDEX pattern) const noexcept;
 
- 	[[nodiscard]] const ModSequence &Order() const;
 
- };
 
- OPENMPT_NAMESPACE_END
 
 
  |