text: /*
   * Copyright (c) 2018 MariaDB Corporation Ab
   *
   * Use of this software is governed by the Business Source License included
   * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
   *
   * Change Date: 2023-01-01
   *
   * On the date above, in accordance with the Business Source License, use
   * of this software will be governed by version 2 or later of the General
   * Public License.
   */
  
  #include "stopwatch.h"
  
  #include <iomanip>
  #include <iostream>
  #include <sstream>
  #include <ctime>
  
  namespace base
  {
  
  StopWatch::StopWatch()
  {
      restart();
  }
  
  Duration StopWatch::lap() const
  {
      return {Clock::now() - m_start};
  }
  
  Duration StopWatch::restart()
  {
      TimePoint now = Clock::now();
      Duration lap = now - m_start;
      m_start = now;
      return lap;
  }
  }   // base
  
  /********** OUTPUT ***********/
  namespace
  {
  using namespace base;
  struct TimeConvert
  {
      double      div;        // divide the value of the previous unit by this
      std::string suffix;     // milliseconds, hours etc.
      double      max_visual; // threashold to switch to the next unit
  };
  // Will never get to centuries because the duration is a long carrying nanoseconds
  TimeConvert convert[]
  {
      {1, "ns", 1000}, {1000, "us", 1000}, {1000, "ms", 1000},
      {1000, "s", 60}, {60, "min", 60}, {60, "hours", 24},
      {24, "days", 365.25}, {365.25, "years", 10000},
      {100, "centuries", std::numeric_limits<double>::max()}
  };
  
  int convert_size = sizeof(convert) / sizeof(convert[0]);
  }
  
  namespace base
  {
  std::pair<double, std::string> dur_to_human_readable(Duration dur)
  {
      using namespace std::chrono;
      double time = duration_cast<nanoseconds>(dur).count();
      bool negative = (time < 0) ? time = -time, true : false;
  
      for (int i = 0; i <= convert_size; ++i)
      {
          if (i == convert_size)
          {
              return std::make_pair(negative ? -time : time,
                                    convert[convert_size - 1].suffix);
          }
  
          time /= convert[i].div;
  
          if (time < convert[i].max_visual)
          {
              return std::make_pair(negative ? -time : time, convert[i].suffix);
          }
      }
  
      abort();    // should never get here
  }
  
  std::ostream& operator<<(std::ostream& os, Duration dur)
  {
      auto p = dur_to_human_readable(dur);
      os << p.first << p.second;
  
      return os;
  }
  
  // TODO: this will require some thought. time_point_to_string() for a system_clock is
  // obvious, but not so for a steady_clock. Maybe TimePoint belongs to a system clock
  // and sould be called something else here, and live in a time_measuring namespace.
  std::string time_point_to_string(TimePoint tp, const std::string& fmt)
  {
      using namespace std::chrono;
      std::time_t timet = system_clock::to_time_t(system_clock::now()
                                                  + (tp - Clock::now()));
      struct tm* ptm;
      ptm = gmtime(&timet);
      const int sz = 1024;
      char buf[sz];
      strftime(buf, sz, fmt.c_str(), ptm);
      return buf;
  }
  
  std::ostream& operator<<(std::ostream& os, TimePoint tp)
  {
      os << time_point_to_string(tp);
      return os;
  }
  
  void test_stopwatch_output(std::ostream& os)
  {
      long long dur[] =
      {
          400,                                // 400ns
          5 * 1000,                           // 5us
          500 * 1000,                         // 500us
          1 * 1000000,                        // 1ms
          700 * 1000000LL,                    // 700ms
          5 * 1000000000LL,                   // 5s
          200 * 1000000000LL,                 // 200s
          5 * 60 * 1000000000LL,              // 5m
          45 * 60 * 1000000000LL,             // 45m
          130 * 60 * 1000000000LL,            // 130m
          24 * 60 * 60 * 1000000000LL,        // 24 hours
          3 * 24 * 60 * 60 * 1000000000LL,    // 72 hours
          180 * 24 * 60 * 60 * 1000000000LL,  // 180 days
          1000 * 24 * 60 * 60 * 1000000000LL  // 1000 days
      };
  
      for (unsigned i = 0; i < sizeof(dur) / sizeof(dur[0]); ++i)
      {
          os << Duration(dur[i]) << std::endl;
      }
  }
  }   // base
  