parent
f8661811a8
commit
9b43a872c6
@ -0,0 +1,197 @@ |
||||
#pragma once |
||||
|
||||
/**
|
||||
@file |
||||
@brief scoped array and aligned array |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#include <new> |
||||
#include <utility> |
||||
#ifdef _WIN32 |
||||
#include <malloc.h> |
||||
#else |
||||
#include <stdlib.h> |
||||
#endif |
||||
#include <cybozu/inttype.hpp> |
||||
|
||||
namespace cybozu { |
||||
|
||||
inline void *AlignedMalloc(size_t size, size_t alignment) |
||||
{ |
||||
#ifdef _WIN32 |
||||
return _aligned_malloc(size, alignment); |
||||
#else |
||||
void *p; |
||||
int ret = posix_memalign(&p, alignment, size); |
||||
return (ret == 0) ? p : 0; |
||||
#endif |
||||
} |
||||
|
||||
inline void AlignedFree(void *p) |
||||
{ |
||||
#ifdef _WIN32 |
||||
if (p == 0) return; |
||||
_aligned_free(p); |
||||
#else |
||||
free(p); |
||||
#endif |
||||
} |
||||
|
||||
template<class T> |
||||
class ScopedArray { |
||||
T *p_; |
||||
size_t size_; |
||||
ScopedArray(const ScopedArray&); |
||||
void operator=(const ScopedArray&); |
||||
public: |
||||
explicit ScopedArray(size_t size) |
||||
: p_(new T[size]) |
||||
, size_(size) |
||||
{ |
||||
} |
||||
~ScopedArray() |
||||
{ |
||||
delete[] p_; |
||||
} |
||||
T& operator[](size_t idx) CYBOZU_NOEXCEPT { return p_[idx]; } |
||||
const T& operator[](size_t idx) const CYBOZU_NOEXCEPT { return p_[idx]; } |
||||
size_t size() const CYBOZU_NOEXCEPT { return size_; } |
||||
bool empty() const CYBOZU_NOEXCEPT { return size_ == 0; } |
||||
T* begin() CYBOZU_NOEXCEPT { return p_; } |
||||
T* end() CYBOZU_NOEXCEPT { return p_ + size_; } |
||||
const T* begin() const CYBOZU_NOEXCEPT { return p_; } |
||||
const T* end() const CYBOZU_NOEXCEPT { return p_ + size_; } |
||||
T* data() CYBOZU_NOEXCEPT { return p_; } |
||||
const T* data() const CYBOZU_NOEXCEPT { return p_; } |
||||
}; |
||||
|
||||
/**
|
||||
T must be POD type |
||||
16byte aligment array |
||||
*/ |
||||
template<class T, size_t N = 16, bool defaultDoClear = true> |
||||
class AlignedArray { |
||||
T *p_; |
||||
size_t size_; |
||||
size_t allocSize_; |
||||
T *alloc(size_t size) const |
||||
{ |
||||
T *p = static_cast<T*>(AlignedMalloc(size * sizeof(T), N)); |
||||
if (p == 0) throw std::bad_alloc(); |
||||
return p; |
||||
} |
||||
void copy(T *dst, const T *src, size_t n) const |
||||
{ |
||||
for (size_t i = 0; i < n; i++) dst[i] = src[i]; |
||||
} |
||||
void setZero(T *p, size_t n) const |
||||
{ |
||||
for (size_t i = 0; i < n; i++) p[i] = 0; |
||||
} |
||||
/*
|
||||
alloc allocN and copy [p, p + copyN) to new p_ |
||||
don't modify size_ |
||||
*/ |
||||
void allocCopy(size_t allocN, const T *p, size_t copyN) |
||||
{ |
||||
T *q = alloc(allocN); |
||||
copy(q, p, copyN); |
||||
AlignedFree(p_); |
||||
p_ = q; |
||||
allocSize_ = allocN; |
||||
} |
||||
public: |
||||
/*
|
||||
don't clear buffer with zero if doClear is false |
||||
*/ |
||||
explicit AlignedArray(size_t size = 0, bool doClear = defaultDoClear) |
||||
: p_(0) |
||||
, size_(0) |
||||
, allocSize_(0) |
||||
{ |
||||
resize(size, doClear); |
||||
} |
||||
AlignedArray(const AlignedArray& rhs) |
||||
: p_(0) |
||||
, size_(0) |
||||
, allocSize_(0) |
||||
{ |
||||
*this = rhs; |
||||
} |
||||
AlignedArray& operator=(const AlignedArray& rhs) |
||||
{ |
||||
if (allocSize_ < rhs.size_) { |
||||
allocCopy(rhs.size_, rhs.p_, rhs.size_); |
||||
} else { |
||||
copy(p_, rhs.p_, rhs.size_); |
||||
} |
||||
size_ = rhs.size_; |
||||
return *this; |
||||
} |
||||
#if (CYBOZU_CPP_VERSION >= CYBOZU_CPP_VERSION_CPP11) |
||||
AlignedArray(AlignedArray&& rhs) CYBOZU_NOEXCEPT |
||||
: p_(rhs.p_) |
||||
, size_(rhs.size_) |
||||
, allocSize_(rhs.allocSize_) |
||||
{ |
||||
rhs.p_ = 0; |
||||
rhs.size_ = 0; |
||||
rhs.allocSize_ = 0; |
||||
} |
||||
AlignedArray& operator=(AlignedArray&& rhs) CYBOZU_NOEXCEPT |
||||
{ |
||||
swap(rhs); |
||||
rhs.clear(); |
||||
return *this; |
||||
} |
||||
#endif |
||||
/*
|
||||
don't clear buffer with zero if doClear is false |
||||
@note don't free if shrinked |
||||
*/ |
||||
void resize(size_t size, bool doClear = defaultDoClear) |
||||
{ |
||||
// shrink
|
||||
if (size <= size_) { |
||||
size_ = size; |
||||
return; |
||||
} |
||||
// realloc if necessary
|
||||
if (size > allocSize_) { |
||||
allocCopy(size, p_, size_); |
||||
} |
||||
if (doClear) setZero(p_ + size_, size - size_); |
||||
size_ = size; |
||||
} |
||||
void clear() // not free
|
||||
{ |
||||
size_ = 0; |
||||
} |
||||
~AlignedArray() |
||||
{ |
||||
AlignedFree(p_); |
||||
} |
||||
void swap(AlignedArray& rhs) CYBOZU_NOEXCEPT |
||||
{ |
||||
std::swap(p_, rhs.p_); |
||||
std::swap(size_, rhs.size_); |
||||
std::swap(allocSize_, rhs.allocSize_); |
||||
} |
||||
T& operator[](size_t idx) CYBOZU_NOEXCEPT { return p_[idx]; } |
||||
const T& operator[](size_t idx) const CYBOZU_NOEXCEPT { return p_[idx]; } |
||||
size_t size() const CYBOZU_NOEXCEPT { return size_; } |
||||
bool empty() const CYBOZU_NOEXCEPT { return size_ == 0; } |
||||
T* begin() CYBOZU_NOEXCEPT { return p_; } |
||||
T* end() CYBOZU_NOEXCEPT { return p_ + size_; } |
||||
const T* begin() const CYBOZU_NOEXCEPT { return p_; } |
||||
const T* end() const CYBOZU_NOEXCEPT { return p_ + size_; } |
||||
T* data() CYBOZU_NOEXCEPT { return p_; } |
||||
const T* data() const CYBOZU_NOEXCEPT { return p_; } |
||||
#if (CYBOZU_CPP_VERSION >= CYBOZU_CPP_VERSION_CPP11) |
||||
const T* cbegin() const CYBOZU_NOEXCEPT { return p_; } |
||||
const T* cend() const CYBOZU_NOEXCEPT { return p_ + size_; } |
||||
#endif |
||||
}; |
||||
|
||||
} // cybozu
|
@ -0,0 +1,239 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief converter between integer and string |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
|
||||
#include <memory.h> |
||||
#include <limits.h> |
||||
#include <limits> |
||||
#include <cybozu/exception.hpp> |
||||
|
||||
namespace cybozu { |
||||
|
||||
namespace atoi_local { |
||||
|
||||
template<typename T, size_t n> |
||||
T convertToInt(bool *b, const char *p, size_t size, const char (&max)[n], T min, T overflow1, char overflow2) |
||||
{ |
||||
if (size > 0 && *p) { |
||||
bool isMinus = false; |
||||
size_t i = 0; |
||||
if (*p == '-') { |
||||
isMinus = true; |
||||
i++; |
||||
} |
||||
if (i < size && p[i]) { |
||||
// skip leading zero
|
||||
while (i < size && p[i] == '0') i++; |
||||
// check minimum
|
||||
if (isMinus && size - i >= n - 1 && memcmp(max, &p[i], n - 1) == 0) { |
||||
if (b) *b = true; |
||||
return min; |
||||
} |
||||
T x = 0; |
||||
for (;;) { |
||||
unsigned char c; |
||||
if (i == size || (c = static_cast<unsigned char>(p[i])) == '\0') { |
||||
if (b) *b = true; |
||||
return isMinus ? -x : x; |
||||
} |
||||
unsigned int y = c - '0'; |
||||
if (y > 9 || x > overflow1 || (x == overflow1 && c >= overflow2)) { |
||||
break; |
||||
} |
||||
x = x * 10 + T(y); |
||||
i++; |
||||
} |
||||
} |
||||
} |
||||
if (b) { |
||||
*b = false; |
||||
return 0; |
||||
} else { |
||||
throw cybozu::Exception("atoi::convertToInt") << cybozu::exception::makeString(p, size); |
||||
} |
||||
} |
||||
|
||||
template<typename T> |
||||
T convertToUint(bool *b, const char *p, size_t size, T overflow1, char overflow2) |
||||
{ |
||||
if (size > 0 && *p) { |
||||
size_t i = 0; |
||||
// skip leading zero
|
||||
while (i < size && p[i] == '0') i++; |
||||
T x = 0; |
||||
for (;;) { |
||||
unsigned char c; |
||||
if (i == size || (c = static_cast<unsigned char>(p[i])) == '\0') { |
||||
if (b) *b = true; |
||||
return x; |
||||
} |
||||
unsigned int y = c - '0'; |
||||
if (y > 9 || x > overflow1 || (x == overflow1 && c >= overflow2)) { |
||||
break; |
||||
} |
||||
x = x * 10 + T(y); |
||||
i++; |
||||
} |
||||
} |
||||
if (b) { |
||||
*b = false; |
||||
return 0; |
||||
} else { |
||||
throw cybozu::Exception("atoi::convertToUint") << cybozu::exception::makeString(p, size); |
||||
} |
||||
} |
||||
|
||||
template<typename T> |
||||
T convertHexToInt(bool *b, const char *p, size_t size) |
||||
{ |
||||
if (size > 0 && *p) { |
||||
size_t i = 0; |
||||
T x = 0; |
||||
for (;;) { |
||||
unsigned int c; |
||||
if (i == size || (c = static_cast<unsigned char>(p[i])) == '\0') { |
||||
if (b) *b = true; |
||||
return x; |
||||
} |
||||
if (c - 'A' <= 'F' - 'A') { |
||||
c = (c - 'A') + 10; |
||||
} else if (c - 'a' <= 'f' - 'a') { |
||||
c = (c - 'a') + 10; |
||||
} else if (c - '0' <= '9' - '0') { |
||||
c = c - '0'; |
||||
} else { |
||||
break; |
||||
} |
||||
// avoid overflow
|
||||
if (x > (std::numeric_limits<T>::max)() / 16) break; |
||||
x = x * 16 + T(c); |
||||
i++; |
||||
} |
||||
} |
||||
if (b) { |
||||
*b = false; |
||||
return 0; |
||||
} else { |
||||
throw cybozu::Exception("atoi::convertHexToInt") << cybozu::exception::makeString(p, size); |
||||
} |
||||
} |
||||
|
||||
} // atoi_local
|
||||
|
||||
/**
|
||||
auto detect return value class
|
||||
@note if you set bool pointer p then throw nothing and set *p = false if bad string |
||||
*/ |
||||
class atoi { |
||||
const char *p_; |
||||
size_t size_; |
||||
bool *b_; |
||||
void set(bool *b, const char *p, size_t size) |
||||
{ |
||||
b_ = b; |
||||
p_ = p; |
||||
size_ = size; |
||||
} |
||||
public: |
||||
atoi(const char *p, size_t size = -1) |
||||
{ |
||||
set(0, p, size); |
||||
} |
||||
atoi(bool *b, const char *p, size_t size = -1) |
||||
{ |
||||
set(b, p, size); |
||||
} |
||||
atoi(const std::string& str) |
||||
{ |
||||
set(0, str.c_str(), str.size()); |
||||
} |
||||
atoi(bool *b, const std::string& str) |
||||
{ |
||||
set(b, str.c_str(), str.size()); |
||||
} |
||||
inline operator signed char() const |
||||
{ |
||||
return atoi_local::convertToInt<signed char>(b_, p_, size_, "128", -128, 12, '8'); |
||||
} |
||||
inline operator unsigned char() const |
||||
{ |
||||
return atoi_local::convertToUint<unsigned char>(b_, p_, size_, 25, '6'); |
||||
} |
||||
inline operator short() const |
||||
{ |
||||
return atoi_local::convertToInt<short>(b_, p_, size_, "32768", -32768, 3276, '8'); |
||||
} |
||||
inline operator unsigned short() const |
||||
{ |
||||
return atoi_local::convertToUint<unsigned short>(b_, p_, size_, 6553, '6'); |
||||
} |
||||
inline operator int() const |
||||
{ |
||||
return atoi_local::convertToInt<int>(b_, p_, size_, "2147483648", INT_MIN, 214748364, '8'); |
||||
} |
||||
inline operator unsigned int() const |
||||
{ |
||||
return atoi_local::convertToUint<unsigned int>(b_, p_, size_, 429496729, '6'); |
||||
} |
||||
inline operator long long() const |
||||
{ |
||||
return atoi_local::convertToInt<long long>(b_, p_, size_, "9223372036854775808", LLONG_MIN, 922337203685477580LL, '8'); |
||||
} |
||||
inline operator unsigned long long() const |
||||
{ |
||||
return atoi_local::convertToUint<unsigned long long>(b_, p_, size_, 1844674407370955161ULL, '6'); |
||||
} |
||||
#if defined(__SIZEOF_LONG__) && (__SIZEOF_LONG__ == 8) |
||||
inline operator long() const { return static_cast<long>(static_cast<long long>(*this)); } |
||||
inline operator unsigned long() const { return static_cast<unsigned long>(static_cast<unsigned long long>(*this)); } |
||||
#else |
||||
inline operator long() const { return static_cast<long>(static_cast<int>(*this)); } |
||||
inline operator unsigned long() const { return static_cast<unsigned long>(static_cast<unsigned int>(*this)); } |
||||
#endif |
||||
}; |
||||
|
||||
class hextoi { |
||||
const char *p_; |
||||
size_t size_; |
||||
bool *b_; |
||||
void set(bool *b, const char *p, size_t size) |
||||
{ |
||||
b_ = b; |
||||
p_ = p; |
||||
size_ = size; |
||||
} |
||||
public: |
||||
hextoi(const char *p, size_t size = -1) |
||||
{ |
||||
set(0, p, size); |
||||
} |
||||
hextoi(bool *b, const char *p, size_t size = -1) |
||||
{ |
||||
set(b, p, size); |
||||
} |
||||
hextoi(const std::string& str) |
||||
{ |
||||
set(0, str.c_str(), str.size()); |
||||
} |
||||
hextoi(bool *b, const std::string& str) |
||||
{ |
||||
set(b, str.c_str(), str.size()); |
||||
} |
||||
operator unsigned char() const { return atoi_local::convertHexToInt<unsigned char>(b_, p_, size_); } |
||||
operator unsigned short() const { return atoi_local::convertHexToInt<unsigned short>(b_, p_, size_); } |
||||
operator unsigned int() const { return atoi_local::convertHexToInt<unsigned int>(b_, p_, size_); } |
||||
operator unsigned long() const { return atoi_local::convertHexToInt<unsigned long>(b_, p_, size_); } |
||||
operator unsigned long long() const { return atoi_local::convertHexToInt<unsigned long long>(b_, p_, size_); } |
||||
operator char() const { return atoi_local::convertHexToInt<char>(b_, p_, size_); } |
||||
operator signed char() const { return atoi_local::convertHexToInt<signed char>(b_, p_, size_); } |
||||
operator short() const { return atoi_local::convertHexToInt<short>(b_, p_, size_); } |
||||
operator int() const { return atoi_local::convertHexToInt<int>(b_, p_, size_); } |
||||
operator long() const { return atoi_local::convertHexToInt<long>(b_, p_, size_); } |
||||
operator long long() const { return atoi_local::convertHexToInt<long long>(b_, p_, size_); } |
||||
}; |
||||
|
||||
} // cybozu
|
@ -0,0 +1,212 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief measure exec time of function |
||||
@author MITSUNARI Shigeo |
||||
*/ |
||||
#if defined(_MSC_VER) && (MSC_VER <= 1500) |
||||
#include <cybozu/inttype.hpp> |
||||
#else |
||||
#include <stdint.h> |
||||
#endif |
||||
#include <stdio.h> |
||||
|
||||
#ifdef __EMSCRIPTEN__ |
||||
#define CYBOZU_BENCH_USE_GETTIMEOFDAY |
||||
#endif |
||||
|
||||
#ifdef CYBOZU_BENCH_USE_GETTIMEOFDAY |
||||
#include <sys/time.h> |
||||
#elif !defined(CYBOZU_BENCH_DONT_USE_RDTSC) |
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) |
||||
#define CYBOZU_BENCH_USE_RDTSC |
||||
#define CYBOZU_BENCH_USE_CPU_TIMER |
||||
#endif |
||||
#if defined(__GNUC__) && defined(__ARM_ARCH_7A__) |
||||
// #define CYBOZU_BENCH_USE_MRC
|
||||
// #define CYBOZU_BENCH_USE_CPU_TIMER
|
||||
#endif |
||||
#endif |
||||
|
||||
|
||||
#include <assert.h> |
||||
#include <time.h> |
||||
#ifdef _MSC_VER |
||||
#include <intrin.h> |
||||
#include <sys/timeb.h> |
||||
#else |
||||
#endif |
||||
|
||||
#ifndef CYBOZU_UNUSED |
||||
#ifdef __GNUC__ |
||||
#define CYBOZU_UNUSED __attribute__((unused)) |
||||
#else |
||||
#define CYBOZU_UNUSED |
||||
#endif |
||||
#endif |
||||
|
||||
namespace cybozu { |
||||
|
||||
namespace bench { |
||||
|
||||
static void (*g_putCallback)(double); |
||||
|
||||
static inline void setPutCallback(void (*f)(double)) |
||||
{ |
||||
g_putCallback = f; |
||||
} |
||||
|
||||
} // cybozu::bench
|
||||
|
||||
class CpuClock { |
||||
public: |
||||
static inline uint64_t getCpuClk() |
||||
{ |
||||
#ifdef CYBOZU_BENCH_USE_RDTSC |
||||
#ifdef _MSC_VER |
||||
return __rdtsc(); |
||||
#else |
||||
unsigned int eax, edx; |
||||
__asm__ volatile("rdtsc" : "=a"(eax), "=d"(edx)); |
||||
return ((uint64_t)edx << 32) | eax; |
||||
#endif |
||||
#elif defined(CYBOZU_BENCH_USE_MRC) |
||||
uint32_t clk; |
||||
__asm__ volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(clk)); |
||||
return clk; |
||||
#else |
||||
#ifdef _MSC_VER |
||||
struct _timeb timeb; |
||||
_ftime_s(&timeb); |
||||
return uint64_t(timeb.time) * 1000000000 + timeb.millitm * 1000000; |
||||
#elif defined(CYBOZU_BENCH_USE_GETTIMEOFDAY) |
||||
struct timeval tv; |
||||
int ret CYBOZU_UNUSED = gettimeofday(&tv, 0); |
||||
assert(ret == 0); |
||||
return uint64_t(tv.tv_sec) * 1000000000 + tv.tv_usec * 1000; |
||||
#else |
||||
struct timespec tp; |
||||
int ret CYBOZU_UNUSED = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp); |
||||
assert(ret == 0); |
||||
return uint64_t(tp.tv_sec) * 1000000000 + tp.tv_nsec; |
||||
#endif |
||||
#endif |
||||
} |
||||
CpuClock() |
||||
: clock_(0) |
||||
, count_(0) |
||||
{ |
||||
} |
||||
void begin() |
||||
{ |
||||
clock_ -= getCpuClk(); |
||||
} |
||||
void end() |
||||
{ |
||||
clock_ += getCpuClk(); |
||||
count_++; |
||||
} |
||||
int getCount() const { return count_; } |
||||
uint64_t getClock() const { return clock_; } |
||||
void clear() { count_ = 0; clock_ = 0; } |
||||
void put(const char *msg = 0, int N = 1) const |
||||
{ |
||||
double t = getClock() / double(getCount()) / N; |
||||
if (msg && *msg) printf("%s ", msg); |
||||
if (bench::g_putCallback) { |
||||
bench::g_putCallback(t); |
||||
return; |
||||
} |
||||
#ifdef CYBOZU_BENCH_USE_CPU_TIMER |
||||
if (t > 1e6) { |
||||
printf("%7.3fMclk", t * 1e-6); |
||||
} else if (t > 1e3) { |
||||
printf("%7.3fKclk", t * 1e-3); |
||||
} else { |
||||
printf("%6.2f clk", t); |
||||
} |
||||
#else |
||||
if (t > 1e6) { |
||||
printf("%7.3fmsec", t * 1e-6); |
||||
} else if (t > 1e3) { |
||||
printf("%7.3fusec", t * 1e-3); |
||||
} else { |
||||
printf("%6.2fnsec", t); |
||||
} |
||||
#endif |
||||
if (msg && *msg) printf("\n"); |
||||
} |
||||
// adhoc constatns for CYBOZU_BENCH
|
||||
#ifdef CYBOZU_BENCH_USE_CPU_TIMER |
||||
static const int loopN1 = 1000; |
||||
static const int loopN2 = 100; |
||||
static const uint64_t maxClk = (uint64_t)1e8; |
||||
#else |
||||
static const int loopN1 = 100; |
||||
static const int loopN2 = 100; |
||||
static const uint64_t maxClk = (uint64_t)1e8; |
||||
#endif |
||||
private: |
||||
uint64_t clock_; |
||||
int count_; |
||||
}; |
||||
|
||||
namespace bench { |
||||
|
||||
static CpuClock g_clk; |
||||
static int CYBOZU_UNUSED g_loopNum; |
||||
|
||||
} // cybozu::bench
|
||||
/*
|
||||
loop counter is automatically determined |
||||
CYBOZU_BENCH(<msg>, <func>, <param1>, <param2>, ...); |
||||
if msg == "" then only set g_clk, g_loopNum |
||||
*/ |
||||
#define CYBOZU_BENCH(msg, func, ...) \ |
||||
{ \
|
||||
const uint64_t _cybozu_maxClk = cybozu::CpuClock::maxClk; \
|
||||
cybozu::CpuClock _cybozu_clk; \
|
||||
for (int _cybozu_i = 0; _cybozu_i < cybozu::CpuClock::loopN2; _cybozu_i++) { \
|
||||
_cybozu_clk.begin(); \
|
||||
for (int _cybozu_j = 0; _cybozu_j < cybozu::CpuClock::loopN1; _cybozu_j++) { func(__VA_ARGS__); } \
|
||||
_cybozu_clk.end(); \
|
||||
if (_cybozu_clk.getClock() > _cybozu_maxClk) break; \
|
||||
} \
|
||||
if (msg && *msg) _cybozu_clk.put(msg, cybozu::CpuClock::loopN1); \
|
||||
cybozu::bench::g_clk = _cybozu_clk; cybozu::bench::g_loopNum = cybozu::CpuClock::loopN1; \
|
||||
} |
||||
|
||||
/*
|
||||
double clk; |
||||
CYBOZU_BENCH_T(clk, <func>, <param1>, <param2>, ...); |
||||
clk is set by CYBOZU_BENCH_T |
||||
*/ |
||||
#define CYBOZU_BENCH_T(clk, func, ...) \ |
||||
{ \
|
||||
const uint64_t _cybozu_maxClk = cybozu::CpuClock::maxClk; \
|
||||
cybozu::CpuClock _cybozu_clk; \
|
||||
for (int _cybozu_i = 0; _cybozu_i < cybozu::CpuClock::loopN2; _cybozu_i++) { \
|
||||
_cybozu_clk.begin(); \
|
||||
for (int _cybozu_j = 0; _cybozu_j < cybozu::CpuClock::loopN1; _cybozu_j++) { func(__VA_ARGS__); } \
|
||||
_cybozu_clk.end(); \
|
||||
if (_cybozu_clk.getClock() > _cybozu_maxClk) break; \
|
||||
} \
|
||||
clk = _cybozu_clk.getClock() / (double)_cybozu_clk.getCount() / cybozu::CpuClock::loopN1; \
|
||||
} |
||||
|
||||
/*
|
||||
loop counter N is given |
||||
CYBOZU_BENCH_C(<msg>, <counter>, <func>, <param1>, <param2>, ...); |
||||
if msg == "" then only set g_clk, g_loopNum |
||||
*/ |
||||
#define CYBOZU_BENCH_C(msg, _N, func, ...) \ |
||||
{ \
|
||||
cybozu::CpuClock _cybozu_clk; \
|
||||
_cybozu_clk.begin(); \
|
||||
for (int _cybozu_j = 0; _cybozu_j < _N; _cybozu_j++) { func(__VA_ARGS__); } \
|
||||
_cybozu_clk.end(); \
|
||||
if (msg && *msg) _cybozu_clk.put(msg, _N); \
|
||||
cybozu::bench::g_clk = _cybozu_clk; cybozu::bench::g_loopNum = _N; \
|
||||
} |
||||
|
||||
} // cybozu
|
@ -0,0 +1,139 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief bit operation |
||||
*/ |
||||
#include <assert.h> |
||||
#include <cybozu/inttype.hpp> |
||||
|
||||
#if (CYBOZU_HOST == CYBOZU_HOST_INTEL) |
||||
#if defined(_WIN32) |
||||
#include <intrin.h> |
||||
#elif defined(__linux__) || defined(__CYGWIN__) || defined(__clang__) |
||||
#include <x86intrin.h> |
||||
#elif defined(__GNUC__) |
||||
#include <emmintrin.h> |
||||
#endif |
||||
#endif |
||||
|
||||
namespace cybozu { |
||||
|
||||
namespace bit_op_local { |
||||
|
||||
template<bool equalTo8> |
||||
struct Tag {}; |
||||
|
||||
// sizeof(T) < 8
|
||||
template<> |
||||
struct Tag<false> { |
||||
template<class T> |
||||
static inline int bsf(T x) |
||||
{ |
||||
#if defined(_MSC_VER) |
||||
unsigned long out; |
||||
_BitScanForward(&out, x); |
||||
#pragma warning(suppress: 6102) |
||||
return out; |
||||
#else |
||||
return __builtin_ctz(x); |
||||
#endif |
||||
} |
||||
template<class T> |
||||
static inline int bsr(T x) |
||||
{ |
||||
#if defined(_MSC_VER) |
||||
unsigned long out; |
||||
_BitScanReverse(&out, x); |
||||
#pragma warning(suppress: 6102) |
||||
return out; |
||||
#else |
||||
return __builtin_clz(x) ^ 0x1f; |
||||
#endif |
||||
} |
||||
}; |
||||
|
||||
// sizeof(T) == 8
|
||||
template<> |
||||
struct Tag<true> { |
||||
template<class T> |
||||
static inline int bsf(T x) |
||||
{ |
||||
#if defined(_MSC_VER) && defined(_WIN64) |
||||
unsigned long out; |
||||
_BitScanForward64(&out, x); |
||||
#pragma warning(suppress: 6102) |
||||
return out; |
||||
#elif defined(__x86_64__) |
||||
return __builtin_ctzll(x); |
||||
#else |
||||
const uint32_t L = uint32_t(x); |
||||
if (L) return Tag<false>::bsf(L); |
||||
const uint32_t H = uint32_t(x >> 32); |
||||
return Tag<false>::bsf(H) + 32; |
||||
#endif |
||||
} |
||||
template<class T> |
||||
static inline int bsr(T x) |
||||
{ |
||||
#if defined(_MSC_VER) && defined(_WIN64) |
||||
unsigned long out; |
||||
_BitScanReverse64(&out, x); |
||||
#pragma warning(suppress: 6102) |
||||
return out; |
||||
#elif defined(__x86_64__) |
||||
return __builtin_clzll(x) ^ 0x3f; |
||||
#else |
||||
const uint32_t H = uint32_t(x >> 32); |
||||
if (H) return Tag<false>::bsr(H) + 32; |
||||
const uint32_t L = uint32_t(x); |
||||
return Tag<false>::bsr(L); |
||||
#endif |
||||
} |
||||
}; |
||||
|
||||
} // bit_op_local
|
||||
|
||||
template<class T> |
||||
int bsf(T x) |
||||
{ |
||||
return bit_op_local::Tag<sizeof(T) == 8>::bsf(x); |
||||
} |
||||
template<class T> |
||||
int bsr(T x) |
||||
{ |
||||
return bit_op_local::Tag<sizeof(T) == 8>::bsr(x); |
||||
} |
||||
|
||||
template<class T> |
||||
uint64_t makeBitMask64(T x) |
||||
{ |
||||
assert(x < 64); |
||||
return (uint64_t(1) << x) - 1; |
||||
} |
||||
|
||||
template<class T> |
||||
uint32_t popcnt(T x); |
||||
|
||||
template<> |
||||
inline uint32_t popcnt<uint32_t>(uint32_t x) |
||||
{ |
||||
#if defined(_MSC_VER) |
||||
return static_cast<uint32_t>(_mm_popcnt_u32(x)); |
||||
#else |
||||
return static_cast<uint32_t>(__builtin_popcount(x)); |
||||
#endif |
||||
} |
||||
|
||||
template<> |
||||
inline uint32_t popcnt<uint64_t>(uint64_t x) |
||||
{ |
||||
#if defined(__x86_64__) |
||||
return static_cast<uint32_t>(__builtin_popcountll(x)); |
||||
#elif defined(_WIN64) |
||||
return static_cast<uint32_t>(_mm_popcnt_u64(x)); |
||||
#else |
||||
return popcnt<uint32_t>(static_cast<uint32_t>(x)) + popcnt<uint32_t>(static_cast<uint32_t>(x >> 32)); |
||||
#endif |
||||
} |
||||
|
||||
} // cybozu
|
@ -0,0 +1,60 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief critical section |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
@author MITSUNARI Shigeo |
||||
*/ |
||||
#include <cybozu/mutex.hpp> |
||||
|
||||
namespace cybozu { |
||||
|
||||
class ConditionVariableCs; |
||||
|
||||
namespace thread { |
||||
|
||||
#ifdef _WIN32 |
||||
typedef CRITICAL_SECTION CsHandle; |
||||
inline void CsInit(CsHandle& cs) { InitializeCriticalSection(&cs); } |
||||
inline void CsLock(CsHandle& cs) { EnterCriticalSection(&cs); } |
||||
inline void CsUnlock(CsHandle& cs) { LeaveCriticalSection(&cs); } |
||||
inline void CsTerm(CsHandle& cs) { DeleteCriticalSection(&cs); } |
||||
#else |
||||
typedef pthread_mutex_t CsHandle; |
||||
inline void CsInit(CsHandle& cs) { pthread_mutex_init(&cs, NULL); } |
||||
inline void CsLock(CsHandle& cs) { pthread_mutex_lock(&cs); } |
||||
inline void CsUnlock(CsHandle& cs) { pthread_mutex_unlock(&cs); } |
||||
inline void CsTerm(CsHandle& cs) { pthread_mutex_destroy(&cs); } |
||||
#endif |
||||
|
||||
} // cybozu::thread
|
||||
|
||||
class CriticalSection { |
||||
friend class cybozu::ConditionVariableCs; |
||||
public: |
||||
CriticalSection() |
||||
{ |
||||
thread::CsInit(hdl_); |
||||
} |
||||
~CriticalSection() |
||||
{ |
||||
thread::CsTerm(hdl_); |
||||
} |
||||
inline void lock() |
||||
{ |
||||
thread::CsLock(hdl_); |
||||
} |
||||
inline void unlock() |
||||
{ |
||||
thread::CsUnlock(hdl_); |
||||
} |
||||
private: |
||||
CriticalSection(const CriticalSection&); |
||||
CriticalSection& operator=(const CriticalSection&); |
||||
thread::CsHandle hdl_; |
||||
}; |
||||
|
||||
typedef cybozu::thread::AutoLockT<cybozu::CriticalSection> AutoLockCs; //!< auto lock critical section
|
||||
|
||||
} // cybozu
|
@ -0,0 +1,321 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief wrap openssl |
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
|
||||
#include <cybozu/exception.hpp> |
||||
#ifdef __APPLE__ |
||||
#pragma GCC diagnostic push |
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
||||
#endif |
||||
#if 0 //#ifdef __APPLE__
|
||||
#define COMMON_DIGEST_FOR_OPENSSL |
||||
#include <CommonCrypto/CommonDigest.h> |
||||
#include <CommonCrypto/CommonHMAC.h> |
||||
#define SHA1 CC_SHA1 |
||||
#define SHA224 CC_SHA224 |
||||
#define SHA256 CC_SHA256 |
||||
#define SHA384 CC_SHA384 |
||||
#define SHA512 CC_SHA512 |
||||
#else |
||||
#include <openssl/hmac.h> |
||||
#include <openssl/evp.h> |
||||
#include <openssl/sha.h> |
||||
#endif |
||||
#ifdef _MSC_VER |
||||
#include <cybozu/link_libeay32.hpp> |
||||
#endif |
||||
|
||||
namespace cybozu { |
||||
|
||||
namespace crypto { |
||||
|
||||
class Hash { |
||||
public: |
||||
enum Name { |
||||
N_SHA1, |
||||
N_SHA224, |
||||
N_SHA256, |
||||
N_SHA384, |
||||
N_SHA512 |
||||
}; |
||||
private: |
||||
Name name_; |
||||
size_t hashSize_; |
||||
union { |
||||
SHA_CTX sha1; |
||||
SHA256_CTX sha256; |
||||
SHA512_CTX sha512; |
||||
} ctx_; |
||||
public: |
||||
static inline size_t getSize(Name name) |
||||
{ |
||||
switch (name) { |
||||
case N_SHA1: return SHA_DIGEST_LENGTH; |
||||
case N_SHA224: return SHA224_DIGEST_LENGTH; |
||||
case N_SHA256: return SHA256_DIGEST_LENGTH; |
||||
case N_SHA384: return SHA384_DIGEST_LENGTH; |
||||
case N_SHA512: return SHA512_DIGEST_LENGTH; |
||||
default: |
||||
throw cybozu::Exception("crypto:Hash:getSize") << name; |
||||
} |
||||
} |
||||
static inline const char *getName(Name name) |
||||
{ |
||||
switch (name) { |
||||
case N_SHA1: return "sha1"; |
||||
case N_SHA224: return "sha224"; |
||||
case N_SHA256: return "sha256"; |
||||
case N_SHA384: return "sha384"; |
||||
case N_SHA512: return "sha512"; |
||||
default: |
||||
throw cybozu::Exception("crypto:Hash:getName") << name; |
||||
} |
||||
} |
||||
static inline Name getName(const std::string& nameStr) |
||||
{ |
||||
static const struct { |
||||
const char *nameStr; |
||||
Name name; |
||||
} tbl[] = { |
||||
{ "sha1", N_SHA1 }, |
||||
{ "sha224", N_SHA224 }, |
||||
{ "sha256", N_SHA256 }, |
||||
{ "sha384", N_SHA384 }, |
||||
{ "sha512", N_SHA512 }, |
||||
}; |
||||
for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) { |
||||
if (nameStr == tbl[i].nameStr) return tbl[i].name; |
||||
} |
||||
throw cybozu::Exception("crypto:Hash:getName") << nameStr; |
||||
} |
||||
explicit Hash(Name name = N_SHA1) |
||||
: name_(name) |
||||
, hashSize_(getSize(name)) |
||||
{ |
||||
reset(); |
||||
} |
||||
void update(const void *buf, size_t bufSize) |
||||
{ |
||||
switch (name_) { |
||||
case N_SHA1: SHA1_Update(&ctx_.sha1, buf, bufSize); break; |
||||
case N_SHA224: SHA224_Update(&ctx_.sha256, buf, bufSize); break; |
||||
case N_SHA256: SHA256_Update(&ctx_.sha256, buf, bufSize); break; |
||||
case N_SHA384: SHA384_Update(&ctx_.sha512, buf, bufSize); break; |
||||
case N_SHA512: SHA512_Update(&ctx_.sha512, buf, bufSize); break; |
||||
} |
||||
} |
||||
void update(const std::string& buf) |
||||
{ |
||||
update(buf.c_str(), buf.size()); |
||||
} |
||||
void reset() |
||||
{ |
||||
switch (name_) { |
||||
case N_SHA1: SHA1_Init(&ctx_.sha1); break; |
||||
case N_SHA224: SHA224_Init(&ctx_.sha256); break; |
||||
case N_SHA256: SHA256_Init(&ctx_.sha256); break; |
||||
case N_SHA384: SHA384_Init(&ctx_.sha512); break; |
||||
case N_SHA512: SHA512_Init(&ctx_.sha512); break; |
||||
default: |
||||
throw cybozu::Exception("crypto:Hash:rset") << name_; |
||||
} |
||||
} |
||||
/*
|
||||
md must have hashSize byte |
||||
@note clear inner buffer after calling digest |
||||
*/ |
||||
void digest(void *out, const void *buf, size_t bufSize) |
||||
{ |
||||
update(buf, bufSize); |
||||
unsigned char *md = reinterpret_cast<unsigned char*>(out); |
||||
switch (name_) { |
||||
case N_SHA1: SHA1_Final(md, &ctx_.sha1); break; |
||||
case N_SHA224: SHA224_Final(md, &ctx_.sha256); break; |
||||
case N_SHA256: SHA256_Final(md, &ctx_.sha256); break; |
||||
case N_SHA384: SHA384_Final(md, &ctx_.sha512); break; |
||||
case N_SHA512: SHA512_Final(md, &ctx_.sha512); break; |
||||
default: |
||||
throw cybozu::Exception("crypto:Hash:digest") << name_; |
||||
} |
||||
reset(); |
||||
} |
||||
std::string digest(const void *buf, size_t bufSize) |
||||
{ |
||||
std::string ret; |
||||
ret.resize(hashSize_); |
||||
digest(&ret[0], buf, bufSize); |
||||
return ret; |
||||
} |
||||
std::string digest(const std::string& buf = "") |
||||
{ |
||||
return digest(buf.c_str(), buf.size()); |
||||
} |
||||
/*
|
||||
out must have necessary size |
||||
@note return written size |
||||
*/ |
||||
static inline size_t digest(void *out, Name name, const void *buf, size_t bufSize) |
||||
{ |
||||
unsigned char *md = (unsigned char*)out; |
||||
const unsigned char *src = cybozu::cast<const unsigned char *>(buf); |
||||
switch (name) { |
||||
case N_SHA1: SHA1(src, bufSize, md); return 160 / 8; |
||||
case N_SHA224: SHA224(src, bufSize, md); return 224 / 8; |
||||
case N_SHA256: SHA256(src, bufSize, md); return 256 / 8; |
||||
case N_SHA384: SHA384(src, bufSize, md); return 384 / 8; |
||||
case N_SHA512: SHA512(src, bufSize, md); return 512 / 8; |
||||
default: |
||||
return 0; |
||||
} |
||||
} |
||||
static inline std::string digest(Name name, const void *buf, size_t bufSize) |
||||
{ |
||||
char md[128]; |
||||
size_t size = digest(md, name, buf, bufSize); |
||||
if (size == 0) throw cybozu::Exception("crypt:Hash:digest") << name; |
||||
return std::string(md, size); |
||||
} |
||||
static inline std::string digest(Name name, const std::string& buf) |
||||
{ |
||||
return digest(name, buf.c_str(), buf.size()); |
||||
} |
||||
}; |
||||
|
||||
class Hmac { |
||||
const EVP_MD *evp_; |
||||
public: |
||||
explicit Hmac(Hash::Name name = Hash::N_SHA1) |
||||
{ |
||||
switch (name) { |
||||
case Hash::N_SHA1: evp_ = EVP_sha1(); break; |
||||
case Hash::N_SHA224: evp_ = EVP_sha224(); break; |
||||
case Hash::N_SHA256: evp_ = EVP_sha256(); break; |
||||
case Hash::N_SHA384: evp_ = EVP_sha384(); break; |
||||
case Hash::N_SHA512: evp_ = EVP_sha512(); break; |
||||
default: |
||||
throw cybozu::Exception("crypto:Hmac:") << name; |
||||
} |
||||
} |
||||
std::string eval(const std::string& key, const std::string& data) |
||||
{ |
||||
std::string out(EVP_MD_size(evp_) + 1, 0); |
||||
unsigned int outLen = 0; |
||||
if (HMAC(evp_, key.c_str(), static_cast<int>(key.size()), |
||||
cybozu::cast<const uint8_t *>(data.c_str()), data.size(), cybozu::cast<uint8_t *>(&out[0]), &outLen)) { |
||||
out.resize(outLen); |
||||
return out; |
||||
} |
||||
throw cybozu::Exception("crypto::Hamc::eval"); |
||||
} |
||||
}; |
||||
|
||||
class Cipher { |
||||
const EVP_CIPHER *cipher_; |
||||
EVP_CIPHER_CTX *ctx_; |
||||
public: |
||||
enum Name { |
||||
N_AES128_CBC, |
||||
N_AES192_CBC, |
||||
N_AES256_CBC, |
||||
N_AES128_ECB, // be carefull to use
|
||||
N_AES192_ECB, // be carefull to use
|
||||
N_AES256_ECB, // be carefull to use
|
||||
}; |
||||
static inline size_t getSize(Name name) |
||||
{ |
||||
switch (name) { |
||||
case N_AES128_CBC: return 128; |
||||
case N_AES192_CBC: return 192; |
||||
case N_AES256_CBC: return 256; |
||||
case N_AES128_ECB: return 128; |
||||
case N_AES192_ECB: return 192; |
||||
case N_AES256_ECB: return 256; |
||||
default: |
||||
throw cybozu::Exception("crypto:Cipher:getSize") << name; |
||||
} |
||||
} |
||||
enum Mode { |
||||
Decoding, |
||||
Encoding |
||||
}; |
||||
explicit Cipher(Name name = N_AES128_CBC) |
||||
: cipher_(0) |
||||
, ctx_(0) |
||||
{ |
||||
ctx_ = EVP_CIPHER_CTX_new(); |
||||
if (ctx_ == 0) throw cybozu::Exception("crypto:Cipher:EVP_CIPHER_CTX_new"); |
||||
switch (name) { |
||||
case N_AES128_CBC: cipher_ = EVP_aes_128_cbc(); break; |
||||
case N_AES192_CBC: cipher_ = EVP_aes_192_cbc(); break; |
||||
case N_AES256_CBC: cipher_ = EVP_aes_256_cbc(); break; |
||||
case N_AES128_ECB: cipher_ = EVP_aes_128_ecb(); break; |
||||
case N_AES192_ECB: cipher_ = EVP_aes_192_ecb(); break; |
||||
case N_AES256_ECB: cipher_ = EVP_aes_256_ecb(); break; |
||||
default: |
||||
throw cybozu::Exception("crypto:Cipher:Cipher:name") << (int)name; |
||||
} |
||||
} |
||||
~Cipher() |
||||
{ |
||||
if (ctx_) EVP_CIPHER_CTX_free(ctx_); |
||||
} |
||||
/*
|
||||
@note don't use padding = true |
||||
*/ |
||||
void setup(Mode mode, const std::string& key, const std::string& iv, bool padding = false) |
||||
{ |
||||
const int keyLen = static_cast<int>(key.size()); |
||||
const int expectedKeyLen = EVP_CIPHER_key_length(cipher_); |
||||
if (keyLen != expectedKeyLen) { |
||||
throw cybozu::Exception("crypto:Cipher:setup:keyLen") << keyLen << expectedKeyLen; |
||||
} |
||||
|
||||
int ret = EVP_CipherInit_ex(ctx_, cipher_, NULL, cybozu::cast<const uint8_t*>(key.c_str()), cybozu::cast<const uint8_t*>(iv.c_str()), mode == Encoding ? 1 : 0); |
||||
if (ret != 1) { |
||||
throw cybozu::Exception("crypto:Cipher:setup:EVP_CipherInit_ex") << ret; |
||||
} |
||||
ret = EVP_CIPHER_CTX_set_padding(ctx_, padding ? 1 : 0); |
||||
if (ret != 1) { |
||||
throw cybozu::Exception("crypto:Cipher:setup:EVP_CIPHER_CTX_set_padding") << ret; |
||||
} |
||||
/*
|
||||
const int ivLen = static_cast<int>(iv.size()); |
||||
const int expectedIvLen = EVP_CIPHER_CTX_iv_length(&ctx_); |
||||
if (ivLen != expectedIvLen) { |
||||
throw cybozu::Exception("crypto:Cipher:setup:ivLen") << ivLen << expectedIvLen; |
||||
} |
||||
*/ |
||||
} |
||||
/*
|
||||
the size of outBuf must be larger than inBufSize + blockSize |
||||
@retval positive or 0 : writeSize(+blockSize) |
||||
@retval -1 : error |
||||
*/ |
||||
int update(char *outBuf, const char *inBuf, int inBufSize) |
||||
{ |
||||
int outLen = 0; |
||||
int ret = EVP_CipherUpdate(ctx_, cybozu::cast<uint8_t*>(outBuf), &outLen, cybozu::cast<const uint8_t*>(inBuf), inBufSize); |
||||
if (ret != 1) return -1; |
||||
return outLen; |
||||
} |
||||
/*
|
||||
return -1 if padding |
||||
@note don't use |
||||
*/ |
||||
int finalize(char *outBuf) |
||||
{ |
||||
int outLen = 0; |
||||
int ret = EVP_CipherFinal_ex(ctx_, cybozu::cast<uint8_t*>(outBuf), &outLen); |
||||
if (ret != 1) return -1; |
||||
return outLen; |
||||
} |
||||
}; |
||||
|
||||
} } // cybozu::crypto
|
||||
|
||||
#ifdef __APPLE__ |
||||
#pragma GCC diagnostic pop |
||||
#endif |
@ -0,0 +1,224 @@ |
||||
#pragma once |
||||
|
||||
/**
|
||||
@file |
||||
@brief deal with big and little endian |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#include <cybozu/inttype.hpp> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
|
||||
namespace cybozu { |
||||
|
||||
#ifdef _MSC_VER |
||||
inline uint16_t byteSwap(uint16_t x) { return _byteswap_ushort(x); } |
||||
inline uint32_t byteSwap(uint32_t x) { return _byteswap_ulong(x); } |
||||
inline uint64_t byteSwap(uint64_t x) { return _byteswap_uint64(x); } |
||||
#else |
||||
#if (((__GNUC__) << 16) + (__GNUC_MINOR__)) >= ((4 << 16) + 8) |
||||
inline uint16_t byteSwap(uint16_t x) { return __builtin_bswap16(x); } |
||||
#else |
||||
inline uint16_t byteSwap(uint16_t x) { return (x >> 8) | (x << 8); } |
||||
#endif |
||||
inline uint32_t byteSwap(uint32_t x) { return __builtin_bswap32(x); } |
||||
inline uint64_t byteSwap(uint64_t x) { return __builtin_bswap64(x); } |
||||
#endif |
||||
|
||||
/**
|
||||
get 16bit integer as little endian |
||||
@param src [in] pointer |
||||
*/ |
||||
inline uint16_t Get16bitAsLE(const void *src) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
uint16_t x; |
||||
memcpy(&x, src, sizeof(x)); |
||||
return x; |
||||
#else |
||||
const uint8_t *p = static_cast<const uint8_t *>(src); |
||||
return p[0] | (p[1] << 8); |
||||
#endif |
||||
} |
||||
|
||||
/**
|
||||
get 32bit integer as little endian |
||||
@param src [in] pointer |
||||
*/ |
||||
inline uint32_t Get32bitAsLE(const void *src) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
uint32_t x; |
||||
memcpy(&x, src, sizeof(x)); |
||||
return x; |
||||
#else |
||||
const uint8_t *p = static_cast<const uint8_t *>(src); |
||||
return Get16bitAsLE(p) | (static_cast<uint32_t>(Get16bitAsLE(p + 2)) << 16); |
||||
#endif |
||||
} |
||||
|
||||
/**
|
||||
get 64bit integer as little endian |
||||
@param src [in] pointer |
||||
*/ |
||||
inline uint64_t Get64bitAsLE(const void *src) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
uint64_t x; |
||||
memcpy(&x, src, sizeof(x)); |
||||
return x; |
||||
#else |
||||
const uint8_t *p = static_cast<const uint8_t *>(src); |
||||
return Get32bitAsLE(p) | (static_cast<uint64_t>(Get32bitAsLE(p + 4)) << 32); |
||||
#endif |
||||
} |
||||
|
||||
/**
|
||||
get 16bit integer as bit endian |
||||
@param src [in] pointer |
||||
*/ |
||||
inline uint16_t Get16bitAsBE(const void *src) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
uint16_t x; |
||||
memcpy(&x, src, sizeof(x)); |
||||
return byteSwap(x); |
||||
#else |
||||
const uint8_t *p = static_cast<const uint8_t *>(src); |
||||
return p[1] | (p[0] << 8); |
||||
#endif |
||||
} |
||||
|
||||
/**
|
||||
get 32bit integer as bit endian |
||||
@param src [in] pointer |
||||
*/ |
||||
inline uint32_t Get32bitAsBE(const void *src) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
uint32_t x; |
||||
memcpy(&x, src, sizeof(x)); |
||||
return byteSwap(x); |
||||
#else |
||||
const uint8_t *p = static_cast<const uint8_t *>(src); |
||||
return Get16bitAsBE(p + 2) | (static_cast<uint32_t>(Get16bitAsBE(p)) << 16); |
||||
#endif |
||||
} |
||||
|
||||
/**
|
||||
get 64bit integer as big endian |
||||
@param src [in] pointer |
||||
*/ |
||||
inline uint64_t Get64bitAsBE(const void *src) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
uint64_t x; |
||||
memcpy(&x, src, sizeof(x)); |
||||
return byteSwap(x); |
||||
#else |
||||
const uint8_t *p = static_cast<const uint8_t *>(src); |
||||
return Get32bitAsBE(p + 4) | (static_cast<uint64_t>(Get32bitAsBE(p)) << 32); |
||||
#endif |
||||
} |
||||
|
||||
/**
|
||||
set 16bit integer as little endian |
||||
@param src [out] pointer |
||||
@param x [in] integer |
||||
*/ |
||||
inline void Set16bitAsLE(void *src, uint16_t x) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
memcpy(src, &x, sizeof(x)); |
||||
#else |
||||
uint8_t *p = static_cast<uint8_t *>(src); |
||||
p[0] = static_cast<uint8_t>(x); |
||||
p[1] = static_cast<uint8_t>(x >> 8); |
||||
#endif |
||||
} |
||||
/**
|
||||
set 32bit integer as little endian |
||||
@param src [out] pointer |
||||
@param x [in] integer |
||||
*/ |
||||
inline void Set32bitAsLE(void *src, uint32_t x) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
memcpy(src, &x, sizeof(x)); |
||||
#else |
||||
uint8_t *p = static_cast<uint8_t *>(src); |
||||
p[0] = static_cast<uint8_t>(x); |
||||
p[1] = static_cast<uint8_t>(x >> 8); |
||||
p[2] = static_cast<uint8_t>(x >> 16); |
||||
p[3] = static_cast<uint8_t>(x >> 24); |
||||
#endif |
||||
} |
||||
/**
|
||||
set 64bit integer as little endian |
||||
@param src [out] pointer |
||||
@param x [in] integer |
||||
*/ |
||||
inline void Set64bitAsLE(void *src, uint64_t x) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
memcpy(src, &x, sizeof(x)); |
||||
#else |
||||
uint8_t *p = static_cast<uint8_t *>(src); |
||||
Set32bitAsLE(p, static_cast<uint32_t>(x)); |
||||
Set32bitAsLE(p + 4, static_cast<uint32_t>(x >> 32)); |
||||
#endif |
||||
} |
||||
/**
|
||||
set 16bit integer as big endian |
||||
@param src [out] pointer |
||||
@param x [in] integer |
||||
*/ |
||||
inline void Set16bitAsBE(void *src, uint16_t x) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
x = byteSwap(x); |
||||
memcpy(src, &x, sizeof(x)); |
||||
#else |
||||
uint8_t *p = static_cast<uint8_t *>(src); |
||||
p[0] = static_cast<uint8_t>(x >> 8); |
||||
p[1] = static_cast<uint8_t>(x); |
||||
#endif |
||||
} |
||||
/**
|
||||
set 32bit integer as big endian |
||||
@param src [out] pointer |
||||
@param x [in] integer |
||||
*/ |
||||
inline void Set32bitAsBE(void *src, uint32_t x) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
x = byteSwap(x); |
||||
memcpy(src, &x, sizeof(x)); |
||||
#else |
||||
uint8_t *p = static_cast<uint8_t *>(src); |
||||
p[0] = static_cast<uint8_t>(x >> 24); |
||||
p[1] = static_cast<uint8_t>(x >> 16); |
||||
p[2] = static_cast<uint8_t>(x >> 8); |
||||
p[3] = static_cast<uint8_t>(x); |
||||
#endif |
||||
} |
||||
/**
|
||||
set 64bit integer as big endian |
||||
@param src [out] pointer |
||||
@param x [in] integer |
||||
*/ |
||||
inline void Set64bitAsBE(void *src, uint64_t x) |
||||
{ |
||||
#if CYBOZU_ENDIAN == CYBOZU_ENDIAN_LITTLE |
||||
x = byteSwap(x); |
||||
memcpy(src, &x, sizeof(x)); |
||||
#else |
||||
uint8_t *p = static_cast<uint8_t *>(src); |
||||
Set32bitAsBE(p, static_cast<uint32_t>(x >> 32)); |
||||
Set32bitAsBE(p + 4, static_cast<uint32_t>(x)); |
||||
#endif |
||||
} |
||||
|
||||
} // cybozu
|
@ -0,0 +1,252 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief definition of abstruct exception class
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#ifdef CYBOZU_MINIMUM_EXCEPTION |
||||
|
||||
#include <cybozu/inttype.hpp> |
||||
|
||||
namespace cybozu { |
||||
|
||||
namespace exception { |
||||
inline const char *makeString(const char *, size_t) |
||||
{ |
||||
return ""; |
||||
} |
||||
|
||||
} // cybozu::exception
|
||||
|
||||
class Exception { |
||||
public: |
||||
explicit Exception(const char* = 0, bool = true) |
||||
{ |
||||
} |
||||
~Exception() CYBOZU_NOEXCEPT {} |
||||
const char *what() const CYBOZU_NOEXCEPT { return "cybozu:Exception"; } |
||||
template<class T> |
||||
Exception& operator<<(const T&) |
||||
{ |
||||
return *this; |
||||
} |
||||
}; |
||||
|
||||
} // cybozu
|
||||
|
||||
#else |
||||
|
||||
#include <string> |
||||
#include <algorithm> |
||||
#include <sstream> |
||||
#include <errno.h> |
||||
#include <stdio.h> |
||||
#ifdef _WIN32 |
||||
#include <winsock2.h> |
||||
#include <windows.h> |
||||
#else |
||||
#include <string.h> // for strerror_r |
||||
#endif |
||||
#include <cybozu/inttype.hpp> |
||||
#ifdef CYBOZU_EXCEPTION_WITH_STACKTRACE |
||||
#include <cybozu/stacktrace.hpp> |
||||
#endif |
||||
|
||||
namespace cybozu { |
||||
|
||||
const bool DontThrow = true; |
||||
|
||||
namespace exception { |
||||
|
||||
/* get max 16 characters to avoid buffer overrun */ |
||||
inline std::string makeString(const char *str, size_t size) |
||||
{ |
||||
return std::string(str, std::min<size_t>(size, 16)); |
||||
} |
||||
|
||||
#ifdef _WIN32 |
||||
inline std::string wstr2str(const std::wstring& wstr) |
||||
{ |
||||
std::string str; |
||||
for (size_t i = 0; i < wstr.size(); i++) { |
||||
uint16_t c = wstr[i]; |
||||
if (c < 0x80) { |
||||
str += char(c); |
||||
} else { |
||||
char buf[16]; |
||||
CYBOZU_SNPRINTF(buf, sizeof(buf), "\\u%04x", c); |
||||
str += buf; |
||||
} |
||||
} |
||||
return str; |
||||
} |
||||
#endif |
||||
|
||||
} // cybozu::exception
|
||||
|
||||
/**
|
||||
convert errno to string |
||||
@param err [in] errno |
||||
@note for both windows and linux |
||||
*/ |
||||
inline std::string ConvertErrorNoToString(int err) |
||||
{ |
||||
char errBuf[256]; |
||||
std::string ret; |
||||
#ifdef _WIN32 |
||||
if (strerror_s(errBuf, sizeof(errBuf), err) == 0) { |
||||
ret = errBuf; |
||||
} else { |
||||
ret = "err"; |
||||
} |
||||
#elif defined(_GNU_SOURCE) |
||||
ret = ::strerror_r(err, errBuf, sizeof(errBuf)); |
||||
#else |
||||
if (strerror_r(err, errBuf, sizeof(errBuf)) == 0) { |
||||
ret = errBuf; |
||||
} else { |
||||
ret = "err"; |
||||
} |
||||
#endif |
||||
char buf2[64]; |
||||
CYBOZU_SNPRINTF(buf2, sizeof(buf2), "(%d)", err); |
||||
ret += buf2; |
||||
return ret; |
||||
} |
||||
|
||||
class Exception : public std::exception { |
||||
mutable std::string str_; |
||||
#ifdef CYBOZU_EXCEPTION_WITH_STACKTRACE |
||||
mutable std::string stackTrace_; |
||||
#endif |
||||
public: |
||||
explicit Exception(const std::string& name = "", bool enableStackTrace = true) |
||||
: str_(name) |
||||
{ |
||||
#ifdef CYBOZU_EXCEPTION_WITH_STACKTRACE |
||||
if (enableStackTrace) stackTrace_ = cybozu::StackTrace().toString(); |
||||
#else |
||||
cybozu::disable_warning_unused_variable(enableStackTrace); |
||||
#endif |
||||
} |
||||
~Exception() CYBOZU_NOEXCEPT {} |
||||
const char *what() const CYBOZU_NOEXCEPT { return toString().c_str(); } |
||||
const std::string& toString() const CYBOZU_NOEXCEPT |
||||
{ |
||||
#ifdef CYBOZU_EXCEPTION_WITH_STACKTRACE |
||||
try { |
||||
if (!stackTrace_.empty()) { |
||||
#ifdef CYBOZU_STACKTRACE_ONELINE |
||||
str_ += "\n<<<STACKTRACE>>> "; |
||||
str_ += stackTrace_; |
||||
#else |
||||
str_ += "\n<<<STACKTRACE\n"; |
||||
str_ += stackTrace_; |
||||
str_ += "\n>>>STACKTRACE"; |
||||
#endif |
||||
} |
||||
} catch (...) { |
||||
} |
||||
stackTrace_.clear(); |
||||
#endif |
||||
return str_; |
||||
} |
||||
Exception& operator<<(const char *s) |
||||
{ |
||||
str_ += ':'; |
||||
str_ += s; |
||||
return *this; |
||||
} |
||||
Exception& operator<<(const std::string& s) |
||||
{ |
||||
return operator<<(s.c_str()); |
||||
} |
||||
#ifdef _WIN32 |
||||
Exception& operator<<(const std::wstring& s) |
||||
{ |
||||
return operator<<(cybozu::exception::wstr2str(s)); |
||||
} |
||||
#endif |
||||
template<class T> |
||||
Exception& operator<<(const T& x) |
||||
{ |
||||
std::ostringstream os; |
||||
os << x; |
||||
return operator<<(os.str()); |
||||
} |
||||
}; |
||||
|
||||
class ErrorNo { |
||||
public: |
||||
#ifdef _WIN32 |
||||
typedef unsigned int NativeErrorNo; |
||||
#else |
||||
typedef int NativeErrorNo; |
||||
#endif |
||||
explicit ErrorNo(NativeErrorNo err) |
||||
: err_(err) |
||||
{ |
||||
} |
||||
ErrorNo() |
||||
: err_(getLatestNativeErrorNo()) |
||||
{ |
||||
} |
||||
NativeErrorNo getLatestNativeErrorNo() const |
||||
{ |
||||
#ifdef _WIN32 |
||||
return ::GetLastError(); |
||||
#else |
||||
return errno; |
||||
#endif |
||||
} |
||||
/**
|
||||
convert NativeErrNo to string(maybe UTF8) |
||||
@param err [in] errno |
||||
@note Linux : same as ConvertErrorNoToString |
||||
Windows : for Win32 API(use en-us) |
||||
*/ |
||||
std::string toString() const |
||||
{ |
||||
#ifdef _WIN32 |
||||
const int msgSize = 256; |
||||
wchar_t msg[msgSize]; |
||||
int size = FormatMessageW( |
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
||||
0, |
||||
err_, |
||||
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), |
||||
msg, |
||||
msgSize, |
||||
NULL |
||||
); |
||||
if (size <= 0) return ""; |
||||
// remove last "\r\n"
|
||||
if (size > 2 && msg[size - 2] == '\r') { |
||||
msg[size - 2] = 0; |
||||
size -= 2; |
||||
} |
||||
std::string ret; |
||||
ret.resize(size); |
||||
// assume ascii only
|
||||
for (int i = 0; i < size; i++) { |
||||
ret[i] = (char)msg[i]; |
||||
} |
||||
char buf2[64]; |
||||
CYBOZU_SNPRINTF(buf2, sizeof(buf2), "(%u)", err_); |
||||
ret += buf2; |
||||
return ret; |
||||
#else |
||||
return ConvertErrorNoToString(err_); |
||||
#endif |
||||
} |
||||
private: |
||||
NativeErrorNo err_; |
||||
}; |
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const cybozu::ErrorNo& self) |
||||
{ |
||||
return os << self.toString(); |
||||
} |
||||
|
||||
} // cybozu
|
||||
#endif |
@ -0,0 +1,67 @@ |
||||
#pragma once |
||||
#include <cybozu/inttype.hpp> |
||||
|
||||
namespace cybozu { |
||||
|
||||
template<class Iter> |
||||
uint32_t hash32(Iter begin, Iter end, uint32_t v = 0) |
||||
{ |
||||
if (v == 0) v = 2166136261U; |
||||
while (begin != end) { |
||||
v ^= *begin++; |
||||
v *= 16777619; |
||||
} |
||||
return v; |
||||
} |
||||
template<class Iter> |
||||
uint64_t hash64(Iter begin, Iter end, uint64_t v = 0) |
||||
{ |
||||
if (v == 0) v = 14695981039346656037ULL; |
||||
while (begin != end) { |
||||
v ^= *begin++; |
||||
v *= 1099511628211ULL; |
||||
} |
||||
v ^= v >> 32; |
||||
return v; |
||||
} |
||||
template<class T> |
||||
uint32_t hash32(const T *x, size_t n, uint32_t v = 0) |
||||
{ |
||||
return hash32(x, x + n, v); |
||||
} |
||||
template<class T> |
||||
uint64_t hash64(const T *x, size_t n, uint64_t v = 0) |
||||
{ |
||||
return hash64(x, x + n, v); |
||||
} |
||||
|
||||
} // cybozu
|
||||
|
||||
namespace boost { |
||||
|
||||
template<class T> |
||||
struct hash; |
||||
|
||||
} // boost
|
||||
|
||||
#if CYBOZU_CPP_VERSION >= CYBOZU_CPP_VERSION_CPP11 |
||||
#include <functional> |
||||
#else |
||||
|
||||
namespace std { CYBOZU_NAMESPACE_TR1_BEGIN |
||||
|
||||
#ifdef _MSC_VER |
||||
#pragma warning(push) |
||||
#pragma warning(disable : 4099) // missmatch class and struct
|
||||
#endif |
||||
#ifndef __APPLE__ |
||||
template<class T> |
||||
struct hash; |
||||
#endif |
||||
#ifdef _MSC_VER |
||||
#pragma warning(pop) |
||||
#endif |
||||
|
||||
CYBOZU_NAMESPACE_TR1_END } // std
|
||||
|
||||
#endif |
@ -0,0 +1,163 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief int type definition and macros |
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
|
||||
#if defined(_MSC_VER) && (MSC_VER <= 1500) && !defined(CYBOZU_DEFINED_INTXX) |
||||
#define CYBOZU_DEFINED_INTXX |
||||
typedef __int64 int64_t; |
||||
typedef unsigned __int64 uint64_t; |
||||
typedef unsigned int uint32_t; |
||||
typedef int int32_t; |
||||
typedef unsigned short uint16_t; |
||||
typedef short int16_t; |
||||
typedef unsigned char uint8_t; |
||||
typedef signed char int8_t; |
||||
#else |
||||
#include <stdint.h> |
||||
#endif |
||||
|
||||
#ifdef _MSC_VER |
||||
#ifndef CYBOZU_DEFINED_SSIZE_T |
||||
#define CYBOZU_DEFINED_SSIZE_T |
||||
#ifdef _WIN64 |
||||
typedef int64_t ssize_t; |
||||
#else |
||||
typedef int32_t ssize_t; |
||||
#endif |
||||
#endif |
||||
#else |
||||
#include <unistd.h> // for ssize_t |
||||
#endif |
||||
|
||||
#ifndef CYBOZU_ALIGN |
||||
#ifdef _MSC_VER |
||||
#define CYBOZU_ALIGN(x) __declspec(align(x)) |
||||
#else |
||||
#define CYBOZU_ALIGN(x) __attribute__((aligned(x))) |
||||
#endif |
||||
#endif |
||||
#ifndef CYBOZU_FORCE_INLINE |
||||
#ifdef _MSC_VER |
||||
#define CYBOZU_FORCE_INLINE __forceinline |
||||
#else |
||||
#define CYBOZU_FORCE_INLINE __attribute__((always_inline)) |
||||
#endif |
||||
#endif |
||||
#ifndef CYBOZU_UNUSED |
||||
#ifdef __GNUC__ |
||||
#define CYBOZU_UNUSED __attribute__((unused)) |
||||
#else |
||||
#define CYBOZU_UNUSED |
||||
#endif |
||||
#endif |
||||
#ifndef CYBOZU_ALLOCA |
||||
#ifdef _MSC_VER |
||||
#include <malloc.h> |
||||
#define CYBOZU_ALLOCA(x) _malloca(x) |
||||
#else |
||||
#define CYBOZU_ALLOCA(x) __builtin_alloca(x) |
||||
#endif |
||||
#endif |
||||
#ifndef CYBOZU_NUM_OF_ARRAY |
||||
#define CYBOZU_NUM_OF_ARRAY(x) (sizeof(x) / sizeof(*x)) |
||||
#endif |
||||
#ifndef CYBOZU_SNPRINTF |
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900) |
||||
#define CYBOZU_SNPRINTF(x, len, ...) (void)_snprintf_s(x, len, len - 1, __VA_ARGS__) |
||||
#else |
||||
#define CYBOZU_SNPRINTF(x, len, ...) (void)snprintf(x, len, __VA_ARGS__) |
||||
#endif |
||||
#endif |
||||
|
||||
#define CYBOZU_CPP_VERSION_CPP03 0 |
||||
#define CYBOZU_CPP_VERSION_TR1 1 |
||||
#define CYBOZU_CPP_VERSION_CPP11 2 |
||||
#define CYBOZU_CPP_VERSION_CPP14 3 |
||||
#define CYBOZU_CPP_VERSION_CPP17 4 |
||||
|
||||
#ifdef __GNUC__ |
||||
#define CYBOZU_GNUC_PREREQ(major, minor) ((__GNUC__) * 100 + (__GNUC_MINOR__) >= (major) * 100 + (minor)) |
||||
#else |
||||
#define CYBOZU_GNUC_PREREQ(major, minor) 0 |
||||
#endif |
||||
|
||||
#if (__cplusplus >= 201703) |
||||
#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_CPP17 |
||||
#elif (__cplusplus >= 201402) |
||||
#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_CPP14 |
||||
#elif (__cplusplus >= 201103) || (_MSC_VER >= 1500) || defined(__GXX_EXPERIMENTAL_CXX0X__) |
||||
#if defined(_MSC_VER) && (_MSC_VER <= 1600) |
||||
#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_TR1 |
||||
#else |
||||
#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_CPP11 |
||||
#endif |
||||
#elif CYBOZU_GNUC_PREREQ(4, 5) || (CYBOZU_GNUC_PREREQ(4, 2) && __GLIBCXX__ >= 20070719) || defined(__INTEL_COMPILER) || (__clang_major__ >= 3) |
||||
#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_TR1 |
||||
#else |
||||
#define CYBOZU_CPP_VERSION CYBOZU_CPP_VERSION_CPP03 |
||||
#endif |
||||
|
||||
#ifdef CYBOZU_USE_BOOST |
||||
#define CYBOZU_NAMESPACE_STD boost |
||||
#define CYBOZU_NAMESPACE_TR1_BEGIN |
||||
#define CYBOZU_NAMESPACE_TR1_END |
||||
#elif (CYBOZU_CPP_VERSION == CYBOZU_CPP_VERSION_TR1) && !defined(__APPLE__) |
||||
#define CYBOZU_NAMESPACE_STD std::tr1 |
||||
#define CYBOZU_NAMESPACE_TR1_BEGIN namespace tr1 { |
||||
#define CYBOZU_NAMESPACE_TR1_END } |
||||
#else |
||||
#define CYBOZU_NAMESPACE_STD std |
||||
#define CYBOZU_NAMESPACE_TR1_BEGIN |
||||
#define CYBOZU_NAMESPACE_TR1_END |
||||
#endif |
||||
|
||||
#ifndef CYBOZU_OS_BIT |
||||
#if defined(_WIN64) || defined(__x86_64__) || defined(__AARCH64EL__) || defined(__EMSCRIPTEN__) |
||||
#define CYBOZU_OS_BIT 64 |
||||
#else |
||||
#define CYBOZU_OS_BIT 32 |
||||
#endif |
||||
#endif |
||||
|
||||
#ifndef CYBOZU_HOST |
||||
#define CYBOZU_HOST_UNKNOWN 0 |
||||
#define CYBOZU_HOST_INTEL 1 |
||||
#define CYBOZU_HOST_ARM 2 |
||||
#if defined(_M_IX86) || defined(_M_AMD64) || defined(__x86_64__) || defined(__i386__) |
||||
#define CYBOZU_HOST CYBOZU_HOST_INTEL |
||||
#elif defined(__arm__) || defined(__AARCH64EL__) |
||||
#define CYBOZU_HOST CYBOZU_HOST_ARM |
||||
#else |
||||
#define CYBOZU_HOST CYBOZU_HOST_UNKNOWN |
||||
#endif |
||||
#endif |
||||
|
||||
#ifndef CYBOZU_ENDIAN |
||||
#define CYBOZU_ENDIAN_UNKNOWN 0 |
||||
#define CYBOZU_ENDIAN_LITTLE 1 |
||||
#define CYBOZU_ENDIAN_BIG 2 |
||||
#if (CYBOZU_HOST == CYBOZU_HOST_INTEL) |
||||
#define CYBOZU_ENDIAN CYBOZU_ENDIAN_LITTLE |
||||
#elif (CYBOZU_HOST == CYBOZU_HOST_ARM) && (defined(__ARM_EABI__) || defined(__AARCH64EL__)) |
||||
#define CYBOZU_ENDIAN CYBOZU_ENDIAN_LITTLE |
||||
#else |
||||
#define CYBOZU_ENDIAN CYBOZU_ENDIAN_UNKNOWN |
||||
#endif |
||||
#endif |
||||
|
||||
#if CYBOZU_CPP_VERSION >= CYBOZU_CPP_VERSION_CPP11 |
||||
#define CYBOZU_NOEXCEPT noexcept |
||||
#else |
||||
#define CYBOZU_NOEXCEPT throw() |
||||
#endif |
||||
namespace cybozu { |
||||
template<class T> |
||||
void disable_warning_unused_variable(const T&) { } |
||||
template<class T, class S> |
||||
T cast(const S* ptr) { return static_cast<T>(static_cast<const void*>(ptr)); } |
||||
template<class T, class S> |
||||
T cast(S* ptr) { return static_cast<T>(static_cast<void*>(ptr)); } |
||||
} // cybozu
|
@ -0,0 +1,337 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief convert integer to string(ascii) |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#include <limits.h> |
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
#include <string> |
||||
#endif |
||||
#include <memory.h> |
||||
#include <cybozu/inttype.hpp> |
||||
#include <cybozu/bit_operation.hpp> |
||||
|
||||
namespace cybozu { |
||||
|
||||
template<class T> |
||||
size_t getHexLength(T x) |
||||
{ |
||||
return x == 0 ? 1 : cybozu::bsr(x) / 4 + 1; |
||||
} |
||||
|
||||
template<class T> |
||||
size_t getBinLength(T x) |
||||
{ |
||||
return x == 0 ? 1 : cybozu::bsr(x) + 1; |
||||
} |
||||
/*
|
||||
convert x to hex string with len |
||||
@note out should have getHexLength(x) size |
||||
out is not NUL terminated |
||||
*/ |
||||
template<class T> |
||||
void itohex(char *out, size_t len, T x, bool upCase = true) |
||||
{ |
||||
static const char *hexTbl[] = { |
||||
"0123456789abcdef", |
||||
"0123456789ABCDEF" |
||||
}; |
||||
const char *tbl = hexTbl[upCase]; |
||||
for (size_t i = 0; i < len; i++) { |
||||
out[len - i - 1] = tbl[x % 16]; |
||||
x /= 16; |
||||
} |
||||
} |
||||
/*
|
||||
convert x to bin string with len |
||||
@note out should have getBinLength(x) size |
||||
out is not NUL terminated |
||||
*/ |
||||
template<class T> |
||||
void itobin(char *out, size_t len, T x) |
||||
{ |
||||
for (size_t i = 0; i < len; i++) { |
||||
out[len - i - 1] = '0' + (x & 1); |
||||
x >>= 1; |
||||
} |
||||
} |
||||
|
||||
namespace itoa_local { |
||||
|
||||
/*
|
||||
convert x to dec |
||||
use buf[0, bufSize) |
||||
return 0 if false |
||||
return writtenSize which is not terminated |
||||
@REMARK the top of string is buf + bufSize - writtenSize |
||||
*/ |
||||
template<class UT> |
||||
size_t uintToDec(char *buf, size_t bufSize, UT x) |
||||
{ |
||||
for (size_t i = 0; i < bufSize; i++) { |
||||
buf[bufSize - 1 - i] = '0' + static_cast<int>(x % 10); |
||||
x /= 10; |
||||
if (x == 0) return i + 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
convert x to hex |
||||
use buf[0, bufSize) |
||||
return 0 if false |
||||
return writtenSize which is not terminated |
||||
@REMARK the top of string is buf + bufSize - writtenSize |
||||
*/ |
||||
template<class UT> |
||||
size_t uintToHex(char *buf, size_t bufSize, UT x, bool upCase = true) |
||||
{ |
||||
static const char *hexTbl[] = { |
||||
"0123456789abcdef", |
||||
"0123456789ABCDEF" |
||||
}; |
||||
const char *tbl = hexTbl[upCase]; |
||||
for (size_t i = 0; i < bufSize; i++) { |
||||
buf[bufSize - 1 - i] = tbl[x % 16]; |
||||
x /= 16; |
||||
if (x == 0) return i + 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
convert x to bin |
||||
use buf[0, bufSize) |
||||
return 0 if false |
||||
return writtenSize which is not terminated |
||||
@REMARK the top of string is buf + bufSize - writtenSize |
||||
*/ |
||||
template<class UT> |
||||
size_t uintToBin(char *buf, size_t bufSize, UT x) |
||||
{ |
||||
for (size_t i = 0; i < bufSize; i++) { |
||||
buf[bufSize - 1 - i] = '0' + (x & 1); |
||||
x >>= 1; |
||||
if (x == 0) return i + 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
template<class T> |
||||
size_t intToDec(char *buf, size_t bufSize, T x) |
||||
{ |
||||
if (x == LLONG_MIN) { |
||||
const char minStr[] = "-9223372036854775808"; |
||||
const size_t minStrLen = sizeof(minStr) - 1; |
||||
if (bufSize < minStrLen) { |
||||
return 0; |
||||
} else { |
||||
memcpy(buf + bufSize - minStrLen, minStr, minStrLen); |
||||
return minStrLen; |
||||
} |
||||
} |
||||
bool negative = x < 0; |
||||
uint64_t absX = negative ? -x : x; |
||||
size_t n = uintToDec(buf, bufSize, absX); |
||||
if (n == 0) return 0; |
||||
if (negative) { |
||||
if (bufSize == n) return 0; |
||||
n++; |
||||
buf[bufSize - n] = '-'; |
||||
} |
||||
return n; |
||||
} |
||||
|
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
template<typename T> |
||||
void convertFromUint(std::string& out, T x) |
||||
{ |
||||
char buf[40]; |
||||
size_t n = uintToDec(buf, sizeof(buf), x); |
||||
assert(n > 0); |
||||
out.assign(buf + sizeof(buf) - n, n); |
||||
} |
||||
|
||||
inline void convertFromInt(std::string& out, long long x) |
||||
{ |
||||
char buf[40]; |
||||
size_t n = intToDec(buf, sizeof(buf), x); |
||||
assert(n > 0); |
||||
out.assign(buf + sizeof(buf) - n, n); |
||||
} |
||||
|
||||
template<typename T> |
||||
void itohexLocal(std::string& out, T x, bool upCase, bool withZero) |
||||
{ |
||||
const size_t size = withZero ? sizeof(T) * 2 : getHexLength(x); |
||||
out.resize(size); |
||||
itohex(&out[0], size, x, upCase); |
||||
} |
||||
|
||||
template<class T> |
||||
void itobinLocal(std::string& out, T x, bool withZero) |
||||
{ |
||||
const size_t size = withZero ? sizeof(T) * 8 : getBinLength(x); |
||||
out.resize(size); |
||||
itobin(&out[0], size, x); |
||||
} |
||||
#endif |
||||
|
||||
} // itoa_local
|
||||
|
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
/**
|
||||
convert int to string |
||||
@param out [out] string |
||||
@param x [in] int |
||||
*/ |
||||
inline void itoa(std::string& out, int x) |
||||
{ |
||||
itoa_local::convertFromInt(out, x); |
||||
} |
||||
|
||||
/**
|
||||
convert long long to string |
||||
@param out [out] string |
||||
@param x [in] long long |
||||
*/ |
||||
inline void itoa(std::string& out, long long x) |
||||
{ |
||||
itoa_local::convertFromInt(out, x); |
||||
} |
||||
|
||||
/**
|
||||
convert unsigned int to string |
||||
@param out [out] string |
||||
@param x [in] unsigned int |
||||
*/ |
||||
inline void itoa(std::string& out, unsigned int x) |
||||
{ |
||||
itoa_local::convertFromUint(out, x); |
||||
} |
||||
|
||||
/**
|
||||
convert unsigned long long to string |
||||
@param out [out] string |
||||
@param x [in] unsigned long long |
||||
*/ |
||||
inline void itoa(std::string& out, unsigned long long x) |
||||
{ |
||||
itoa_local::convertFromUint(out, x); |
||||
} |
||||
|
||||
#if defined(__SIZEOF_LONG__) && (__SIZEOF_LONG__ == 8) |
||||
inline void itoa(std::string& out, long x) { itoa(out, static_cast<long long>(x)); } |
||||
inline void itoa(std::string& out, unsigned long x) { itoa(out, static_cast<unsigned long long>(x)); } |
||||
#else |
||||
inline void itoa(std::string& out, long x) { itoa(out, static_cast<int>(x)); } |
||||
inline void itoa(std::string& out, unsigned long x) { itoa(out, static_cast<int>(x)); } |
||||
#endif |
||||
/**
|
||||
convert integer to string |
||||
@param x [in] int |
||||
*/ |
||||
template<typename T> |
||||
inline std::string itoa(T x) |
||||
{ |
||||
std::string ret; |
||||
itoa(ret, x); |
||||
return ret; |
||||
} |
||||
|
||||
inline void itohex(std::string& out, unsigned char x, bool upCase = true, bool withZero = true) |
||||
{ |
||||
itoa_local::itohexLocal(out, x, upCase, withZero); |
||||
} |
||||
|
||||
inline void itohex(std::string& out, unsigned short x, bool upCase = true, bool withZero = true) |
||||
{ |
||||
itoa_local::itohexLocal(out, x, upCase, withZero); |
||||
} |
||||
|
||||
inline void itohex(std::string& out, unsigned int x, bool upCase = true, bool withZero = true) |
||||
{ |
||||
itoa_local::itohexLocal(out, x, upCase, withZero); |
||||
} |
||||
|
||||
inline void itohex(std::string& out, unsigned long x, bool upCase = true, bool withZero = true) |
||||
{ |
||||
itoa_local::itohexLocal(out, x, upCase, withZero); |
||||
} |
||||
|
||||
inline void itohex(std::string& out, unsigned long long x, bool upCase = true, bool withZero = true) |
||||
{ |
||||
itoa_local::itohexLocal(out, x, upCase, withZero); |
||||
} |
||||
|
||||
template<typename T> |
||||
inline std::string itobin(T x, bool withZero = true) |
||||
{ |
||||
std::string out; |
||||
itoa_local::itobinLocal(out, x, withZero); |
||||
return out; |
||||
} |
||||
|
||||
inline void itobin(std::string& out, unsigned char x, bool withZero = true) |
||||
{ |
||||
itoa_local::itobinLocal(out, x, withZero); |
||||
} |
||||
|
||||
inline void itobin(std::string& out, unsigned short x, bool withZero = true) |
||||
{ |
||||
itoa_local::itobinLocal(out, x, withZero); |
||||
} |
||||
|
||||
inline void itobin(std::string& out, unsigned int x, bool withZero = true) |
||||
{ |
||||
itoa_local::itobinLocal(out, x, withZero); |
||||
} |
||||
|
||||
inline void itobin(std::string& out, unsigned long x, bool withZero = true) |
||||
{ |
||||
itoa_local::itobinLocal(out, x, withZero); |
||||
} |
||||
|
||||
inline void itobin(std::string& out, unsigned long long x, bool withZero = true) |
||||
{ |
||||
itoa_local::itobinLocal(out, x, withZero); |
||||
} |
||||
|
||||
template<typename T> |
||||
inline std::string itohex(T x, bool upCase = true, bool withZero = true) |
||||
{ |
||||
std::string out; |
||||
itohex(out, x, upCase, withZero); |
||||
return out; |
||||
} |
||||
/**
|
||||
convert integer to string with zero padding |
||||
@param x [in] int |
||||
@param len [in] minimum lengh of string |
||||
@param c [in] padding character |
||||
@note |
||||
itoa(12, 4) == "0012" |
||||
itoa(1234, 4) == "1234" |
||||
itoa(12345, 4) == "12345" |
||||
itoa(-12, 4) == "-012" |
||||
*/ |
||||
template<typename T> |
||||
inline std::string itoaWithZero(T x, size_t len, char c = '0') |
||||
{ |
||||
std::string ret; |
||||
itoa(ret, x); |
||||
if (ret.size() < len) { |
||||
std::string zero(len - ret.size(), c); |
||||
if (x >= 0) { |
||||
ret = zero + ret; |
||||
} else { |
||||
ret = "-" + zero + ret.substr(1); |
||||
} |
||||
} |
||||
return ret; |
||||
} |
||||
#endif |
||||
|
||||
} // cybozu
|
@ -0,0 +1,21 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief link libeay32.lib of openssl |
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#if defined(_WIN32) && defined(_MT) |
||||
#if _MSC_VER >= 1900 // VC2015
|
||||
#ifdef _WIN64 |
||||
#pragma comment(lib, "mt/14/libeay32.lib") |
||||
#else |
||||
#pragma comment(lib, "mt/14/32/libeay32.lib") |
||||
#endif |
||||
// #elif _MSC_VER == 1800 // VC2013
|
||||
#else |
||||
#pragma comment(lib, "mt/12/libeay32.lib") |
||||
#endif |
||||
#pragma comment(lib, "advapi32.lib") |
||||
#pragma comment(lib, "gdi32.lib") |
||||
#pragma comment(lib, "user32.lib") |
||||
#endif |
@ -0,0 +1,18 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief link mpir/mpirxx of mpir |
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#if defined(_WIN32) && defined(_MT) |
||||
#if _MSC_VER >= 1900 // VC2015, VC2017(1910)
|
||||
#pragma comment(lib, "mt/14/mpir.lib") |
||||
#pragma comment(lib, "mt/14/mpirxx.lib") |
||||
#elif _MSC_VER == 1800 // VC2013
|
||||
#pragma comment(lib, "mt/12/mpir.lib") |
||||
#pragma comment(lib, "mt/12/mpirxx.lib") |
||||
#elif _MSC_VER == 1700 // VC2012
|
||||
#pragma comment(lib, "mt/11/mpir.lib") |
||||
#pragma comment(lib, "mt/11/mpirxx.lib") |
||||
#endif |
||||
#endif |
@ -0,0 +1,19 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief link ssleay32.lib of openssl |
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#if defined(_WIN32) && defined(_MT) |
||||
#if _MSC_VER >= 1900 // VC2015
|
||||
#ifdef _WIN64 |
||||
#pragma comment(lib, "mt/14/ssleay32.lib") |
||||
#else |
||||
#pragma comment(lib, "mt/14/32/ssleay32.lib") |
||||
#endif |
||||
// #elif _MSC_VER == 1800 // VC2013
|
||||
#else |
||||
#pragma comment(lib, "mt/12/ssleay32.lib") |
||||
#endif |
||||
#pragma comment(lib, "user32.lib") |
||||
#endif |
@ -0,0 +1,141 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief mutex |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
@author MITSUNARI Shigeo |
||||
*/ |
||||
|
||||
#ifdef _WIN32 |
||||
#include <windows.h> |
||||
#else |
||||
#include <pthread.h> |
||||
#include <time.h> |
||||
#endif |
||||
#include <assert.h> |
||||
#include <stdlib.h> |
||||
|
||||
namespace cybozu { |
||||
|
||||
class ConditionVariable; |
||||
|
||||
namespace thread { |
||||
|
||||
#ifdef _WIN32 |
||||
typedef HANDLE MutexHandle; |
||||
inline void MutexInit(MutexHandle& mutex) |
||||
{ |
||||
// mutex = CreateSemaphore(NULL /* no security */, 1 /* init */, 0x7FFFFFFF /* max */, NULL /* no name */);
|
||||
mutex = CreateMutex(NULL /* no security */, FALSE /* no owner */, NULL /* no name */); |
||||
} |
||||
inline void MutexLock(MutexHandle& mutex) { WaitForSingleObject(mutex, INFINITE); } |
||||
/*
|
||||
return false if timeout |
||||
@param msec [in] msec |
||||
*/ |
||||
inline bool MutexLockTimeout(MutexHandle& mutex, int msec) |
||||
{ |
||||
DWORD ret = WaitForSingleObject(mutex, msec); |
||||
if (ret == WAIT_OBJECT_0) { |
||||
return true; |
||||
} |
||||
if (ret == WAIT_TIMEOUT) { |
||||
return false; |
||||
} |
||||
/* ret == WAIT_ABANDONED */ |
||||
assert(0); |
||||
return false; |
||||
} |
||||
inline void MutexUnlock(MutexHandle& mutex) |
||||
{ |
||||
// ReleaseSemaphore(mutex, 1, NULL);
|
||||
ReleaseMutex(mutex); |
||||
} |
||||
inline void MutexTerm(MutexHandle& mutex) { CloseHandle(mutex); } |
||||
#else |
||||
typedef pthread_mutex_t MutexHandle; |
||||
inline void MutexInit(MutexHandle& mutex) |
||||
{ |
||||
#if 1 |
||||
pthread_mutex_init(&mutex, NULL); |
||||
#else |
||||
pthread_mutexattr_t attr; |
||||
pthread_mutexattr_init(&attr); |
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_TIMED_NP)) { |
||||
perror("pthread_mutexattr_settype"); |
||||
exit(1); |
||||
} |
||||
pthread_mutex_init(&mutex, &attr); |
||||
pthread_mutexattr_destroy(&attr); |
||||
#endif |
||||
} |
||||
inline void MutexLock(MutexHandle& mutex) { pthread_mutex_lock(&mutex); } |
||||
#if 0 |
||||
inline bool MutexLockTimeout(MutexHandle& mutex, int msec) |
||||
{ |
||||
timespec absTime; |
||||
clock_gettime(CLOCK_REALTIME, &absTime); |
||||
absTime.tv_sec += msec / 1000; |
||||
absTime.tv_nsec += msec % 1000; |
||||
bool ret = pthread_mutex_timedlock(&mutex, &absTime) == 0; |
||||
return ret; |
||||
} |
||||
#endif |
||||
inline void MutexUnlock(MutexHandle& mutex) { pthread_mutex_unlock(&mutex); } |
||||
inline void MutexTerm(MutexHandle& mutex) { pthread_mutex_destroy(&mutex); } |
||||
#endif |
||||
|
||||
template<class T> |
||||
class AutoLockT { |
||||
public: |
||||
explicit AutoLockT(T &t) |
||||
: t_(t) |
||||
{ |
||||
t_.lock(); |
||||
} |
||||
~AutoLockT() |
||||
{ |
||||
t_.unlock(); |
||||
} |
||||
private: |
||||
T& t_; |
||||
AutoLockT& operator=(const AutoLockT&); |
||||
}; |
||||
|
||||
} // cybozu::thread
|
||||
|
||||
class Mutex { |
||||
friend class cybozu::ConditionVariable; |
||||
public: |
||||
Mutex() |
||||
{ |
||||
thread::MutexInit(hdl_); |
||||
} |
||||
~Mutex() |
||||
{ |
||||
thread::MutexTerm(hdl_); |
||||
} |
||||
void lock() |
||||
{ |
||||
thread::MutexLock(hdl_); |
||||
} |
||||
#if 0 |
||||
bool lockTimeout(int msec) |
||||
{ |
||||
return thread::MutexLockTimeout(hdl_, msec); |
||||
} |
||||
#endif |
||||
void unlock() |
||||
{ |
||||
thread::MutexUnlock(hdl_); |
||||
} |
||||
private: |
||||
Mutex(const Mutex&); |
||||
Mutex& operator=(const Mutex&); |
||||
thread::MutexHandle hdl_; |
||||
}; |
||||
|
||||
typedef cybozu::thread::AutoLockT<cybozu::Mutex> AutoLock; |
||||
|
||||
} // cybozu
|
@ -0,0 +1,723 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief command line parser |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#include <string> |
||||
#include <vector> |
||||
#include <map> |
||||
#include <sstream> |
||||
#include <iostream> |
||||
#include <limits> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <assert.h> |
||||
#include <cybozu/exception.hpp> |
||||
#include <cybozu/atoi.hpp> |
||||
|
||||
/*
|
||||
Option parser |
||||
|
||||
progName (opt1-name|opt2-name|...) param1 param2 ... |
||||
param1:param1-help |
||||
param2:param2-help |
||||
-op1-name:opt1-help |
||||
... |
||||
|
||||
How to setup |
||||
int num; |
||||
-n num ; (optional) option => appendOpt(&x, <defaultValue>, "num", "num-help"); |
||||
-n num ; must option => appendMust(&x, "num", "num-help"); |
||||
|
||||
std::vector<int> v; |
||||
-v s1 s2 s3 ... => appendVec(&v, "v"); |
||||
|
||||
Remark1: terminate parsing of v if argv begins with '-[^0-9]' |
||||
Remark2: the begining character of opt-name is not a number ('0'...'9') |
||||
because avoid conflict with minus number |
||||
|
||||
std::string file1; |
||||
file1 is param => appendParam(&file1, "input-file"); |
||||
file2 is optional param => appendParamOpt(&file2, "output-file"); |
||||
|
||||
How to use |
||||
opt.parse(argc, argv); |
||||
|
||||
see sample/option_smpl.cpp |
||||
*/ |
||||
|
||||
namespace cybozu { |
||||
|
||||
struct OptionError : public cybozu::Exception { |
||||
enum Type { |
||||
NoError = 0, |
||||
BAD_OPT = 1, |
||||
BAD_VALUE, |
||||
NO_VALUE, |
||||
OPT_IS_NECESSARY, |
||||
PARAM_IS_NECESSARY, |
||||
REDUNDANT_VAL, |
||||
BAD_ARGC |
||||
}; |
||||
Type type; |
||||
int argPos; |
||||
OptionError() |
||||
: cybozu::Exception("OptionError", false) |
||||
, type(NoError) |
||||
, argPos(0) |
||||
{ |
||||
} |
||||
cybozu::Exception& set(Type _type, int _argPos = 0) |
||||
{ |
||||
this->type = _type; |
||||
this->argPos = _argPos; |
||||
switch (_type) { |
||||
case BAD_OPT: |
||||
(*this) << "bad opt"; |
||||
break; |
||||
case BAD_VALUE: |
||||
(*this) << "bad value"; |
||||
break; |
||||
case NO_VALUE: |
||||
(*this) << "no value"; |
||||
break; |
||||
case OPT_IS_NECESSARY: |
||||
(*this) << "opt is necessary"; |
||||
break; |
||||
case PARAM_IS_NECESSARY: |
||||
(*this) << "param is necessary"; |
||||
break; |
||||
case REDUNDANT_VAL: |
||||
(*this) << "redundant argVal"; |
||||
break; |
||||
case BAD_ARGC: |
||||
(*this) << "bad argc"; |
||||
default: |
||||
break; |
||||
} |
||||
return *this; |
||||
} |
||||
}; |
||||
|
||||
namespace option_local { |
||||
|
||||
template<class T> |
||||
bool convert(T* x, const char *str) |
||||
{ |
||||
std::istringstream is(str); |
||||
is >> *x; |
||||
return !!is; |
||||
} |
||||
|
||||
template<> |
||||
inline bool convert(std::string* x, const char *str) |
||||
{ |
||||
*x = str; |
||||
return true; |
||||
} |
||||
|
||||
template<class T> |
||||
bool convertInt(T* x, const char *str) |
||||
{ |
||||
if (str[0] == '0' && str[1] == 'x') { |
||||
bool b; |
||||
*x = cybozu::hextoi(&b, str + 2); |
||||
return b; |
||||
} |
||||
size_t len = strlen(str); |
||||
int factor = 1; |
||||
if (len > 1) { |
||||
switch (str[len - 1]) { |
||||
case 'k': factor = 1000; len--; break; |
||||
case 'm': factor = 1000 * 1000; len--; break; |
||||
case 'g': factor = 1000 * 1000 * 1000; len--; break; |
||||
case 'K': factor = 1024; len--; break; |
||||
case 'M': factor = 1024 * 1024; len--; break; |
||||
case 'G': factor = 1024 * 1024 * 1024; len--; break; |
||||
default: break; |
||||
} |
||||
} |
||||
bool b; |
||||
T y = cybozu::atoi(&b, str, len); |
||||
if (!b) return false; |
||||
if (factor > 1) { |
||||
if ((std::numeric_limits<T>::min)() / factor <= y |
||||
&& y <= (std::numeric_limits<T>::max)() / factor) { |
||||
*x = y * factor; |
||||
} else { |
||||
return false; |
||||
} |
||||
} else { |
||||
*x = y; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
#define CYBOZU_OPTION_DEFINE_CONVERT_INT(type) \ |
||||
template<>inline bool convert(type* x, const char *str) { return convertInt(x, str); } |
||||
|
||||
CYBOZU_OPTION_DEFINE_CONVERT_INT(int) |
||||
CYBOZU_OPTION_DEFINE_CONVERT_INT(long) |
||||
CYBOZU_OPTION_DEFINE_CONVERT_INT(long long) |
||||
|
||||
CYBOZU_OPTION_DEFINE_CONVERT_INT(unsigned int) |
||||
CYBOZU_OPTION_DEFINE_CONVERT_INT(unsigned long) |
||||
CYBOZU_OPTION_DEFINE_CONVERT_INT(unsigned long long) |
||||
|
||||
#undef CYBOZU_OPTION_DEFINE_CONVERT_INT |
||||
|
||||
struct HolderBase { |
||||
virtual ~HolderBase(){} |
||||
virtual bool set(const char*) = 0; |
||||
virtual HolderBase *clone() const = 0; |
||||
virtual std::string toStr() const = 0; |
||||
virtual const void *get() const = 0; |
||||
}; |
||||
|
||||
template<class T> |
||||
struct Holder : public HolderBase { |
||||
T *p_; |
||||
Holder(T *p) : p_(p) {} |
||||
HolderBase *clone() const { return new Holder(p_); } |
||||
bool set(const char *str) { return option_local::convert(p_, str); } |
||||
std::string toStr() const |
||||
{ |
||||
std::ostringstream os; |
||||
os << *p_; |
||||
return os.str(); |
||||
} |
||||
const void *get() const { return (void*)p_; } |
||||
}; |
||||
|
||||
/*
|
||||
for gcc 7 with -fnew-ttp-matching |
||||
this specialization is not necessary under -fno-new-ttp-matching |
||||
*/ |
||||
template struct Holder<std::string>; |
||||
|
||||
template<class T, class Alloc, template<class T_, class Alloc_>class Container> |
||||
struct Holder<Container<T, Alloc> > : public HolderBase { |
||||
typedef Container<T, Alloc> Vec; |
||||
Vec *p_; |
||||
Holder(Vec *p) : p_(p) {} |
||||
HolderBase *clone() const { return new Holder<Vec>(p_); } |
||||
bool set(const char *str) |
||||
{ |
||||
T t; |
||||
bool b = option_local::convert(&t, str); |
||||
if (b) p_->push_back(t); |
||||
return b; |
||||
} |
||||
std::string toStr() const |
||||
{ |
||||
std::ostringstream os; |
||||
bool isFirst = true; |
||||
for (typename Vec::const_iterator i = p_->begin(), ie = p_->end(); i != ie; ++i) { |
||||
if (isFirst) { |
||||
isFirst = false; |
||||
} else { |
||||
os << ' '; |
||||
} |
||||
os << *i; |
||||
} |
||||
return os.str(); |
||||
} |
||||
const void *get() const { return (void*)p_; } |
||||
}; |
||||
|
||||
class Var { |
||||
HolderBase *p_; |
||||
bool isSet_; |
||||
public: |
||||
Var() : p_(0), isSet_(false) { } |
||||
Var(const Var& rhs) : p_(rhs.p_->clone()), isSet_(false) { } |
||||
template<class T> |
||||
explicit Var(T *x) : p_(new Holder<T>(x)), isSet_(false) { } |
||||
|
||||
~Var() { delete p_; } |
||||
|
||||
void swap(Var& rhs) CYBOZU_NOEXCEPT |
||||
{ |
||||
std::swap(p_, rhs.p_); |
||||
std::swap(isSet_, rhs.isSet_); |
||||
} |
||||
void operator=(const Var& rhs) |
||||
{ |
||||
Var v(rhs); |
||||
swap(v); |
||||
} |
||||
bool set(const char *str) |
||||
{ |
||||
isSet_ = true; |
||||
return p_->set(str); |
||||
} |
||||
std::string toStr() const { return p_ ? p_->toStr() : ""; } |
||||
bool isSet() const { return isSet_; } |
||||
const void *get() const { return p_ ? p_->get() : 0; } |
||||
}; |
||||
|
||||
} // option_local
|
||||
|
||||
class Option { |
||||
enum Mode { // for opt
|
||||
N_is0 = 0, // for bool by appendBoolOpt()
|
||||
N_is1 = 1, |
||||
N_any = 2 |
||||
}; |
||||
enum ParamMode { |
||||
P_exact = 0, // one
|
||||
P_optional = 1, // zero or one
|
||||
P_variable = 2 // zero or greater
|
||||
}; |
||||
struct Info { |
||||
option_local::Var var; |
||||
Mode mode; // 0 or 1 or any ; for opt, not used for Param
|
||||
bool isMust; // this option is must
|
||||
std::string opt; // option param name without '-'
|
||||
std::string help; // description of option
|
||||
|
||||
Info() : mode(N_is0), isMust(false) {} |
||||
template<class T> |
||||
Info(T* pvar, Mode mode, bool isMust, const char *opt, const std::string& help) |
||||
: var(pvar) |
||||
, mode(mode) |
||||
, isMust(isMust) |
||||
, opt(opt) |
||||
, help(help) |
||||
{ |
||||
} |
||||
friend inline std::ostream& operator<<(std::ostream& os, const Info& self) |
||||
{ |
||||
os << self.opt << '=' << self.var.toStr(); |
||||
if (self.var.isSet()) { |
||||
os << " (set)"; |
||||
} else { |
||||
os << " (default)"; |
||||
} |
||||
return os; |
||||
} |
||||
void put() const |
||||
{ |
||||
std::cout << *this; |
||||
} |
||||
void usage() const |
||||
{ |
||||
printf(" -%s %s%s\n", opt.c_str(), help.c_str(), isMust ? " (must)" : ""); |
||||
} |
||||
void shortUsage() const |
||||
{ |
||||
printf(" -%s %s", opt.c_str(), mode == N_is0 ? "" : mode == N_is1 ? "para" : "para..."); |
||||
} |
||||
bool isSet() const { return var.isSet(); } |
||||
const void *get() const { return var.get(); } |
||||
}; |
||||
typedef std::vector<Info> InfoVec; |
||||
typedef std::vector<std::string> StrVec; |
||||
typedef std::map<std::string, size_t> OptMap; |
||||
InfoVec infoVec_; |
||||
InfoVec paramVec_; |
||||
Info remains_; |
||||
OptMap optMap_; |
||||
bool showOptUsage_; |
||||
ParamMode paramMode_; |
||||
std::string progName_; |
||||
std::string desc_; |
||||
std::string helpOpt_; |
||||
std::string help_; |
||||
std::string usage_; |
||||
StrVec delimiters_; |
||||
StrVec *remainsAfterDelimiter_; |
||||
int nextDelimiter_; |
||||
template<class T> |
||||
void appendSub(T *pvar, Mode mode, bool isMust, const char *opt, const std::string& help) |
||||
{ |
||||
const char c = opt[0]; |
||||
if ('0' <= c && c <= '9') throw cybozu::Exception("Option::appendSub:opt must begin with not number") << opt; |
||||
if (optMap_.find(opt) != optMap_.end()) { |
||||
throw cybozu::Exception("Option::append:duplicate option") << opt; |
||||
} |
||||
optMap_[opt] = infoVec_.size(); |
||||
infoVec_.push_back(Info(pvar, mode, isMust, opt, help)); |
||||
} |
||||
|
||||
template<class T, class U> |
||||
void append(T *pvar, const U& defaultVal, bool isMust, const char *opt, const std::string& help = "") |
||||
{ |
||||
*pvar = defaultVal; |
||||
appendSub(pvar, N_is1, isMust, opt, help); |
||||
} |
||||
/*
|
||||
don't deal with negative number as option |
||||
*/ |
||||
bool isOpt(const char *str) const |
||||
{ |
||||
if (str[0] != '-') return false; |
||||
const char c = str[1]; |
||||
if ('0' <= c && c <= '9') return false; |
||||
return true; |
||||
} |
||||
void verifyParamMode() |
||||
{ |
||||
if (paramMode_ != P_exact) throw cybozu::Exception("Option:appendParamVec:appendParam is forbidden after appendParamOpt/appendParamVec"); |
||||
} |
||||
std::string getBaseName(const std::string& name) const |
||||
{ |
||||
size_t pos = name.find_last_of("/\\"); |
||||
if (pos == std::string::npos) return name; |
||||
return name.substr(pos + 1); |
||||
} |
||||
bool inDelimiters(const std::string& str) const |
||||
{ |
||||
return std::find(delimiters_.begin(), delimiters_.end(), str) != delimiters_.end(); |
||||
} |
||||
public: |
||||
Option() |
||||
: showOptUsage_(true) |
||||
, paramMode_(P_exact) |
||||
, remainsAfterDelimiter_(0) |
||||
, nextDelimiter_(-1) |
||||
{ |
||||
} |
||||
virtual ~Option() {} |
||||
/*
|
||||
append optional option with default value |
||||
@param pvar [in] pointer to option variable |
||||
@param defaultVal [in] default value |
||||
@param opt [in] option name |
||||
@param help [in] option help |
||||
@note you can use 123k, 56M if T is int/long/long long |
||||
k : *1000 |
||||
m : *1000000 |
||||
g : *1000000000 |
||||
K : *1024 |
||||
M : *1024*1024 |
||||
G : *1024*1024*1024 |
||||
*/ |
||||
template<class T, class U> |
||||
void appendOpt(T *pvar, const U& defaultVal, const char *opt, const std::string& help = "") |
||||
{ |
||||
append(pvar, defaultVal, false, opt, help); |
||||
} |
||||
/*
|
||||
default value of *pvar is false |
||||
*/ |
||||
void appendBoolOpt(bool *pvar, const char *opt, const std::string& help = "") |
||||
{ |
||||
*pvar = false; |
||||
appendSub(pvar, N_is0, false, opt, help); |
||||
} |
||||
/*
|
||||
append necessary option |
||||
@param pvar [in] pointer to option variable |
||||
@param opt [in] option name |
||||
@param help [in] option help |
||||
*/ |
||||
template<class T> |
||||
void appendMust(T *pvar, const char *opt, const std::string& help = "") |
||||
{ |
||||
append(pvar, T(), true, opt, help); |
||||
} |
||||
/*
|
||||
append vector option |
||||
@param pvar [in] pointer to option variable |
||||
@param opt [in] option name |
||||
@param help [in] option help |
||||
*/ |
||||
template<class T, class Alloc, template<class T_, class Alloc_>class Container> |
||||
void appendVec(Container<T, Alloc> *pvar, const char *opt, const std::string& help = "") |
||||
{ |
||||
appendSub(pvar, N_any, false, opt, help); |
||||
} |
||||
/*
|
||||
append parameter |
||||
@param pvar [in] pointer to parameter |
||||
@param opt [in] option name |
||||
@param help [in] option help |
||||
*/ |
||||
template<class T> |
||||
void appendParam(T *pvar, const char *opt, const std::string& help = "") |
||||
{ |
||||
verifyParamMode(); |
||||
paramVec_.push_back(Info(pvar, N_is1, true, opt, help)); |
||||
} |
||||
/*
|
||||
append optional parameter |
||||
@param pvar [in] pointer to parameter |
||||
@param defaultVal [in] default value |
||||
@param opt [in] option name |
||||
@param help [in] option help |
||||
@note you can call appendParamOpt once after appendParam |
||||
*/ |
||||
template<class T, class U> |
||||
void appendParamOpt(T *pvar, const U& defaultVal, const char *opt, const std::string& help = "") |
||||
{ |
||||
verifyParamMode(); |
||||
*pvar = defaultVal; |
||||
paramMode_ = P_optional; |
||||
paramVec_.push_back(Info(pvar, N_is1, false, opt, help)); |
||||
} |
||||
/*
|
||||
append remain parameter |
||||
@param pvar [in] pointer to vector of parameter |
||||
@param opt [in] option name |
||||
@param help [in] option help |
||||
@note you can call appendParamVec once after appendParam |
||||
*/ |
||||
template<class T, class Alloc, template<class T_, class Alloc_>class Container> |
||||
void appendParamVec(Container<T, Alloc> *pvar, const char *name, const std::string& help = "") |
||||
{ |
||||
verifyParamMode(); |
||||
paramMode_ = P_variable; |
||||
remains_.var = option_local::Var(pvar); |
||||
remains_.mode = N_any; |
||||
remains_.isMust = false; |
||||
remains_.opt = name; |
||||
remains_.help = help; |
||||
} |
||||
void appendHelp(const char *opt, const std::string& help = ": show this message") |
||||
{ |
||||
helpOpt_ = opt; |
||||
help_ = help; |
||||
} |
||||
/*
|
||||
stop parsing after delimiter is found |
||||
@param delimiter [in] string to stop |
||||
@param remain [out] set remaining strings if remain |
||||
*/ |
||||
void setDelimiter(const std::string& delimiter, std::vector<std::string> *remain = 0) |
||||
{ |
||||
delimiters_.push_back(delimiter); |
||||
remainsAfterDelimiter_ = remain; |
||||
} |
||||
/*
|
||||
stop parsing after delimiter is found |
||||
@param delimiter [in] string to stop to append list of delimiters |
||||
*/ |
||||
void appendDelimiter(const std::string& delimiter) |
||||
{ |
||||
delimiters_.push_back(delimiter); |
||||
} |
||||
/*
|
||||
clear list of delimiters |
||||
*/ |
||||
void clearDelimiterList() { delimiters_.clear(); } |
||||
/*
|
||||
return the next position of delimiter between [0, argc] |
||||
@note return argc if delimiter is not set nor found |
||||
*/ |
||||
int getNextPositionOfDelimiter() const { return nextDelimiter_; } |
||||
/*
|
||||
parse (argc, argv) |
||||
@param argc [in] argc of main |
||||
@param argv [in] argv of main |
||||
@param startPos [in] start position of argc |
||||
@param progName [in] used instead of argv[0] |
||||
*/ |
||||
bool parse(int argc, const char *const argv[], int startPos = 1, const char *progName = 0) |
||||
{ |
||||
if (argc < 1 || startPos > argc) return false; |
||||
progName_ = getBaseName(progName ? progName : argv[startPos - 1]); |
||||
nextDelimiter_ = argc; |
||||
OptionError err; |
||||
for (int pos = startPos; pos < argc; pos++) { |
||||
if (inDelimiters(argv[pos])) { |
||||
nextDelimiter_ = pos + 1; |
||||
if (remainsAfterDelimiter_) { |
||||
for (int i = nextDelimiter_; i < argc; i++) { |
||||
remainsAfterDelimiter_->push_back(argv[i]); |
||||
} |
||||
} |
||||
break; |
||||
} |
||||
if (isOpt(argv[pos])) { |
||||
const std::string str = argv[pos] + 1; |
||||
if (helpOpt_ == str) { |
||||
usage(); |
||||
exit(0); |
||||
} |
||||
OptMap::const_iterator i = optMap_.find(str); |
||||
if (i == optMap_.end()) { |
||||
err.set(OptionError::BAD_OPT, pos); |
||||
goto ERR; |
||||
} |
||||
|
||||
Info& info = infoVec_[i->second]; |
||||
switch (info.mode) { |
||||
case N_is0: |
||||
if (!info.var.set("1")) { |
||||
err.set(OptionError::BAD_VALUE, pos); |
||||
goto ERR; |
||||
} |
||||
break; |
||||
case N_is1: |
||||
pos++; |
||||
if (pos == argc) { |
||||
err.set(OptionError::BAD_VALUE, pos) << (std::string("no value for -") + info.opt); |
||||
goto ERR; |
||||
} |
||||
if (!info.var.set(argv[pos])) { |
||||
err.set(OptionError::BAD_VALUE, pos) << (std::string(argv[pos]) + " for -" + info.opt); |
||||
goto ERR; |
||||
} |
||||
break; |
||||
case N_any: |
||||
default: |
||||
{ |
||||
pos++; |
||||
int j = 0; |
||||
while (pos < argc && !isOpt(argv[pos])) { |
||||
if (!info.var.set(argv[pos])) { |
||||
err.set(OptionError::BAD_VALUE, pos) << (std::string(argv[pos]) + " for -" + info.opt) << j; |
||||
goto ERR; |
||||
} |
||||
pos++; |
||||
j++; |
||||
} |
||||
if (j > 0) { |
||||
pos--; |
||||
} else { |
||||
err.set(OptionError::NO_VALUE, pos) << (std::string("for -") + info.opt); |
||||
goto ERR; |
||||
} |
||||
} |
||||
break; |
||||
} |
||||
} else { |
||||
bool used = false; |
||||
for (size_t i = 0; i < paramVec_.size(); i++) { |
||||
Info& param = paramVec_[i]; |
||||
if (!param.var.isSet()) { |
||||
if (!param.var.set(argv[pos])) { |
||||
err.set(OptionError::BAD_VALUE, pos) << (std::string(argv[pos]) + " for " + param.opt); |
||||
goto ERR; |
||||
} |
||||
used = true; |
||||
break; |
||||
} |
||||
} |
||||
if (!used) { |
||||
if (paramMode_ == P_variable) { |
||||
remains_.var.set(argv[pos]); |
||||
} else { |
||||
err.set(OptionError::REDUNDANT_VAL, pos) << argv[pos]; |
||||
goto ERR; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
// check whether must-opt is set
|
||||
for (size_t i = 0; i < infoVec_.size(); i++) { |
||||
const Info& info = infoVec_[i]; |
||||
if (info.isMust && !info.var.isSet()) { |
||||
err.set(OptionError::OPT_IS_NECESSARY) << info.opt; |
||||
goto ERR; |
||||
} |
||||
} |
||||
// check whether param is set
|
||||
for (size_t i = 0; i < paramVec_.size(); i++) { |
||||
const Info& param = paramVec_[i]; |
||||
if (param.isMust && !param.var.isSet()) { |
||||
err.set(OptionError::PARAM_IS_NECESSARY) << param.opt; |
||||
goto ERR; |
||||
} |
||||
} |
||||
// check whether remains is set
|
||||
if (paramMode_ == P_variable && remains_.isMust && !remains_.var.isSet()) { |
||||
err.set(OptionError::PARAM_IS_NECESSARY) << remains_.opt; |
||||
goto ERR; |
||||
} |
||||
return true; |
||||
ERR: |
||||
assert(err.type); |
||||
printf("%s\n", err.what()); |
||||
return false; |
||||
} |
||||
/*
|
||||
show desc at first in usage() |
||||
*/ |
||||
void setDescription(const std::string& desc) |
||||
{ |
||||
desc_ = desc; |
||||
} |
||||
/*
|
||||
show command line after desc |
||||
don't put option message if not showOptUsage |
||||
*/ |
||||
void setUsage(const std::string& usage, bool showOptUsage = false) |
||||
{ |
||||
usage_ = usage; |
||||
showOptUsage_ = showOptUsage; |
||||
} |
||||
void usage() const |
||||
{ |
||||
if (!desc_.empty()) printf("%s\n", desc_.c_str()); |
||||
if (usage_.empty()) { |
||||
printf("usage:%s", progName_.c_str()); |
||||
if (!infoVec_.empty()) printf(" [opt]"); |
||||
for (size_t i = 0; i < infoVec_.size(); i++) { |
||||
if (infoVec_[i].isMust) infoVec_[i].shortUsage(); |
||||
} |
||||
for (size_t i = 0; i < paramVec_.size(); i++) { |
||||
printf(" %s", paramVec_[i].opt.c_str()); |
||||
} |
||||
if (paramMode_ == P_variable) { |
||||
printf(" %s", remains_.opt.c_str()); |
||||
} |
||||
printf("\n"); |
||||
} else { |
||||
printf("%s\n", usage_.c_str()); |
||||
if (!showOptUsage_) return; |
||||
} |
||||
for (size_t i = 0; i < paramVec_.size(); i++) { |
||||
const Info& param = paramVec_[i]; |
||||
if (!param.help.empty()) printf(" %s %s\n", paramVec_[i].opt.c_str(), paramVec_[i].help.c_str()); |
||||
} |
||||
if (!remains_.help.empty()) printf(" %s %s\n", remains_.opt.c_str(), remains_.help.c_str()); |
||||
if (!helpOpt_.empty()) { |
||||
printf(" -%s %s\n", helpOpt_.c_str(), help_.c_str()); |
||||
} |
||||
for (size_t i = 0; i < infoVec_.size(); i++) { |
||||
infoVec_[i].usage(); |
||||
} |
||||
} |
||||
friend inline std::ostream& operator<<(std::ostream& os, const Option& self) |
||||
{ |
||||
for (size_t i = 0; i < self.paramVec_.size(); i++) { |
||||
const Info& param = self.paramVec_[i]; |
||||
os << param.opt << '=' << param.var.toStr() << std::endl; |
||||
} |
||||
if (self.paramMode_ == P_variable) { |
||||
os << "remains=" << self.remains_.var.toStr() << std::endl; |
||||
} |
||||
for (size_t i = 0; i < self.infoVec_.size(); i++) { |
||||
os << self.infoVec_[i] << std::endl; |
||||
} |
||||
return os; |
||||
} |
||||
void put() const |
||||
{ |
||||
std::cout << *this; |
||||
} |
||||
/*
|
||||
whether pvar is set or not |
||||
*/ |
||||
template<class T> |
||||
bool isSet(const T* pvar) const |
||||
{ |
||||
const void *p = static_cast<const void*>(pvar); |
||||
for (size_t i = 0; i < paramVec_.size(); i++) { |
||||
const Info& v = paramVec_[i]; |
||||
if (v.get() == p) return v.isSet(); |
||||
} |
||||
if (remains_.get() == p) return remains_.isSet(); |
||||
for (size_t i = 0; i < infoVec_.size(); i++) { |
||||
const Info& v = infoVec_[i]; |
||||
if (v.get() == p) return v.isSet(); |
||||
} |
||||
throw cybozu::Exception("Option:isSet:no assigned var") << pvar; |
||||
} |
||||
}; |
||||
|
||||
} // cybozu
|
@ -0,0 +1,139 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief pseudrandom generator |
||||
@author MITSUNARI Shigeo(@herumi) |
||||
@license modified new BSD license |
||||
http://opensource.org/licenses/BSD-3-Clause
|
||||
*/ |
||||
|
||||
#include <cybozu/exception.hpp> |
||||
#ifdef _WIN32 |
||||
#include <winsock2.h> |
||||
#include <windows.h> |
||||
#include <wincrypt.h> |
||||
#ifdef _MSC_VER |
||||
#pragma comment (lib, "advapi32.lib") |
||||
#endif |
||||
#include <cybozu/critical_section.hpp> |
||||
#else |
||||
#include <sys/types.h> |
||||
#include <fcntl.h> |
||||
#endif |
||||
|
||||
namespace cybozu { |
||||
|
||||
class RandomGenerator { |
||||
RandomGenerator(const RandomGenerator&); |
||||
void operator=(const RandomGenerator&); |
||||
public: |
||||
uint32_t operator()() |
||||
{ |
||||
return get32(); |
||||
} |
||||
uint32_t get32() |
||||
{ |
||||
uint32_t ret; |
||||
read(&ret, 1); |
||||
return ret; |
||||
} |
||||
uint64_t get64() |
||||
{ |
||||
uint64_t ret; |
||||
read(&ret, 1); |
||||
return ret; |
||||
} |
||||
#ifdef _WIN32 |
||||
RandomGenerator() |
||||
: prov_(0) |
||||
, pos_(bufSize) |
||||
{ |
||||
DWORD flagTbl[] = { 0, CRYPT_NEWKEYSET }; |
||||
for (int i = 0; i < 2; i++) { |
||||
if (CryptAcquireContext(&prov_, NULL, NULL, PROV_RSA_FULL, flagTbl[i]) != 0) return; |
||||
} |
||||
throw cybozu::Exception("randomgenerator"); |
||||
} |
||||
void read_inner(void *buf, size_t byteSize) |
||||
{ |
||||
if (CryptGenRandom(prov_, static_cast<DWORD>(byteSize), static_cast<BYTE*>(buf)) == 0) { |
||||
throw cybozu::Exception("randomgenerator:read") << byteSize; |
||||
} |
||||
} |
||||
~RandomGenerator() |
||||
{ |
||||
if (prov_) { |
||||
CryptReleaseContext(prov_, 0); |
||||
} |
||||
} |
||||
/*
|
||||
fill buf[0..bufNum-1] with random data |
||||
@note bufNum is not byte size |
||||
*/ |
||||
template<class T> |
||||
void read(T *buf, size_t bufNum) |
||||
{ |
||||
cybozu::AutoLockCs al(cs_); |
||||
const size_t byteSize = sizeof(T) * bufNum; |
||||
if (byteSize > bufSize) { |
||||
read_inner(buf, byteSize); |
||||
} else { |
||||
if (pos_ + byteSize > bufSize) { |
||||
read_inner(buf_, bufSize); |
||||
pos_ = 0; |
||||
} |
||||
memcpy(buf, buf_ + pos_, byteSize); |
||||
pos_ += byteSize; |
||||
} |
||||
} |
||||
private: |
||||
HCRYPTPROV prov_; |
||||
static const size_t bufSize = 1024; |
||||
char buf_[bufSize]; |
||||
size_t pos_; |
||||
cybozu::CriticalSection cs_; |
||||
#else |
||||
RandomGenerator() |
||||
: fp_(::fopen("/dev/urandom", "rb")) |
||||
{ |
||||
if (!fp_) throw cybozu::Exception("randomgenerator"); |
||||
} |
||||
~RandomGenerator() |
||||
{ |
||||
if (fp_) ::fclose(fp_); |
||||
} |
||||
/*
|
||||
fill buf[0..bufNum-1] with random data |
||||
@note bufNum is not byte size |
||||
*/ |
||||
template<class T> |
||||
void read(T *buf, size_t bufNum) |
||||
{ |
||||
const size_t byteSize = sizeof(T) * bufNum; |
||||
if (::fread(buf, 1, (int)byteSize, fp_) != byteSize) { |
||||
throw cybozu::Exception("randomgenerator:read") << byteSize; |
||||
} |
||||
} |
||||
#endif |
||||
private: |
||||
FILE *fp_; |
||||
}; |
||||
|
||||
template<class T, class RG> |
||||
void shuffle(T* v, size_t n, RG& rg) |
||||
{ |
||||
if (n <= 1) return; |
||||
for (size_t i = 0; i < n - 1; i++) { |
||||
size_t r = i + size_t(rg.get64() % (n - i)); |
||||
using namespace std; |
||||
swap(v[i], v[r]); |
||||
} |
||||
} |
||||
|
||||
template<class V, class RG> |
||||
void shuffle(V& v, RG& rg) |
||||
{ |
||||
shuffle(v.data(), v.size(), rg); |
||||
} |
||||
|
||||
} // cybozu
|
@ -0,0 +1,363 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief serializer for vector, list, map and so on |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#include <assert.h> |
||||
#include <cybozu/stream.hpp> |
||||
|
||||
#ifdef _MSC_VER |
||||
#pragma warning(push) |
||||
#pragma warning(disable : 4127) |
||||
#endif |
||||
|
||||
//#define CYBOZU_SERIALIZER_FIXED_SIZE_INTEGER
|
||||
|
||||
namespace cybozu { |
||||
|
||||
namespace serializer_local { |
||||
|
||||
template<class T> |
||||
union ci { |
||||
T i; |
||||
uint8_t c[sizeof(T)]; |
||||
}; |
||||
|
||||
template<class S, void (S::*)(size_t)> |
||||
struct HasMemFunc { }; |
||||
|
||||
template<class T> |
||||
void dispatch_reserve(T& t, size_t size, int, HasMemFunc<T, &T::reserve>* = 0) |
||||
{ |
||||
t.reserve(size); |
||||
} |
||||
|
||||
template<class T> |
||||
void dispatch_reserve(T&, size_t, int*) |
||||
{ |
||||
} |
||||
|
||||
template<class T> |
||||
void reserve_if_exists(T& t, size_t size) |
||||
{ |
||||
dispatch_reserve(t, size, 0); |
||||
} |
||||
|
||||
} // serializer_local
|
||||
|
||||
template<class InputStream, class T> |
||||
void loadRange(T *p, size_t num, InputStream& is) |
||||
{ |
||||
cybozu::read(p, num * sizeof(T), is); |
||||
} |
||||
|
||||
template<class OutputStream, class T> |
||||
void saveRange(OutputStream& os, const T *p, size_t num) |
||||
{ |
||||
cybozu::write(os, p, num * sizeof(T)); |
||||
} |
||||
|
||||
template<class InputStream, class T> |
||||
void loadPod(T& x, InputStream& is) |
||||
{ |
||||
serializer_local::ci<T> ci; |
||||
loadRange(ci.c, sizeof(ci.c), is); |
||||
x = ci.i; |
||||
} |
||||
|
||||
template<class OutputStream, class T> |
||||
void savePod(OutputStream& os, const T& x) |
||||
{ |
||||
serializer_local::ci<T> ci; |
||||
ci.i = x; |
||||
saveRange(os, ci.c, sizeof(ci.c)); |
||||
} |
||||
|
||||
template<class InputStream, class T> |
||||
void load(T& x, InputStream& is) |
||||
{ |
||||
x.load(is); |
||||
} |
||||
|
||||
template<class OutputStream, class T> |
||||
void save(OutputStream& os, const T& x) |
||||
{ |
||||
x.save(os); |
||||
} |
||||
|
||||
#define CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(type) \ |
||||
template<class InputStream>void load(type& x, InputStream& is) { loadPod(x, is); } \
|
||||
template<class OutputStream>void save(OutputStream& os, type x) { savePod(os, x); } |
||||
|
||||
CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(bool) |
||||
CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(char) |
||||
CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(short) |
||||
CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(unsigned char) |
||||
CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(unsigned short) |
||||
CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(wchar_t) |
||||
|
||||
CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(float) |
||||
CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(double) |
||||
|
||||
#ifdef CYBOZU_SERIALIZER_FIXED_SIZE_INTEGER |
||||
|
||||
#define CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER(type) CYBOZU_SERIALIZER_MAKE_SERIALIZER_F(type) |
||||
|
||||
#else |
||||
|
||||
namespace serializer_local { |
||||
|
||||
template<class S, class T> |
||||
bool isRecoverable(T x) |
||||
{ |
||||
return T(S(x)) == x; |
||||
} |
||||
/*
|
||||
data structure H:D of integer x |
||||
H:header(1byte) |
||||
0x80 ; D = 1 byte zero ext |
||||
0x81 ; D = 2 byte zero ext |
||||
0x82 ; D = 4 byte zero ext |
||||
0x83 ; D = 8 byte zero ext |
||||
0x84 ; D = 1 byte signed ext |
||||
0x85 ; D = 2 byte signed ext |
||||
0x86 ; D = 4 byte signed ext |
||||
0x87 ; D = 8 byte signed ext |
||||
other; x = signed H, D = none |
||||
*/ |
||||
template<class OutputStream, class T> |
||||
void saveVariableInt(OutputStream& os, const T& x) |
||||
{ |
||||
if (isRecoverable<int8_t>(x)) { |
||||
uint8_t u8 = uint8_t(x); |
||||
if (unsigned(u8 - 0x80) <= 7) { |
||||
savePod(os, uint8_t(0x84)); |
||||
} |
||||
savePod(os, u8); |
||||
} else if (isRecoverable<uint8_t>(x)) { |
||||
savePod(os, uint8_t(0x80)); |
||||
savePod(os, uint8_t(x)); |
||||
} else if (isRecoverable<uint16_t>(x) || isRecoverable<int16_t>(x)) { |
||||
savePod(os, uint8_t(isRecoverable<uint16_t>(x) ? 0x81 : 0x85)); |
||||
savePod(os, uint16_t(x)); |
||||
} else if (isRecoverable<uint32_t>(x) || isRecoverable<int32_t>(x)) { |
||||
savePod(os, uint8_t(isRecoverable<uint32_t>(x) ? 0x82 : 0x86)); |
||||
savePod(os, uint32_t(x)); |
||||
} else { |
||||
assert(sizeof(T) == 8); |
||||
savePod(os, uint8_t(0x83)); |
||||
savePod(os, uint64_t(x)); |
||||
} |
||||
} |
||||
|
||||
template<class InputStream, class T> |
||||
void loadVariableInt(T& x, InputStream& is) |
||||
{ |
||||
uint8_t h; |
||||
loadPod(h, is); |
||||
if (h == 0x80) { |
||||
uint8_t v; |
||||
loadPod(v, is); |
||||
x = v; |
||||
} else if (h == 0x81) { |
||||
uint16_t v; |
||||
loadPod(v, is); |
||||
x = v; |
||||
} else if (h == 0x82) { |
||||
uint32_t v; |
||||
loadPod(v, is); |
||||
x = v; |
||||
} else if (h == 0x83) { |
||||
if (sizeof(T) == 4) throw cybozu::Exception("loadVariableInt:bad header") << h; |
||||
uint64_t v; |
||||
loadPod(v, is); |
||||
x = static_cast<T>(v); |
||||
} else if (h == 0x84) { |
||||
int8_t v; |
||||
loadPod(v, is); |
||||
x = v; |
||||
} else if (h == 0x85) { |
||||
int16_t v; |
||||
loadPod(v, is); |
||||
x = v; |
||||
} else if (h == 0x86) { |
||||
int32_t v; |
||||
loadPod(v, is); |
||||
x = v; |
||||
} else if (h == 0x87) { |
||||
if (sizeof(T) == 4) throw cybozu::Exception("loadVariableInt:bad header") << h; |
||||
int64_t v; |
||||
loadPod(v, is); |
||||
x = static_cast<T>(v); |
||||
} else { |
||||
x = static_cast<int8_t>(h); |
||||
} |
||||
} |
||||
|
||||
} // serializer_local
|
||||
|
||||
#define CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER(type) \ |
||||
template<class InputStream>void load(type& x, InputStream& is) { serializer_local::loadVariableInt(x, is); } \
|
||||
template<class OutputStream>void save(OutputStream& os, type x) { serializer_local::saveVariableInt(os, x); } |
||||
|
||||
#endif |
||||
|
||||
CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER(int) |
||||
CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER(long) |
||||
CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER(long long) |
||||
CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER(unsigned int) |
||||
CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER(unsigned long) |
||||
CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER(unsigned long long) |
||||
|
||||
#undef CYBOZU_SERIALIZER_MAKE_INT_SERIALIZER |
||||
#undef CYBOZU_SERIALIZER_MAKE_UNT_SERIALIZER |
||||
#undef CYBOZU_SERIALIZER_MAKE_SERIALIZER_F |
||||
#undef CYBOZU_SERIALIZER_MAKE_SERIALIZER_V |
||||
|
||||
// only for std::vector<POD>
|
||||
template<class V, class InputStream> |
||||
void loadPodVec(V& v, InputStream& is) |
||||
{ |
||||
size_t size; |
||||
load(size, is); |
||||
v.resize(size); |
||||
if (size > 0) loadRange(&v[0], size, is); |
||||
} |
||||
|
||||
// only for std::vector<POD>
|
||||
template<class V, class OutputStream> |
||||
void savePodVec(OutputStream& os, const V& v) |
||||
{ |
||||
save(os, v.size()); |
||||
if (!v.empty()) saveRange(os, &v[0], v.size()); |
||||
} |
||||
|
||||
template<class InputStream> |
||||
void load(std::string& str, InputStream& is) |
||||
{ |
||||
loadPodVec(str, is); |
||||
} |
||||
|
||||
template<class OutputStream> |
||||
void save(OutputStream& os, const std::string& str) |
||||
{ |
||||
savePodVec(os, str); |
||||
} |
||||
|
||||
template<class OutputStream> |
||||
void save(OutputStream& os, const char *x) |
||||
{ |
||||
const size_t len = strlen(x); |
||||
save(os, len); |
||||
if (len > 0) saveRange(os, x, len); |
||||
} |
||||
|
||||
|
||||
// for vector, list
|
||||
template<class InputStream, class T, class Alloc, template<class T_, class Alloc_>class Container> |
||||
void load(Container<T, Alloc>& x, InputStream& is) |
||||
{ |
||||
size_t size; |
||||
load(size, is); |
||||
serializer_local::reserve_if_exists(x, size); |
||||
for (size_t i = 0; i < size; i++) { |
||||
x.push_back(T()); |
||||
T& t = x.back(); |
||||
load(t, is); |
||||
} |
||||
} |
||||
|
||||
template<class OutputStream, class T, class Alloc, template<class T_, class Alloc_>class Container> |
||||
void save(OutputStream& os, const Container<T, Alloc>& x) |
||||
{ |
||||
typedef Container<T, Alloc> V; |
||||
save(os, x.size()); |
||||
for (typename V::const_iterator i = x.begin(), end = x.end(); i != end; ++i) { |
||||
save(os, *i); |
||||
} |
||||
} |
||||
|
||||
// for set
|
||||
template<class InputStream, class K, class Pred, class Alloc, template<class K_, class Pred_, class Alloc_>class Container> |
||||
void load(Container<K, Pred, Alloc>& x, InputStream& is) |
||||
{ |
||||
size_t size; |
||||
load(size, is); |
||||
for (size_t i = 0; i < size; i++) { |
||||
K t; |
||||
load(t, is); |
||||
x.insert(t); |
||||
} |
||||
} |
||||
|
||||
template<class OutputStream, class K, class Pred, class Alloc, template<class K_, class Pred_, class Alloc_>class Container> |
||||
void save(OutputStream& os, const Container<K, Pred, Alloc>& x) |
||||
{ |
||||
typedef Container<K, Pred, Alloc> Set; |
||||
save(os, x.size()); |
||||
for (typename Set::const_iterator i = x.begin(), end = x.end(); i != end; ++i) { |
||||
save(os, *i); |
||||
} |
||||
} |
||||
|
||||
// for map
|
||||
template<class InputStream, class K, class V, class Pred, class Alloc, template<class K_, class V_, class Pred_, class Alloc_>class Container> |
||||
void load(Container<K, V, Pred, Alloc>& x, InputStream& is) |
||||
{ |
||||
typedef Container<K, V, Pred, Alloc> Map; |
||||
size_t size; |
||||
load(size, is); |
||||
for (size_t i = 0; i < size; i++) { |
||||
std::pair<typename Map::key_type, typename Map::mapped_type> vt; |
||||
load(vt.first, is); |
||||
load(vt.second, is); |
||||
x.insert(vt); |
||||
} |
||||
} |
||||
|
||||
template<class OutputStream, class K, class V, class Pred, class Alloc, template<class K_, class V_, class Pred_, class Alloc_>class Container> |
||||
void save(OutputStream& os, const Container<K, V, Pred, Alloc>& x) |
||||
{ |
||||
typedef Container<K, V, Pred, Alloc> Map; |
||||
save(os, x.size()); |
||||
for (typename Map::const_iterator i = x.begin(), end = x.end(); i != end; ++i) { |
||||
save(os, i->first); |
||||
save(os, i->second); |
||||
} |
||||
} |
||||
|
||||
// unordered_map
|
||||
template<class InputStream, class K, class V, class Hash, class Pred, class Alloc, template<class K_, class V_, class Hash_, class Pred_, class Alloc_>class Container> |
||||
void load(Container<K, V, Hash, Pred, Alloc>& x, InputStream& is) |
||||
{ |
||||
typedef Container<K, V, Hash, Pred, Alloc> Map; |
||||
size_t size; |
||||
load(size, is); |
||||
// x.reserve(size); // tr1::unordered_map may not have reserve
|
||||
cybozu::serializer_local::reserve_if_exists(x, size); |
||||
for (size_t i = 0; i < size; i++) { |
||||
std::pair<typename Map::key_type, typename Map::mapped_type> vt; |
||||
load(vt.first, is); |
||||
load(vt.second, is); |
||||
x.insert(vt); |
||||
} |
||||
} |
||||
|
||||
template<class OutputStream, class K, class V, class Hash, class Pred, class Alloc, template<class K_, class V_, class Hash_, class Pred_, class Alloc_>class Container> |
||||
void save(OutputStream& os, const Container<K, V, Hash, Pred, Alloc>& x) |
||||
{ |
||||
typedef Container<K, V, Hash, Pred, Alloc> Map; |
||||
save(os, x.size()); |
||||
for (typename Map::const_iterator i = x.begin(), end = x.end(); i != end; ++i) { |
||||
save(os, i->first); |
||||
save(os, i->second); |
||||
} |
||||
} |
||||
|
||||
} // cybozu
|
||||
|
||||
#ifdef _MSC_VER |
||||
#pragma warning(pop) |
||||
#endif |
@ -0,0 +1,438 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief SHA-256, SHA-512 class
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
@license modified new BSD license |
||||
http://opensource.org/licenses/BSD-3-Clause
|
||||
*/ |
||||
#include <cybozu/endian.hpp> |
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
#include <cybozu/itoa.hpp> |
||||
#include <string> |
||||
#endif |
||||
#include <memory.h> |
||||
#include <assert.h> |
||||
|
||||
namespace cybozu { |
||||
|
||||
namespace sha2_local { |
||||
|
||||
template<class T> |
||||
T min_(T x, T y) { return x < y ? x : y;; } |
||||
|
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
inline void uint32toHexStr(char *buf, const uint32_t *x, size_t n) |
||||
{ |
||||
for (size_t i = 0; i < n; i++) { |
||||
cybozu::itohex(buf + i * 8, 8, x[i], false); |
||||
} |
||||
} |
||||
|
||||
inline void uint64toHexStr(char *buf, const uint64_t *x, size_t n) |
||||
{ |
||||
for (size_t i = 0; i < n; i++) { |
||||
cybozu::itohex(buf + i * 16, 16, x[i], false); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
inline uint32_t rot32(uint32_t x, int s) |
||||
{ |
||||
#ifdef _MSC_VER |
||||
return _rotr(x, s); |
||||
#else |
||||
return (x >> s) | (x << (32 - s)); |
||||
#endif |
||||
} |
||||
|
||||
inline uint64_t rot64(uint64_t x, int s) |
||||
{ |
||||
#ifdef _MSC_VER |
||||
return _rotr64(x, s); |
||||
#else |
||||
return (x >> s) | (x << (64 - s)); |
||||
#endif |
||||
} |
||||
|
||||
} // cybozu::sha2_local
|
||||
|
||||
class Sha256 { |
||||
private: |
||||
static const size_t blockSize_ = 64; |
||||
static const size_t hSize_ = 8; |
||||
uint64_t totalSize_; |
||||
size_t roundBufSize_; |
||||
char roundBuf_[blockSize_]; |
||||
uint32_t h_[hSize_]; |
||||
static const size_t outByteSize_ = hSize_ * sizeof(uint32_t); |
||||
const uint32_t *k_; |
||||
|
||||
/**
|
||||
@param buf [in] buffer(64byte) |
||||
*/ |
||||
void round(const char *buf) |
||||
{ |
||||
using namespace sha2_local; |
||||
uint32_t w[64]; |
||||
for (int i = 0; i < 16; i++) { |
||||
w[i] = cybozu::Get32bitAsBE(&buf[i * 4]); |
||||
} |
||||
for (int i = 16 ; i < 64; i++) { |
||||
uint32_t t = w[i - 15]; |
||||
uint32_t s0 = rot32(t, 7) ^ rot32(t, 18) ^ (t >> 3); |
||||
t = w[i - 2]; |
||||
uint32_t s1 = rot32(t, 17) ^ rot32(t, 19) ^ (t >> 10); |
||||
w[i] = w[i - 16] + s0 + w[i - 7] + s1; |
||||
} |
||||
uint32_t a = h_[0]; |
||||
uint32_t b = h_[1]; |
||||
uint32_t c = h_[2]; |
||||
uint32_t d = h_[3]; |
||||
uint32_t e = h_[4]; |
||||
uint32_t f = h_[5]; |
||||
uint32_t g = h_[6]; |
||||
uint32_t h = h_[7]; |
||||
for (int i = 0; i < 64; i++) { |
||||
uint32_t s1 = rot32(e, 6) ^ rot32(e, 11) ^ rot32(e, 25); |
||||
uint32_t ch = g ^ (e & (f ^ g)); |
||||
uint32_t t1 = h + s1 + ch + k_[i] + w[i]; |
||||
uint32_t s0 = rot32(a, 2) ^ rot32(a, 13) ^ rot32(a, 22); |
||||
uint32_t maj = ((a | b) & c) | (a & b); |
||||
uint32_t t2 = s0 + maj; |
||||
h = g; |
||||
g = f; |
||||
f = e; |
||||
e = d + t1; |
||||
d = c; |
||||
c = b; |
||||
b = a; |
||||
a = t1 + t2; |
||||
} |
||||
h_[0] += a; |
||||
h_[1] += b; |
||||
h_[2] += c; |
||||
h_[3] += d; |
||||
h_[4] += e; |
||||
h_[5] += f; |
||||
h_[6] += g; |
||||
h_[7] += h; |
||||
totalSize_ += 64; |
||||
} |
||||
/*
|
||||
final phase |
||||
*/ |
||||
void term(const char *buf, size_t bufSize) |
||||
{ |
||||
assert(bufSize < blockSize_); |
||||
const uint64_t totalSize = totalSize_ + bufSize; |
||||
|
||||
uint8_t last[blockSize_]; |
||||
memcpy(last, buf, bufSize); |
||||
memset(&last[bufSize], 0, blockSize_ - bufSize); |
||||
last[bufSize] = uint8_t(0x80); /* top bit = 1 */ |
||||
if (bufSize >= blockSize_ - 8) { |
||||
round(reinterpret_cast<const char*>(last)); |
||||
memset(last, 0, sizeof(last)); // clear stack
|
||||
} |
||||
cybozu::Set32bitAsBE(&last[56], uint32_t(totalSize >> 29)); |
||||
cybozu::Set32bitAsBE(&last[60], uint32_t(totalSize * 8)); |
||||
round(reinterpret_cast<const char*>(last)); |
||||
} |
||||
public: |
||||
Sha256() |
||||
{ |
||||
clear(); |
||||
} |
||||
Sha256(const void *buf, size_t bufSize) |
||||
{ |
||||
clear(); |
||||
digest(buf, bufSize); |
||||
} |
||||
void clear() |
||||
{ |
||||
static const uint32_t kTbl[] = { |
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, |
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, |
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, |
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, |
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, |
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, |
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, |
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 |
||||
}; |
||||
k_ = kTbl; |
||||
totalSize_ = 0; |
||||
roundBufSize_ = 0; |
||||
h_[0] = 0x6a09e667; |
||||
h_[1] = 0xbb67ae85; |
||||
h_[2] = 0x3c6ef372; |
||||
h_[3] = 0xa54ff53a; |
||||
h_[4] = 0x510e527f; |
||||
h_[5] = 0x9b05688c; |
||||
h_[6] = 0x1f83d9ab; |
||||
h_[7] = 0x5be0cd19; |
||||
} |
||||
void update(const void *buf_, size_t bufSize) |
||||
{ |
||||
const char *buf = reinterpret_cast<const char*>(buf_); |
||||
if (bufSize == 0) return; |
||||
if (roundBufSize_ > 0) { |
||||
size_t size = sha2_local::min_(blockSize_ - roundBufSize_, bufSize); |
||||
memcpy(roundBuf_ + roundBufSize_, buf, size); |
||||
roundBufSize_ += size; |
||||
buf += size; |
||||
bufSize -= size; |
||||
} |
||||
if (roundBufSize_ == blockSize_) { |
||||
round(roundBuf_); |
||||
roundBufSize_ = 0; |
||||
} |
||||
while (bufSize >= blockSize_) { |
||||
assert(roundBufSize_ == 0); |
||||
round(buf); |
||||
buf += blockSize_; |
||||
bufSize -= blockSize_; |
||||
} |
||||
if (bufSize > 0) { |
||||
assert(bufSize < blockSize_); |
||||
assert(roundBufSize_ == 0); |
||||
memcpy(roundBuf_, buf, bufSize); |
||||
roundBufSize_ = bufSize; |
||||
} |
||||
assert(roundBufSize_ < blockSize_); |
||||
} |
||||
void digest(const void *buf, size_t bufSize) |
||||
{ |
||||
update(buf, bufSize); |
||||
term(roundBuf_, roundBufSize_); |
||||
} |
||||
size_t get(void *out) const |
||||
{ |
||||
char *p = reinterpret_cast<char*>(out); |
||||
for (size_t i = 0; i < hSize_; i++) { |
||||
cybozu::Set32bitAsBE(&p[i * sizeof(h_[0])], h_[i]); |
||||
} |
||||
return outByteSize_; |
||||
} |
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
void update(const std::string& buf) |
||||
{ |
||||
update(buf.c_str(), buf.size()); |
||||
} |
||||
void digest(const std::string& str = "") |
||||
{ |
||||
digest(str.c_str(), str.size()); |
||||
} |
||||
std::string get() const |
||||
{ |
||||
char out[outByteSize_]; |
||||
get(out); |
||||
return std::string(out, sizeof(out)); |
||||
} |
||||
std::string toHexStr() const |
||||
{ |
||||
char buf[outByteSize_ * 2]; |
||||
sha2_local::uint32toHexStr(buf, h_, hSize_); |
||||
return std::string(buf, sizeof(buf)); |
||||
} |
||||
#endif |
||||
}; |
||||
|
||||
class Sha512 { |
||||
private: |
||||
static const size_t blockSize_ = 128; |
||||
static const size_t hSize_ = 8; |
||||
uint64_t totalSize_; |
||||
size_t roundBufSize_; |
||||
char roundBuf_[blockSize_]; |
||||
uint64_t h_[hSize_]; |
||||
static const size_t outByteSize_ = hSize_ * sizeof(uint64_t); |
||||
const uint64_t *k_; |
||||
|
||||
template<size_t i0, size_t i1, size_t i2, size_t i3, size_t i4, size_t i5, size_t i6, size_t i7> |
||||
void round1(uint64_t *S, const uint64_t *w, size_t i) |
||||
{ |
||||
using namespace sha2_local; |
||||
uint64_t& a = S[i0]; |
||||
uint64_t& b = S[i1]; |
||||
uint64_t& c = S[i2]; |
||||
uint64_t& d = S[i3]; |
||||
uint64_t& e = S[i4]; |
||||
uint64_t& f = S[i5]; |
||||
uint64_t& g = S[i6]; |
||||
uint64_t& h = S[i7]; |
||||
|
||||
uint64_t s1 = rot64(e, 14) ^ rot64(e, 18) ^ rot64(e, 41); |
||||
uint64_t ch = g ^ (e & (f ^ g)); |
||||
uint64_t t0 = h + s1 + ch + k_[i] + w[i]; |
||||
uint64_t s0 = rot64(a, 28) ^ rot64(a, 34) ^ rot64(a, 39); |
||||
uint64_t maj = ((a | b) & c) | (a & b); |
||||
uint64_t t1 = s0 + maj; |
||||
d += t0; |
||||
h = t0 + t1; |
||||
} |
||||
/**
|
||||
@param buf [in] buffer(64byte) |
||||
*/ |
||||
void round(const char *buf) |
||||
{ |
||||
using namespace sha2_local; |
||||
uint64_t w[80]; |
||||
for (int i = 0; i < 16; i++) { |
||||
w[i] = cybozu::Get64bitAsBE(&buf[i * 8]); |
||||
} |
||||
for (int i = 16 ; i < 80; i++) { |
||||
uint64_t t = w[i - 15]; |
||||
uint64_t s0 = rot64(t, 1) ^ rot64(t, 8) ^ (t >> 7); |
||||
t = w[i - 2]; |
||||
uint64_t s1 = rot64(t, 19) ^ rot64(t, 61) ^ (t >> 6); |
||||
w[i] = w[i - 16] + s0 + w[i - 7] + s1; |
||||
} |
||||
uint64_t s[8]; |
||||
for (int i = 0; i < 8; i++) { |
||||
s[i] = h_[i]; |
||||
} |
||||
for (int i = 0; i < 80; i += 8) { |
||||
round1<0, 1, 2, 3, 4, 5, 6, 7>(s, w, i + 0); |
||||
round1<7, 0, 1, 2, 3, 4, 5, 6>(s, w, i + 1); |
||||
round1<6, 7, 0, 1, 2, 3, 4, 5>(s, w, i + 2); |
||||
round1<5, 6, 7, 0, 1, 2, 3, 4>(s, w, i + 3); |
||||
round1<4, 5, 6, 7, 0, 1, 2, 3>(s, w, i + 4); |
||||
round1<3, 4, 5, 6, 7, 0, 1, 2>(s, w, i + 5); |
||||
round1<2, 3, 4, 5, 6, 7, 0, 1>(s, w, i + 6); |
||||
round1<1, 2, 3, 4, 5, 6, 7, 0>(s, w, i + 7); |
||||
} |
||||
for (int i = 0; i < 8; i++) { |
||||
h_[i] += s[i]; |
||||
} |
||||
totalSize_ += blockSize_; |
||||
} |
||||
/*
|
||||
final phase |
||||
*/ |
||||
void term(const char *buf, size_t bufSize) |
||||
{ |
||||
assert(bufSize < blockSize_); |
||||
const uint64_t totalSize = totalSize_ + bufSize; |
||||
|
||||
uint8_t last[blockSize_]; |
||||
memcpy(last, buf, bufSize); |
||||
memset(&last[bufSize], 0, blockSize_ - bufSize); |
||||
last[bufSize] = uint8_t(0x80); /* top bit = 1 */ |
||||
if (bufSize >= blockSize_ - 16) { |
||||
round(reinterpret_cast<const char*>(last)); |
||||
memset(last, 0, sizeof(last)); // clear stack
|
||||
} |
||||
cybozu::Set64bitAsBE(&last[blockSize_ - 8], totalSize * 8); |
||||
round(reinterpret_cast<const char*>(last)); |
||||
} |
||||
public: |
||||
Sha512() |
||||
{ |
||||
clear(); |
||||
} |
||||
Sha512(const void *buf, size_t bufSize) |
||||
{ |
||||
clear(); |
||||
digest(buf, bufSize); |
||||
} |
||||
void clear() |
||||
{ |
||||
static const uint64_t kTbl[] = { |
||||
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, |
||||
0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, |
||||
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, |
||||
0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, |
||||
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL, |
||||
0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, |
||||
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, |
||||
0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, |
||||
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, |
||||
0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, |
||||
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, |
||||
0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, |
||||
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL, |
||||
0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, |
||||
0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, |
||||
0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL |
||||
}; |
||||
k_ = kTbl; |
||||
totalSize_ = 0; |
||||
roundBufSize_ = 0; |
||||
h_[0] = 0x6a09e667f3bcc908ull; |
||||
h_[1] = 0xbb67ae8584caa73bull; |
||||
h_[2] = 0x3c6ef372fe94f82bull; |
||||
h_[3] = 0xa54ff53a5f1d36f1ull; |
||||
h_[4] = 0x510e527fade682d1ull; |
||||
h_[5] = 0x9b05688c2b3e6c1full; |
||||
h_[6] = 0x1f83d9abfb41bd6bull; |
||||
h_[7] = 0x5be0cd19137e2179ull; |
||||
} |
||||
void update(const void *buf_, size_t bufSize) |
||||
{ |
||||
const char *buf = reinterpret_cast<const char*>(buf_); |
||||
if (bufSize == 0) return; |
||||
if (roundBufSize_ > 0) { |
||||
size_t size = sha2_local::min_(blockSize_ - roundBufSize_, bufSize); |
||||
memcpy(roundBuf_ + roundBufSize_, buf, size); |
||||
roundBufSize_ += size; |
||||
buf += size; |
||||
bufSize -= size; |
||||
} |
||||
if (roundBufSize_ == blockSize_) { |
||||
round(roundBuf_); |
||||
roundBufSize_ = 0; |
||||
} |
||||
while (bufSize >= blockSize_) { |
||||
assert(roundBufSize_ == 0); |
||||
round(buf); |
||||
buf += blockSize_; |
||||
bufSize -= blockSize_; |
||||
} |
||||
if (bufSize > 0) { |
||||
assert(bufSize < blockSize_); |
||||
assert(roundBufSize_ == 0); |
||||
memcpy(roundBuf_, buf, bufSize); |
||||
roundBufSize_ = bufSize; |
||||
} |
||||
assert(roundBufSize_ < blockSize_); |
||||
} |
||||
void digest(const void *buf, size_t bufSize) |
||||
{ |
||||
update(buf, bufSize); |
||||
term(roundBuf_, roundBufSize_); |
||||
} |
||||
size_t get(void *out) const |
||||
{ |
||||
char *p = reinterpret_cast<char*>(out); |
||||
for (size_t i = 0; i < hSize_; i++) { |
||||
cybozu::Set64bitAsBE(&p[i * sizeof(h_[0])], h_[i]); |
||||
} |
||||
return outByteSize_; |
||||
} |
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
void digest(const std::string& str = "") |
||||
{ |
||||
digest(str.c_str(), str.size()); |
||||
} |
||||
void update(const std::string& buf) |
||||
{ |
||||
update(buf.c_str(), buf.size()); |
||||
} |
||||
std::string get() const |
||||
{ |
||||
char out[outByteSize_]; |
||||
get(out); |
||||
return std::string(out, sizeof(out)); |
||||
} |
||||
std::string toHexStr() const |
||||
{ |
||||
char buf[outByteSize_ * 2]; |
||||
sha2_local::uint64toHexStr(buf, h_, hSize_); |
||||
return std::string(buf, sizeof(buf)); |
||||
} |
||||
#endif |
||||
}; |
||||
|
||||
} // cybozu
|
@ -0,0 +1,267 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief stream and line stream class
|
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
#include <string> |
||||
#include <iosfwd> |
||||
#endif |
||||
#include <cybozu/exception.hpp> |
||||
#include <memory.h> |
||||
|
||||
namespace cybozu { |
||||
|
||||
namespace stream_local { |
||||
|
||||
template <typename From, typename To> |
||||
struct is_convertible { |
||||
typedef char yes; |
||||
typedef int no; |
||||
|
||||
static no test(...); |
||||
static yes test(const To*); |
||||
static const bool value = sizeof(test(static_cast<const From*>(0))) == sizeof(yes); |
||||
}; |
||||
|
||||
template <bool b, class T = void> |
||||
struct enable_if { typedef T type; }; |
||||
|
||||
template <class T> |
||||
struct enable_if<false, T> {}; |
||||
|
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
/* specialization for istream */ |
||||
template<class InputStream> |
||||
size_t readSome_inner(void *buf, size_t size, InputStream& is, typename enable_if<is_convertible<InputStream, std::istream>::value>::type* = 0) |
||||
{ |
||||
if (size > 0x7fffffff) size = 0x7fffffff; |
||||
is.read(static_cast<char *>(buf), size); |
||||
const int64_t readSize = is.gcount(); |
||||
if (readSize < 0) return 0; |
||||
if (size == 1 && readSize == 0) is.clear(); |
||||
return static_cast<size_t>(readSize); |
||||
} |
||||
|
||||
/* generic version for size_t readSome(void *, size_t) */ |
||||
template<class InputStream> |
||||
size_t readSome_inner(void *buf, size_t size, InputStream& is, typename enable_if<!is_convertible<InputStream, std::istream>::value>::type* = 0) |
||||
{ |
||||
return is.readSome(buf, size); |
||||
} |
||||
#else |
||||
template<class InputStream> |
||||
size_t readSome_inner(void *buf, size_t size, InputStream& is) |
||||
{ |
||||
return is.readSome(buf, size); |
||||
} |
||||
#endif |
||||
|
||||
#ifndef CYBOZU_DONT_USE_EXCEPTION |
||||
/* specialization for ostream */ |
||||
template<class OutputStream> |
||||
void writeSub(OutputStream& os, const void *buf, size_t size, typename enable_if<is_convertible<OutputStream, std::ostream>::value>::type* = 0) |
||||
{ |
||||
if (!os.write(static_cast<const char *>(buf), size)) throw cybozu::Exception("stream:writeSub") << size; |
||||
} |
||||
#endif |
||||
|
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
/* generic version for void write(const void*, size_t), which writes all data */ |
||||
template<class OutputStream> |
||||
void writeSub(OutputStream& os, const void *buf, size_t size, typename enable_if<!is_convertible<OutputStream, std::ostream>::value>::type* = 0) |
||||
{ |
||||
os.write(buf, size); |
||||
} |
||||
|
||||
template<class OutputStream> |
||||
void writeSub(bool *pb, OutputStream& os, const void *buf, size_t size, typename enable_if<is_convertible<OutputStream, std::ostream>::value>::type* = 0) |
||||
{ |
||||
*pb = !!os.write(static_cast<const char *>(buf), size); |
||||
} |
||||
|
||||
/* generic version for void write(const void*, size_t), which writes all data */ |
||||
template<class OutputStream> |
||||
void writeSub(bool *pb, OutputStream& os, const void *buf, size_t size, typename enable_if<!is_convertible<OutputStream, std::ostream>::value>::type* = 0) |
||||
{ |
||||
os.write(pb, buf, size); |
||||
} |
||||
#else |
||||
template<class OutputStream> |
||||
void writeSub(bool *pb, OutputStream& os, const void *buf, size_t size) |
||||
{ |
||||
os.write(pb, buf, size); |
||||
} |
||||
#endif |
||||
|
||||
} // stream_local
|
||||
|
||||
/*
|
||||
make a specializaiton of class to use new InputStream, OutputStream |
||||
*/ |
||||
template<class InputStream> |
||||
struct InputStreamTag { |
||||
static size_t readSome(void *buf, size_t size, InputStream& is) |
||||
{ |
||||
return stream_local::readSome_inner<InputStream>(buf, size, is); |
||||
} |
||||
static bool readChar(char *c, InputStream& is) |
||||
{ |
||||
return readSome(c, 1, is) == 1; |
||||
} |
||||
}; |
||||
|
||||
template<class OutputStream> |
||||
struct OutputStreamTag { |
||||
static void write(OutputStream& os, const void *buf, size_t size) |
||||
{ |
||||
stream_local::writeSub<OutputStream>(os, buf, size); |
||||
} |
||||
}; |
||||
|
||||
class MemoryInputStream { |
||||
const char *p_; |
||||
size_t size_; |
||||
size_t pos; |
||||
public: |
||||
MemoryInputStream(const void *p, size_t size) : p_(static_cast<const char *>(p)), size_(size), pos(0) {} |
||||
size_t readSome(void *buf, size_t size) |
||||
{ |
||||
if (size > size_ - pos) size = size_ - pos; |
||||
memcpy(buf, p_ + pos, size); |
||||
pos += size; |
||||
return size; |
||||
} |
||||
size_t getPos() const { return pos; } |
||||
}; |
||||
|
||||
class MemoryOutputStream { |
||||
char *p_; |
||||
size_t size_; |
||||
size_t pos; |
||||
public: |
||||
MemoryOutputStream(void *p, size_t size) : p_(static_cast<char *>(p)), size_(size), pos(0) {} |
||||
void write(bool *pb, const void *buf, size_t size) |
||||
{ |
||||
if (size > size_ - pos) { |
||||
*pb = false; |
||||
return; |
||||
} |
||||
memcpy(p_ + pos, buf, size); |
||||
pos += size; |
||||
*pb = true; |
||||
} |
||||
#ifndef CYBOZU_DONT_USE_EXCEPTION |
||||
void write(const void *buf, size_t size) |
||||
{ |
||||
bool b; |
||||
write(&b, buf, size); |
||||
if (!b) throw cybozu::Exception("MemoryOutputStream:write") << size << size_ << pos; |
||||
} |
||||
#endif |
||||
size_t getPos() const { return pos; } |
||||
}; |
||||
|
||||
#ifndef CYBOZU_DONT_USE_STRING |
||||
class StringInputStream { |
||||
const std::string& str_; |
||||
size_t pos; |
||||
StringInputStream(const StringInputStream&); |
||||
void operator=(const StringInputStream&); |
||||
public: |
||||
explicit StringInputStream(const std::string& str) : str_(str), pos(0) {} |
||||
size_t readSome(void *buf, size_t size) |
||||
{ |
||||
const size_t remainSize = str_.size() - pos; |
||||
if (size > remainSize) size = remainSize; |
||||
memcpy(buf, &str_[pos], size); |
||||
pos += size; |
||||
return size; |
||||
} |
||||
size_t getPos() const { return pos; } |
||||
}; |
||||
|
||||
class StringOutputStream { |
||||
std::string& str_; |
||||
StringOutputStream(const StringOutputStream&); |
||||
void operator=(const StringOutputStream&); |
||||
public: |
||||
explicit StringOutputStream(std::string& str) : str_(str) {} |
||||
void write(bool *pb, const void *buf, size_t size) |
||||
{ |
||||
str_.append(static_cast<const char *>(buf), size); |
||||
*pb = true; |
||||
} |
||||
void write(const void *buf, size_t size) |
||||
{ |
||||
str_.append(static_cast<const char *>(buf), size); |
||||
} |
||||
size_t getPos() const { return str_.size(); } |
||||
}; |
||||
#endif |
||||
|
||||
template<class InputStream> |
||||
size_t readSome(void *buf, size_t size, InputStream& is) |
||||
{ |
||||
return stream_local::readSome_inner(buf, size, is); |
||||
} |
||||
|
||||
template<class OutputStream> |
||||
void write(OutputStream& os, const void *buf, size_t size) |
||||
{ |
||||
stream_local::writeSub(os, buf, size); |
||||
} |
||||
|
||||
template<class OutputStream> |
||||
void write(bool *pb, OutputStream& os, const void *buf, size_t size) |
||||
{ |
||||
stream_local::writeSub(pb, os, buf, size); |
||||
} |
||||
|
||||
template<typename InputStream> |
||||
void read(bool *pb, void *buf, size_t size, InputStream& is) |
||||
{ |
||||
char *p = static_cast<char*>(buf); |
||||
while (size > 0) { |
||||
size_t readSize = cybozu::readSome(p, size, is); |
||||
if (readSize == 0) { |
||||
*pb = false; |
||||
return; |
||||
} |
||||
p += readSize; |
||||
size -= readSize; |
||||
} |
||||
*pb = true; |
||||
} |
||||
|
||||
#ifndef CYBOZU_DONT_USE_EXCEPTION |
||||
template<typename InputStream> |
||||
void read(void *buf, size_t size, InputStream& is) |
||||
{ |
||||
bool b; |
||||
read(&b, buf, size, is); |
||||
if (!b) throw cybozu::Exception("stream:read"); |
||||
} |
||||
#endif |
||||
|
||||
template<class InputStream> |
||||
bool readChar(char *c, InputStream& is) |
||||
{ |
||||
return readSome(c, 1, is) == 1; |
||||
} |
||||
|
||||
template<class OutputStream> |
||||
void writeChar(OutputStream& os, char c) |
||||
{ |
||||
cybozu::write(os, &c, 1); |
||||
} |
||||
|
||||
template<class OutputStream> |
||||
void writeChar(bool *pb, OutputStream& os, char c) |
||||
{ |
||||
cybozu::write(pb, os, &c, 1); |
||||
} |
||||
|
||||
} // cybozu
|
@ -0,0 +1,373 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief unit test class
|
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <string> |
||||
#include <list> |
||||
#include <iostream> |
||||
#include <utility> |
||||
#if defined(_MSC_VER) && (MSC_VER <= 1500) |
||||
#include <cybozu/inttype.hpp> |
||||
#else |
||||
#include <stdint.h> |
||||
#endif |
||||
|
||||
namespace cybozu { namespace test { |
||||
|
||||
class AutoRun { |
||||
typedef void (*Func)(); |
||||
typedef std::list<std::pair<const char*, Func> > UnitTestList; |
||||
public: |
||||
AutoRun() |
||||
: init_(0) |
||||
, term_(0) |
||||
, okCount_(0) |
||||
, ngCount_(0) |
||||
, exceptionCount_(0) |
||||
{ |
||||
} |
||||
void setup(Func init, Func term) |
||||
{ |
||||
init_ = init; |
||||
term_ = term; |
||||
} |
||||
void append(const char *name, Func func) |
||||
{ |
||||
list_.push_back(std::make_pair(name, func)); |
||||
} |
||||
void set(bool isOK) |
||||
{ |
||||
if (isOK) { |
||||
okCount_++; |
||||
} else { |
||||
ngCount_++; |
||||
} |
||||
} |
||||
std::string getBaseName(const std::string& name) const |
||||
{ |
||||
#ifdef _WIN32 |
||||
const char sep = '\\'; |
||||
#else |
||||
const char sep = '/'; |
||||
#endif |
||||
size_t pos = name.find_last_of(sep); |
||||
std::string ret = name.substr(pos + 1); |
||||
pos = ret.find('.'); |
||||
return ret.substr(0, pos); |
||||
} |
||||
int run(int, char *argv[]) |
||||
{ |
||||
std::string msg; |
||||
try { |
||||
if (init_) init_(); |
||||
for (UnitTestList::const_iterator i = list_.begin(), ie = list_.end(); i != ie; ++i) { |
||||
std::cout << "ctest:module=" << i->first << std::endl; |
||||
try { |
||||
(i->second)(); |
||||
} catch (std::exception& e) { |
||||
exceptionCount_++; |
||||
std::cout << "ctest: " << i->first << " is stopped by exception " << e.what() << std::endl; |
||||
} catch (...) { |
||||
exceptionCount_++; |
||||
std::cout << "ctest: " << i->first << " is stopped by unknown exception" << std::endl; |
||||
} |
||||
} |
||||
if (term_) term_(); |
||||
} catch (std::exception& e) { |
||||
msg = std::string("ctest:err:") + e.what(); |
||||
} catch (...) { |
||||
msg = "ctest:err: catch unknown exception"; |
||||
} |
||||
fflush(stdout); |
||||
if (msg.empty()) { |
||||
int err = ngCount_ + exceptionCount_; |
||||
int total = okCount_ + err; |
||||
std::cout << "ctest:name=" << getBaseName(*argv) |
||||
<< ", module=" << list_.size() |
||||
<< ", total=" << total |
||||
<< ", ok=" << okCount_ |
||||
<< ", ng=" << ngCount_ |
||||
<< ", exception=" << exceptionCount_ << std::endl; |
||||
return err > 0 ? 1 : 0; |
||||
} else { |
||||
std::cout << msg << std::endl; |
||||
return 1; |
||||
} |
||||
} |
||||
static inline AutoRun& getInstance() |
||||
{ |
||||
static AutoRun instance; |
||||
return instance; |
||||
} |
||||
private: |
||||
Func init_; |
||||
Func term_; |
||||
int okCount_; |
||||
int ngCount_; |
||||
int exceptionCount_; |
||||
UnitTestList list_; |
||||
}; |
||||
|
||||
static AutoRun& autoRun = AutoRun::getInstance(); |
||||
|
||||
inline void test(bool ret, const std::string& msg, const std::string& param, const char *file, int line) |
||||
{ |
||||
autoRun.set(ret); |
||||
if (!ret) { |
||||
printf("%s(%d):ctest:%s(%s);\n", file, line, msg.c_str(), param.c_str()); |
||||
} |
||||
} |
||||
|
||||
template<typename T, typename U> |
||||
bool isEqual(const T& lhs, const U& rhs) |
||||
{ |
||||
return lhs == rhs; |
||||
} |
||||
|
||||
// avoid warning of comparision of integers of different signs
|
||||
inline bool isEqual(size_t lhs, int rhs) |
||||
{ |
||||
return lhs == size_t(rhs); |
||||
} |
||||
inline bool isEqual(int lhs, size_t rhs) |
||||
{ |
||||
return size_t(lhs) == rhs; |
||||
} |
||||
inline bool isEqual(const char *lhs, const char *rhs) |
||||
{ |
||||
return strcmp(lhs, rhs) == 0; |
||||
} |
||||
inline bool isEqual(char *lhs, const char *rhs) |
||||
{ |
||||
return strcmp(lhs, rhs) == 0; |
||||
} |
||||
inline bool isEqual(const char *lhs, char *rhs) |
||||
{ |
||||
return strcmp(lhs, rhs) == 0; |
||||
} |
||||
inline bool isEqual(char *lhs, char *rhs) |
||||
{ |
||||
return strcmp(lhs, rhs) == 0; |
||||
} |
||||
// avoid to compare float directly
|
||||
inline bool isEqual(float lhs, float rhs) |
||||
{ |
||||
union fi { |
||||
float f; |
||||
uint32_t i; |
||||
} lfi, rfi; |
||||
lfi.f = lhs; |
||||
rfi.f = rhs; |
||||
return lfi.i == rfi.i; |
||||
} |
||||
// avoid to compare double directly
|
||||
inline bool isEqual(double lhs, double rhs) |
||||
{ |
||||
union di { |
||||
double d; |
||||
uint64_t i; |
||||
} ldi, rdi; |
||||
ldi.d = lhs; |
||||
rdi.d = rhs; |
||||
return ldi.i == rdi.i; |
||||
} |
||||
|
||||
} } // cybozu::test
|
||||
|
||||
#ifndef CYBOZU_TEST_DISABLE_AUTO_RUN |
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
return cybozu::test::autoRun.run(argc, argv); |
||||
} |
||||
#endif |
||||
|
||||
/**
|
||||
alert if !x |
||||
@param x [in] |
||||
*/ |
||||
#define CYBOZU_TEST_ASSERT(x) cybozu::test::test(!!(x), "CYBOZU_TEST_ASSERT", #x, __FILE__, __LINE__) |
||||
|
||||
/**
|
||||
alert if x != y |
||||
@param x [in] |
||||
@param y [in] |
||||
*/ |
||||
#define CYBOZU_TEST_EQUAL(x, y) { \ |
||||
bool _cybozu_eq = cybozu::test::isEqual(x, y); \
|
||||
cybozu::test::test(_cybozu_eq, "CYBOZU_TEST_EQUAL", #x ", " #y, __FILE__, __LINE__); \
|
||||
if (!_cybozu_eq) { \
|
||||
std::cout << "ctest: lhs=" << (x) << std::endl; \
|
||||
std::cout << "ctest: rhs=" << (y) << std::endl; \
|
||||
} \
|
||||
} |
||||
/**
|
||||
alert if fabs(x, y) >= eps |
||||
@param x [in] |
||||
@param y [in] |
||||
*/ |
||||
#define CYBOZU_TEST_NEAR(x, y, eps) { \ |
||||
bool _cybozu_isNear = fabs((x) - (y)) < eps; \
|
||||
cybozu::test::test(_cybozu_isNear, "CYBOZU_TEST_NEAR", #x ", " #y, __FILE__, __LINE__); \
|
||||
if (!_cybozu_isNear) { \
|
||||
std::cout << "ctest: lhs=" << (x) << std::endl; \
|
||||
std::cout << "ctest: rhs=" << (y) << std::endl; \
|
||||
} \
|
||||
} |
||||
|
||||
#define CYBOZU_TEST_EQUAL_POINTER(x, y) { \ |
||||
bool _cybozu_eq = x == y; \
|
||||
cybozu::test::test(_cybozu_eq, "CYBOZU_TEST_EQUAL_POINTER", #x ", " #y, __FILE__, __LINE__); \
|
||||
if (!_cybozu_eq) { \
|
||||
std::cout << "ctest: lhs=" << static_cast<const void*>(x) << std::endl; \
|
||||
std::cout << "ctest: rhs=" << static_cast<const void*>(y) << std::endl; \
|
||||
} \
|
||||
} |
||||
/**
|
||||
alert if x[] != y[] |
||||
@param x [in] |
||||
@param y [in] |
||||
@param n [in] |
||||
*/ |
||||
#define CYBOZU_TEST_EQUAL_ARRAY(x, y, n) { \ |
||||
for (size_t _cybozu_test_i = 0, _cybozu_ie = (size_t)(n); _cybozu_test_i < _cybozu_ie; _cybozu_test_i++) { \
|
||||
bool _cybozu_eq = cybozu::test::isEqual((x)[_cybozu_test_i], (y)[_cybozu_test_i]); \
|
||||
cybozu::test::test(_cybozu_eq, "CYBOZU_TEST_EQUAL_ARRAY", #x ", " #y ", " #n, __FILE__, __LINE__); \
|
||||
if (!_cybozu_eq) { \
|
||||
std::cout << "ctest: i=" << _cybozu_test_i << std::endl; \
|
||||
std::cout << "ctest: lhs=" << (x)[_cybozu_test_i] << std::endl; \
|
||||
std::cout << "ctest: rhs=" << (y)[_cybozu_test_i] << std::endl; \
|
||||
} \
|
||||
} \
|
||||
} |
||||
|
||||
/**
|
||||
always alert |
||||
@param msg [in] |
||||
*/ |
||||
#define CYBOZU_TEST_FAIL(msg) cybozu::test::test(false, "CYBOZU_TEST_FAIL", msg, __FILE__, __LINE__) |
||||
|
||||
/**
|
||||
verify message in exception |
||||
*/ |
||||
#define CYBOZU_TEST_EXCEPTION_MESSAGE(statement, Exception, msg) \ |
||||
{ \
|
||||
int _cybozu_ret = 0; \
|
||||
std::string _cybozu_errMsg; \
|
||||
try { \
|
||||
statement; \
|
||||
_cybozu_ret = 1; \
|
||||
} catch (const Exception& _cybozu_e) { \
|
||||
_cybozu_errMsg = _cybozu_e.what(); \
|
||||
if (_cybozu_errMsg.find(msg) == std::string::npos) { \
|
||||
_cybozu_ret = 2; \
|
||||
} \
|
||||
} catch (...) { \
|
||||
_cybozu_ret = 3; \
|
||||
} \
|
||||
if (_cybozu_ret) { \
|
||||
cybozu::test::test(false, "CYBOZU_TEST_EXCEPTION_MESSAGE", #statement ", " #Exception ", " #msg, __FILE__, __LINE__); \
|
||||
if (_cybozu_ret == 1) { \
|
||||
std::cout << "ctest: no exception" << std::endl; \
|
||||
} else if (_cybozu_ret == 2) { \
|
||||
std::cout << "ctest: bad exception msg:" << _cybozu_errMsg << std::endl; \
|
||||
} else { \
|
||||
std::cout << "ctest: unexpected exception" << std::endl; \
|
||||
} \
|
||||
} else { \
|
||||
cybozu::test::autoRun.set(true); \
|
||||
} \
|
||||
} |
||||
|
||||
#define CYBOZU_TEST_EXCEPTION(statement, Exception) \ |
||||
{ \
|
||||
int _cybozu_ret = 0; \
|
||||
try { \
|
||||
statement; \
|
||||
_cybozu_ret = 1; \
|
||||
} catch (const Exception&) { \
|
||||
} catch (...) { \
|
||||
_cybozu_ret = 2; \
|
||||
} \
|
||||
if (_cybozu_ret) { \
|
||||
cybozu::test::test(false, "CYBOZU_TEST_EXCEPTION", #statement ", " #Exception, __FILE__, __LINE__); \
|
||||
if (_cybozu_ret == 1) { \
|
||||
std::cout << "ctest: no exception" << std::endl; \
|
||||
} else { \
|
||||
std::cout << "ctest: unexpected exception" << std::endl; \
|
||||
} \
|
||||
} else { \
|
||||
cybozu::test::autoRun.set(true); \
|
||||
} \
|
||||
} |
||||
|
||||
/**
|
||||
verify statement does not throw |
||||
*/ |
||||
#define CYBOZU_TEST_NO_EXCEPTION(statement) \ |
||||
try { \
|
||||
statement; \
|
||||
cybozu::test::autoRun.set(true); \
|
||||
} catch (...) { \
|
||||
cybozu::test::test(false, "CYBOZU_TEST_NO_EXCEPTION", #statement, __FILE__, __LINE__); \
|
||||
} |
||||
|
||||
/**
|
||||
append auto unit test |
||||
@param name [in] module name |
||||
*/ |
||||
#define CYBOZU_TEST_AUTO(name) \ |
||||
void cybozu_test_ ## name(); \
|
||||
struct cybozu_test_local_ ## name { \
|
||||
cybozu_test_local_ ## name() \
|
||||
{ \
|
||||
cybozu::test::autoRun.append(#name, cybozu_test_ ## name); \
|
||||
} \
|
||||
} cybozu_test_local_instance_ ## name; \
|
||||
void cybozu_test_ ## name() |
||||
|
||||
/**
|
||||
append auto unit test with fixture |
||||
@param name [in] module name |
||||
*/ |
||||
#define CYBOZU_TEST_AUTO_WITH_FIXTURE(name, Fixture) \ |
||||
void cybozu_test_ ## name(); \
|
||||
void cybozu_test_real_ ## name() \
|
||||
{ \
|
||||
Fixture f; \
|
||||
cybozu_test_ ## name(); \
|
||||
} \
|
||||
struct cybozu_test_local_ ## name { \
|
||||
cybozu_test_local_ ## name() \
|
||||
{ \
|
||||
cybozu::test::autoRun.append(#name, cybozu_test_real_ ## name); \
|
||||
} \
|
||||
} cybozu_test_local_instance_ ## name; \
|
||||
void cybozu_test_ ## name() |
||||
|
||||
/**
|
||||
setup fixture |
||||
@param Fixture [in] class name of fixture |
||||
@note cstr of Fixture is called before test and dstr of Fixture is called after test |
||||
*/ |
||||
#define CYBOZU_TEST_SETUP_FIXTURE(Fixture) \ |
||||
Fixture *cybozu_test_local_fixture; \
|
||||
void cybozu_test_local_init() \
|
||||
{ \
|
||||
cybozu_test_local_fixture = new Fixture(); \
|
||||
} \
|
||||
void cybozu_test_local_term() \
|
||||
{ \
|
||||
delete cybozu_test_local_fixture; \
|
||||
} \
|
||||
struct cybozu_test_local_fixture_setup_ { \
|
||||
cybozu_test_local_fixture_setup_() \
|
||||
{ \
|
||||
cybozu::test::autoRun.setup(cybozu_test_local_init, cybozu_test_local_term); \
|
||||
} \
|
||||
} cybozu_test_local_fixture_setup_instance_; |
@ -0,0 +1,13 @@ |
||||
#pragma once |
||||
|
||||
#include <cybozu/inttype.hpp> |
||||
|
||||
#ifdef CYBOZU_USE_BOOST |
||||
#include <boost/unordered_map.hpp> |
||||
#elif (CYBOZU_CPP_VERSION >= CYBOZU_CPP_VERSION_CPP11) || (defined __APPLE__) |
||||
#include <unordered_map> |
||||
#elif (CYBOZU_CPP_VERSION == CYBOZU_CPP_VERSION_TR1) |
||||
#include <list> |
||||
#include <tr1/unordered_map> |
||||
#endif |
||||
|
@ -0,0 +1,172 @@ |
||||
#pragma once |
||||
/**
|
||||
@file |
||||
@brief XorShift |
||||
|
||||
@author MITSUNARI Shigeo(@herumi) |
||||
@author MITSUNARI Shigeo |
||||
*/ |
||||
#include <cybozu/inttype.hpp> |
||||
|
||||
namespace cybozu { |
||||
|
||||
class XorShift { |
||||
uint32_t x_, y_, z_, w_; |
||||
public: |
||||
explicit XorShift(uint32_t x = 0, uint32_t y = 0, uint32_t z = 0, uint32_t w = 0) |
||||
{ |
||||
init(x, y, z, w); |
||||
} |
||||
void init(uint32_t x = 0, uint32_t y = 0, uint32_t z = 0, uint32_t w = 0) |
||||
{ |
||||
x_ = x ? x : 123456789; |
||||
y_ = y ? y : 362436069; |
||||
z_ = z ? z : 521288629; |
||||
w_ = w ? w : 88675123; |
||||
} |
||||
uint32_t get32() |
||||
{ |
||||
unsigned int t = x_ ^ (x_ << 11); |
||||
x_ = y_; y_ = z_; z_ = w_; |
||||
return w_ = (w_ ^ (w_ >> 19)) ^ (t ^ (t >> 8)); |
||||
} |
||||
uint32_t operator()() { return get32(); } |
||||
uint64_t get64() |
||||
{ |
||||
uint32_t a = get32(); |
||||
uint32_t b = get32(); |
||||
return (uint64_t(a) << 32) | b; |
||||
} |
||||
template<class T> |
||||
void read(T *x, size_t n) |
||||
{ |
||||
const size_t size = sizeof(T) * n; |
||||
uint8_t *p8 = static_cast<uint8_t*>(x); |
||||
for (size_t i = 0; i < size; i++) { |
||||
p8[i] = static_cast<uint8_t>(get32()); |
||||
} |
||||
} |
||||
void read(uint32_t *x, size_t n) |
||||
{ |
||||
for (size_t i = 0; i < n; i++) { |
||||
x[i] = get32(); |
||||
} |
||||
} |
||||
void read(uint64_t *x, size_t n) |
||||
{ |
||||
for (size_t i = 0; i < n; i++) { |
||||
x[i] = get64(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// see http://xorshift.di.unimi.it/xorshift128plus.c
|
||||
class XorShift128Plus { |
||||
uint64_t s_[2]; |
||||
static const uint64_t seed0 = 123456789; |
||||
static const uint64_t seed1 = 987654321; |
||||
public: |
||||
explicit XorShift128Plus(uint64_t s0 = seed0, uint64_t s1 = seed1) |
||||
{ |
||||
init(s0, s1); |
||||
} |
||||
void init(uint64_t s0 = seed0, uint64_t s1 = seed1) |
||||
{ |
||||
s_[0] = s0; |
||||
s_[1] = s1; |
||||
} |
||||
uint32_t get32() |
||||
{ |
||||
return static_cast<uint32_t>(get64()); |
||||
} |
||||
uint64_t operator()() { return get64(); } |
||||
uint64_t get64() |
||||
{ |
||||
uint64_t s1 = s_[0]; |
||||
const uint64_t s0 = s_[1]; |
||||
s_[0] = s0; |
||||
s1 ^= s1 << 23; |
||||
s_[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); |
||||
return s_[1] + s0; |
||||
} |
||||
template<class T> |
||||
void read(T *x, size_t n) |
||||
{ |
||||
const size_t size = sizeof(T) * n; |
||||
uint8_t *p8 = static_cast<uint8_t*>(x); |
||||
for (size_t i = 0; i < size; i++) { |
||||
p8[i] = static_cast<uint8_t>(get32()); |
||||
} |
||||
} |
||||
void read(uint32_t *x, size_t n) |
||||
{ |
||||
for (size_t i = 0; i < n; i++) { |
||||
x[i] = get32(); |
||||
} |
||||
} |
||||
void read(uint64_t *x, size_t n) |
||||
{ |
||||
for (size_t i = 0; i < n; i++) { |
||||
x[i] = get64(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// see http://xoroshiro.di.unimi.it/xoroshiro128plus.c
|
||||
class Xoroshiro128Plus { |
||||
uint64_t s_[2]; |
||||
static const uint64_t seed0 = 123456789; |
||||
static const uint64_t seed1 = 987654321; |
||||
uint64_t rotl(uint64_t x, unsigned int k) const |
||||
{ |
||||
return (x << k) | (x >> (64 - k)); |
||||
} |
||||
public: |
||||
explicit Xoroshiro128Plus(uint64_t s0 = seed0, uint64_t s1 = seed1) |
||||
{ |
||||
init(s0, s1); |
||||
} |
||||
void init(uint64_t s0 = seed0, uint64_t s1 = seed1) |
||||
{ |
||||
s_[0] = s0; |
||||
s_[1] = s1; |
||||
} |
||||
uint32_t get32() |
||||
{ |
||||
return static_cast<uint32_t>(get64()); |
||||
} |
||||
uint64_t operator()() { return get64(); } |
||||
uint64_t get64() |
||||
{ |
||||
uint64_t s0 = s_[0]; |
||||
uint64_t s1 = s_[1]; |
||||
uint64_t result = s0 + s1; |
||||
s1 ^= s0; |
||||
s_[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); |
||||
s_[1] = rotl(s1, 36); |
||||
return result; |
||||
} |
||||
template<class T> |
||||
void read(T *x, size_t n) |
||||
{ |
||||
const size_t size = sizeof(T) * n; |
||||
uint8_t *p8 = static_cast<uint8_t*>(x); |
||||
for (size_t i = 0; i < size; i++) { |
||||
p8[i] = static_cast<uint8_t>(get32()); |
||||
} |
||||
} |
||||
void read(uint32_t *x, size_t n) |
||||
{ |
||||
for (size_t i = 0; i < n; i++) { |
||||
x[i] = get32(); |
||||
} |
||||
} |
||||
void read(uint64_t *x, size_t n) |
||||
{ |
||||
for (size_t i = 0; i < n; i++) { |
||||
x[i] = get64(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
} // cybozu
|
@ -1,2 +1,2 @@ |
||||
set CFLAGS=/MT /DNOMINMAX /Ox /DNDEBUG /W4 /Zi /EHsc /nologo -I ./include -I../xbyak -I../cybozulib/include -I../cybozulib_ext/include |
||||
set CFLAGS=/MT /DNOMINMAX /Ox /DNDEBUG /W4 /Zi /EHsc /nologo -I./include -I../cybozulib_ext/include |
||||
set LDFLAGS=/LIBPATH:..\cybozulib_ext\lib /LIBPATH:.\lib |
||||
|
Loading…
Reference in new issue