Exemplo n.º 1
0
	void CDataStorage::operator+=(CUnit* from)
	{
		/*
		 *  constructor already positioned the CUnit; make sure our assumption sticks:
		 *
		 *  we assume a usage style like this, i.e. += add in order AND BEFORE the next
		 *  unit is requested/constructed:
		 *
		 *   p1 = new CUnit(store, ...);
		 *   ...
		 *   store += p1;
		 *   p2 = new CUnit(store, ...);
		 *   ...
		 *   store += p2;
		 *
		 *  and we currently FAIL for any other 'mixed' usage pattern, e.g. this will b0rk:
		 *
		 *   p1 = new CUnit(store, ...);
		 *   p2 = new CUnit(store, ...);
		 *   ...
		 *   store += p1;
		 *   store += p2;
		 */
		XL_ASSERT(from->m_Index == (int)store.size() - 1);

		m_DataSize += from->GetDataSize();

		// and 'persist' the data associated with the CUnit from here-on after...
		// (that way, we can safely 'delete CUnit' and still have the data generated by the CUnit intact)
		store[(size_t)from->m_Index].MakeSticky();
		// and signal that we've made our data 'sticky':
		XL_ASSERT(from->m_Index >= 0);
		from->m_Index = -1 ^ from->m_Index;
		XL_ASSERT(from->m_Index < 0);
	}
Exemplo n.º 2
0
	/*
	 *  This copy constructor is required as otherwise you'd get nuked by CDataStore
	 *  when it has to redimension its vector store when more units than
	 *  anticipated are requested: internally, STL detroys each unit during this
	 *  vector resize operation, so we'll need to copy the data to new space, especially
	 *  when we're m_varying_width !!!
	 */
	CUnitStore::CUnitStore(const CUnitStore &src)
	{
		if (&src == this) {
			return;
		}

		m_varying_width = src.m_varying_width;
		m_is_in_use = src.m_is_in_use;
		m_is_sticky = src.m_is_sticky;
		m_nDataSize = src.m_nDataSize;
		if (!m_varying_width) {
			XL_ASSERT(m_nDataSize <= FIXEDWIDTH_STORAGEUNIT_SIZE);
			memcpy(&s, &src.s, sizeof(s));
		} else {
			XL_ASSERT(m_is_in_use);
			XL_ASSERT(src.s.vary.m_nSize > 0);
			s.vary.m_pData = (unsigned8_t *)malloc(src.s.vary.m_nSize);
			if (!s.vary.m_pData) {
				// ret = ERR_UNABLE_TOALLOCATE_MEMORY;
				m_nDataSize = s.vary.m_nSize = 0;
			} else {
				memcpy(s.vary.m_pData, src.s.vary.m_pData, m_nDataSize);
				s.vary.m_nSize = src.s.vary.m_nSize;
			}
		}
	}
Exemplo n.º 3
0
void UpdateApplication::startDownload()
// ----------------------------------------------------------------------------
//    Start downloading the URL of the Tao file
// ----------------------------------------------------------------------------
{
    IFTRACE(update)
    debug() << "Downloading: '" << +url.toString() << "'\n";

    XL_ASSERT(!reply);
    XL_ASSERT(!url.isEmpty());

    progress->setLabelText(tr("Downloading %1 %2...").arg(appName())
                           .arg(remoteVer()));
    progress->show();

    state = Downloading;
    downloadTime.start();

    request.setUrl(url);
    reply = manager->get(request);
    connect(reply, SIGNAL(metaDataChanged()), this, SLOT(processReply()));
    connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
    connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
            this, SLOT(downloadProgress(qint64,qint64)));
}
Exemplo n.º 4
0
	CUnitStore& CDataStorage::operator[](signed32_t index)
	{
		XL_ASSERT(index != INVALID_STORE_INDEX);
		XL_ASSERT(index >= 0 ? index < (int)store.size() : 1);
		XL_ASSERT(index < 0 ? (~index) < (int)store.size() : 1);

		return index >= 0 ? store[(size_t)index] : store[~(size_t)index];
	}
