1936 lines
43 KiB
C++
1936 lines
43 KiB
C++
//
|
|
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
|
|
// Copyright (c) YaPB Development Team.
|
|
//
|
|
// This software is licensed under the BSD-style license.
|
|
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
|
|
// https://yapb.ru/license
|
|
//
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
#include <platform.h>
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
#include <direct.h>
|
|
#else
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifndef PLATFORM_WIN32
|
|
#define _unlink unlink
|
|
#define _mkdir(p) mkdir (p, 0777)
|
|
#define stricmp strcasecmp
|
|
#else
|
|
#define stricmp _stricmp
|
|
#endif
|
|
|
|
#ifdef PLATFORM_HAS_SSE2
|
|
#include <immintrin.h>
|
|
#endif
|
|
|
|
#undef min
|
|
#undef max
|
|
|
|
// to remove ugly A_ prefixes
|
|
namespace cr {
|
|
|
|
namespace types {
|
|
|
|
using int8 = signed char;
|
|
using int16 = signed short;
|
|
using int32 = signed long;
|
|
using uint8 = unsigned char;
|
|
using uint16 = unsigned short;
|
|
using uint32 = unsigned long;
|
|
using uint64 = unsigned long long;
|
|
|
|
};
|
|
|
|
using namespace cr::types;
|
|
|
|
constexpr float ONEPSILON = 0.01f;
|
|
constexpr float EQEPSILON = 0.001f;
|
|
constexpr float FLEPSILON = 1.192092896e-07f;
|
|
|
|
constexpr float PI = 3.141592653589793115997963468544185161590576171875f;
|
|
constexpr float PI_RECIPROCAL = 1.0f / PI;
|
|
constexpr float PI_HALF = PI / 2;
|
|
|
|
constexpr float D2R = PI / 180.0f;
|
|
constexpr float R2D = 180.0f / PI;
|
|
|
|
// from metamod-p
|
|
static inline bool checkptr (void *ptr) {
|
|
#ifdef PLATFORM_WIN32
|
|
if (IsBadCodePtr (reinterpret_cast <FARPROC> (ptr))) {
|
|
return false;
|
|
}
|
|
#else
|
|
(void) (ptr);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
template <typename T, size_t N> constexpr size_t bufsize (const T (&)[N]) {
|
|
return N - 1;
|
|
}
|
|
|
|
template <typename T, size_t N> constexpr size_t arrsize (const T (&)[N]) {
|
|
return N;
|
|
}
|
|
|
|
constexpr float square (const float value) {
|
|
return value * value;
|
|
}
|
|
|
|
template <typename T> constexpr T min (const T a, const T b) {
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
template <typename T> constexpr T max (const T a, const T b) {
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
template <typename T> constexpr T clamp (const T x, const T a, const T b) {
|
|
return min (max (x, a), b);
|
|
}
|
|
|
|
template <typename T> constexpr T abs (const T a) {
|
|
return a > 0 ? a : -a;
|
|
}
|
|
|
|
template <typename T> constexpr T bit (const T a) {
|
|
return 1 << a;
|
|
}
|
|
|
|
static inline float powf (const float x, const float y) {
|
|
union {
|
|
float d;
|
|
int x;
|
|
} res { x };
|
|
|
|
res.x = static_cast <int> (y * (res.x - 1064866805) + 1064866805);
|
|
return res.d;
|
|
}
|
|
|
|
static inline float sqrtf (const float value) {
|
|
return powf (value, 0.5f);
|
|
}
|
|
|
|
static inline float sinf (const float value) {
|
|
const auto sign = static_cast <signed long> (value * PI_RECIPROCAL);
|
|
const float calc = (value - static_cast <float> (sign) * PI);
|
|
|
|
const float sqr = square (calc);
|
|
const float res = 1.00000000000000000000e+00f + sqr * (-1.66666671633720397949e-01f + sqr * (8.33333376795053482056e-03f + sqr * (-1.98412497411482036114e-04f +
|
|
sqr * (2.75565571428160183132e-06f + sqr * (-2.50368472620721149724e-08f + sqr * (1.58849267073435385100e-10f + sqr * -6.58925550841432672300e-13f))))));
|
|
|
|
return (sign & 1) ? -calc * res : value * res;
|
|
}
|
|
|
|
static inline float cosf (const float value) {
|
|
const auto sign = static_cast <signed long> (value * PI_RECIPROCAL);
|
|
const float calc = (value - static_cast <float> (sign) * PI);
|
|
|
|
const float sqr = square (calc);
|
|
const float res = sqr * (-5.00000000000000000000e-01f + sqr * (4.16666641831398010254e-02f + sqr * (-1.38888671062886714935e-03f + sqr * (2.48006890615215525031e-05f +
|
|
sqr * (-2.75369927749125054106e-07f + sqr * (2.06207229069832465029e-09f + sqr * -9.77507137733812925262e-12f))))));
|
|
|
|
const float f = -1.00000000000000000000e+00f;
|
|
|
|
return (sign & 1) ? f - res : -f + res;
|
|
}
|
|
|
|
static inline float tanf (const float value) {
|
|
return sinf (value) / cosf (value);
|
|
}
|
|
|
|
static inline float atan2f (const float y, const float x) {
|
|
auto atanf = [](const float x) {
|
|
const float sqr = square (x);
|
|
return x * (48.70107004404898384f + sqr * (49.5326263772254345f + sqr * 9.40604244231624f)) / (48.70107004404996166f + sqr * (65.7663163908956299f + sqr * (21.587934067020262f + sqr)));
|
|
};
|
|
|
|
const float ax = abs (x);
|
|
const float ay = abs (y);
|
|
|
|
if (ax < 1e-7f && ay < 1e-7f) {
|
|
return 0.0f;
|
|
}
|
|
|
|
if (ax > ay) {
|
|
if (x < 0.0f) {
|
|
if (y >= 0.0f) {
|
|
return atanf (y / x) + PI;
|
|
}
|
|
return atanf (y / x) - PI;
|
|
}
|
|
return atanf (y / x);
|
|
}
|
|
|
|
if (y < 0.0f) {
|
|
return atanf (-x / y) - PI_HALF;
|
|
}
|
|
return atanf (-x / y) + PI_HALF;
|
|
}
|
|
|
|
static inline float ceilf (const float x) {
|
|
return static_cast <float> (65536 - static_cast <int> (65536.0f - x));
|
|
}
|
|
|
|
static inline void sincosf (const float x, const float y, const float z, float *sines, float *cosines) {
|
|
// this is the only place where sse2 stuff actually faster than chebyshev pads as we're can calculate 3 sines + 3 cosines
|
|
// using only two sse calls instead of 6 calls to sin/cos with standard functions
|
|
|
|
#if defined (PLATFORM_HAS_SSE2)
|
|
auto inputSet = _mm_set_ps (x, y, z, 0.0f);
|
|
|
|
auto _mm_sin = [] (__m128 rad) -> __m128 {
|
|
static auto pi2 = _mm_set_ps1 (PI * 2);
|
|
static auto rp1 = _mm_set_ps1 (4.0f / PI);
|
|
static auto rp2 = _mm_set_ps1 (-4.0f / (PI * PI));
|
|
static auto val = _mm_cmpnlt_ps (rad, _mm_set_ps1 (PI));
|
|
static auto csi = _mm_castsi128_ps (_mm_set1_epi32 (0x80000000));
|
|
|
|
val = _mm_and_ps (val, pi2);
|
|
rad = _mm_sub_ps (rad, val);
|
|
val = _mm_cmpngt_ps (rad, _mm_set_ps1 (-PI));
|
|
val = _mm_and_ps (val, pi2);
|
|
rad = _mm_add_ps (rad, val);
|
|
val = _mm_mul_ps (_mm_andnot_ps (csi, rad), rp2);
|
|
val = _mm_add_ps (val, rp1);
|
|
|
|
auto si = _mm_mul_ps (val, rad);
|
|
|
|
val = _mm_mul_ps (_mm_andnot_ps (csi, si), si);
|
|
val = _mm_sub_ps (val, si);
|
|
val = _mm_mul_ps (val, _mm_set_ps1 (0.225f));
|
|
|
|
return _mm_add_ps (val, si);
|
|
};
|
|
static auto hpi = _mm_set_ps1 (PI_HALF);
|
|
|
|
auto s = _mm_sin (inputSet);
|
|
auto c = _mm_sin (_mm_add_ps (inputSet, hpi));
|
|
|
|
_mm_store_ps (sines, _mm_shuffle_ps (s, s, _MM_SHUFFLE (0, 1, 2, 3)));
|
|
_mm_store_ps (cosines, _mm_shuffle_ps (c, c, _MM_SHUFFLE (0, 1, 2, 3)));
|
|
#else
|
|
sines[0] = sinf (x);
|
|
sines[1] = sinf (y);
|
|
sines[2] = sinf (z);
|
|
|
|
cosines[0] = cosf (x);
|
|
cosines[1] = cosf (y);
|
|
cosines[2] = cosf (z);
|
|
#endif
|
|
}
|
|
|
|
constexpr bool fzero (const float entry) {
|
|
return abs (entry) < ONEPSILON;
|
|
}
|
|
|
|
constexpr bool fequal (const float entry1, const float entry2) {
|
|
return abs (entry1 - entry2) < EQEPSILON;
|
|
}
|
|
|
|
constexpr float rad2deg (const float radian) {
|
|
return radian * R2D;
|
|
}
|
|
|
|
constexpr float deg2rad (const float degree) {
|
|
return degree * D2R;
|
|
}
|
|
|
|
constexpr float angleMod (const float angle) {
|
|
return 360.0f / 65536.0f * (static_cast <int> (angle * (65536.0f / 360.0f)) & 65535);
|
|
}
|
|
|
|
constexpr float angleNorm (const float angle) {
|
|
return 360.0f / 65536.0f * (static_cast <int> ((angle + 180.0f) * (65536.0f / 360.0f)) & 65535) - 180.0f;
|
|
}
|
|
|
|
constexpr float angleDiff (const float dest, const float src) {
|
|
return angleNorm (dest - src);
|
|
}
|
|
|
|
template <typename T> struct ClearRef {
|
|
using Type = T;
|
|
};
|
|
|
|
template <typename T> struct ClearRef <T &> {
|
|
using Type = T;
|
|
};
|
|
|
|
template <typename T> struct ClearRef <T &&> {
|
|
using Type = T;
|
|
};
|
|
|
|
template <typename T> static inline typename ClearRef <T>::Type &&move (T &&type) {
|
|
return static_cast <typename ClearRef <T>::Type &&> (type);
|
|
}
|
|
|
|
template <typename T> static inline void swap (T &left, T &right) {
|
|
auto temp = move (left);
|
|
left = move (right);
|
|
right = move (temp);
|
|
}
|
|
|
|
template <typename T> static constexpr inline T &&forward (typename ClearRef <T>::Type &type) noexcept {
|
|
return static_cast <T &&> (type);
|
|
}
|
|
|
|
template <typename T> static constexpr inline T &&forward (typename ClearRef <T>::Type &&type) noexcept {
|
|
return static_cast <T &&> (type);
|
|
}
|
|
|
|
template <typename T> static inline void transfer (T *dest, T *src, size_t length) {
|
|
for (size_t i = 0; i < length; i++) {
|
|
dest[i] = move (src[i]);
|
|
}
|
|
}
|
|
|
|
namespace classes {
|
|
|
|
class NonCopyable {
|
|
protected:
|
|
NonCopyable (void) = default;
|
|
~NonCopyable (void) = default;
|
|
|
|
public:
|
|
NonCopyable (const NonCopyable &) = delete;
|
|
NonCopyable &operator = (const NonCopyable &) = delete;
|
|
};
|
|
|
|
template <typename T> class Singleton : private NonCopyable {
|
|
public:
|
|
inline static T *ptr (void) {
|
|
return &ref ();
|
|
}
|
|
|
|
inline static T &ref (void) {
|
|
static T ref;
|
|
return ref;
|
|
};
|
|
};
|
|
|
|
// see: https://github.com/preshing/RandomSequence/
|
|
class RandomSequence : public Singleton <RandomSequence> {
|
|
private:
|
|
uint32 m_index;
|
|
uint32 m_intermediateOffset;
|
|
uint64 m_divider;
|
|
|
|
private:
|
|
uint32 premute (uint32 x) {
|
|
static constexpr uint32 prime = 4294967291u;
|
|
|
|
if (x >= prime) {
|
|
return x;
|
|
}
|
|
const uint32 residue = (static_cast <unsigned long long> (x) * x) % prime;
|
|
return (x <= prime / 2) ? residue : prime - residue;
|
|
}
|
|
|
|
uint32 random (void) {
|
|
return premute ((premute (m_index++) + m_intermediateOffset) ^ 0x5bf03635);
|
|
}
|
|
|
|
public:
|
|
RandomSequence (void) {
|
|
const auto seedBase = static_cast <uint32> (time (nullptr));
|
|
const auto seedOffset = seedBase + 1;
|
|
|
|
m_index = premute (premute (seedBase) + 0x682f0161);
|
|
m_intermediateOffset = premute (premute (seedOffset) + 0x46790905);
|
|
m_divider = (static_cast <uint64> (1)) << 32;
|
|
}
|
|
|
|
template <typename U> inline U getInt (U low, U high) {
|
|
return static_cast <U> (random () * (static_cast <double> (high) - static_cast <double> (low) + 1.0) / m_divider + static_cast <double> (low));
|
|
}
|
|
|
|
inline float getFloat (float low, float high) {
|
|
return static_cast <float> (random () * (static_cast <double> (high) - static_cast <double> (low)) / (m_divider - 1) + static_cast <double> (low));
|
|
}
|
|
|
|
template <typename U> inline bool chance (const U max, const U maxChance = 100) {
|
|
return getInt <U> (0, maxChance) < max;
|
|
}
|
|
};
|
|
|
|
class SimpleColor final : private NonCopyable {
|
|
public:
|
|
int red = 0, green = 0, blue = 0;
|
|
|
|
inline void reset (void) {
|
|
red = green = blue = 0;
|
|
}
|
|
|
|
inline int avg (void) const {
|
|
return sum () / (sizeof (SimpleColor) / sizeof (int));
|
|
}
|
|
|
|
inline int sum (void) const {
|
|
return red + green + blue;
|
|
}
|
|
};
|
|
|
|
class Vector final {
|
|
public:
|
|
float x, y, z;
|
|
|
|
public:
|
|
inline Vector (float scaler = 0.0f) : x (scaler), y (scaler), z (scaler) {}
|
|
inline Vector (float inputX, float inputY, float inputZ) : x (inputX), y (inputY), z (inputZ) {}
|
|
inline Vector (float *other) : x (other[0]), y (other[1]), z (other[2]) {}
|
|
inline Vector (const Vector &right) = default;
|
|
|
|
public:
|
|
inline operator float * (void) {
|
|
return &x;
|
|
}
|
|
|
|
inline operator const float * (void) const {
|
|
return &x;
|
|
}
|
|
|
|
inline Vector operator + (const Vector &right) const {
|
|
return Vector (x + right.x, y + right.y, z + right.z);
|
|
}
|
|
|
|
inline Vector operator - (const Vector &right) const {
|
|
return Vector (x - right.x, y - right.y, z - right.z);
|
|
}
|
|
|
|
inline Vector operator - (void) const {
|
|
return Vector (-x, -y, -z);
|
|
}
|
|
|
|
friend inline Vector operator * (const float vec, const Vector &right) {
|
|
return Vector (right.x * vec, right.y * vec, right.z * vec);
|
|
}
|
|
|
|
inline Vector operator * (float vec) const {
|
|
return Vector (vec * x, vec * y, vec * z);
|
|
}
|
|
|
|
inline Vector operator / (float vec) const {
|
|
const float inv = 1 / vec;
|
|
return Vector (inv * x, inv * y, inv * z);
|
|
}
|
|
|
|
// cross product
|
|
inline Vector operator ^ (const Vector &right) const {
|
|
return Vector (y * right.z - z * right.y, z * right.x - x * right.z, x * right.y - y * right.x);
|
|
}
|
|
|
|
// dot product
|
|
inline float operator | (const Vector &right) const {
|
|
return x * right.x + y * right.y + z * right.z;
|
|
}
|
|
|
|
inline const Vector &operator += (const Vector &right) {
|
|
x += right.x;
|
|
y += right.y;
|
|
z += right.z;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline const Vector &operator -= (const Vector &right) {
|
|
x -= right.x;
|
|
y -= right.y;
|
|
z -= right.z;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline const Vector &operator *= (float vec) {
|
|
x *= vec;
|
|
y *= vec;
|
|
z *= vec;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline const Vector &operator /= (float vec) {
|
|
const float inv = 1 / vec;
|
|
|
|
x *= inv;
|
|
y *= inv;
|
|
z *= inv;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline bool operator == (const Vector &right) const {
|
|
return fequal (x, right.x) && fequal (y, right.y) && fequal (z, right.z);
|
|
}
|
|
|
|
inline bool operator != (const Vector &right) const {
|
|
return !fequal (x, right.x) && !fequal (y, right.y) && !fequal (z, right.z);
|
|
}
|
|
|
|
inline Vector &operator = (const Vector &right) = default;
|
|
|
|
public:
|
|
inline float length (void) const {
|
|
return sqrtf (x * x + y * y + z * z);
|
|
}
|
|
|
|
inline float length2D (void) const {
|
|
return sqrtf (x * x + y * y);
|
|
}
|
|
|
|
inline float lengthSq (void) const {
|
|
return x * x + y * y + z * z;
|
|
}
|
|
|
|
inline Vector make2D (void) const {
|
|
return Vector (x, y, 0.0f);
|
|
}
|
|
|
|
inline Vector normalize (void) const {
|
|
float len = length () + static_cast <float> (FLEPSILON);
|
|
|
|
if (fzero (len)) {
|
|
return Vector (0.0f, 0.0f, 1.0f);
|
|
}
|
|
len = 1.0f / len;
|
|
return Vector (x * len, y * len, z * len);
|
|
}
|
|
|
|
inline Vector normalize2D (void) const {
|
|
float len = length2D () + static_cast <float> (FLEPSILON);
|
|
|
|
if (fzero (len)) {
|
|
return Vector (0.0f, 1.0f, 0.0f);
|
|
}
|
|
len = 1.0f / len;
|
|
return Vector (x * len, y * len, 0.0f);
|
|
}
|
|
|
|
inline bool empty (void) const {
|
|
return fzero (x) && fzero (y) && fzero (z);
|
|
}
|
|
|
|
inline static const Vector &null (void) {
|
|
static const Vector s_zero = Vector (0.0f, 0.0f, 0.0f);
|
|
return s_zero;
|
|
}
|
|
|
|
inline void nullify (void) {
|
|
x = y = z = 0.0f;
|
|
}
|
|
|
|
inline Vector clampAngles (void) {
|
|
x = angleNorm (x);
|
|
y = angleNorm (y);
|
|
z = 0.0f;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline float toPitch (void) const {
|
|
if (fzero (x) && fzero (y)) {
|
|
return 0.0f;
|
|
}
|
|
return rad2deg (atan2f (z, length2D ()));
|
|
}
|
|
|
|
inline float toYaw (void) const {
|
|
if (fzero (x) && fzero (y)) {
|
|
return 0.0f;
|
|
}
|
|
return rad2deg (atan2f (y, x));
|
|
}
|
|
|
|
inline Vector toAngles (void) const {
|
|
if (fzero (x) && fzero (y)) {
|
|
return Vector (z > 0.0f ? 90.0f : 270.0f, 0.0, 0.0f);
|
|
}
|
|
return Vector (rad2deg (atan2f (z, length2D ())), rad2deg (atan2f (y, x)), 0.0f);
|
|
}
|
|
|
|
inline void makeVectors (Vector *forward, Vector *right, Vector *upward) const {
|
|
enum { pitch, yaw, roll, unused, max };
|
|
|
|
float sines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
float cosines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
|
|
// compute the sine and cosine compontents
|
|
sincosf (deg2rad (x), deg2rad (y), deg2rad (z), sines, cosines);
|
|
|
|
if (forward) {
|
|
forward->x = cosines[pitch] * cosines[yaw];
|
|
forward->y = cosines[pitch] * sines[yaw];
|
|
forward->z = -sines[pitch];
|
|
}
|
|
|
|
if (right) {
|
|
right->x = -sines[roll] * sines[pitch] * cosines[yaw] + cosines[roll] * sines[yaw];
|
|
right->y = -sines[roll] * sines[pitch] * sines[yaw] - cosines[roll] * cosines[yaw];
|
|
right->z = -sines[roll] * cosines[pitch];
|
|
}
|
|
|
|
if (upward) {
|
|
upward->x = cosines[roll] * sines[pitch] * cosines[yaw] + sines[roll] * sines[yaw];
|
|
upward->y = cosines[roll] * sines[pitch] * sines[yaw] - sines[roll] * cosines[yaw];
|
|
upward->z = cosines[roll] * cosines[pitch];
|
|
}
|
|
}
|
|
};
|
|
|
|
class Library final : private NonCopyable {
|
|
private:
|
|
void *m_ptr = nullptr;
|
|
|
|
public:
|
|
explicit Library (void) = default;
|
|
|
|
Library (const char *filename) {
|
|
if (!filename) {
|
|
return;
|
|
}
|
|
load (filename);
|
|
}
|
|
|
|
virtual ~Library (void) {
|
|
unload ();
|
|
}
|
|
|
|
public:
|
|
inline void *load (const char *filename) noexcept {
|
|
#ifdef PLATFORM_WIN32
|
|
m_ptr = LoadLibrary (filename);
|
|
#else
|
|
m_ptr = dlopen (filename, RTLD_NOW);
|
|
#endif
|
|
return m_ptr;
|
|
}
|
|
|
|
inline void unload (void) noexcept {
|
|
if (!isValid ()) {
|
|
return;
|
|
}
|
|
#ifdef PLATFORM_WIN32
|
|
FreeLibrary (static_cast <HMODULE> (m_ptr));
|
|
#else
|
|
dlclose (m_ptr);
|
|
#endif
|
|
}
|
|
|
|
template <typename R> R resolve (const char *function) {
|
|
if (!isValid ()) {
|
|
return nullptr;
|
|
}
|
|
return reinterpret_cast <R> (
|
|
#ifdef PLATFORM_WIN32
|
|
GetProcAddress (static_cast <HMODULE> (m_ptr), function)
|
|
#else
|
|
dlsym (m_ptr, function)
|
|
#endif
|
|
);
|
|
}
|
|
|
|
template <typename R> R handle (void) {
|
|
return static_cast <R> (m_ptr);
|
|
}
|
|
|
|
inline bool isValid (void) const {
|
|
return m_ptr != nullptr;
|
|
}
|
|
};
|
|
|
|
template <typename A, typename B> class Pair final {
|
|
public:
|
|
A first = move (A ());
|
|
B second = move (B ());
|
|
|
|
public:
|
|
Pair (const A &a, const B &b) : first (move (a)), second (move (b)) {
|
|
}
|
|
|
|
public:
|
|
Pair (void) = default;
|
|
~Pair (void) = default;
|
|
};
|
|
|
|
template <typename T> class Array : private NonCopyable {
|
|
public:
|
|
static constexpr size_t INVALID_INDEX = static_cast <size_t> (-1);
|
|
|
|
protected:
|
|
T *m_data = nullptr;
|
|
size_t m_capacity = 0;
|
|
size_t m_length = 0;
|
|
|
|
public:
|
|
Array (void) = default;
|
|
|
|
Array (Array &&other) noexcept {
|
|
m_data = other.m_data;
|
|
m_length = other.m_length;
|
|
m_capacity = other.m_capacity;
|
|
|
|
other.reset ();
|
|
}
|
|
|
|
~Array (void) {
|
|
destroy ();
|
|
}
|
|
|
|
public:
|
|
void destroy (void) {
|
|
delete[] m_data;
|
|
reset ();
|
|
}
|
|
|
|
void reset (void) {
|
|
m_data = nullptr;
|
|
m_capacity = 0;
|
|
m_length = 0;
|
|
}
|
|
|
|
bool reserve (size_t growSize) {
|
|
if (m_length + growSize < m_capacity) {
|
|
return true;
|
|
}
|
|
auto maxSize = max <size_t> (m_capacity + sizeof (T), static_cast <size_t> (16));
|
|
|
|
while (m_length + growSize > maxSize) {
|
|
maxSize *= 2;
|
|
}
|
|
|
|
if (maxSize >= INT_MAX / sizeof (T)) {
|
|
assert (!"Allocation Overflow!");
|
|
return false;
|
|
}
|
|
auto buffer = new T[maxSize];
|
|
|
|
if (m_data != nullptr) {
|
|
if (maxSize < m_length) {
|
|
m_length = maxSize;
|
|
}
|
|
transfer (buffer, m_data, m_length);
|
|
delete[] m_data;
|
|
}
|
|
m_data = move (buffer);
|
|
m_capacity = move (maxSize);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool resize (size_t newSize) {
|
|
bool res = reserve (newSize);
|
|
|
|
while (m_length < newSize) {
|
|
push (move (T ()));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
size_t length (void) const {
|
|
return m_length;
|
|
}
|
|
|
|
size_t capacity (void) const {
|
|
return m_capacity;
|
|
}
|
|
|
|
bool set (size_t index, T object) {
|
|
if (index >= m_capacity) {
|
|
if (!reserve (index + 2)) {
|
|
return false;
|
|
}
|
|
}
|
|
m_data[index] = forward <T> (object);
|
|
|
|
if (index >= m_length) {
|
|
m_length = index + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
T &at (size_t index) {
|
|
return m_data[index];
|
|
}
|
|
|
|
const T &at (size_t index) const {
|
|
return m_data[index];
|
|
}
|
|
|
|
bool at (size_t index, T &object) {
|
|
if (index >= m_length) {
|
|
return false;
|
|
}
|
|
object = m_data[index];
|
|
return true;
|
|
}
|
|
|
|
bool insert (size_t index, T object) {
|
|
return insert (index, &object, 1);
|
|
}
|
|
|
|
bool insert (size_t index, T *objects, size_t count = 1) {
|
|
if (!objects || !count) {
|
|
return false;
|
|
}
|
|
size_t newSize = (m_length > index ? m_length : index) + count;
|
|
|
|
if (newSize >= m_capacity && !reserve (newSize)) {
|
|
return false;
|
|
}
|
|
|
|
if (index >= m_length) {
|
|
for (size_t i = 0; i < count; i++) {
|
|
m_data[i + index] = forward <T> (objects[i]);
|
|
}
|
|
m_length = newSize;
|
|
}
|
|
else {
|
|
size_t i = 0;
|
|
|
|
for (i = m_length; i > index; i--) {
|
|
m_data[i + count - 1] = move (m_data[i - 1]);
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
m_data[i + index] = forward <T> (objects[i]);
|
|
}
|
|
m_length += count;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool insert (size_t at, Array &other) {
|
|
if (&other == this) {
|
|
return false;
|
|
}
|
|
return insert (at, other.m_data, other.m_length);
|
|
}
|
|
|
|
bool erase (size_t index, size_t count) {
|
|
if (index + count > m_capacity) {
|
|
return false;
|
|
}
|
|
m_length -= count;
|
|
|
|
for (size_t i = index; i < m_length; i++) {
|
|
m_data[i] = move (m_data[i + count]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool shift (void) {
|
|
return erase (0, 1);
|
|
}
|
|
|
|
bool unshift (T object) {
|
|
return insert (0, &object);
|
|
}
|
|
|
|
bool erase (T &item) {
|
|
return erase (index (item), 1);
|
|
}
|
|
|
|
bool push (T object) {
|
|
return insert (m_length, &object);
|
|
}
|
|
|
|
bool push (T *objects, size_t count) {
|
|
return insert (m_length, objects, count);
|
|
}
|
|
|
|
bool extend (Array &other) {
|
|
if (&other == this) {
|
|
return false;
|
|
}
|
|
return insert (m_length, other.m_data, other.m_length);
|
|
}
|
|
|
|
void clear (void) {
|
|
m_length = 0;
|
|
}
|
|
|
|
bool empty (void) const {
|
|
return m_length == 0;
|
|
}
|
|
|
|
void shrink (bool destroyEmpty = true) {
|
|
if (!m_length) {
|
|
if (destroyEmpty) {
|
|
destroy ();
|
|
}
|
|
return;
|
|
}
|
|
T *buffer = new T[m_length];
|
|
|
|
if (m_data != nullptr) {
|
|
transfer (buffer, m_data);
|
|
delete[] m_data;
|
|
}
|
|
m_data = move (buffer);
|
|
m_capacity = move (m_length);
|
|
}
|
|
|
|
size_t index (T &item) {
|
|
return &item - &m_data[0];
|
|
}
|
|
|
|
T pop (void) {
|
|
T item = move (m_data[m_length - 1]);
|
|
erase (m_length - 1, 1);
|
|
|
|
return item;
|
|
}
|
|
|
|
const T &back (void) const {
|
|
return m_data[m_length - 1];
|
|
}
|
|
|
|
T &back (void) {
|
|
return m_data[m_length - 1];
|
|
}
|
|
|
|
bool last (T &item) {
|
|
if (m_length == 0) {
|
|
return false;
|
|
}
|
|
item = m_data[m_length - 1];
|
|
return true;
|
|
}
|
|
|
|
bool assign (const Array &other) {
|
|
if (&other == this) {
|
|
return true;
|
|
}
|
|
if (!reserve (other.m_length)) {
|
|
return false;
|
|
}
|
|
transfer (m_data, other.m_data, other.m_length);
|
|
m_length = other.m_length;
|
|
|
|
return true;
|
|
}
|
|
|
|
void shuffle (void) {
|
|
for (size_t i = m_length; i >= 1; i--) {
|
|
swap (m_data[i - 1], m_data[RandomSequence::ref ().getInt (i, m_length - 2)]);
|
|
}
|
|
}
|
|
|
|
void reverse (void) {
|
|
for (size_t i = 0; i < m_length / 2; i++) {
|
|
swap (m_data[i], m_data[m_length - 1 - i]);
|
|
}
|
|
}
|
|
|
|
T &random (void) const {
|
|
return m_data[RandomSequence::ref ().getInt (0, static_cast <int> (m_length - 1))];
|
|
}
|
|
|
|
Array &operator = (Array &&other) noexcept {
|
|
destroy ();
|
|
|
|
m_data = other.m_data;
|
|
m_length = other.m_length;
|
|
m_capacity = other.m_capacity;
|
|
|
|
other.reset ();
|
|
return *this;
|
|
}
|
|
|
|
T &operator [] (size_t index) {
|
|
return at (index);
|
|
}
|
|
|
|
const T &operator [] (size_t index) const {
|
|
return at (index);
|
|
}
|
|
|
|
T *begin (void) {
|
|
return m_data;
|
|
}
|
|
|
|
T *begin (void) const {
|
|
return m_data;
|
|
}
|
|
|
|
T *end (void) {
|
|
return m_data + m_length;
|
|
}
|
|
|
|
T *end (void) const {
|
|
return m_data + m_length;
|
|
}
|
|
};
|
|
|
|
template <typename A, typename B, size_t Capacity = 0> class BinaryHeap final : private Array <Pair <A, B>> {
|
|
private:
|
|
using type = Pair <A, B>;
|
|
using base = Array <type>;
|
|
|
|
using base::m_data;
|
|
using base::m_length;
|
|
|
|
public:
|
|
BinaryHeap (void) {
|
|
base::reserve (Capacity);
|
|
}
|
|
BinaryHeap (BinaryHeap &&other) noexcept : base (move (other)) { }
|
|
|
|
public:
|
|
void push (const A &first, const B &second) {
|
|
push ({ first, second });
|
|
}
|
|
|
|
void push (type &&pair) {
|
|
base::push (forward <type> (pair));
|
|
|
|
if (length () > 1) {
|
|
siftUp ();
|
|
}
|
|
}
|
|
|
|
A pop (void) {
|
|
assert (!empty ());
|
|
|
|
if (length () == 1) {
|
|
return base::pop ().first;
|
|
}
|
|
type value = move (m_data[0]);
|
|
|
|
m_data[0] = move (base::back ());
|
|
base::pop ();
|
|
|
|
siftDown ();
|
|
return value.first;
|
|
}
|
|
|
|
bool empty (void) const {
|
|
return !length ();
|
|
}
|
|
|
|
size_t length (void) const {
|
|
return m_length;
|
|
}
|
|
|
|
void clear (void) {
|
|
base::clear ();
|
|
}
|
|
|
|
void siftUp (void) {
|
|
size_t child = m_length - 1;
|
|
|
|
while (child != 0) {
|
|
size_t parentIndex = getParent (child);
|
|
|
|
if (m_data[parentIndex].second <= m_data[child].second) {
|
|
break;
|
|
}
|
|
swap (m_data[child], m_data[parentIndex]);
|
|
child = parentIndex;
|
|
}
|
|
}
|
|
|
|
void siftDown (void) {
|
|
size_t parent = 0;
|
|
size_t leftIndex = getLeft (parent);
|
|
|
|
auto ref = move (m_data[parent]);
|
|
|
|
while (leftIndex < m_length) {
|
|
size_t rightIndex = getRight (parent);
|
|
|
|
if (rightIndex < m_length) {
|
|
if (m_data[rightIndex].second < m_data[leftIndex].second) {
|
|
leftIndex = rightIndex;
|
|
}
|
|
}
|
|
|
|
if (ref.second <= m_data[leftIndex].second) {
|
|
break;
|
|
}
|
|
m_data[parent] = move (m_data[leftIndex]);
|
|
|
|
parent = leftIndex;
|
|
leftIndex = getLeft (parent);
|
|
}
|
|
m_data[parent] = move (ref);
|
|
}
|
|
|
|
BinaryHeap &operator = (BinaryHeap &&other) noexcept {
|
|
base::operator = (move (other));
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
static constexpr size_t getLeft (size_t index) {
|
|
return index << 1 | 1;
|
|
}
|
|
|
|
static constexpr size_t getRight (size_t index) {
|
|
return ++index << 1;
|
|
}
|
|
|
|
static constexpr size_t getParent (size_t index) {
|
|
return --index >> 1;
|
|
}
|
|
};
|
|
|
|
|
|
// some fast string class
|
|
class String final : private Array <char> {
|
|
public:
|
|
static constexpr size_t INVALID_INDEX = Array <char>::INVALID_INDEX;
|
|
|
|
private:
|
|
using base = Array <char>;
|
|
|
|
private:
|
|
bool isTrimChar (char chr, const char *chars) {
|
|
do {
|
|
if (*chars == chr) {
|
|
return true;
|
|
}
|
|
chars++;
|
|
} while (*chars);
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
String (String &&other) noexcept : base (move (other)) { }
|
|
|
|
String (void) = default;
|
|
~String (void) = default;
|
|
|
|
public:
|
|
String (const char chr) {
|
|
assign (chr);
|
|
}
|
|
|
|
String (const char *str, size_t length = 0) {
|
|
assign (str, length);
|
|
}
|
|
|
|
String (const String &str, size_t length = 0) : base () {
|
|
assign (str.chars (), length);
|
|
}
|
|
|
|
public:
|
|
String &assign (const char *str, size_t length = 0) {
|
|
length = length > 0 ? length : strlen (str);
|
|
|
|
base::clear ();
|
|
base::reserve (length + 1);
|
|
|
|
if (base::push (const_cast <char *> (str), length)) {
|
|
terminate ();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String &assign (const String &str, size_t length = 0) {
|
|
return assign (str.chars (), length);
|
|
}
|
|
|
|
String &assign (const char chr) {
|
|
resize (1);
|
|
m_data[0] = chr;
|
|
|
|
return *this;
|
|
}
|
|
|
|
String &append (const char *str) {
|
|
if (empty ()) {
|
|
return assign (str);
|
|
}
|
|
if (push (const_cast <char *> (str), strlen (str))) {
|
|
terminate ();
|
|
};
|
|
return *this;
|
|
}
|
|
|
|
String &append (const String &str) {
|
|
return append (str.chars ());
|
|
}
|
|
|
|
String &append (const char chr) {
|
|
const char app[] = { chr, '\0' };
|
|
return append (app);
|
|
}
|
|
|
|
const char *chars (void) const {
|
|
return empty () ? "" : m_data;
|
|
}
|
|
|
|
size_t length (void) const {
|
|
return base::length ();
|
|
}
|
|
|
|
size_t capacity (void) const {
|
|
return base::capacity ();
|
|
}
|
|
|
|
bool empty (void) const {
|
|
return !length ();
|
|
}
|
|
|
|
void clear (void) {
|
|
base::clear ();
|
|
|
|
if (m_data) {
|
|
m_data[0] = '\0';
|
|
}
|
|
}
|
|
|
|
void erase (size_t index, size_t count = 1) {
|
|
base::erase (index, count);
|
|
terminate ();
|
|
}
|
|
|
|
void reserve (size_t count) {
|
|
base::reserve (count);
|
|
}
|
|
|
|
void resize (size_t count) {
|
|
base::resize (count);
|
|
terminate ();
|
|
}
|
|
|
|
int32 toInt32 (void) const {
|
|
return atoi (chars ());
|
|
}
|
|
|
|
float toFloat (void) const {
|
|
return static_cast <float> (atof (chars ()));
|
|
}
|
|
|
|
void terminate (void) {
|
|
m_data[m_length] = '\0';
|
|
}
|
|
|
|
char &at (size_t index) {
|
|
return m_data[index];
|
|
}
|
|
|
|
const char &at (size_t index) const {
|
|
return m_data[index];
|
|
}
|
|
|
|
int32 compare (const String &what) const {
|
|
return strcmp (m_data, what.begin ());
|
|
}
|
|
|
|
int32 compare (const char *what) const {
|
|
return strcmp (m_data, what);
|
|
}
|
|
|
|
bool contains (const String &what) const {
|
|
return strstr (m_data, what.begin ()) != nullptr;
|
|
}
|
|
|
|
template <typename ...Args> void assign (const char *fmt, Args ...args) {
|
|
const size_t size = snprintf (nullptr, 0, fmt, args...);
|
|
|
|
reserve (size + 2); // for null-terminator
|
|
resize (size);
|
|
|
|
snprintf (&m_data[0], size + 1, fmt, args...);
|
|
}
|
|
|
|
template <typename ...Args> void append (const char *fmt, Args ...args) {
|
|
const size_t size = snprintf (nullptr, 0, fmt, args...) + m_length;
|
|
const size_t len = m_length;
|
|
|
|
reserve (size + 2); // for null-terminator
|
|
resize (size);
|
|
|
|
snprintf (&m_data[len], size + 1, fmt, args...);
|
|
}
|
|
|
|
size_t insert (size_t at, const String &str) {
|
|
if (base::insert (at, str.begin (), str.length ())) {
|
|
terminate ();
|
|
return length ();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
size_t find (char val, size_t pos) const {
|
|
for (size_t i = pos; i < m_length; i++) {
|
|
if (m_data[i] == val) {
|
|
return i;
|
|
}
|
|
}
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
size_t find (const String &search, size_t pos) const {
|
|
size_t len = search.length ();
|
|
|
|
if (len > m_length || pos > m_length) {
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
for (size_t i = pos; i <= m_length - len; i++) {
|
|
size_t at = 0;
|
|
|
|
for (; search.m_data[at]; at++) {
|
|
if (m_data[i + at] != search.m_data[at]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!search.m_data[at]) {
|
|
return i;
|
|
}
|
|
}
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
size_t replace (const String &what, const String &to) {
|
|
if (!what.length () || !to.length ()) {
|
|
return 0;
|
|
}
|
|
size_t numReplaced = 0, posIndex = 0;
|
|
|
|
while (posIndex < m_length) {
|
|
posIndex = find (what, posIndex);
|
|
|
|
if (posIndex == INVALID_INDEX) {
|
|
break;
|
|
}
|
|
erase (posIndex, what.length ());
|
|
insert (posIndex, to);
|
|
|
|
posIndex += to.length ();
|
|
numReplaced++;
|
|
}
|
|
return numReplaced;
|
|
}
|
|
|
|
String substr (size_t start, size_t count = INVALID_INDEX) const {
|
|
String result;
|
|
|
|
if (start >= m_length || empty ()) {
|
|
return move (result);
|
|
}
|
|
if (count == INVALID_INDEX) {
|
|
count = m_length - start;
|
|
}
|
|
else if (start + count >= m_length) {
|
|
count = m_length - start;
|
|
}
|
|
result.resize (count);
|
|
|
|
// copy, not move
|
|
for (size_t i = 0; i < count; i++) {
|
|
result[i] = m_data[start + i];
|
|
}
|
|
result[count] = '\0';
|
|
return move (result);
|
|
}
|
|
|
|
String &rtrim (const char *chars = "\r\n\t ") {
|
|
if (empty ()) {
|
|
return *this;
|
|
}
|
|
char *str = end () - 1;
|
|
|
|
while (*str != 0) {
|
|
if (isTrimChar (*str, chars)) {
|
|
erase (str - begin ());
|
|
str--;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String <rim (const char *chars = "\r\n\t ") {
|
|
if (empty ()) {
|
|
return *this;
|
|
}
|
|
char *str = begin ();
|
|
|
|
while (isTrimChar (*str, chars)) {
|
|
str++;
|
|
}
|
|
|
|
if (begin () != str) {
|
|
erase (0, str - begin ());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String &trim (const char *chars = "\r\n\t ") {
|
|
return rtrim (chars).ltrim (chars);
|
|
}
|
|
|
|
Array <String> split (const String &delim) const {
|
|
Array <String> tokens;
|
|
size_t prev = 0, pos = 0;
|
|
|
|
do {
|
|
pos = find (delim, prev);
|
|
|
|
if (pos == INVALID_INDEX) {
|
|
pos = m_length;
|
|
}
|
|
tokens.push (move (substr (prev, pos - prev)));
|
|
|
|
prev = pos + delim.length ();
|
|
} while (pos < m_length && prev < m_length);
|
|
|
|
return move (tokens);
|
|
}
|
|
|
|
String &lowercase (void) {
|
|
for (auto &ch : *this) {
|
|
ch = static_cast <char> (tolower (ch));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String &uppercase (void) {
|
|
for (auto &ch : *this) {
|
|
ch = static_cast <char> (toupper (ch));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
friend String operator + (const String &lhs, const String &rhs) {
|
|
return String (lhs).append (rhs);
|
|
}
|
|
|
|
friend String operator + (const String &lhs, char rhs) {
|
|
return String (lhs).append (rhs);
|
|
}
|
|
|
|
friend String operator + (char lhs, const String &rhs) {
|
|
return String (lhs).append (rhs);
|
|
}
|
|
|
|
friend String operator + (const String &lhs, const char *rhs) {
|
|
return String (lhs).append (rhs);
|
|
}
|
|
|
|
friend String operator + (const char *lhs, const String &rhs) {
|
|
return String (lhs).append (rhs);
|
|
}
|
|
|
|
friend bool operator == (const String &lhs, const String &rhs) {
|
|
return lhs.compare (rhs) == 0;
|
|
}
|
|
|
|
friend bool operator < (const String &lhs, const String &rhs) {
|
|
return lhs.compare (rhs) < 0;
|
|
}
|
|
|
|
friend bool operator > (const String &lhs, const String &rhs) {
|
|
return lhs.compare (rhs) > 0;
|
|
}
|
|
|
|
friend bool operator == (const char *lhs, const String &rhs) {
|
|
return rhs.compare (lhs) == 0;
|
|
}
|
|
|
|
friend bool operator == (const String &lhs, const char *rhs) {
|
|
return lhs.compare (rhs) == 0;
|
|
}
|
|
|
|
friend bool operator != (const String &lhs, const String &rhs) {
|
|
return lhs.compare (rhs) != 0;
|
|
}
|
|
|
|
friend bool operator != (const char *lhs, const String &rhs) {
|
|
return rhs.compare (lhs) != 0;
|
|
}
|
|
|
|
friend bool operator != (const String &lhs, const char *rhs) {
|
|
return lhs.compare (rhs) != 0;
|
|
}
|
|
|
|
String &operator = (const String &rhs) {
|
|
return assign (rhs);
|
|
}
|
|
|
|
String &operator = (const char *rhs) {
|
|
return assign (rhs);
|
|
}
|
|
|
|
String &operator = (char rhs) {
|
|
return assign (rhs);
|
|
}
|
|
|
|
String &operator = (String &&other) noexcept {
|
|
base::operator = (move (other));
|
|
return *this;
|
|
}
|
|
|
|
String &operator += (const String &rhs) {
|
|
return append (rhs);
|
|
}
|
|
|
|
String &operator += (const char *rhs) {
|
|
return append (rhs);
|
|
}
|
|
|
|
char &operator [] (size_t index) {
|
|
return at (index);
|
|
}
|
|
|
|
const char &operator [] (size_t index) const {
|
|
return at (index);
|
|
}
|
|
|
|
public:
|
|
char *begin (void) {
|
|
return base::begin ();
|
|
}
|
|
|
|
char *begin (void) const {
|
|
return base::begin ();
|
|
}
|
|
|
|
char *end (void) {
|
|
return base::end ();
|
|
}
|
|
|
|
char *end (void) const {
|
|
return base::end ();
|
|
}
|
|
|
|
public:
|
|
static String join (const Array <String> &sequence, const String &delim, size_t start = 0) {
|
|
if (sequence.empty ()) {
|
|
return "";
|
|
}
|
|
|
|
if (sequence.length () == 1) {
|
|
return sequence.at (0);
|
|
}
|
|
String result;
|
|
|
|
for (size_t index = start; index < sequence.length (); index++) {
|
|
if (index != start) {
|
|
result += delim + sequence[index];
|
|
}
|
|
else {
|
|
result += sequence[index];
|
|
}
|
|
}
|
|
return move (result);
|
|
}
|
|
};
|
|
|
|
template <typename K> struct StringHash {
|
|
unsigned long operator () (const K &key) const {
|
|
char *str = const_cast <char *> (key.chars ());
|
|
size_t hash = key.length ();
|
|
|
|
while (*str++) {
|
|
hash = ((hash << 5) + hash) + *str;
|
|
}
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
template <typename K> struct IntHash {
|
|
unsigned long operator () (K key) const {
|
|
key = ((key >> 16) ^ key) * 0x119de1f3;
|
|
key = ((key >> 16) ^ key) * 0x119de1f3;
|
|
key = (key >> 16) ^ key;
|
|
|
|
return key;
|
|
}
|
|
};
|
|
|
|
template <typename K, typename V, typename H = StringHash <K>, size_t I = 256> class HashMap final : private NonCopyable {
|
|
public:
|
|
using Bucket = Pair <K, V>;
|
|
|
|
private:
|
|
Array <Bucket> m_buckets[I];
|
|
H m_hash;
|
|
|
|
Array <Bucket> &getBucket (const K &key) {
|
|
return m_buckets[m_hash (key) % I];
|
|
}
|
|
|
|
public:
|
|
HashMap (void) = default;
|
|
~HashMap (void) = default;
|
|
|
|
public:
|
|
bool exists (const K &key) {
|
|
return get (key, nullptr);
|
|
}
|
|
|
|
bool get (const K &key, V *val) {
|
|
for (auto &entry : getBucket (key)) {
|
|
if (entry.first == key) {
|
|
if (val != nullptr) {
|
|
*val = entry.second;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool get (const K &key, V &val) {
|
|
return get (key, &val);
|
|
}
|
|
|
|
bool put (const K &key, const V &val) {
|
|
if (exists (key)) {
|
|
return false;
|
|
}
|
|
getBucket (key).push (move (Pair <K, V> (key, val)));
|
|
return true;
|
|
}
|
|
|
|
bool erase (const K &key) {
|
|
auto &bucket = getBucket (key);
|
|
|
|
for (auto &entry : getBucket (key)) {
|
|
if (entry.first == key) {
|
|
bucket.erase (entry);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
V &operator [] (const K &key) {
|
|
for (auto &entry : getBucket (key)) {
|
|
if (entry.first == key) {
|
|
return entry.second;
|
|
}
|
|
}
|
|
static V ret;
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
class File : private NonCopyable {
|
|
private:
|
|
FILE *m_handle = nullptr;
|
|
size_t m_size = 0;
|
|
|
|
public:
|
|
File (void) = default;
|
|
File (const String &fileName, const String &mode = "rt") {
|
|
open (fileName, mode);
|
|
}
|
|
|
|
~File (void) {
|
|
close ();
|
|
}
|
|
|
|
bool open (const String &fileName, const String &mode) {
|
|
if ((m_handle = fopen (fileName.chars (), mode.chars ())) == nullptr) {
|
|
return false;
|
|
}
|
|
fseek (m_handle, 0L, SEEK_END);
|
|
m_size = ftell (m_handle);
|
|
fseek (m_handle, 0L, SEEK_SET);
|
|
|
|
return true;
|
|
}
|
|
|
|
void close (void) {
|
|
if (m_handle != nullptr) {
|
|
fclose (m_handle);
|
|
m_handle = nullptr;
|
|
}
|
|
m_size = 0;
|
|
}
|
|
|
|
bool eof (void) {
|
|
return feof (m_handle) ? true : false;
|
|
}
|
|
|
|
bool flush (void) {
|
|
return fflush (m_handle) ? false : true;
|
|
}
|
|
|
|
char getch (void) {
|
|
return static_cast <char> (fgetc (m_handle));
|
|
}
|
|
|
|
char *gets (char *buffer, int count) {
|
|
return fgets (buffer, count, m_handle);
|
|
}
|
|
|
|
bool getLine (String &line) {
|
|
line.clear ();
|
|
char ch;
|
|
|
|
while ((ch = getch ()) != '\n' && ch != EOF && !eof ()) {
|
|
line += ch;
|
|
}
|
|
return !eof ();
|
|
}
|
|
|
|
int writeFormat (const char *format, ...) {
|
|
assert (m_handle != nullptr);
|
|
|
|
va_list ap;
|
|
va_start (ap, format);
|
|
int written = vfprintf (m_handle, format, ap);
|
|
va_end (ap);
|
|
|
|
if (written < 0) {
|
|
return 0;
|
|
}
|
|
return written;
|
|
}
|
|
|
|
char putch (char ch) {
|
|
return static_cast <char> (fputc (ch, m_handle));
|
|
}
|
|
|
|
bool puts (const String &buffer) {
|
|
assert (m_handle != nullptr);
|
|
|
|
if (fputs (buffer.chars (), m_handle) < 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
size_t read (void *buffer, size_t size, size_t count = 1) {
|
|
return fread (buffer, size, count, m_handle);
|
|
}
|
|
|
|
size_t write (void *buffer, size_t size, size_t count = 1) {
|
|
return fwrite (buffer, size, count, m_handle);
|
|
}
|
|
|
|
bool seek (long offset, int origin) {
|
|
if (fseek (m_handle, offset, origin) != 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void rewind (void) {
|
|
::rewind (m_handle);
|
|
}
|
|
|
|
size_t getSize (void) const {
|
|
return m_size;
|
|
}
|
|
|
|
bool isValid (void) const {
|
|
return m_handle != nullptr;
|
|
}
|
|
|
|
public:
|
|
static inline bool exists (const String &filename) {
|
|
File fp;
|
|
|
|
if (fp.open (filename, "rb")) {
|
|
fp.close ();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static inline void pathCreate (char *path) {
|
|
for (char *str = path + 1; *str; str++) {
|
|
if (*str == '/') {
|
|
*str = 0;
|
|
_mkdir (path);
|
|
*str = '/';
|
|
}
|
|
}
|
|
_mkdir (path);
|
|
}
|
|
};
|
|
|
|
class MemoryLoader : public Singleton <MemoryLoader> {
|
|
private:
|
|
using Load = uint8 * (*) (const char *, int *);
|
|
using Unload = void (*) (void *);
|
|
|
|
private:
|
|
Load m_load;
|
|
Unload m_unload;
|
|
|
|
public:
|
|
MemoryLoader (void) = default;
|
|
~MemoryLoader (void) = default;
|
|
|
|
public:
|
|
void setup (Load load, Unload unload) {
|
|
m_load = load;
|
|
m_unload = unload;
|
|
}
|
|
|
|
uint8 *load (const char *name, int *size) {
|
|
if (m_load) {
|
|
return m_load (name, size);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void unload (void *buffer) {
|
|
if (m_unload) {
|
|
m_unload (buffer);
|
|
}
|
|
}
|
|
};
|
|
|
|
class MemFile : private NonCopyable {
|
|
private:
|
|
size_t m_size = 0;
|
|
size_t m_pos = 0;
|
|
uint8 *m_buffer = nullptr;
|
|
|
|
public:
|
|
MemFile (void) = default;
|
|
MemFile (const String &filename) {
|
|
open (filename);
|
|
}
|
|
|
|
virtual ~MemFile (void) {
|
|
close ();
|
|
}
|
|
|
|
bool open (const String &filename) {
|
|
m_size = 0;
|
|
m_pos = 0;
|
|
m_buffer = MemoryLoader::ref ().load (filename.chars (), reinterpret_cast <int *> (&m_size));
|
|
|
|
if (!m_buffer) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void close (void) {
|
|
MemoryLoader::ref ().unload (m_buffer);
|
|
|
|
m_size = 0;
|
|
m_pos = 0;
|
|
m_buffer = nullptr;
|
|
}
|
|
|
|
char getch (void) {
|
|
if (!m_buffer || m_pos >= m_size) {
|
|
return -1;
|
|
}
|
|
int readCh = m_buffer[m_pos];
|
|
m_pos++;
|
|
|
|
return static_cast <char> (readCh);
|
|
}
|
|
|
|
char *gets (char *buffer, size_t count) {
|
|
if (!m_buffer || m_pos >= m_size) {
|
|
return nullptr;
|
|
}
|
|
size_t index = 0;
|
|
buffer[0] = 0;
|
|
|
|
for (; index < count - 1;) {
|
|
if (m_pos < m_size) {
|
|
buffer[index] = m_buffer[m_pos++];
|
|
|
|
if (buffer[index++] == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
buffer[index] = 0;
|
|
return index ? buffer : nullptr;
|
|
}
|
|
|
|
bool getLine (String &line) {
|
|
line.clear ();
|
|
char ch;
|
|
|
|
while ((ch = getch ()) != '\n' && ch != EOF && m_pos < m_size) {
|
|
line += ch;
|
|
}
|
|
return m_pos < m_size;
|
|
}
|
|
|
|
size_t read (void *buffer, size_t size, size_t count = 1) {
|
|
if (!m_buffer || m_size <= m_pos || !buffer || !size || !count) {
|
|
return 0;
|
|
}
|
|
size_t blocksRead = size * count <= m_size - m_pos ? size * count : m_size - m_pos;
|
|
|
|
memcpy (buffer, &m_buffer[m_pos], blocksRead);
|
|
m_pos += blocksRead;
|
|
|
|
return blocksRead / size;
|
|
}
|
|
|
|
bool seek (size_t offset, int origin) {
|
|
if (!m_buffer || m_pos >= m_size) {
|
|
return false;
|
|
}
|
|
if (origin == SEEK_SET) {
|
|
if (offset >= m_size) {
|
|
return false;
|
|
}
|
|
m_pos = offset;
|
|
}
|
|
else if (origin == SEEK_END) {
|
|
if (offset >= m_size) {
|
|
return false;
|
|
}
|
|
m_pos = m_size - offset;
|
|
}
|
|
else {
|
|
if (m_pos + offset >= m_size) {
|
|
return false;
|
|
}
|
|
m_pos += offset;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
size_t getSize (void) const {
|
|
return m_size;
|
|
}
|
|
|
|
bool isValid (void) const {
|
|
return m_buffer && m_size > 0;
|
|
}
|
|
};
|
|
|
|
}};
|
|
|
|
namespace cr {
|
|
namespace types {
|
|
|
|
using StringArray = cr::classes::Array <cr::classes::String>;
|
|
using IntArray = cr::classes::Array <int>;
|
|
|
|
}};
|