/*!
@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