Exemplo n.º 5
0
	void CUnitStore::Reset()
	{
		if (m_varying_width && s.vary.m_pData) {
			XL_ASSERT(m_is_in_use);
			free((void *)s.vary.m_pData);
		}
		m_varying_width = false;
		m_is_in_use = false;
		m_is_sticky = false;
		m_nDataSize = 0;
		memset(&s, 0, sizeof(s));
		XL_ASSERT(s.vary.m_pData == NULL);
	}
Exemplo n.º 6
0
	signed8_t CUnitStore::InitWithValue(unsigned8_t value, size_t size)
	{
		signed8_t ret;

		XL_ASSERT(m_is_in_use);
		XL_ASSERT(size > 0);
		ret = Resize(size);
		if (ret == NO_ERRORS) {
			memset(GetBuffer(), value, size);
			SetDataSize(size);
		}
		return ret;
	}
Exemplo n.º 7
0
	signed8_t CUnitStore::Init(const unsigned8_t *data, size_t size, size_t datasize)
	{
		signed8_t ret;

		XL_ASSERT(m_is_in_use);
		XL_ASSERT(size > 0);
		XL_ASSERT(datasize <= size);
		ret = Resize(size);
		if (ret == NO_ERRORS) {
			memcpy(GetBuffer(), data, datasize);
			SetDataSize(datasize);
		}
		return ret;
	}
Exemplo n.º 8
0
void PointCloudVBO::checkGLContext()
// ----------------------------------------------------------------------------
//   Do what's needed if GL context has changed
// ----------------------------------------------------------------------------
{
    if (QGLContext::currentContext() != context)
    {
        IFTRACE(pointcloud)
            debug() << "GL context changed\n";

        // Re-create VBO(s)
        genPointBuffer();
        if (colored())
            genColorBuffer();

        if (optimized)
        {
            IFTRACE(pointcloud)
                debug() << "GL context changed on optimized cloud\n";

            XL_ASSERT(file != "" || nbRandom != 0);

            if (file != "")
            {
                IFTRACE(pointcloud)
                    debug() << "Reloading file\n";
                text f = file;
                clear();
                loadData(f, sep, xi, yi, zi, colorScale, ri, gi, bi, ai);
            }
            else if (nbRandom != 0)
            {
                IFTRACE(pointcloud)
                    debug() << "Re-creating random points\n";
                unsigned n = nbRandom;
                clear();
                randomPoints(n, coloredRandom);
            }

            optimized = false;
            XL_ASSERT(!dirty);
        }
        else
            updateVbo();

        context = QGLContext::currentContext();
    }
}
Exemplo n.º 9
0
	signed8_t CUnitStore::Prepare(size_t minimum_size)
	{
		signed8_t ret = NO_ERRORS;

		// allocate space in the 'variable sized store' if we cannot fit in a fixed-width unit:
		if (minimum_size <= FIXEDWIDTH_STORAGEUNIT_SIZE) {
			m_varying_width = false;
			m_is_in_use = true;
			m_is_sticky = false;
			m_nDataSize = 0;
			memset(&s, 0, sizeof(s));

			// range: 0 ... +oo
		} else {
			m_varying_width = true;
			m_is_in_use = true;
			m_is_sticky = false;
			m_nDataSize = 0;
			memset(&s, 0, sizeof(s));
			XL_ASSERT(s.vary.m_pData == NULL);
			if (minimum_size > 0) {
				s.vary.m_pData = (unsigned8_t *)malloc(minimum_size);
				if (!s.vary.m_pData) {
					ret = ERR_UNABLE_TOALLOCATE_MEMORY;
					minimum_size = 0;
				}
				s.vary.m_nSize = minimum_size;
			}
		}

		return ret;
	}
