Newer
Older
Import / applications / HighwayDash / ports / Framework / Utilities.h
/*!
 @header    Utilities.h
 @brief     Various utility functions
 @version   0.0.1
 @author    John Ryland
 @copyright Copyright 2017 John Ryland. All rights reserved.
*/
//  BlockyFroggy
//  Copyright © 2017 John Ryland.
//  All rights reserved.
#pragma once
#ifndef UTILITIES_H
#define UTILITIES_H


#include <string>
#include <chrono>
#include <thread>
#include <vector>
#include <fstream>
#include <cstdio>
#include <ctime>
#include <cstdarg>
#include <cstdint>
#include <map>
#include "Common.h"


#define STRINGIZE_(arg)  #arg
#define STRINGIZE(arg)   STRINGIZE_(arg)


//! Allocation tracking for memory debugging.
namespace AllocationTracking {


#define ENABLE_ALLOCATION_DEBUGGING   0
#if ENABLE_ALLOCATION_DEBUGGING
extern thread_local bool g_enableAllocTracing;
#else
extern bool g_enableAllocTracing;
#endif
extern int frameAllocations;
extern int allocationCount;
extern int deallocationCount;


} // namespace AllocationTracking


//! Utility functions
namespace Utilities {


std::vector<std::string> split(const std::string &s, char delim);
const char* baseNameCStr(const char* a_fileName);
std::string baseName(std::string a_fileName);
void setProgramName(const char* argv0);
const std::string& programName();
std::string vFormattedString(const char* a_fmt, va_list a_args);
std::string formattedString(const char* a_fmt, ...);
std::string longFormatDateTime();


//! f is a function which maps T -> R.
//! memorize will cache results of f
template <typename R, typename T, R (*f)(T)>
R memorize(T x)
{
  static std::map<T, R> cache;
  if (!cache.count(x))
    cache[x] = f(x);
  return cache[x];
}


#define SessionRegisterListener(f) \
          GetSession()->RegisterListener(this, &std::remove_reference(decltype(*this))::type::f);


#if __cplusplus >= 201402L
#  define HAVE_MAKE_UNIQUE
#endif


#ifndef HAVE_MAKE_UNIQUE
namespace std
{
  // probably not identical in terms of exception handling
  template <class T, typename ...Ts>
  unique_ptr<T> make_unique(Ts&& ...args)
  {
    return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
  }
}
#endif


// See also GameTime in SystemInformation.h
typedef std::chrono::duration<uint64_t, std::nano>                               Duration;
typedef std::chrono::duration<uint64_t, std::nano>                               Ticks;
typedef std::chrono::time_point<std::chrono::high_resolution_clock, Ticks>       TimePoint;


inline TimePoint Now()
{
  // return std::chrono::time_point_cast<Ticks>(std::chrono::high_resolution_clock::now());
  return std::chrono::high_resolution_clock::now();
}


inline void Sleep(Duration sleepTime)
{
  std::this_thread::sleep_for(sleepTime);
}


inline void MilliSecondsSleep(uint32_t ms)
{
  std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}


inline void SecondsSleep(uint32_t sec)
{
  std::this_thread::sleep_for(std::chrono::seconds(sec));
}


/*

// If chrono is not available, then may need to roll our own TimePoint etc
struct Ticks
{
  uint64_t nanoSeconds;
};
struct TimePoint
{
  Ticks ticksFromEpoc;
  static const TimePoint& getCurrentTime();
};

TimePoint operator+=(TimePoint, Ticks);
Ticks operator-(TimePoint, TimePoint);

*/


//! Reads in file contents of filename to dat
inline void slurp(const char* filename, std::vector<uint8_t> &dat)
{
  std::ifstream file(filename, std::ifstream::ate | std::ifstream::binary);
  dat.resize(file.tellg());
  dat.assign((std::istreambuf_iterator<char>(file.seekg(0, std::ios::beg))), std::istreambuf_iterator<char>());
}

//! Output dat formatted as code to output file
inline void dumpAsCode(const char* output, const char* var, const std::vector<uint8_t> &dat)
{
  FILE *out = fopen(output, "w");
  fprintf(out, "// This file was generated\n#pragma once\n#include <cstdint>\n#include <array>\n\nstd::array<uint8_t, %zu> %s =\n{\n  ", dat.size(), var);
  for (int i = 0; i < dat.size(); i++)
    fprintf(out, "0x%02x%s%s", dat[i], (i == dat.size()-1) ? "" : ", ", (i % 18) == 17 ? "\n  " : "");
  fprintf(out, "\n};\n\n");
  fclose(out);
}

//! Reads in file filename to memory, then outputs it as a header file to
//! the file output formatted as code with a std::array variable named var
inline void xxd(const char* output, const char* filename, const char* var)
{
  std::vector<uint8_t> dat;
  slurp(filename, dat);
  dumpAsCode(output, var, dat);
}

//! Helper for visiting each string and characters and adding together the total size
class SizeVisitor
{
public:
  void append(const char* str) { m_size += strlen(str); }
  void append(char ch) { m_size++; }
  size_t m_size = 0;
};

//! Helper which appends each visited string and character to a buffer.
//! It is expected the buffer is large enough as the required size can be
//! obtained by using the SizeVisitor.
class OutputVisitor
{
public:
  explicit OutputVisitor(char *b) : m_buf(b) {}
  void append(const char* str) { while (*str) { *m_buf++ = *str++; } }
  void append(char ch) { *m_buf++ = ch; }
  char* m_buf;
};

//! Container for holding a value which can be one of a variety of types.
//! When holding a value of string, no memory management of the raw pointer is performed
//! and neither does it perform a copy of the string.
class Variant
{
public:
  enum Type
  {
    Invalid,
    Bool,
    String,
    Int,
    Float
  };

