LCOV - code coverage report
Current view: top level - port - cpl_progress.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 57 84 67.9 %
Date: 2024-11-21 22:18:42 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       5             :  * Purpose:  Progress function implementations.
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2013, Frank Warmerdam
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_progress.h"
      14             : 
      15             : #include <cmath>
      16             : #include <cstdio>
      17             : #include <ctime>
      18             : 
      19             : #include <algorithm>
      20             : 
      21             : #include "cpl_conv.h"
      22             : 
      23             : /************************************************************************/
      24             : /*                         GDALDummyProgress()                          */
      25             : /************************************************************************/
      26             : 
      27             : /**
      28             :  * \brief Stub progress function.
      29             :  *
      30             :  * This is a stub (does nothing) implementation of the GDALProgressFunc()
      31             :  * semantics.  It is primarily useful for passing to functions that take
      32             :  * a GDALProgressFunc() argument but for which the application does not want
      33             :  * to use one of the other progress functions that actually do something.
      34             :  */
      35             : 
      36      451591 : int CPL_STDCALL GDALDummyProgress(double /* dfComplete */,
      37             :                                   const char * /* pszMessage */,
      38             :                                   void * /* pData */)
      39             : {
      40      451591 :     return TRUE;
      41             : }
      42             : 
      43             : /************************************************************************/
      44             : /*                         GDALScaledProgress()                         */
      45             : /************************************************************************/
      46             : typedef struct
      47             : {
      48             :     GDALProgressFunc pfnProgress;
      49             :     void *pData;
      50             :     double dfMin;
      51             :     double dfMax;
      52             : } GDALScaledProgressInfo;
      53             : 
      54             : /**
      55             :  * \brief Scaled progress transformer.
      56             :  *
      57             :  * This is the progress function that should be passed along with the
      58             :  * callback data returned by GDALCreateScaledProgress().
      59             :  */
      60             : 
      61      831860 : int CPL_STDCALL GDALScaledProgress(double dfComplete, const char *pszMessage,
      62             :                                    void *pData)
      63             : 
      64             : {
      65      831860 :     GDALScaledProgressInfo *psInfo =
      66             :         reinterpret_cast<GDALScaledProgressInfo *>(pData);
      67             : 
      68             :     // Optimization if GDALCreateScaledProgress() provided with
      69             :     // GDALDummyProgress.
      70      831860 :     if (psInfo == nullptr)
      71      476261 :         return TRUE;
      72             : 
      73      711198 :     return psInfo->pfnProgress(dfComplete * (psInfo->dfMax - psInfo->dfMin) +
      74      355599 :                                    psInfo->dfMin,
      75      355599 :                                pszMessage, psInfo->pData);
      76             : }
      77             : 
      78             : /************************************************************************/
      79             : /*                      GDALCreateScaledProgress()                      */
      80             : /************************************************************************/
      81             : 
      82             : /**
      83             :  * \brief Create scaled progress transformer.
      84             :  *
      85             :  * Sometimes when an operations wants to report progress it actually
      86             :  * invokes several subprocesses which also take GDALProgressFunc()s,
      87             :  * and it is desirable to map the progress of each sub operation into
      88             :  * a portion of 0.0 to 1.0 progress of the overall process.  The scaled
      89             :  * progress function can be used for this.
      90             :  *
      91             :  * For each subsection a scaled progress function is created and
      92             :  * instead of passing the overall progress func down to the sub functions,
      93             :  * the GDALScaledProgress() function is passed instead.
      94             :  *
      95             :  * @param dfMin the value to which 0.0 in the sub operation is mapped.
      96             :  * @param dfMax the value to which 1.0 is the sub operation is mapped.
      97             :  * @param pfnProgress the overall progress function.
      98             :  * @param pData the overall progress function callback data.
      99             :  *
     100             :  * @return pointer to pass as pProgressArg to sub functions.  Should be freed
     101             :  * with GDALDestroyScaledProgress().
     102             :  *
     103             :  * Example:
     104             :  *
     105             :  * \code
     106             :  *   int MyOperation( ..., GDALProgressFunc pfnProgress, void *pProgressData );
     107             :  *
     108             :  *   {
     109             :  *       void *pScaledProgress;
     110             :  *
     111             :  *       pScaledProgress = GDALCreateScaledProgress( 0.0, 0.5, pfnProgress,
     112             :  *                                                   pProgressData );
     113             :  *       GDALDoLongSlowOperation( ..., GDALScaledProgress, pScaledProgress );
     114             :  *       GDALDestroyScaledProgress( pScaledProgress );
     115             :  *
     116             :  *       pScaledProgress = GDALCreateScaledProgress( 0.5, 1.0, pfnProgress,
     117             :  *                                                   pProgressData );
     118             :  *       GDALDoAnotherOperation( ..., GDALScaledProgress, pScaledProgress );
     119             :  *       GDALDestroyScaledProgress( pScaledProgress );
     120             :  *
     121             :  *       return ...;
     122             :  *   }
     123             :  * \endcode
     124             :  */
     125             : 
     126      505625 : void *CPL_STDCALL GDALCreateScaledProgress(double dfMin, double dfMax,
     127             :                                            GDALProgressFunc pfnProgress,
     128             :                                            void *pData)
     129             : 
     130             : {
     131      505625 :     if (pfnProgress == nullptr || pfnProgress == GDALDummyProgress)
     132      495427 :         return nullptr;
     133             : 
     134             :     GDALScaledProgressInfo *psInfo = static_cast<GDALScaledProgressInfo *>(
     135       10198 :         CPLCalloc(sizeof(GDALScaledProgressInfo), 1));
     136             : 
     137       10198 :     if (std::abs(dfMin - dfMax) < 0.0000001)
     138         164 :         dfMax = dfMin + 0.01;
     139             : 
     140       10198 :     psInfo->pData = pData;
     141       10198 :     psInfo->pfnProgress = pfnProgress;
     142       10198 :     psInfo->dfMin = dfMin;
     143       10198 :     psInfo->dfMax = dfMax;
     144             : 
     145       10198 :     return static_cast<void *>(psInfo);
     146             : }
     147             : 
     148             : /************************************************************************/
     149             : /*                     GDALDestroyScaledProgress()                      */
     150             : /************************************************************************/
     151             : 
     152             : /**
     153             :  * \brief Cleanup scaled progress handle.
     154             :  *
     155             :  * This function cleans up the data associated with a scaled progress function
     156             :  * as returned by GADLCreateScaledProgress().
     157             :  *
     158             :  * @param pData scaled progress handle returned by GDALCreateScaledProgress().
     159             :  */
     160             : 
     161      505566 : void CPL_STDCALL GDALDestroyScaledProgress(void *pData)
     162             : 
     163             : {
     164      505566 :     CPLFree(pData);
     165      505559 : }
     166             : 
     167             : /************************************************************************/
     168             : /*                      GDALTermProgressWidth()                         */
     169             : /************************************************************************/
     170             : 
     171             : static constexpr int GDALTermProgressWidth(int nMaxTicks, int nMajorTickSpacing)
     172             : {
     173             :     int nWidth = 0;
     174             :     for (int i = 0; i <= nMaxTicks; i++)
     175             :     {
     176             :         if (i % nMajorTickSpacing == 0)
     177             :         {
     178             :             int nPercent = (i * 100) / nMaxTicks;
     179             :             do
     180             :             {
     181             :                 nWidth++;
     182             :             } while (nPercent /= 10);
     183             :         }
     184             :         else
     185             :         {
     186             :             nWidth += 1;
     187             :         }
     188             :     }
     189             :     return nWidth;
     190             : }
     191             : 
     192             : /************************************************************************/
     193             : /*                          GDALTermProgress()                          */
     194             : /************************************************************************/
     195             : 
     196             : /**
     197             :  * \fn GDALTermProgress(double, const char*, void*)
     198             :  * \brief Simple progress report to terminal.
     199             :  *
     200             :  * This progress reporter prints simple progress report to the
     201             :  * terminal window.  The progress report generally looks something like
     202             :  * this:
     203             : 
     204             : \verbatim
     205             : 0...10...20...30...40...50...60...70...80...90...100 - done.
     206             : \endverbatim
     207             : 
     208             :  * Starting with GDAL 3.11, for tasks estimated to take more than 10 seconds,
     209             :  * an estimated remaining time is also displayed at the end. And for tasks
     210             :  * taking more than 5 seconds to complete, the total time is displayed upon
     211             :  * completion.
     212             :  *
     213             :  * Every 2.5% of progress another number or period is emitted.  Note that
     214             :  * GDALTermProgress() uses internal static data to keep track of the last
     215             :  * percentage reported and will get confused if two terminal based progress
     216             :  * reportings are active at the same time.
     217             :  *
     218             :  * The GDALTermProgress() function maintains an internal memory of the
     219             :  * last percentage complete reported in a static variable, and this makes
     220             :  * it unsuitable to have multiple GDALTermProgress()'s active either in a
     221             :  * single thread or across multiple threads.
     222             :  *
     223             :  * @param dfComplete completion ratio from 0.0 to 1.0.
     224             :  * @param pszMessage optional message.
     225             :  * @param pProgressArg ignored callback data argument.
     226             :  *
     227             :  * @return Always returns TRUE indicating the process should continue.
     228             :  */
     229             : 
     230      112263 : int CPL_STDCALL GDALTermProgress(double dfComplete,
     231             :                                  CPL_UNUSED const char *pszMessage,
     232             :                                  CPL_UNUSED void *pProgressArg)
     233             : {
     234      112263 :     constexpr int MAX_TICKS = 40;
     235      112263 :     constexpr int MAJOR_TICK_SPACING = 4;
     236      112263 :     constexpr int LENGTH_OF_0_TO_100_PROGRESS =
     237             :         GDALTermProgressWidth(MAX_TICKS, MAJOR_TICK_SPACING);
     238             : 
     239             :     const int nThisTick = std::min(
     240      112263 :         MAX_TICKS, std::max(0, static_cast<int>(dfComplete * MAX_TICKS)));
     241             : 
     242             :     // Have we started a new progress run?
     243             :     static int nLastTick = -1;
     244             :     static time_t nStartTime = 0;
     245             :     // whether estimated remaining time is displayed
     246             :     static bool bETADisplayed = false;
     247             :     // number of characters displayed during last progress call
     248             :     static int nCharacterCountLastTime = 0;
     249             :     // maximum number of characters displayed during previous calls
     250             :     static int nCharacterCountMax = 0;
     251      112263 :     if (nThisTick < nLastTick && nLastTick >= MAX_TICKS - 1)
     252             :     {
     253          19 :         bETADisplayed = false;
     254          19 :         nLastTick = -1;
     255          19 :         nCharacterCountLastTime = 0;
     256          19 :         nCharacterCountMax = 0;
     257             :     }
     258             : 
     259      112263 :     if (nThisTick <= nLastTick)
     260      104597 :         return TRUE;
     261             : 
     262        7666 :     const time_t nCurTime = time(nullptr);
     263        7666 :     if (nLastTick < 0)
     264         444 :         nStartTime = nCurTime;
     265             : 
     266        7666 :     constexpr int MIN_DELAY_FOR_ETA = 5;  // in seconds
     267        7666 :     if (nCurTime - nStartTime >= MIN_DELAY_FOR_ETA && dfComplete > 0 &&
     268             :         dfComplete < 0.5)
     269             :     {
     270           0 :         static bool bIsTTY = CPLIsInteractive(stdout);
     271           0 :         bETADisplayed = bIsTTY;
     272             :     }
     273        7666 :     if (bETADisplayed)
     274             :     {
     275           0 :         for (int i = 0; i < nCharacterCountLastTime; ++i)
     276           0 :             fprintf(stdout, "\b");
     277           0 :         nLastTick = -1;
     278           0 :         nCharacterCountLastTime = 0;
     279             :     }
     280             : 
     281       23872 :     while (nThisTick > nLastTick)
     282             :     {
     283       16206 :         ++nLastTick;
     284       16206 :         if (nLastTick % MAJOR_TICK_SPACING == 0)
     285             :         {
     286        4384 :             const int nPercent = (nLastTick * 100) / MAX_TICKS;
     287        4384 :             nCharacterCountLastTime += fprintf(stdout, "%d", nPercent);
     288             :         }
     289             :         else
     290             :         {
     291       11822 :             nCharacterCountLastTime += fprintf(stdout, ".");
     292             :         }
     293             :     }
     294             : 
     295        7666 :     if (nThisTick == MAX_TICKS)
     296             :     {
     297         393 :         nCharacterCountLastTime += fprintf(stdout, " - done");
     298         393 :         if (nCurTime - nStartTime >= MIN_DELAY_FOR_ETA)
     299             :         {
     300           0 :             const int nElapsed = static_cast<int>(nCurTime - nStartTime);
     301           0 :             const int nHours = nElapsed / 3600;
     302           0 :             const int nMins = (nElapsed % 3600) / 60;
     303           0 :             const int nSecs = nElapsed % 60;
     304           0 :             nCharacterCountLastTime +=
     305           0 :                 fprintf(stdout, " in %02d:%02d:%02d.", nHours, nMins, nSecs);
     306           0 :             for (int i = nCharacterCountLastTime; i < nCharacterCountMax; ++i)
     307           0 :                 nCharacterCountLastTime += fprintf(stdout, " ");
     308             :         }
     309             :         else
     310             :         {
     311         393 :             fprintf(stdout, ".");
     312             :         }
     313         393 :         fprintf(stdout, "\n");
     314             :     }
     315             :     else
     316             :     {
     317        7273 :         if (bETADisplayed)
     318             :         {
     319           0 :             for (int i = nCharacterCountLastTime;
     320           0 :                  i < LENGTH_OF_0_TO_100_PROGRESS; ++i)
     321           0 :                 nCharacterCountLastTime += fprintf(stdout, " ");
     322             : 
     323           0 :             const double dfETA =
     324           0 :                 (nCurTime - nStartTime) * (1.0 / dfComplete - 1);
     325           0 :             const int nETA = static_cast<int>(dfETA + 0.5);
     326           0 :             const int nHours = nETA / 3600;
     327           0 :             const int nMins = (nETA % 3600) / 60;
     328           0 :             const int nSecs = nETA % 60;
     329           0 :             nCharacterCountLastTime +=
     330           0 :                 fprintf(stdout, " - estimated remaining time: %02d:%02d:%02d",
     331             :                         nHours, nMins, nSecs);
     332           0 :             for (int i = nCharacterCountLastTime; i < nCharacterCountMax; ++i)
     333           0 :                 nCharacterCountLastTime += fprintf(stdout, " ");
     334             :         }
     335        7273 :         fflush(stdout);
     336             :     }
     337             : 
     338        7666 :     if (nCharacterCountLastTime > nCharacterCountMax)
     339        7666 :         nCharacterCountMax = nCharacterCountLastTime;
     340             : 
     341        7666 :     return TRUE;
     342             : }

Generated by: LCOV version 1.14