Exemplo n.º 10
0
	CUnitStore::CUnitStore() :
		m_varying_width(false),
		m_is_in_use(false),
		m_is_sticky(false),
		m_nDataSize(0)
	{
		memset(&s, 0, sizeof(s));
		XL_ASSERT(s.vary.m_pData == NULL);
	}
Exemplo n.º 11
0
void PointCloudVBO::genColorBuffer()
// ----------------------------------------------------------------------------
//   Allocate new VBO for colors
// ----------------------------------------------------------------------------
{
    XL_ASSERT(colored());
    GL.GenBuffers(1, &colorVbo);
    IFTRACE(pointcloud)
        debug() << "Allocated VBO #" << colorVbo << " for colors\n";
}
Exemplo n.º 12
0
void PerLayoutStatistics::endDraw(XL::Tree *body)
// ----------------------------------------------------------------------------
//    End of drawing phase timing
// ----------------------------------------------------------------------------
{
    if (body)
    {
        PerLayoutStatistics *info = body->GetInfo<PerLayoutStatistics>();
        XL_ASSERT(info);
        info->totalDrawTime += info->drawTime.nsecsElapsed();
    }
}
Exemplo n.º 13
0
void PerLayoutStatistics::endExec(XL::Tree *body)
// ----------------------------------------------------------------------------
//    End of execution phase timing
// ----------------------------------------------------------------------------
{
    if (body)
    {
        PerLayoutStatistics *info = body->GetInfo<PerLayoutStatistics>();
        XL_ASSERT(info);
        info->totalExecTime += info->execTime.nsecsElapsed();
    }
}
Exemplo n.º 14
0
void PointCloudVBO::removePoints(unsigned n)
// ----------------------------------------------------------------------------
//   Drop n points from the cloud
// ----------------------------------------------------------------------------
{
    XL_ASSERT(!optimized);

    PointCloud::removePoints(n);
    if (useVbo())
        updateVbo();
    noOptimize = true;
}
Exemplo n.º 15
0
	signed8_t CUnitStore::Resize(size_t newlen)
	{
		signed8_t ret = NO_ERRORS;

		XL_ASSERT(m_is_in_use);
		XL_ASSERT(newlen > 0);
		XL_ASSERT(newlen >= m_nDataSize);

		if (!m_varying_width) {
			if (newlen > FIXEDWIDTH_STORAGEUNIT_SIZE) {
				// turn this node into a varying-width unit store:
				unsigned8_t *p = (unsigned8_t *)malloc(newlen);
				if (!p)	{
					ret = ERR_UNABLE_TOALLOCATE_MEMORY;
					newlen = 0;
				} else {
					memcpy(p, s.fixed.m_pData, m_nDataSize);
				}
				s.vary.m_pData = p;
				s.vary.m_nSize = newlen;
				m_varying_width = true;
			}
		} else {
			if (newlen != s.vary.m_nSize) {
				if (!s.vary.m_pData) {
					XL_ASSERT(m_nDataSize == 0);
					s.vary.m_pData = (unsigned8_t *)malloc(newlen);
				} else {
					s.vary.m_pData = (unsigned8_t *)realloc((void *)s.vary.m_pData, newlen);
				}
				if (!s.vary.m_pData) {
					ret = ERR_UNABLE_TOALLOCATE_MEMORY;
					newlen = 0;
				}
				s.vary.m_nSize = newlen;
			}
		}

		return ret;
	}