  union
  {
    bool        m_bool;
    const char* m_str;
    int64_t     m_int;
    double      m_flt;
  };

  Type m_type = Invalid;
  
  void set(bool x)        { m_type = Bool;   m_bool = x; }
  void set(const char* x) { m_type = String; m_str = x; }
  void set(int32_t x)     { m_type = Int;    m_int = x; }
  void set(int64_t x)     { m_type = Int;    m_int = x; }
  void set(double x)      { m_type = Float;  m_flt = x; }

  operator bool()         { return m_bool; }
  operator const char* () { return m_str; }
  operator int64_t()      { return m_int; }
  operator double()       { return m_flt; }

  template <typename visitor>
  void visit(visitor& v)
  {
    switch (m_type) {
      case Invalid: v.append("invalid"); break;
      case Bool:    m_bool ? v.append("true") : v.append("false"); break; //! @todo test
      case String:  v.append(m_str); break;
      case Int:
      {
        bool begun = false;
        uint64_t remainder = m_int;
        uint64_t tensPlace = 10000000000000000000ULL;
        while (tensPlace)
        {
          uint64_t highOrderTens = remainder / tensPlace;
          if (!begun)
            begun = !!highOrderTens;
          if (begun) // execute also if begun changed to true this iteration from above
            v.append(highOrderTens + '0');
          remainder -= highOrderTens * tensPlace;
          tensPlace /= 10;
        }
        if (!begun)
          v.append('0');
        break;
      }
      case Float:   { uint32_t t = m_flt; while (t) { v.append((t%10)+'0'); t /= 10; } break; } //! @todo fix
    }
  }

  size_t formattedSize()
  {
    SizeVisitor v;
    visit(v);
    return v.m_size;
  }

  char* formatted(char* buf)
  {
    OutputVisitor v(buf);
    visit(v);
    return v.m_buf;
  }
};


template <std::size_t ARGS = 0>
class StackString : public StackOnlyClass
{
public:
  // Only string literals
  template <std::size_t N>
  explicit StackString(const char (&fmt)[N]) : m_fmt(fmt)
  {
  }

  // Expanding the args
  StackString(StackString<ARGS-1>& a_other) : m_fmt(a_other.m_fmt)
  {
    memcpy(m_args, a_other.m_args, sizeof(Variant)*(ARGS-1));
  }

  template <typename T>
  StackString<ARGS+1> arg(T a_arg)
  {
    StackString<ARGS+1> newStr(*this);
    newStr.m_args[ARGS].set(a_arg);
    return newStr;
  }

  template <typename visitor>
  void visit(visitor& v)
  {
    const char* f = m_fmt;
    bool insideFmt = false;
    bool haveArgIdx = false;
    int argIdx = 0;
    while (*f) {
      if (insideFmt) {
        if (*f >= '0' && *f <= '9') {
          argIdx *= 10;
          argIdx += *f - '0';
          haveArgIdx = true;
        } else {
          if (haveArgIdx)
            m_args[argIdx].visit(v);
          v.append(*f);
          insideFmt = false;
          haveArgIdx = false;
          argIdx = 0;
        }
      } else {
        if (*f == '$') {
          insideFmt = true;
        } else {
          v.append(*f);
        }
      }
      f++;
    }
  }

  size_t formattedSize()
  {
    SizeVisitor v;
    visit(v);
    return v.m_size + 1;
  }

  char* formatted(char* buf)
  {
    OutputVisitor v(buf);
    visit(v);
    *v.m_buf = 0;
    return v.m_buf;
  }

  const char* m_fmt;
  Variant m_args[ARGS];

  //StackString<ARGS-1>* m_parent = nullptr;
  //Variant m_arg;
};


class StackFormat
{
public:
  template <typename visitor>
  static void visit(visitor& v, const char* m_fmt, va_list a_args)
  {
    const char* f = m_fmt;
    bool insideFmt = false;
    while (*f) {
      if (insideFmt) {
        Variant var;
        if (*f == 'd' || *f == 'i' || *f == 'u' || *f == 'o' || *f == 'x' || *f == 'p')
          var.set(va_arg(a_args, int));
        else if (*f == 's')
          var.set(va_arg(a_args, const char*));
        else if (*f == 'a' || *f == 'e' || *f == 'f' || *f == 'g')
          var.set(va_arg(a_args, double));
        else if (*f == 'c')
          var.set(va_arg(a_args, int));
        else
          v.append(*f);
        var.visit(v);
        insideFmt = false;
      } else {
        if (*f == '%') {
          insideFmt = true;
        } else {
          v.append(*f);
        }
      }
      f++;
    }
  }

  static size_t vsize(const char* m_fmt, va_list a_args)
  {
    va_list argsCopy;
    va_copy(argsCopy, a_args);
    SizeVisitor v;
    visit(v, m_fmt, argsCopy);
    va_end(argsCopy);
    return v.m_size + 1;
  }

  static size_t size(const char* a_fmt, ...)
  {
    va_list args;
    va_start(args, a_fmt);
    size_t ret = vsize(a_fmt, args);
    va_end(args);
    return ret;
  }

  static char* vstring(char* buf, const char* m_fmt, va_list a_args)
  {
    va_list argsCopy;
    va_copy(argsCopy, a_args);
    OutputVisitor v(buf);
    visit(v, m_fmt, argsCopy);
    va_end(argsCopy);
    *v.m_buf = 0;
    return v.m_buf;
  }

  static void string(char* buf, const char* a_fmt, ...)
  {
    va_list args;
    va_start(args, a_fmt);
    vstring(buf, a_fmt, args);
    va_end(args);
  }
};


} // namespace Utilities


#endif // UTILITIES_H