/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "ExUndoController.h" #include "FlatMemStream.h" #include "StaticRxObject.h" #define STL_USING_ALGORITHM #include "OdaSTL.h" #include "RxObjectImpl.h" ExUndoController::ExUndoController() : m_nMaxSteps(0xFFFFFFFF) , m_nMaxMemory(0x10000000) { m_nMemoryUsed = sizeof(ExUndoController); } void ExUndoController::setLimits(OdUInt32 nMaxSteps, OdUInt64 nMaxMemory) { m_nMaxSteps = nMaxSteps; m_nMaxMemory = nMaxMemory; freeExtra(); } OdUInt32 ExUndoController::recordMemory(OdUInt32 nDataSize) { return sizeof(OdUInt8Array) + sizeof(OdArrayBuffer) + sizeof(void*) * 2 + nDataSize; } OdUInt32 ExUndoController::frontRecordMemory() const { return recordMemory(m_records.front().size()); } OdUInt32 ExUndoController::backRecordMemory() const { return recordMemory(m_records.back().size()); } void ExUndoController::freeFrontRecord() { ODA_ASSERT(m_records.size()); m_nMemoryUsed -= frontRecordMemory(); m_records.pop_front(); } void ExUndoController::freeBackRecord() { ODA_ASSERT(m_records.size()); m_nMemoryUsed -= backRecordMemory(); m_records.pop_back(); } void ExUndoController::freeExtra() { while(m_records.size()) { if(m_nMemoryUsed <= m_nMaxMemory && m_records.size() <= m_nMaxSteps) return ; freeFrontRecord(); } } bool ExUndoController::pushRecord(OdUInt64 nSizeOfRecToAppend) { if(m_records.size() + 1 <= m_nMaxSteps && m_nMemoryUsed + nSizeOfRecToAppend <= m_nMaxMemory) { OdUInt8Array data((OdUInt8Array::size_type)nSizeOfRecToAppend); data.resize((OdUInt8Array::size_type)nSizeOfRecToAppend); m_records.push_back(data); m_nMemoryUsed += backRecordMemory(); return true; } while(m_records.size()) { if(m_nMemoryUsed + nSizeOfRecToAppend - frontRecordMemory() <= m_nMaxMemory) { OdUInt8Array data = m_records.front(); m_records.pop_front(); m_nMemoryUsed -= data.size(); data.setPhysicalLength((OdUInt8Array::size_type)nSizeOfRecToAppend); data.resize((OdUInt8Array::size_type)nSizeOfRecToAppend); m_records.push_back(data); m_nMemoryUsed += nSizeOfRecToAppend; return true; } freeFrontRecord(); } return false; } void ExUndoController::pushData(OdStreamBuf* pStream, OdUInt64 nSize, OdUInt32 opt) { if (pushRecord(nSize + sizeof(OdUInt32))) { OdStaticRxObject ms; ms.init(m_records.back().asArrayPtr(), nSize + sizeof(OdUInt32)); ms.putBytes(&opt, sizeof(opt)); pStream->copyDataTo(&ms, pStream->tell(), pStream->tell()+nSize); } else { throw OdError(eOutOfMemory); } } bool ExUndoController::hasData() const { return !m_records.empty(); } OdUInt32 ExUndoController::popData(OdStreamBuf* pStream) { if(!hasData()) throw OdError(eEndOfFile); OdUInt32 nSize = m_records.back().size(); OdStaticRxObject ms; ms.init(const_cast(m_records.back().getPtr()), nSize); OdUInt32 opt; ms.getBytes(&opt, sizeof(opt)); ms.copyDataTo(pStream, ms.tell(), nSize); freeBackRecord(); return opt; } class ExUndoControllerRecord : public OdDbUndoControllerRecord { public: OdStreamBufPtr m_pStream; OdUInt32 options() const { auto s = getData(); auto pos = s->tell(); s->rewind(); OdUInt32 opt; s->getBytes(&opt, sizeof(opt)); s->seek(pos, OdDb::kSeekFromStart); return opt; } virtual OdUInt64 size() const { return getData()->length(); } virtual OdStreamBufPtr getData() const { return m_pStream; } }; class ExUndoControllerIterator : public OdRxIterator { public: OdList::const_reverse_iterator m_iter; OdList::const_reverse_iterator m_end; bool done() const { return (m_iter==m_end); } bool next() { if(done()) return false; ++m_iter; return !done(); } OdRxObjectPtr object() const { if (done()) throw OdError(eIteratorDone); OdSmartPtr pRec = OdRxObjectImpl::createObject(); pRec->m_pStream = OdFlatMemStream::createNew(const_cast(m_iter->getPtr()), m_iter->length()); return pRec; } }; OdRxIteratorPtr ExUndoController::newRecordStackIterator() const { OdSmartPtr pIter = OdRxObjectImpl::createObject(); pIter->m_iter = m_records.rbegin(); pIter->m_end = m_records.rend(); return pIter; } void ExUndoController::clearData() { m_records.clear(); m_nMemoryUsed = 0; }