Exemplo n.º 16
0
bool DDEWidget::ddeInitiate(MSG* message, long* result)
// ----------------------------------------------------------------------------
//   Process a Windows WM_DDE_INITIATE message
// ----------------------------------------------------------------------------
{
    if ((0 != LOWORD(message->lParam)) &&
            (0 != HIWORD(message->lParam)) &&
            (LOWORD(message->lParam) == appAtom) &&
            (HIWORD(message->lParam) == systemTopicAtom))
    {
        // make duplicates of the incoming atoms (really adding a reference)
        wchar_t atomName[_MAX_PATH];
        bool ok;
        ok = (::GlobalGetAtomNameW(appAtom, atomName, _MAX_PATH-1) != 0);
        XL_ASSERT(ok);
        if (ok)
            ok = (::GlobalAddAtomW(atomName) == appAtom);
        XL_ASSERT(ok);
        if (ok)
            ok = (::GlobalGetAtomNameW(systemTopicAtom, atomName, _MAX_PATH-1)
                  != 0);
        XL_ASSERT(ok);
        if (ok)
            ok = (::GlobalAddAtomW(atomName) == systemTopicAtom);
        XL_ASSERT(ok);

        if (!ok)
            return false;

        // send the WM_DDE_ACK (caller will delete duplicate atoms)
        ::SendMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(),
                      MAKELPARAM(appAtom, systemTopicAtom));
    }
    *result = 0;
    return true;
}
Exemplo n.º 17
0
	/*
	 * Clip to Max Data Size, as the rest of the record will be placed in continue records.
	 * This means that we will duplicate data. Its possible to create a new UnitStorage that takes just a pointer;
	 *     however, these records are quite rare, so why go to the effort.
	 */
	size_t CDataStorage::Clip(CUnit* unit)
	{
		XL_ASSERT(unit == m_FlushStack.back());
		CRecord *record = (CRecord *)unit;

		// Use this record, but only record the first big chunk
		record->SetRecordLength(MAX_RECORD_SIZE);

		// No way to directly change the size of a Unit's storage space, so we fake it by telling the UnitStore that its now smaller
		CUnitStore& unitStore = (*this)[record->GetIndex()];
		size_t realSize = unitStore.GetDataSize() - RECORD_HEADER_SIZE;
		unitStore.SetDataSize( MAX_RECORD_SIZE + RECORD_HEADER_SIZE );

		return realSize;
	}
Exemplo n.º 18
0
void UpdateApplication::saveDownloadedData()
// ----------------------------------------------------------------------------
//    Finish download: write data to file, show confirmation, cleanup
// ----------------------------------------------------------------------------
{
    XL_ASSERT(file);

    file->write(reply->readAll());
    file->close();

    showDownloadSuccessful();
    delete file;
    file = NULL;

    reply->deleteLater();
    reply = NULL;

    progress->close();
    state = Idle;
}
Exemplo n.º 19
0
bool DDEWidget::ddeExecute(MSG* message, long* result)
// ----------------------------------------------------------------------------
//   Process a Windows WM_DDE_EXECUTE message
// ----------------------------------------------------------------------------
{
    // unpack the DDE message
    UINT_PTR unused;
    HGLOBAL hData;
    bool ok;
    //IA64: Assume DDE LPARAMs are still 32-bit
    ok = (::UnpackDDElParam(WM_DDE_EXECUTE, message->lParam, &unused,
                            (UINT_PTR*)&hData) != 0);
    XL_ASSERT(ok);
    if (!ok)
        return false;

    QString command = QString::fromWCharArray((LPCWSTR)::GlobalLock(hData));
    ::GlobalUnlock(hData);

    // acknowledge now - before attempting to execute
    ::PostMessage((HWND)message->wParam, WM_DDE_ACK, (WPARAM)winId(),
                  //IA64: Assume DDE LPARAMs are still 32-bit
                  ReuseDDElParam(message->lParam, WM_DDE_EXECUTE, WM_DDE_ACK,
                                 (UINT)0x8000, (UINT_PTR)hData));

    // don't execute the command when the window is disabled
    if (!isEnabled())
    {
        *result = 0;
        return true;
    }

    QRegExp regCommand("^\\[(\\w+)\\((.*)\\)\\]$");
    if(regCommand.exactMatch(command))
    {
        executeDdeCommand(regCommand.cap(1), regCommand.cap(2));
    }

    *result = 0;
    return true;
}
Exemplo n.º 20
0
void PointCloudVBO::updateVbo()
// ----------------------------------------------------------------------------
//   Take into account a change in point data
// ----------------------------------------------------------------------------
{
    XL_ASSERT(!optimized);

    if (QThread::currentThread() != qApp->thread())
    {
        // OpenGL functions may be called only from the main thread, which
        // owns the GL context
        IFTRACE(pointcloud)
            debug() << "Not updating VBO (not main thread)\n";
        return;
    }

    IFTRACE(pointcloud)
        debug() << "Updating VBO #" << vbo << " (" << size() << " points)\n";

    GL.BindBuffer(GL_ARRAY_BUFFER, vbo);
    GL.BufferData(GL_ARRAY_BUFFER, size()*sizeof(Point), &points[0].x,
                 GL_STATIC_DRAW);
    GL.BindBuffer(GL_ARRAY_BUFFER, 0);

    if (colored())
    {
        if (colorVbo == 0)
            genColorBuffer();

        IFTRACE(pointcloud)
            debug() << "Updating VBO #" << colorVbo << " (" << size()
                    << " colors)\n";

        GL.BindBuffer(GL_ARRAY_BUFFER, colorVbo);
        GL.BufferData(GL_ARRAY_BUFFER, size()*sizeof(Color), &colors[0].r,
                     GL_STATIC_DRAW);
        GL.BindBuffer(GL_ARRAY_BUFFER, 0);
    }
    dirty = false;
}
Exemplo n.º 21
0
void UpdateApplication::readIniFile()
// ----------------------------------------------------------------------------
//    Get version, name and path of latest update from received .ini file
// ----------------------------------------------------------------------------
{
    XL_ASSERT(file);

    IFTRACE(update)
    {
        file->open(QIODevice::ReadOnly | QIODevice::Text);
        while (file->bytesAvailable())
            debug() << " " << +QString(file->readLine());
        file->close();
    }

    QSettings settings(file->fileName(), QSettings::IniFormat);
    settings.setIniCodec("UTF-8");
    remoteVersion = settings.value("version", "-1").toDouble();
    // Read optional, localized description text
    // For instance for french: read description-fr then description
    QString descKey = QString("description-%1").arg(TaoApp->lang);
    description = settings.value(descKey,
                                 settings.value("description")).toString();
    settings.beginGroup(target);
    url.setUrl(settings.value(edition).toString());
    settings.endGroup();

    IFTRACE(update)
    {
        debug() << "Remote version is " << +remoteVersion
                << " download URL: '" << +url.toString() << "'\n";
        if (!description.isEmpty())
            debug() << "Description string for update dialog:\n"
                    << +description << "\n";
    }
}
Exemplo n.º 22
0
	CUnitStore::~CUnitStore()
	{
		Reset();
		XL_ASSERT(s.vary.m_pData == NULL);
	}
Exemplo n.º 23
0
void UpdateApplication::downloadFinished()
// ----------------------------------------------------------------------------
//    Process downloaded data reply
// ----------------------------------------------------------------------------
{
    if (state == NetworkErrorCheck || state == NetworkErrorDownload)
    {
        QString details = reply->errorString();
        reply->abort();
        progress->close();

        if (state == NetworkErrorDownload || show)
        {
            QString msg = tr("<h3>Network error</h3>");
            QString info = tr("<p>Impossible to obtain update information.</p>"
                              "<p>Please make sure that you are connected to "
                              "the Internet and try again.</p>");
            QMessageBox box(TaoApp->windowWidget());
            setBoxMinimumWidth(box, 400);
            box.setIconPixmap(connectionErrorIcon);
            box.setWindowTitle(dialogTitle);
            box.setText(msg);
            box.setInformativeText(info);
            box.setDetailedText(details);
            box.setStandardButtons(QMessageBox::Ok);
            box.exec();
        }

        return;
    }

    if (code != 200)
        return;

    bool updateAvailable = false;
    qint64 pid = 0;
    QString name;
    switch (state)
    {
    case WaitingForUpdate:

        // Save and process the update information we have just received

        progress->hide();

        XL_ASSERT(!file);
        pid = QCoreApplication::applicationPid();
        name = QString("tao_update.%1").arg(pid);
        file = new QFile(QDir::temp().filePath(name));
        if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate))
        {
            IFTRACE(update)
            debug() << "Failed to open: " << +file->fileName() << "\n";
            delete file;
            file = NULL;
            return;
        }
        file->write(reply->readAll());
        file->close();

        reply->deleteLater();
        reply = NULL;

        readIniFile();

        file->remove();
        delete file;
        file = NULL;

        setLastChecked(QDateTime::currentDateTime());

        // Propose to update if current version is older than the remote one
        updateAvailable = (version < remoteVersion) && !url.isEmpty();
        IFTRACE(update)
        debug() << "Update available: " << updateAvailable
                << " (local " << version << " remote " << remoteVersion
                << ")\n";
        if (updateAvailable)
        {
            bool ok = false;
            double min = QSettings().value("AppUpdatePromptMinVersion", -1.0)
                         .toDouble(&ok);
            if (!ok)
                min = -1.0;

            if (show || remoteVersion > min)
            {
                if (remoteVersion > min)
                {
                    // Remember that user was prompted to download  this
                    // version. He/she will not be prompted again on application
                    // startup, unless a newer version exists.
                    QSettings().setValue("AppUpdatePromptMinVersion",
                                         remoteVersion);
                    IFTRACE2(update, settings)
                    debug() << "Saving AppUpdatePromptMinVersion="
                            << remoteVersion << "\n";
                }

                // Show update dialog
                QString msg = tr("<h3>Update available</h3>");
                QString info = tr("<p>%1 version %2 is available."
                                  " Do you want to download it now?</p>")
                               .arg(appName()).arg(remoteVer());
                QMessageBox box(TaoApp->windowWidget());
                setBoxMinimumWidth(box, 400);
                box.setIconPixmap(downloadIcon);
                box.setWindowTitle(dialogTitle);
                box.setText(msg);
                box.setInformativeText(info);
                if (!description.isEmpty())
                    box.setDetailedText(description);
                box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
                if (box.exec() == QMessageBox::Yes)
                {
                    startDownload();
                    break;
                }
            }
            else
            {
                IFTRACE(update)
                debug() << "Update available dialog not shown due to "
                        "AppUpdatePromptMinVersion=" << min << "\n";
            }
        }
        else
        {
            if (show)
                showNoUpdateAvailable();
        }

        progress->close();
        break;

    case Downloading:

        IFTRACE(update)
        debug() << "Download finished\n";

        progress->hide();
        state = Downloaded;
        if (file)
            saveDownloadedData();

        break;

    default:
        break;
    }
}
Exemplo n.º 24
0
bool UpdateApplication::createFile()
// ----------------------------------------------------------------------------
//    Create the update file
// ----------------------------------------------------------------------------
{
    XL_ASSERT(!file);

    // Keep the name of the remote file in the URL
    QString fileName = QFileInfo(url.path()).fileName();

    // Choose folder
#if QT_VERSION >= 0x050000
    // Thank you Qt5 for this insanity, just in case we have several ~/Desktop
    QString desktop = "";
    QStringList desktops = QStandardPaths::standardLocations(
                               QStandardPaths::DesktopLocation);
    if (desktops.size() >= 1)
        desktop = desktops[0];
#else
    QString desktop = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
#endif

    QString folder = QFileDialog::getExistingDirectory(NULL,
                     tr("Select destination folder"),
                     desktop);

    if (!folder.isEmpty())
    {
        // Set complete filename
        QString completeFileName = folder + "/" + fileName;

        file = new QFile(completeFileName);

        if (file->exists())
        {
            QString msg = tr("<h3>File exists</h3>");
            QString info = tr("<p>The selected folder already contains a file "
                              "called <b>%1</b>.</p>"
                              "<p>Save anyway?</p>").arg(fileName);
            QMessageBox box(TaoApp->windowWidget());
            setBoxMinimumWidth(box, 400);
            box.setIcon(QMessageBox::Warning);
            box.setWindowTitle(dialogTitle);
            box.setText(msg);
            box.setInformativeText(info);
            box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
            box.setDefaultButton(QMessageBox::No);

            if (box.exec() == QMessageBox::No)
            {
                delete file;
                file = NULL;
                return false;
            }
        }

        if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate))
        {
            IFTRACE(update)
            debug() << "Could not open file for writing: '"
                    << +completeFileName
                    << "' error: " << +file->errorString() << "\n";

            QString msg = tr("<h3>Write error</h3>");
            QString info = tr("<p>The upgrade cannot be saved to <b>%1</b>.</p>")
                           .arg(fileName);
            QString details = file->errorString();
            QMessageBox box(TaoApp->windowWidget());
            setBoxMinimumWidth(box, 400);
            box.setIcon(QMessageBox::Critical);
            box.setWindowTitle(dialogTitle);
            box.setText(msg);
            box.setInformativeText(info);
            box.setDetailedText(details);
            box.setStandardButtons(QMessageBox::Ok);

            box.exec();

            delete file;
            file = NULL;
            return false;
        }

        IFTRACE(update)
        debug() << "Save to: '" << +completeFileName << "'\n";

        if (state == Downloaded)
        {
            // Download completed while user was still choosing file path
            saveDownloadedData();
        }

        return true;
    }

    return false;
}
Exemplo n.º 25
0
	void CDataStorage::FlushEm(unsigned16_t backpatch_level)
	{
		/*
		 *  delete units which don't need to live any longer.
		 *
		 *  In the same loop, we shrink the 'stack' for
		 *  future speed and to keep storage requirements in check.
		 */
		//printf("FLUSH-EM %d\n", backpatch_level);
		UnitList_Itor_t start = m_FlushStack.begin();
		if (m_FlushLastEndLevel == backpatch_level
			&& backpatch_level != BACKPATCH_LEVEL_EVERYONE // do not use cached position when 'flushing all'
		    //&& m_FlushLastEndPos != m_FlushStack.begin()
			&& m_FlushLastEndPos != m_FlushStack.size()) { //.end())
			XL_ASSERT(start != m_FlushStack.end());
			start = m_FlushStack.begin() + (signed32_t)m_FlushLastEndPos;
			XL_ASSERT(m_FlushLastEndPos <= m_FlushStack.size());
			XL_ASSERT(start != m_FlushStack.end());
			start++;
		}

		UnitList_Itor_t j = start;
		size_t cnt = 0;
		size_t cntleft = 0;
		for (UnitList_Itor_t i = j; i != m_FlushStack.end(); i++) {
			CUnit *up = *i;
			if (up->m_Backpatching_Level <= backpatch_level) {
				XL_ASSERT(up != NULL);
				delete up;
				(*i) = NULL;
				cnt++;
				continue;
			}

			XL_ASSERT(up->m_Backpatching_Level <= 4);

			// do we need to move-copy the unit reference down as part of a shrink operation?
			if (i != j)	{
				(*j) = up;
			}
			j++;
			cntleft++;
		}

		size_t count = (size_t)(j - m_FlushStack.begin());

#if OLE_DEBUG
		std::cerr << "number of records deleted: " << cnt << ", left: " << cntleft << ", new.size: " << count << std::endl;
#endif

		m_FlushStack.resize(count);
		XL_ASSERT(m_FlushStack.size() == count);

		// remember for the next time around
		m_FlushLastEndLevel = backpatch_level;
		j = m_FlushStack.end();
		if (m_FlushStack.size() > 0) {
			j--;
		} else {
#if OLE_DEBUG
			std::cerr << "empty!" << std::endl;
#endif
		}
		m_FlushLastEndPos = (size_t)(j - m_FlushStack.begin());
	}