merge changes from the defunct ubot

This commit is contained in:
dmitry 2020-06-12 18:52:38 +03:00 committed by jeefo
commit cf501b75b7
90 changed files with 11977 additions and 3907 deletions

92
ext/crlib/cr-alloc.h Normal file
View file

@ -0,0 +1,92 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <new>
#include <crlib/cr-basic.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-platform.h>
CR_NAMESPACE_BEGIN
// default allocator for cr-objects
class Allocator : public Singleton <Allocator> {
public:
Allocator () = default;
~Allocator () = default;
public:
template <typename T> T *allocate (const size_t length = 1) {
auto ptr = reinterpret_cast <T *> (malloc (length * sizeof (T)));
if (!ptr) {
plat.abort ();
}
return ptr;
}
template <typename T> void deallocate (T *memory) {
free (memory);
memory = nullptr;
}
public:
template <typename T, typename ...Args> void construct (T *memory, Args &&...args) {
new (memory) T (cr::forward <Args> (args)...);
}
template <typename T> void destruct (T *memory) {
memory->~T ();
}
public:
template <typename T, typename ...Args> T *create (Args &&...args) {
auto d = allocate <T> ();
new (d) T (cr::forward <Args> (args)...);
return d;
}
template <typename T> T *createArray (const size_t amount) {
auto memory = allocate <T> (amount);
for (size_t i = 0; i < amount; ++i) {
new (memory + i) T ();
}
return memory;
}
template <typename T> void destroy (T *memory) {
if (memory) {
destruct (memory);
deallocate (memory);
}
}
};
CR_EXPOSE_GLOBAL_SINGLETON (Allocator, alloc);
template <typename T> class UniquePtr;
// implment singleton with UniquePtr
template <typename T> T &Singleton <T>::instance () {
static const UniquePtr <T> instance_ { alloc.create <T> () };
return *instance_;
}
CR_NAMESPACE_END

416
ext/crlib/cr-array.h Normal file
View file

@ -0,0 +1,416 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-random.h>
#include <initializer_list>
// policy to reserve memory
CR_DECLARE_SCOPED_ENUM (ReservePolicy,
Multiple,
Single,
)
CR_NAMESPACE_BEGIN
// simple array class like std::vector
template <typename T, ReservePolicy R = ReservePolicy::Multiple, size_t S = 0> class Array : public DenyCopying {
private:
T *m_data {};
size_t m_capacity {};
size_t m_length {};
public:
explicit Array () {
if (fix (S > 0)) {
reserve (S);
}
}
Array (const size_t amount) {
reserve (amount);
}
Array (Array &&rhs) noexcept {
m_data = rhs.m_data;
m_length = rhs.m_length;
m_capacity = rhs.m_capacity;
rhs.reset ();
}
Array (const std::initializer_list <T> &list) {
for (const auto &elem : list) {
push (elem);
}
}
~Array () {
destroy ();
}
private:
void destructElements () noexcept {
for (size_t i = 0; i < m_length; ++i) {
alloc.destruct (&m_data[i]);
}
}
void transferElements (T *dest, T *src, size_t length) noexcept {
for (size_t i = 0; i < length; ++i) {
alloc.construct (&dest[i], cr::move (src[i]));
alloc.destruct (&src[i]);
}
}
void destroy () {
destructElements ();
alloc.deallocate (m_data);
}
void reset () {
m_data = nullptr;
m_capacity = 0;
m_length = 0;
}
public:
bool reserve (const size_t amount) {
if (m_length + amount < m_capacity) {
return true;
}
auto capacity = m_capacity ? m_capacity : 12;
if (cr::fix (R == ReservePolicy::Multiple)) {
while (m_length + amount > capacity) {
capacity *= 2;
}
}
else {
capacity = amount + m_capacity + 1;
}
auto data = alloc.allocate <T> (capacity);
if (m_data) {
transferElements (data, m_data, m_length);
alloc.deallocate (m_data);
}
m_data = data;
m_capacity = capacity;
return true;
}
bool resize (const size_t amount) {
if (amount < m_length) {
while (amount < m_length) {
discard ();
}
}
else if (amount > m_length) {
if (!ensure (amount)) {
return false;
}
size_t resizeLength = amount - m_length;
while (resizeLength--) {
emplace ();
}
}
return true;
}
bool ensure (const size_t amount) {
if (amount <= m_length) {
return true;
}
return reserve (amount - m_length);
}
template <typename U = size_t> U length () const {
return static_cast <U> (m_length);
}
size_t capacity () const {
return m_capacity;
}
template <typename U> bool set (size_t index, U &&object) {
if (index >= m_capacity) {
if (!reserve (index + 1)) {
return false;
}
}
alloc.construct (&m_data[index], cr::forward <U> (object));
if (index >= m_length) {
m_length = index + 1;
}
return true;
}
template <typename U> bool insert (size_t index, U &&object) {
return insert (index, &object, 1);
}
template <typename U> bool insert (size_t index, U *objects, size_t count = 1) {
if (!objects || !count) {
return false;
}
const size_t capacity = (m_length > index ? m_length : index) + count;
if (capacity >= m_capacity && !reserve (capacity)) {
return false;
}
if (index >= m_length) {
for (size_t i = 0; i < count; ++i) {
alloc.construct (&m_data[i + index], cr::forward <U> (objects[i]));
}
m_length = capacity;
}
else {
size_t i = 0;
for (i = m_length; i > index; --i) {
m_data[i + count - 1] = cr::move (m_data[i - 1]);
}
for (i = 0; i < count; ++i) {
alloc.construct (&m_data[i + index], cr::forward <U> (objects[i]));
}
m_length += count;
}
return true;
}
bool insert (size_t at, const Array &rhs) {
if (&rhs == this) {
return false;
}
return insert (at, &rhs.m_data[0], rhs.m_length);
}
bool erase (const size_t index, const size_t count) {
if (index + count > m_capacity) {
return false;
}
for (size_t i = index; i < index + count; ++i) {
alloc.destruct (&m_data[i]);
}
m_length -= count;
for (size_t i = index; i < m_length; ++i) {
m_data[i] = cr::move (m_data[i + count]);
}
return true;
}
bool shift () {
return erase (0, 1);
}
template <typename U> bool unshift (U &&object) {
return insert (0, &object);
}
bool remove (const T &object) {
return erase (index (object), 1);
}
template <typename U> bool push (U &&object) {
if (!reserve (1)) {
return false;
}
alloc.construct (&m_data[m_length], cr::forward <U> (object));
++m_length;
return true;
}
template <typename ...Args> bool emplace (Args &&...args) {
if (!reserve (1)) {
return false;
}
alloc.construct (&m_data[m_length], cr::forward <Args> (args)...);
++m_length;
return true;
}
T pop () {
auto object = cr::move (m_data[m_length - 1]);
discard ();
return object;
}
void discard () {
erase (m_length - 1, 1);
}
size_t index (const T &object) const {
return &object - &m_data[0];
}
void shuffle () {
for (size_t i = m_length; i >= 1; --i) {
cr::swap (m_data[i - 1], m_data[rg.int_ (i, m_length - 2)]);
}
}
void reverse () {
for (size_t i = 0; i < m_length / 2; ++i) {
cr::swap (m_data[i], m_data[m_length - 1 - i]);
}
}
template <typename U> bool extend (U &&rhs) {
if (m_length == 0) {
*this = cr::move (rhs);
}
else {
for (size_t i = 0; i < rhs.length (); ++i) {
if (!push (cr::move (rhs[i]))) {
return false;
}
}
}
return true;
}
template <typename U> bool assign (U &&rhs) {
clear ();
return extend (cr::move (rhs));
}
void clear () {
destructElements ();
m_length = 0;
}
bool empty () const {
return m_length == 0;
}
bool shrink () {
if (m_length == m_capacity || !m_length) {
return false;
}
auto data = alloc.allocate <T> (m_length);
transferElements (data, m_data, m_length);
alloc.deallocate (m_data);
m_data = data;
m_capacity = m_length;
return true;
}
const T &at (size_t index) const {
return m_data[index];
}
T &at (size_t index) {
return m_data[index];
}
const T &first () const {
return m_data[0];
}
T &first () {
return m_data[0];
}
T &last () {
return m_data[m_length - 1];
}
const T &last () const {
return m_data[m_length - 1];
}
const T &random () const {
return m_data[rg.int_ <size_t> (0, m_length - 1)];
}
T &random () {
return m_data[rg.int_ <size_t> (0u, m_length - 1u)];
}
T *data () {
return m_data;
}
T *data () const {
return m_data;
}
public:
Array &operator = (Array &&rhs) noexcept {
if (this != &rhs) {
destroy ();
m_data = rhs.m_data;
m_length = rhs.m_length;
m_capacity = rhs.m_capacity;
rhs.reset ();
}
return *this;
}
public:
const T &operator [] (size_t index) const {
return at (index);
}
T &operator [] (size_t index) {
return at (index);
}
// for range-based loops
public:
T *begin () {
return m_data;
}
T *begin () const {
return m_data;
}
T *end () {
return m_data + m_length;
}
T *end () const {
return m_data + m_length;
}
};
// small array (with minimal reserve policy, something like fixed array, but still able to grow, by default allocates 64 elements)
template <typename T> using SmallArray = Array <T, ReservePolicy::Single, 64>;
CR_NAMESPACE_END

134
ext/crlib/cr-basic.h Normal file
View file

@ -0,0 +1,134 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
// our global namaespace
#define CR_NAMESPACE_BEGIN namespace cr {
#define CR_NAMESPACE_END }
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
CR_NAMESPACE_BEGIN
//
// some useful type definitions
//
namespace types {
using int8 = signed char;
using int16 = signed short;
using int32 = signed int;
using uint8 = unsigned char;
using uint16 = unsigned short;
using uint32 = unsigned int;
using uint64 = unsigned long long;
}
// make types available for our own use
using namespace cr::types;
//
// global helper stuff
//
template <typename T, size_t N> constexpr size_t bufsize (const T (&)[N]) {
return N - 1;
}
template <typename T> constexpr T abs (const T &a) {
return a > 0 ? a : -a;
}
template <typename T> constexpr T bit (const T &a) {
return static_cast <T> (1ULL << a);
}
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 square (const T &value) {
return value * value;
}
template <typename T> constexpr T clamp (const T &x, const T &a, const T &b) {
return min (max (x, a), b);
}
template <typename T> const T &fix (const T &type) {
return type;
}
//
// base classes
//
// simple non-copying base class
class DenyCopying {
protected:
explicit DenyCopying () = default;
~DenyCopying () = default;
public:
DenyCopying (const DenyCopying &) = delete;
DenyCopying &operator = (const DenyCopying &) = delete;
};
// singleton for objects
template<typename T> class Singleton : public DenyCopying {
protected:
Singleton ()
{ }
public:
static T &instance (); // implemented in cr-alloc.h
public:
T *operator -> () {
return &instance ();
}
};
// simple scoped enum, instaed of enum class
#define CR_DECLARE_SCOPED_ENUM_TYPE(enumName, enumType, ...) \
CR_NAMESPACE_BEGIN \
namespace enums { \
struct _##enumName : protected DenyCopying { \
enum Type : enumType { \
__VA_ARGS__ \
}; \
}; \
} \
CR_NAMESPACE_END \
using enumName = ::cr::enums::_##enumName::Type; \
// same as above, but with int32 type
#define CR_DECLARE_SCOPED_ENUM(enumName, ...) \
CR_DECLARE_SCOPED_ENUM_TYPE(enumName, int32, __VA_ARGS__) \
// exposes global variable from class singleton
#define CR_EXPOSE_GLOBAL_SINGLETON(className, variable) \
static auto &variable = className::instance () \
CR_NAMESPACE_END
// platform-dependant-stuff
#include <crlib/cr-platform.h>

159
ext/crlib/cr-binheap.h Normal file
View file

@ -0,0 +1,159 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-array.h>
CR_NAMESPACE_BEGIN
// simple priority queue
template <typename T> class BinaryHeap final : public DenyCopying {
private:
Array <T> contents_;
public:
explicit BinaryHeap () = default;
BinaryHeap (BinaryHeap &&rhs) noexcept : contents_ (cr::move (rhs.contents_))
{ }
~BinaryHeap () = default;
public:
template <typename U> bool push (U &&item) {
if (!contents_.push (cr::move (item))) {
return false;
}
const size_t length = contents_.length ();
if (length > 1) {
percolateUp (length - 1);
}
return true;
}
template <typename ...Args> bool emplace (Args &&...args) {
if (!contents_.emplace (cr::forward <Args> (args)...)) {
return false;
}
const size_t length = contents_.length ();
if (length > 1) {
percolateUp (length - 1);
}
return true;
}
const T &top () const {
return contents_[0];
}
T pop () {
if (contents_.length () == 1) {
return contents_.pop ();
}
auto key (cr::move (contents_[0]));
contents_[0] = cr::move (contents_.last ());
contents_.discard ();
percolateDown (0);
return key;
}
public:
size_t length () const {
return contents_.length ();
}
bool empty () const {
return !contents_.length ();
}
void clear () {
contents_.clear ();
}
private:
void percolateUp (size_t index) {
while (index != 0) {
const size_t parentIndex = parent (index);
if (contents_[parentIndex] > contents_[index]) {
cr::swap (contents_[index], contents_[parentIndex]);
index = parentIndex;
}
else {
break;
}
}
}
void percolateDown (size_t index) {
while (hasLeft (index)) {
size_t bestIndex = left (index);
if (hasRight (index)) {
const size_t rightIndex = right (index);
if (contents_[rightIndex] < contents_[bestIndex]) {
bestIndex = rightIndex;
}
}
if (contents_[index] > contents_[bestIndex]) {
cr::swap (contents_[index], contents_[bestIndex]);
index = bestIndex;
bestIndex = left (index);
}
else {
break;
}
}
}
private:
BinaryHeap &operator = (BinaryHeap &&rhs) noexcept {
if (this != &rhs) {
contents_ = cr::move (rhs.contents_);
}
return *this;
}
private:
static constexpr size_t parent (size_t index) {
return (index - 1) / 2;
}
static constexpr size_t left (size_t index) {
return (index * 2) + 1;
}
static constexpr size_t right (size_t index) {
return (index * 2) + 2;
}
bool hasLeft (size_t index) const {
return left (index) < contents_.length ();
}
bool hasRight (size_t index) const {
return right (index) < contents_.length ();
}
};
CR_NAMESPACE_END

47
ext/crlib/cr-color.h Normal file
View file

@ -0,0 +1,47 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
// simple color holder
class Color final {
public:
int32 red = 0, green = 0, blue = 0;
public:
Color (int32 r, int32 g, int32 b) : red (r), green (g), blue (b) { }
explicit Color () = default;
~Color () = default;
public:
void reset () {
red = green = blue = 0;
}
int32 avg () const {
return sum () / (sizeof (Color) / sizeof (int32));
}
int32 sum () const {
return red + green + blue;
}
};
CR_NAMESPACE_END

47
ext/crlib/cr-complete.h Normal file
View file

@ -0,0 +1,47 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <stdio.h>
#include <crlib/cr-platform.h>
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-array.h>
#include <crlib/cr-binheap.h>
#include <crlib/cr-files.h>
#include <crlib/cr-lambda.h>
#include <crlib/cr-http.h>
#include <crlib/cr-library.h>
#include <crlib/cr-dict.h>
#include <crlib/cr-logger.h>
#include <crlib/cr-math.h>
#include <crlib/cr-vector.h>
#include <crlib/cr-random.h>
#include <crlib/cr-ulz.h>
#include <crlib/cr-color.h>
#include <crlib/cr-hook.h>
CR_NAMESPACE_BEGIN
namespace types {
using StringArray = Array <String>;
using IntArray = Array <int>;
}
using namespace cr::types;
CR_NAMESPACE_END

270
ext/crlib/cr-dict.h Normal file
View file

@ -0,0 +1,270 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-array.h>
#include <crlib/cr-string.h>
#include <crlib/cr-twin.h>
CR_NAMESPACE_BEGIN
// template for hashing our string
template <typename K> struct StringHash {
uint32 operator () (const K &key) const {
auto str = const_cast <char *> (key.chars ());
uint32 hash = 0;
while (*str++) {
hash = ((hash << 5) + hash) + *str;
}
return hash;
}
};
// template for hashing integers
template <typename K> struct IntHash {
uint32 operator () (K key) const {
key = ((key >> 16) ^ key) * 0x119de1f3;
key = ((key >> 16) ^ key) * 0x119de1f3;
key = (key >> 16) ^ key;
return key;
}
};
// template for np hashing integers
template <typename K> struct IntNoHash {
uint32 operator () (K key) const {
return static_cast <uint32> (key);
}
};
namespace detail {
struct DictionaryList {
uint32 index;
DictionaryList *next;
};
template <typename K, typename V> struct DictionaryBucket {
uint32 hash = static_cast <uint32> (-1);
K key {};
V value {};
public:
DictionaryBucket () = default;
~DictionaryBucket () = default;
public:
DictionaryBucket (DictionaryBucket &&rhs) noexcept : hash (rhs.hash), key (cr::move (rhs.key)), value (cr::move (rhs.value))
{ }
public:
DictionaryBucket &operator = (DictionaryBucket &&rhs) noexcept {
if (this != &rhs) {
key = cr::move (rhs.key);
value = cr::move (rhs.value);
hash = rhs.hash;
}
return *this;
}
};
}
// basic dictionary
template <class K, class V, class H = StringHash <K>, size_t HashSize = 36> class Dictionary final : public DenyCopying {
private:
using DictBucket = detail::DictionaryBucket <K, V>;
using DictList = detail::DictionaryList;
public:
enum : size_t {
InvalidIndex = static_cast <size_t> (-1)
};
private:
Array <DictList *> m_table;
Array <DictBucket> m_buckets{};
H m_hasher;
private:
uint32 hash (const K &key) const {
return m_hasher (key);
}
size_t find (const K &key, bool allocate) {
auto hashed = hash (key);
auto pos = hashed % m_table.length ();
for (auto bucket = m_table[pos]; bucket != nullptr; bucket = bucket->next) {
if (m_buckets[bucket->index].hash == hashed) {
return bucket->index;
}
}
if (allocate) {
size_t created = m_buckets.length ();
m_buckets.resize (created + 1);
auto allocated = alloc.allocate <DictList> ();
allocated->index = static_cast <int32> (created);
allocated->next = m_table[pos];
m_table[pos] = allocated;
m_buckets[created].key = key;
m_buckets[created].hash = hashed;
return created;
}
return InvalidIndex;
}
size_t findIndex (const K &key) const {
return const_cast <Dictionary *> (this)->find (key, false);
}
public:
explicit Dictionary () {
reset ();
}
Dictionary (Dictionary &&rhs) noexcept : m_table (cr::move (rhs.m_table)), m_buckets (cr::move (rhs.m_buckets)), m_hasher (cr::move (rhs.m_hasher))
{ }
~Dictionary () {
clear ();
}
public:
bool exists (const K &key) const {
return findIndex (key) != InvalidIndex;
}
bool empty () const {
return m_buckets.empty ();
}
size_t length () const {
return m_buckets.length ();
}
bool find (const K &key, V &value) const {
size_t index = findIndex (key);
if (index == InvalidIndex) {
return false;
}
value = m_buckets[index].value;
return true;
}
template <typename U> bool push (const K &key, U &&value) {
operator [] (key) = cr::forward <U> (value);
return true;
}
bool remove (const K &key) {
auto hashed = hash (key);
auto pos = hashed % m_table.length ();
auto *bucket = m_table[pos];
DictList *next = nullptr;
while (bucket != nullptr) {
if (m_buckets[bucket->index].hash == hashed) {
if (!next) {
m_table[pos] = bucket->next;
}
else {
next->next = bucket->next;
}
m_buckets.erase (bucket->index, 1);
alloc.deallocate (bucket);
bucket = nullptr;
return true;
}
next = bucket;
bucket = bucket->next;
}
return false;
}
void clear () {
for (auto object : m_table) {
while (object != nullptr) {
auto next = object->next;
alloc.deallocate (object);
object = next;
}
}
m_table.clear ();
m_buckets.clear ();
reset ();
}
void reset () {
m_table.resize (HashSize);
for (size_t i = 0; i < HashSize; ++i) {
m_table[i] = nullptr;
}
}
public:
V &operator [] (const K &key) {
return m_buckets[find (key, true)].value;
}
const V &operator [] (const K &key) const {
return m_buckets[findIndex (key)].value;
}
Dictionary &operator = (Dictionary &&rhs) noexcept {
if (this != &rhs) {
m_table = cr::move (rhs.m_table);
m_buckets = cr::move (rhs.m_buckets);
m_hasher = cr::move (rhs.m_hasher);
}
return *this;
}
// for range-based loops
public:
DictBucket *begin () {
return m_buckets.begin ();
}
DictBucket *begin () const {
return m_buckets.begin ();
}
DictBucket *end () {
return m_buckets.end ();
}
DictBucket *end () const {
return m_buckets.end ();
}
};
CR_NAMESPACE_END

393
ext/crlib/cr-files.h Normal file
View file

@ -0,0 +1,393 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <stdio.h>
#include <crlib/cr-string.h>
#include <crlib/cr-lambda.h>
CR_NAMESPACE_BEGIN
// simple stdio file wrapper
class File final : private DenyCopying {
private:
FILE *handle_ = nullptr;
size_t length_ {};
public:
explicit File () = default;
File (StringRef file, StringRef mode = "rt") {
open (file, mode);
}
~File () {
close ();
}
public:
bool open (StringRef file, StringRef mode) {
if (!openFile (file, mode)) {
return false;
}
fseek (handle_, 0L, SEEK_END);
length_ = ftell (handle_);
fseek (handle_, 0L, SEEK_SET);
return true;
}
void close () {
if (handle_ != nullptr) {
fclose (handle_);
handle_ = nullptr;
}
length_ = 0;
}
bool eof () const {
return !!feof (handle_);
}
bool flush () const {
return !!fflush (handle_);
}
int get () const {
return fgetc (handle_);
}
char *getString (char *buffer, int count) const {
return fgets (buffer, count, handle_);
}
bool getLine (String &line) {
int ch = 0;
SmallArray <char> data (255);
while ((ch = get ()) != EOF && !eof ()) {
data.push (static_cast <char> (ch));
if (ch == '\n') {
break;
}
}
line.assign (data.data (), data.length ());
return !eof ();
}
template <typename ...Args> size_t puts (const char *fmt, Args &&...args) {
if (!*this) {
return 0;
}
return fputs (strings.format (fmt, cr::forward <Args> (args)...), handle_);
}
bool puts (const char *buffer) {
if (!*this) {
return 0;
}
if (fputs (buffer, handle_) < 0) {
return false;
}
return true;
}
int putChar (int ch) {
return fputc (ch, handle_);
}
size_t read (void *buffer, size_t size, size_t count = 1) {
return fread (buffer, size, count, handle_);
}
size_t write (void *buffer, size_t size, size_t count = 1) {
return fwrite (buffer, size, count, handle_);
}
bool seek (long offset, int origin) {
if (fseek (handle_, offset, origin) != 0) {
return false;
}
return true;
}
void rewind () {
::rewind (handle_);
}
size_t length () const {
return length_;
}
public:
explicit operator bool () const {
return handle_ != nullptr;
}
public:
static inline bool exists (StringRef file) {
File fp;
if (fp.open (file, "rb")) {
fp.close ();
return true;
}
return false;
}
static inline void createPath (const char *path) {
for (auto str = const_cast <char *> (path) + 1; *str; ++str) {
if (*str == '/') {
*str = 0;
plat.createDirectory (path);
*str = '/';
}
}
plat.createDirectory (path);
}
private:
bool openFile (StringRef filename, StringRef mode) {
if (*this) {
close ();
}
#if defined (CR_WINDOWS)
fopen_s (&handle_, filename.chars (), mode.chars ());
#else
handle_ = fopen (filename.chars (), mode.chars ());
#endif
return handle_ != nullptr;
}
};
// wrapper for memory file for loading data into the memory
class MemFileStorage : public Singleton <MemFileStorage> {
private:
using LoadFunction = Lambda <uint8 * (const char *, int *)>;
using FreeFunction = Lambda <void (void *)>;
private:
LoadFunction loadFun_ = nullptr;
FreeFunction freeFun_ = nullptr;
public:
inline MemFileStorage () = default;
inline ~MemFileStorage () = default;
public:
void initizalize (LoadFunction loader, FreeFunction unloader) {
loadFun_ = cr::move (loader);
freeFun_ = cr::move (unloader);
}
public:
uint8 *load (StringRef file, int *size) {
if (loadFun_) {
return loadFun_ (file.chars (), size);
}
return nullptr;
}
void unload (void *buffer) {
if (freeFun_) {
freeFun_ (buffer);
}
}
public:
static uint8 *defaultLoad (const char *path, int *size) {
File file (path, "rb");
if (!file) {
*size = 0;
return nullptr;
}
*size = static_cast <int> (file.length ());
auto data = alloc.allocate <uint8> (*size);
file.read (data, *size);
return data;
}
static void defaultUnload (void *buffer) {
alloc.deallocate (buffer);
}
static String loadToString (StringRef filename) {
int32 result = 0;
auto buffer = defaultLoad (filename.chars (), &result);
if (result > 0 && buffer) {
String data (reinterpret_cast <char *> (buffer), result);
defaultUnload (buffer);
return data;
}
return "";
}
};
class MemFile final : public DenyCopying {
private:
enum : char {
Eof = static_cast <char> (-1)
};
private:
uint8 *contents_ = nullptr;
size_t length_ {};
size_t seek_ {};
public:
explicit MemFile () = default;
MemFile (StringRef file) {
open (file);
}
~MemFile () {
close ();
}
public:
bool open (StringRef file) {
length_ = 0;
seek_ = 0;
contents_ = MemFileStorage::instance ().load (file.chars (), reinterpret_cast <int *> (&length_));
if (!contents_) {
return false;
}
return true;
}
void close () {
MemFileStorage::instance ().unload (contents_);
length_ = 0;
seek_ = 0;
contents_ = nullptr;
}
char get () {
if (!contents_ || seek_ >= length_) {
return Eof;
}
auto ch = contents_[seek_];
++seek_;
return static_cast <char> (ch);
}
char *getString (char *buffer, size_t count) {
if (!contents_ || seek_ >= length_) {
return nullptr;
}
size_t index = 0;
buffer[0] = 0;
for (; index < count - 1;) {
if (seek_ < length_) {
buffer[index] = contents_[seek_++];
if (buffer[index++] == '\n') {
break;
}
}
else {
break;
}
}
buffer[index] = 0;
return index ? buffer : nullptr;
}
bool getLine (String &line) {
char ch;
SmallArray <char> data (255);
while ((ch = get ()) != Eof) {
data.push (ch);
if (ch == '\n') {
break;
}
}
line.assign (data.data (), data.length ());
return !eof ();
}
size_t read (void *buffer, size_t size, size_t count = 1) {
if (!contents_ || length_ <= seek_ || !buffer || !size || !count) {
return 0;
}
size_t blocksRead = size * count <= length_ - seek_ ? size * count : length_ - seek_;
memcpy (buffer, &contents_[seek_], blocksRead);
seek_ += blocksRead;
return blocksRead / size;
}
bool seek (size_t offset, int origin) {
if (!contents_ || seek_ >= length_) {
return false;
}
if (origin == SEEK_SET) {
if (offset >= length_) {
return false;
}
seek_ = offset;
}
else if (origin == SEEK_END) {
if (offset >= length_) {
return false;
}
seek_ = length_ - offset;
}
else {
if (seek_ + offset >= length_) {
return false;
}
seek_ += offset;
}
return true;
}
size_t length () const {
return length_;
}
bool eof () const {
return seek_ >= length_;
}
void rewind () {
seek_ = 0;
}
public:
explicit operator bool () const {
return !!contents_ && length_ > 0;
}
};
CR_NAMESPACE_END

170
ext/crlib/cr-hook.h Normal file
View file

@ -0,0 +1,170 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
#if !defined (CR_WINDOWS)
# include <sys/mman.h>
#endif
CR_NAMESPACE_BEGIN
class SimpleHook : DenyCopying {
private:
#if defined (CR_ARCH_X64)
using uint = uint64;
#else
using uint = uint32;
#endif
private:
enum : uint32 {
#if defined (CR_ARCH_X64)
CodeLength = 5 + sizeof (uint)
#else
CodeLength = 1 + sizeof (uint)
#endif
};
private:
bool patched_;
uint pageSize_;
uint originalFun_;
uint hookedFun_;
UniquePtr <uint8 []> originalBytes_;
UniquePtr <uint8 []> hookedBytes_;
private:
void setPageSize () {
#if defined (CR_WINDOWS)
SYSTEM_INFO sysinfo;
GetSystemInfo (&sysinfo);
pageSize_ = sysinfo.dwPageSize;
#else
pageSize_ = sysconf (_SC_PAGESIZE);
#endif
}
#if !defined (CR_WINDOWS)
void *align (void *address) {
return reinterpret_cast <void *> ((reinterpret_cast <long> (address) & ~(pageSize_ - 1)));
}
#endif
bool unprotect () {
auto orig = reinterpret_cast <void *> (originalFun_);
#if defined (CR_WINDOWS)
DWORD oldProt;
FlushInstructionCache (GetCurrentProcess (), orig, CodeLength);
return VirtualProtect (orig, CodeLength, PAGE_EXECUTE_READWRITE, &oldProt);
#else
auto aligned = align (orig);
return !mprotect (aligned, pageSize_, PROT_READ | PROT_WRITE | PROT_EXEC);
#endif
}
public:
SimpleHook () : patched_ (false), pageSize_ (0), originalFun_ (0), hookedFun_ (0) {
setPageSize ();
originalBytes_ = makeUnique <uint8 []> (CodeLength);
hookedBytes_ = makeUnique <uint8 []> (CodeLength);
}
~SimpleHook () {
disable ();
}
public:
bool patch (void *address, void *replacement) {
constexpr uint16 jmp = 0x25ff;
if (plat.arm) {
return false;
}
auto ptr = reinterpret_cast <uint8 *> (address);
while (*reinterpret_cast <uint16 *> (ptr) == jmp) {
ptr = **reinterpret_cast <uint8 ***> (ptr + 2);
}
originalFun_ = reinterpret_cast <uint> (ptr);
hookedFun_ = reinterpret_cast <uint> (replacement);
memcpy (originalBytes_.get (), reinterpret_cast <void *> (originalFun_), CodeLength);
if (plat.x64) {
const uint16 nop = 0x00000000;
memcpy (&hookedBytes_[0], &jmp, sizeof (uint16));
memcpy (&hookedBytes_[2], &nop, sizeof (uint16));
memcpy (&hookedBytes_[6], &replacement, sizeof (uint));
}
else {
hookedBytes_[0] = 0xe9;
auto rel = hookedFun_ - originalFun_ - CodeLength;
memcpy (&hookedBytes_[0] + 1, &rel, sizeof (rel));
}
return enable ();
}
bool enable () {
if (patched_) {
return false;
}
patched_ = true;
if (unprotect ()) {
memcpy (reinterpret_cast <void *> (originalFun_), hookedBytes_.get (), CodeLength);
return true;
}
return false;
}
bool disable () {
if (!patched_) {
return false;
}
patched_ = false;
if (unprotect ()) {
memcpy (reinterpret_cast <void *> (originalFun_), originalBytes_.get (), CodeLength);
return true;
}
return false;
}
bool enabled () const {
return patched_;
}
public:
template <typename T, typename... Args > decltype (auto) call (Args &&...args) {
disable ();
auto res = reinterpret_cast <T *> (originalFun_) (args...);
enable ();
return res;
}
};
CR_NAMESPACE_END

468
ext/crlib/cr-http.h Normal file
View file

@ -0,0 +1,468 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <stdio.h>
#include <crlib/cr-string.h>
#include <crlib/cr-files.h>
#include <crlib/cr-logger.h>
#include <crlib/cr-platform.h>
#if defined (CR_LINUX) || defined (CR_OSX)
# include <netinet/in.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <sys/uio.h>
# include <arpa/inet.h>
# include <unistd.h>
# include <errno.h>
# include <netdb.h>
# include <fcntl.h>
#elif defined (CR_WINDOWS)
# include <winsock2.h>
# include <ws2tcpip.h>
#endif
// status codes for http client
CR_DECLARE_SCOPED_ENUM (HttpClientResult,
OK = 0,
NotFound,
Forbidden,
SocketError,
ConnectError,
HttpOnly,
Undefined,
NoLocalFile = -1,
LocalFileExists = -2
)
CR_NAMESPACE_BEGIN
class Socket final : public DenyCopying {
private:
int32 socket_;
uint32 timeout_;
public:
Socket () : socket_ (-1), timeout_ (2) {
#if defined(CR_WINDOWS)
WSADATA wsa;
if (WSAStartup (MAKEWORD (2, 2), &wsa) != 0) {
logger.error ("Unable to inialize sockets.");
}
#endif
}
~Socket () {
disconnect ();
#if defined (CR_WINDOWS)
WSACleanup ();
#endif
}
public:
bool connect (StringRef hostname) {
addrinfo hints, *result = nullptr;
plat.bzero (&hints, sizeof (hints));
constexpr auto NumericServ = 0x00000008;
hints.ai_flags = NumericServ;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo (hostname.chars (), "80", &hints, &result) != 0) {
return false;
}
socket_ = static_cast <int> (socket (result->ai_family, result->ai_socktype, 0));
if (socket_ < 0) {
freeaddrinfo (result);
return false;
}
auto getTimeouts = [&] () -> Twin <char *, int32> {
#if defined (CR_WINDOWS)
DWORD tv = timeout_ * 1000;
#else
timeval tv { static_cast <time_t> (timeout_), 0 };
#endif
return { reinterpret_cast <char *> (&tv), static_cast <int32> (sizeof (tv)) };
};
auto timeouts = getTimeouts ();
if (setsockopt (socket_, SOL_SOCKET, SO_RCVTIMEO, timeouts.first, timeouts.second) == -1) {
logger.message ("Unable to set SO_RCVTIMEO.");
}
if (setsockopt (socket_, SOL_SOCKET, SO_SNDTIMEO, timeouts.first, timeouts.second) == -1) {
logger.message ("Unable to set SO_SNDTIMEO.");
}
if (::connect (socket_, result->ai_addr, static_cast <int32> (result->ai_addrlen)) == -1) {
disconnect ();
freeaddrinfo (result);
return false;
}
freeaddrinfo (result);
return true;
}
void setTimeout (uint32 timeout) {
timeout_ = timeout;
}
void disconnect () {
#if defined(CR_WINDOWS)
if (socket_ != -1) {
closesocket (socket_);
}
#else
if (socket_ != -1)
close (socket_);
#endif
}
public:
template <typename U> int32 send (const U *buffer, int32 length) const {
return ::send (socket_, reinterpret_cast <const char *> (buffer), length, 0);
}
template <typename U> int32 recv (U *buffer, int32 length) {
return ::recv (socket_, reinterpret_cast <char *> (buffer), length, 0);
}
public:
static int32 CR_STDCALL sendto (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int32 destLength) {
#if defined (CR_WINDOWS)
WSABUF buffer = { static_cast <ULONG> (length), const_cast <char *> (reinterpret_cast <const char *> (message)) };
DWORD sendLength = 0;
if (WSASendTo (socket, &buffer, 1, &sendLength, flags, dest, destLength, NULL, NULL) == SOCKET_ERROR) {
errno = WSAGetLastError ();
return -1;
}
return static_cast <int32> (sendLength);
#else
iovec iov = { const_cast <void *> (message), length };
msghdr msg {};
msg.msg_name = reinterpret_cast <void *> (const_cast <struct sockaddr *> (dest));
msg.msg_namelen = destLength;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
return sendmsg (socket, &msg, flags);
#endif
}
};
namespace detail {
// simple http uri omitting query-string and port
struct HttpUri {
String path, protocol, host;
public:
static HttpUri parse (StringRef uri) {
HttpUri result;
if (uri.empty ()) {
return result;
}
size_t protocol = uri.find ("://");
if (protocol != String::InvalidIndex) {
result.protocol = uri.substr (0, protocol);
size_t hostIndex = uri.find ("/", protocol + 3);
if (hostIndex != String::InvalidIndex) {
result.path = uri.substr (hostIndex + 1);
result.host = uri.substr (protocol + 3, hostIndex - protocol - 3);
return result;
}
}
return result;
}
};
}
// simple http client for downloading/uploading files only
class HttpClient final : public Singleton <HttpClient> {
private:
enum : int32 {
MaxReceiveErrors = 12
};
private:
Socket socket_;
String userAgent_ = "crlib";
HttpClientResult statusCode_ = HttpClientResult::Undefined;
int32 chunkSize_ = 4096;
public:
HttpClient () = default;
~HttpClient () = default;
private:
HttpClientResult parseResponseHeader (uint8 *buffer) {
bool isFinished = false;
int32 pos = 0, symbols = 0, errors = 0;
// prase response header
while (!isFinished && pos < chunkSize_) {
if (socket_.recv (&buffer[pos], 1) < 1) {
if (++errors > MaxReceiveErrors) {
isFinished = true;
}
else {
continue;
}
}
switch (buffer[pos]) {
case '\r':
break;
case '\n':
isFinished = (symbols == 0);
symbols = 0;
break;
default:
++symbols;
break;
}
++pos;
}
String response { reinterpret_cast <const char *> (buffer) };
size_t responseCodeStart = response.find ("HTTP/1.1");
if (responseCodeStart != String::InvalidIndex) {
String respCode = response.substr (responseCodeStart + 9, 3);
respCode.trim ();
if (respCode == "200") {
return HttpClientResult::OK;
}
else if (respCode == "403") {
return HttpClientResult::Forbidden;
}
else if (respCode == "404") {
return HttpClientResult::NotFound;
}
}
return HttpClientResult::NotFound;
}
public:
// simple blocked download
bool downloadFile (StringRef url, StringRef localPath) {
if (File::exists (localPath.chars ())) {
statusCode_ = HttpClientResult::LocalFileExists;
return false;
}
auto uri = detail::HttpUri::parse (url);
// no https...
if (uri.protocol == "https") {
statusCode_ = HttpClientResult::HttpOnly;
return false;
}
// unable to connect...
if (!socket_.connect (uri.host)) {
statusCode_ = HttpClientResult::ConnectError;
socket_.disconnect ();
return false;
}
String request;
request.appendf ("GET /%s HTTP/1.1\r\n", uri.path);
request.append ("Accept: */*\r\n");
request.append ("Connection: close\r\n");
request.append ("Keep-Alive: 115\r\n");
request.appendf ("User-Agent: %s\r\n", userAgent_);
request.appendf ("Host: %s\r\n\r\n", uri.host);
if (socket_.send (request.chars (), static_cast <int32> (request.length ())) < 1) {
statusCode_ = HttpClientResult::SocketError;
socket_.disconnect ();
return false;
}
SmallArray <uint8> buffer (chunkSize_);
statusCode_ = parseResponseHeader (buffer.data ());
if (statusCode_ != HttpClientResult::OK) {
socket_.disconnect ();
return false;
}
// receive the file
File file (localPath, "wb");
if (!file) {
statusCode_ = HttpClientResult::Undefined;
socket_.disconnect ();
return false;
}
int32 length = 0;
int32 errors = 0;
for (;;) {
length = socket_.recv (buffer.data (), chunkSize_);
if (length > 0) {
file.write (buffer.data (), length);
}
else if (++errors > 12) {
break;
}
}
file.close ();
socket_.disconnect ();
statusCode_ = HttpClientResult::OK;
return true;
}
bool uploadFile (StringRef url, StringRef localPath) {
if (!File::exists (localPath.chars ())) {
statusCode_ = HttpClientResult::NoLocalFile;
return false;
}
auto uri = detail::HttpUri::parse (url);
// no https...
if (uri.protocol == "https") {
statusCode_ = HttpClientResult::HttpOnly;
return false;
}
// unable to connect...
if (!socket_.connect (uri.host)) {
statusCode_ = HttpClientResult::ConnectError;
socket_.disconnect ();
return false;
}
// receive the file
File file (localPath, "rb");
if (!file) {
statusCode_ = HttpClientResult::Undefined;
socket_.disconnect ();
return false;
}
String boundaryName = localPath;
size_t boundarySlash = localPath.findLastOf ("\\/");
if (boundarySlash != String::InvalidIndex) {
boundaryName = localPath.substr (boundarySlash + 1);
}
StringRef boundaryLine = "---crlib_upload_boundary_1337";
String request, start, end;
start.appendf ("--%s\r\n", boundaryLine);
start.appendf ("Content-Disposition: form-data; name='file'; filename='%s'\r\n", boundaryName);
start.append ("Content-Type: application/octet-stream\r\n\r\n");
end.appendf ("\r\n--%s--\r\n\r\n", boundaryLine);
request.appendf ("POST /%s HTTP/1.1\r\n", uri.path);
request.appendf ("Host: %s\r\n", uri.host);
request.appendf ("User-Agent: %s\r\n", userAgent_);
request.appendf ("Content-Type: multipart/form-data; boundary=%s\r\n", boundaryLine);
request.appendf ("Content-Length: %d\r\n\r\n", file.length () + start.length () + end.length ());
// send the main request
if (socket_.send (request.chars (), static_cast <int32> (request.length ())) < 1) {
statusCode_ = HttpClientResult::SocketError;
socket_.disconnect ();
return false;
}
// send boundary start
if (socket_.send (start.chars (), static_cast <int32> (start.length ())) < 1) {
statusCode_ = HttpClientResult::SocketError;
socket_.disconnect ();
return false;
}
SmallArray <uint8> buffer (chunkSize_);
int32 length = 0;
for (;;) {
length = static_cast <int32> (file.read (buffer.data (), 1, chunkSize_));
if (length > 0) {
socket_.send (buffer.data (), length);
}
else {
break;
}
}
// send boundary end
if (socket_.send (end.chars (), static_cast <int32> (end.length ())) < 1) {
statusCode_ = HttpClientResult::SocketError;
socket_.disconnect ();
return false;
}
statusCode_ = parseResponseHeader (buffer.data ());
socket_.disconnect ();
return statusCode_ == HttpClientResult::OK;
}
public:
void setUserAgent (StringRef ua) {
userAgent_ = ua;
}
HttpClientResult getLastStatusCode () {
return statusCode_;
}
void setChunkSize (int32 chunkSize) {
chunkSize_ = chunkSize;
}
void setTimeout (uint32 timeout) {
socket_.setTimeout (timeout);
}
};
// expose global http client
CR_EXPOSE_GLOBAL_SINGLETON (HttpClient, http);
CR_NAMESPACE_END

182
ext/crlib/cr-lambda.h Normal file
View file

@ -0,0 +1,182 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-alloc.h>
#include <crlib/cr-uniqueptr.h>
CR_NAMESPACE_BEGIN
template <typename> class Lambda;
template <typename R, typename ...Args> class Lambda <R (Args...)> {
private:
enum : uint32 {
LamdaSmallBufferLength = sizeof (void *) * 16
};
private:
class LambdaFunctorWrapper {
public:
LambdaFunctorWrapper () = default;
virtual ~LambdaFunctorWrapper () = default;
public:
virtual void move (uint8 *to) = 0;
virtual void small (uint8 *to) const = 0;
virtual R invoke (Args &&...) = 0;
virtual UniquePtr <LambdaFunctorWrapper> clone () const = 0;
public:
void operator delete (void *ptr) {
alloc.deallocate (ptr);
}
};
template <typename T> class LambdaFunctor : public LambdaFunctorWrapper {
private:
T callable_;
public:
LambdaFunctor (const T &callable) : LambdaFunctorWrapper (), callable_ (callable)
{ }
LambdaFunctor (T &&callable) : LambdaFunctorWrapper (), callable_ (cr::move (callable))
{ }
~LambdaFunctor () override = default;
public:
void move (uint8 *to) override {
new (to) LambdaFunctor <T> (cr::move (callable_));
}
void small (uint8 *to) const override {
new (to) LambdaFunctor <T> (callable_);
}
R invoke (Args &&... args) override {
return callable_ (cr::forward <Args> (args)...);
}
UniquePtr <LambdaFunctorWrapper> clone () const override {
return makeUnique <LambdaFunctor <T>> (callable_);
}
};
union {
UniquePtr <LambdaFunctorWrapper> functor_;
uint8 small_[LamdaSmallBufferLength];
};
bool ssoObject_ = false;
private:
void destroy () {
if (ssoObject_) {
reinterpret_cast <LambdaFunctorWrapper *> (small_)->~LambdaFunctorWrapper ();
}
else {
functor_.reset ();
}
}
void swap (Lambda &rhs) noexcept {
cr::swap (rhs, *this);
}
public:
explicit Lambda () noexcept : Lambda (nullptr)
{ }
Lambda (decltype (nullptr)) noexcept : functor_ (nullptr), ssoObject_ (false)
{ }
Lambda (const Lambda &rhs) {
if (rhs.ssoObject_) {
reinterpret_cast <const LambdaFunctorWrapper *> (rhs.small_)->small (small_);
}
else {
new (small_) UniquePtr <LambdaFunctorWrapper> (rhs.functor_->clone ());
}
ssoObject_ = rhs.ssoObject_;
}
Lambda (Lambda &&rhs) noexcept {
if (rhs.ssoObject_) {
reinterpret_cast <LambdaFunctorWrapper *> (rhs.small_)->move (small_);
new (rhs.small_) UniquePtr <LambdaFunctorWrapper> (nullptr);
}
else {
new (small_) UniquePtr <LambdaFunctorWrapper> (cr::move (rhs.functor_));
}
ssoObject_ = rhs.ssoObject_;
rhs.ssoObject_ = false;
}
template <typename F> Lambda (F function) {
if (cr::fix (sizeof (function) > LamdaSmallBufferLength)) {
ssoObject_ = false;
new (small_) UniquePtr <LambdaFunctorWrapper> (makeUnique <LambdaFunctor <F>> (cr::move (function)));
}
else {
ssoObject_ = true;
new (small_) LambdaFunctor <F> (cr::move (function));
}
}
~Lambda () {
destroy ();
}
public:
Lambda &operator = (const Lambda &rhs) {
destroy ();
Lambda tmp (rhs);
swap (tmp);
return *this;
}
Lambda &operator = (Lambda &&rhs) noexcept {
destroy ();
if (rhs.ssoObject_) {
reinterpret_cast <LambdaFunctorWrapper *> (rhs.small_)->move (small_);
new (rhs.small_) UniquePtr <LambdaFunctorWrapper> (nullptr);
}
else {
new (small_) UniquePtr <LambdaFunctorWrapper> (cr::move (rhs.functor_));
}
ssoObject_ = rhs.ssoObject_;
rhs.ssoObject_ = false;
return *this;
}
explicit operator bool () const noexcept {
return ssoObject_ || !!functor_;
}
public:
R operator () (Args ...args) {
return ssoObject_ ? reinterpret_cast <LambdaFunctorWrapper *> (small_)->invoke (cr::forward <Args> (args)...) : functor_->invoke (cr::forward <Args> (args)...);
}
};
CR_NAMESPACE_END

130
ext/crlib/cr-library.h Normal file
View file

@ -0,0 +1,130 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-string.h>
#if defined (CR_LINUX) || defined (CR_OSX)
# include <dlfcn.h>
# include <errno.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <unistd.h>
#endif
CR_NAMESPACE_BEGIN
// handling dynamic library loading
class SharedLibrary final : public DenyCopying {
public:
using Handle = void *;
private:
Handle handle_ = nullptr;
public:
explicit SharedLibrary () = default;
SharedLibrary (StringRef file) {
if (file.empty ()) {
return;
}
load (file);
}
~SharedLibrary () {
unload ();
}
public:
bool load (StringRef file) noexcept {
if (*this) {
unload ();
}
#if defined (CR_WINDOWS)
handle_ = LoadLibraryA (file.chars ());
#else
handle_ = dlopen (file.chars (), RTLD_LAZY);
#endif
return handle_ != nullptr;
}
bool locate (Handle address) {
#if defined (CR_WINDOWS)
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery (address, &mbi, sizeof (mbi))) {
return false;
}
if (mbi.State != MEM_COMMIT) {
return false;
}
handle_ = reinterpret_cast <Handle> (mbi.AllocationBase);
#else
Dl_info dli;
plat.bzero (&dli, sizeof (dli));
if (dladdr (address, &dli)) {
return load (dli.dli_fname);
}
#endif
return handle_ != nullptr;
}
void unload () noexcept {
if (!*this) {
return;
}
#if defined (CR_WINDOWS)
FreeLibrary (static_cast <HMODULE> (handle_));
#else
dlclose (handle_);
#endif
handle_ = nullptr;
}
template <typename R> R resolve (const char *function) const {
if (!*this) {
return nullptr;
}
return SharedLibrary::getSymbol <R> (handle (), function);
}
Handle handle () const {
return handle_;
}
public:
explicit operator bool () const {
return handle_ != nullptr;
}
public:
template <typename R> static inline R CR_STDCALL getSymbol (Handle module, const char *function) {
return reinterpret_cast <R> (
#if defined (CR_WINDOWS)
GetProcAddress (static_cast <HMODULE> (module), function)
#else
dlsym (module, function)
#endif
);
}
};
CR_NAMESPACE_END

108
ext/crlib/cr-logger.h Normal file
View file

@ -0,0 +1,108 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <time.h>
#include <crlib/cr-files.h>
#include <crlib/cr-lambda.h>
CR_NAMESPACE_BEGIN
class SimpleLogger final : public Singleton <SimpleLogger> {
public:
using PrintFunction = Lambda <void (const char *)>;
private:
File handle_;
PrintFunction printFun_;
public:
SimpleLogger () = default;
~SimpleLogger () {
handle_.close ();
}
private:
void logToFile (const char *level, const char *msg) {
if (!handle_) {
return;
}
time_t ticks = time (&ticks);
tm *timeinfo = nullptr;
#if defined (CR_WINDOWS)
tm get;
localtime_s (&get, &ticks);
timeinfo = &get;
#else
timeinfo = localtime (&ticks);
#endif
auto timebuf = strings.chars ();
strftime (timebuf, StringBuffer::StaticBufferSize, "%Y-%m-%d %H:%M:%S", timeinfo);
handle_.puts ("%s (%s): %s\n", timebuf, level, msg);
}
public:
template <typename ...Args> void fatal (const char *fmt, Args &&...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("FATAL", msg);
if (printFun_) {
printFun_ (msg);
}
plat.abort (msg);
}
template <typename ...Args> void error (const char *fmt, Args &&...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("ERROR", msg);
if (printFun_) {
printFun_ (msg);
}
}
template <typename ...Args> void message (const char *fmt, Args &&...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("INFO", msg);
if (printFun_) {
printFun_ (msg);
}
}
public:
void initialize (StringRef filename, PrintFunction printFunction) {
if (handle_) {
handle_.close ();
}
printFun_ = cr::move (printFunction);
handle_.open (filename, "at");
}
};
// expose global instance
CR_EXPOSE_GLOBAL_SINGLETON (SimpleLogger, logger);
CR_NAMESPACE_END

143
ext/crlib/cr-math.h Normal file
View file

@ -0,0 +1,143 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
#if defined (CR_HAS_SSE)
# include <pmmintrin.h>
#endif
#include <math.h>
CR_NAMESPACE_BEGIN
constexpr float kFloatEpsilon = 0.01f;
constexpr float kFloatEqualEpsilon = 0.001f;
constexpr float kFloatCmpEpsilon = 1.192092896e-07f;
constexpr float kMathPi = 3.141592653589793115997963468544185161590576171875f;
constexpr float kMathPiHalf = kMathPi * 0.5f;
constexpr float kDegreeToRadians = kMathPi / 180.0f;
constexpr float kRadiansToDegree = 180.0f / kMathPi;
constexpr bool fzero (const float e) {
return cr::abs (e) < kFloatEpsilon;
}
constexpr bool fequal (const float a, const float b) {
return cr:: abs (a - b) < kFloatEqualEpsilon;
}
constexpr float rad2deg (const float r) {
return r * kRadiansToDegree;
}
constexpr float deg2rad (const float d) {
return d * kDegreeToRadians;
}
constexpr float modAngles (const float a) {
return 360.0f / 65536.0f * (static_cast <int> (a * (65536.0f / 360.0f)) & 65535);
}
constexpr float normalizeAngles (const float a) {
return 360.0f / 65536.0f * (static_cast <int> ((a + 180.0f) * (65536.0f / 360.0f)) & 65535) - 180.0f;
}
constexpr float anglesDifference (const float a, const float b) {
return normalizeAngles (a - b);
}
inline float sinf (const float value) {
return ::sinf (value);
}
inline float cosf (const float value) {
return ::cosf (value);
}
inline float atanf (const float value) {
return ::atanf (value);
}
inline float atan2f (const float y, const float x) {
return ::atan2f (y, x);
}
inline float powf (const float x, const float y) {
return ::powf (x, y);
}
inline float sqrtf (const float value) {
return ::sqrtf (value);
}
inline float tanf (const float value) {
return ::tanf (value);
}
inline float ceilf (const float value) {
return ::ceilf (value);
}
inline void sincosf (const float x, const float y, const float z, float *sines, float *cosines) {
#if defined (CR_HAS_SSE)
auto set = _mm_set_ps (x, y, z, 0.0f);
auto _mm_sin = [] (__m128 rad) -> __m128 {
static auto pi2 = _mm_set_ps1 (kMathPi * 2);
static auto rp1 = _mm_set_ps1 (4.0f / kMathPi);
static auto rp2 = _mm_set_ps1 (-4.0f / (kMathPi * kMathPi));
static auto val = _mm_cmpnlt_ps (rad, _mm_set_ps1 (kMathPi));
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 (-kMathPi));
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 (kMathPiHalf);
auto s = _mm_sin (set);
auto c = _mm_sin (_mm_add_ps (set, 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] = cr::sinf (x);
sines[1] = cr::sinf (y);
sines[2] = cr::sinf (z);
cosines[0] = cr::cosf (x);
cosines[1] = cr::cosf (y);
cosines[2] = cr::cosf (z);
#endif
}
CR_NAMESPACE_END

64
ext/crlib/cr-movable.h Normal file
View file

@ -0,0 +1,64 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
namespace detail {
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> typename detail::ClearRef <T>::Type constexpr &&move (T &&type) noexcept {
return static_cast <typename detail::ClearRef <T>::Type &&> (type);
}
template <typename T> constexpr T &&forward (typename detail::ClearRef <T>::Type &type) noexcept {
return static_cast <T &&> (type);
}
template <typename T> constexpr T &&forward (typename detail::ClearRef <T>::Type &&type) noexcept {
return static_cast <T &&> (type);
}
template <typename T> inline void swap (T &left, T &right) noexcept {
auto temp = cr::move (left);
left = cr::move (right);
right = cr::move (temp);
}
template <typename T, size_t S> inline void swap (T (&left)[S], T (&right)[S]) noexcept {
if (&left == &right) {
return;
}
auto begin = left;
auto end = begin + S;
for (auto temp = right; begin != end; ++begin, ++temp) {
cr::swap (*begin, *temp);
}
}
CR_NAMESPACE_END

283
ext/crlib/cr-platform.h Normal file
View file

@ -0,0 +1,283 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
CR_NAMESPACE_BEGIN
// detects the build platform
#if defined(__linux__)
# define CR_LINUX
#elif defined(__APPLE__)
# define CR_OSX
#elif defined(_WIN32)
# define CR_WINDOWS
#endif
#if defined(__ANDROID__)
# define CR_ANDROID
# if defined(LOAD_HARDFP)
# define CR_ANDROID_HARD_FP
# endif
#endif
// detects the compiler
#if defined(_MSC_VER)
# define CR_CXX_MSVC _MSC_VER
#endif
#if defined(__clang__)
# define CR_CXX_CLANG __clang__
#endif
#if defined(__INTEL_COMPILER)
# define CR_CXX_INTEL __INTEL_COMPILER
#endif
#if defined(__GNUC__)
# define CR_CXX_GCC __GNUC__
#endif
// configure macroses
#define CR_LINKAGE_C extern "C"
#if defined(CR_WINDOWS)
# define CR_EXPORT CR_LINKAGE_C __declspec (dllexport)
# define CR_STDCALL __stdcall
#elif defined(CR_LINUX) || defined(CR_OSX)
# define CR_EXPORT CR_LINKAGE_C __attribute__((visibility("default")))
# define CR_STDCALL
#else
# error "Can't configure export macros. Compiler unrecognized."
#endif
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__amd64) || (defined(_MSC_VER) && defined(_M_X64))
# define CR_ARCH_X64
#elif defined(__i686) || defined(__i686__) || defined(__i386) || defined(__i386__) || defined(i386) || (defined(_MSC_VER) && defined(_M_IX86))
# define CR_ARCH_X86
#endif
#if defined(__arm__)
# define CR_ARCH_ARM
# if defined(__aarch64__)
# define CR_ARCH_ARM64
# endif
#endif
#if (defined(CR_ARCH_X86) || defined(CR_ARCH_X64)) && !defined(CR_DEBUG)
# define CR_HAS_SSE
#endif
#if defined(CR_HAS_SSE)
# if defined (CR_CXX_MSVC)
# define CR_ALIGN16 __declspec (align (16))
# else
# define CR_ALIGN16 __attribute__((aligned(16)))
# endif
#endif
// disable warnings regarding intel compiler
#if defined(CR_CXX_INTEL)
# pragma warning (disable : 3280) // declaration hides member "XXX" (declared at line XX)
# pragma warning (disable : 2415) // variable "XXX" of static storage duration was declared but never referenced
# pragma warning (disable : 873) // function "operator new(size_t={unsigned int}, void *)" has no corresponding operator delete (to be called if an exception is thrown during initialization of an allocated object)
# pragma warning (disable : 383) // value copied to temporary, reference to temporary used
# pragma warning (disable : 11074 11075) // remarks about inlining bla-bla-bla
#endif
CR_NAMESPACE_END
#if defined(CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <direct.h>
# if defined (max)
# undef max
# endif
# if defined (min)
# undef min
# endif
#else
# include <unistd.h>
# include <strings.h>
# include <sys/stat.h>
# include <sys/time.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <locale.h>
#include <string.h>
#include <stdarg.h>
#if defined (CR_ANDROID)
# include <android/log.h>
#endif
CR_NAMESPACE_BEGIN
// helper struct for platform detection
struct Platform : public Singleton <Platform> {
bool win32 = false;
bool linux = false;
bool osx = false;
bool android = false;
bool hfp = false;
bool x64 = false;
bool arm = false;
Platform () {
#if defined(CR_WINDOWS)
win32 = true;
#endif
#if defined(CR_ANDROID)
android = true;
# if defined (CR_ANDROID_HARD_FP)
hfp = true;
# endif
#endif
#if defined(CR_LINUX)
linux = true;
#endif
#if defined(CR_OSX)
osx = true;
#endif
#if defined(CR_ARCH_X64) || defined(CR_ARCH_ARM64)
x64 = true;
#endif
#if defined(CR_ARCH_ARM)
arm = true;
android = true;
#endif
}
// helper platform-dependant functions
template <typename U> bool checkPointer (U *ptr) {
#if defined(CR_WINDOWS)
if (IsBadCodePtr (reinterpret_cast <FARPROC> (ptr))) {
return false;
}
#else
(void) (ptr);
#endif
return true;
}
bool createDirectory (const char *dir) {
int result = 0;
#if defined(CR_WINDOWS)
result = _mkdir (dir);
#else
result = mkdir (dir, 0777);
#endif
return !!result;
}
bool removeFile (const char *dir) {
#if defined(CR_WINDOWS)
_unlink (dir);
#else
unlink (dir);
#endif
return true;
}
bool hasModule (const char *mod) {
#if defined(CR_WINDOWS)
return GetModuleHandleA (mod) != nullptr;
#else
(void) (mod);
return true;
#endif
}
float seconds () {
#if defined(CR_WINDOWS)
LARGE_INTEGER count, freq;
count.QuadPart = 0;
freq.QuadPart = 0;
QueryPerformanceFrequency (&freq);
QueryPerformanceCounter (&count);
return static_cast <float> (count.QuadPart) / static_cast <float> (freq.QuadPart);
#else
timeval tv;
gettimeofday (&tv, NULL);
return static_cast <float> (tv.tv_sec) + (static_cast <float> (tv.tv_usec)) / 1000000.0;
#endif
}
void abort (const char *msg = "OUT OF MEMORY!") noexcept {
fprintf (stderr, "%s\n", msg);
#if defined (CR_ANDROID)
__android_log_write (ANDROID_LOG_ERROR, "crlib.fatal", msg);
#endif
#if defined(CR_WINDOWS)
DestroyWindow (GetForegroundWindow ());
MessageBoxA (GetActiveWindow (), msg, "crlib.fatal", MB_ICONSTOP);
#endif
::abort ();
}
// anologue of memset
template <typename U> void bzero (U *ptr, size_t len) noexcept {
#if defined (CR_WINDOWS)
memset (ptr, 0, len);
#else
auto zeroing = reinterpret_cast <uint8 *> (ptr);
for (size_t i = 0; i < len; ++i) {
zeroing[i] = 0;
}
#endif
}
const char *env (const char *var) {
static char result[256];
bzero (result, sizeof (result));
#if defined(CR_WINDOWS) && !defined(CR_CXX_GCC)
char *buffer = nullptr;
size_t size = 0;
if (_dupenv_s (&buffer, &size, var) == 0 && buffer != nullptr) {
strncpy_s (result, buffer, sizeof (result));
free (buffer);
}
#else
strncpy (result, getenv (var), cr::bufsize (result));
#endif
return result;
}
};
// expose platform singleton
CR_EXPOSE_GLOBAL_SINGLETON (Platform, plat);
CR_NAMESPACE_END

72
ext/crlib/cr-random.h Normal file
View file

@ -0,0 +1,72 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <time.h>
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
// random number generator see: https://github.com/preshing/RandomSequence/
class Random final : public Singleton <Random> {
private:
uint32 index_, offset_;
uint64 divider_;
public:
explicit Random () {
const auto base = static_cast <uint32> (time (nullptr));
const auto offset = base + 1;
index_ = premute (premute (base) + 0x682f0161);
offset_ = premute (premute (offset) + 0x46790905);
divider_ = (static_cast <uint64> (1)) << 32;
}
~Random () = default;
private:
uint32 premute (uint32 index) {
static constexpr auto prime = 4294967291u;
if (index >= prime) {
return index;
}
const uint32 residue = (static_cast <uint64> (index) * index) % prime;
return (index <= prime / 2) ? residue : prime - residue;
}
uint32 generate () {
return premute ((premute (index_++) + offset_) ^ 0x5bf03635);
}
public:
template <typename U> U int_ (U low, U high) {
return static_cast <U> (generate () * (static_cast <double> (high) - static_cast <double> (low) + 1.0) / divider_ + static_cast <double> (low));
}
float float_ (float low, float high) {
return static_cast <float> (generate () * (static_cast <double> (high) - static_cast <double> (low)) / (divider_ - 1) + static_cast <double> (low));
}
template <typename U> bool chance (const U max, const U maxChance = 100) {
return int_ <U> (0, maxChance) < max;
}
};
// expose global random generator
CR_EXPOSE_GLOBAL_SINGLETON (Random, rg);
CR_NAMESPACE_END

1133
ext/crlib/cr-string.h Normal file

File diff suppressed because it is too large Load diff

64
ext/crlib/cr-twin.h Normal file
View file

@ -0,0 +1,64 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-movable.h>
CR_NAMESPACE_BEGIN
// simple pair (twin)
template <typename A, typename B> class Twin final {
public:
A first;
B second;
public:
template <typename T, typename U> Twin (T &&a, U &&b) : first (cr::forward <T> (a)), second (cr::forward <U> (b)) { }
template <typename T, typename U> Twin (const Twin <T, U> &rhs) : first (rhs.first), second (rhs.second) { }
template <typename T, typename U> Twin (Twin <T, U> &&rhs) noexcept : first (cr::move (rhs.first)), second (cr::move (rhs.second)) { }
public:
explicit Twin () = default;
~Twin () = default;
public:
template <typename T, typename U> Twin &operator = (const Twin <T, U> &rhs) {
first = rhs.first;
second = rhs.second;
return *this;
}
template <typename T, typename U> Twin &operator = (Twin <T, U> &&rhs) {
first = cr::move (rhs.first);
second = cr::move (rhs.second);
return *this;
}
// specialized operators for binary heap, do not use as it's test only second element
public:
friend bool operator < (const Twin &a, const Twin &b) {
return a.second < b.second;
}
friend bool operator > (const Twin &a, const Twin &b) {
return b.second < a.second;
}
};
CR_NAMESPACE_END

317
ext/crlib/cr-ulz.h Normal file
View file

@ -0,0 +1,317 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-array.h>
CR_NAMESPACE_BEGIN
// see https://github.com/encode84/ulz/
class ULZ final : DenyCopying {
public:
enum : int32 {
Excess = 16,
UncompressFailure = -1
};
private:
enum : int32 {
WindowBits = 17,
WindowSize = cr::bit (WindowBits),
WindowMask = WindowSize - 1,
MinMatch = 4,
MaxChain = cr::bit (5),
HashBits = 19,
HashLength = cr::bit (HashBits),
EmptyHash = -1,
};
private:
SmallArray <int32> hashTable_;
SmallArray <int32> prevTable_;
public:
explicit ULZ () {
hashTable_.resize (HashLength);
prevTable_.resize (WindowSize);
}
~ULZ () = default;
public:
int32 compress (uint8 *in, int32 inputLength, uint8 *out) {
for (auto &htb : hashTable_) {
htb = EmptyHash;
}
auto op = out;
int32 anchor = 0;
int32 cur = 0;
while (cur < inputLength) {
const int32 maxMatch = inputLength - cur;
int32 bestLength = 0;
int32 dist = 0;
if (maxMatch >= MinMatch) {
const int32 limit = cr::max <int32> (cur - WindowSize, EmptyHash);
int32 chainLength = MaxChain;
int32 lookup = hashTable_[hash32 (&in[cur])];
while (lookup > limit) {
if (in[lookup + bestLength] == in[cur + bestLength] && load <uint32> (&in[lookup]) == load <uint32> (&in[cur])) {
int32 length = MinMatch;
while (length < maxMatch && in[lookup + length] == in[cur + length]) {
++length;
}
if (length > bestLength) {
bestLength = length;
dist = cur - lookup;
if (length == maxMatch) {
break;
}
}
}
if (--chainLength == 0) {
break;
}
lookup = prevTable_[lookup & WindowMask];
}
}
if (bestLength == MinMatch && (cur - anchor) >= (7 + 128)) {
bestLength = 0;
}
if (bestLength >= MinMatch && bestLength < maxMatch && (cur - anchor) != 6) {
const auto next = cur + 1;
const auto target = bestLength + 1;
const auto limit = cr::max <int32> (next - WindowSize, EmptyHash);
int32 chainLength = MaxChain;
int32 lookup = hashTable_[hash32 (&in[next])];
while (lookup > limit) {
if (in[lookup + bestLength] == in[next + bestLength] && load <uint32> (&in[lookup]) == load <uint32> (&in[next])) {
int32 length = MinMatch;
while (length < target && in[lookup + length] == in[next + length]) {
++length;
}
if (length == target) {
bestLength = 0;
break;
}
}
if (--chainLength == 0) {
break;
}
lookup = prevTable_[lookup & WindowMask];
}
}
if (bestLength >= MinMatch) {
const auto length = bestLength - MinMatch;
const auto token = ((dist >> 12) & 16) + cr::min <int32> (length, 15);
if (anchor != cur) {
const auto run = cur - anchor;
if (run >= 7) {
add (op, (7 << 5) + token);
encode (op, run - 7);
}
else {
add (op, (run << 5) + token);
}
copy (op, &in[anchor], run);
op += run;
}
else {
add (op, token);
}
if (length >= 15) {
encode (op, length - 15);
}
store16 (op, static_cast <uint16> (dist));
op += 2;
while (bestLength-- != 0) {
const auto hash = hash32 (&in[cur]);
prevTable_[cur & WindowMask] = hashTable_[hash];
hashTable_[hash] = cur++;
}
anchor = cur;
}
else {
const auto hash = hash32 (&in[cur]);
prevTable_[cur & WindowMask] = hashTable_[hash];
hashTable_[hash] = cur++;
}
}
if (anchor != cur) {
const auto run = cur - anchor;
if (run >= 7) {
add (op, 7 << 5);
encode (op, run - 7);
}
else {
add (op, run << 5);
}
copy (op, &in[anchor], run);
op += run;
}
return static_cast <int32> (op - out);
}
int32 uncompress (uint8 *in, int32 inputLength, uint8 *out, int32 outLength) {
auto op = out;
auto ip = in;
const auto opEnd = op + outLength;
const auto ipEnd = ip + inputLength;
while (ip < ipEnd) {
const auto token = *ip++;
if (token >= 32) {
auto run = token >> 5;
if (run == 7) {
run += decode (ip);
}
if ((opEnd - op) < run || (ipEnd - ip) < run) {
return UncompressFailure;
}
copy (op, ip, run);
op += run;
ip += run;
if (ip >= ipEnd) {
break;
}
}
auto length = (token & 15) + MinMatch;
if (length == (15 + MinMatch)) {
length += decode (ip);
}
if ((opEnd - op) < length) {
return UncompressFailure;
}
const auto dist = ((token & 16) << 12) + load <uint16> (ip);
ip += 2;
auto cp = op - dist;
if ((op - out) < dist) {
return UncompressFailure;
}
if (dist >= 8) {
copy (op, cp, length);
op += length;
}
else {
for (int32 i = 0; i < 4; ++i) {
*op++ = *cp++;
}
while (length-- != 4) {
*op++ = *cp++;
}
}
}
return static_cast <int32> (ip == ipEnd) ? static_cast <int32> (op - out) : UncompressFailure;
}
private:
template <typename U> U load (void *ptr) {
U ret;
memcpy (&ret, ptr, sizeof (U));
return ret;
}
void store16 (void *ptr, uint16 val) {
memcpy (ptr, &val, sizeof (uint16));
}
void copy64 (void *dst, void *src) {
memcpy (dst, src, sizeof (uint64));
}
uint32 hash32 (void *ptr) {
return (load <uint32> (ptr) * 0x9e3779b9) >> (32 - HashBits);
}
void copy (uint8 *dst, uint8 *src, int32 count) {
copy64 (dst, src);
for (int32 i = 8; i < count; i += 8) {
copy64 (dst + i, src + i);
}
}
void add (uint8 *&dst, int32 val) {
*dst++ = static_cast <uint8> (val);
}
void encode (uint8 *&ptr, uint32 val) {
while (val >= 128) {
val -= 128;
*ptr++ = 128 + (val & 127);
val >>= 7;
}
*ptr++ = static_cast <uint8> (val);
}
uint32 decode (uint8 *&ptr) {
uint32 val = 0;
for (int32 i = 0; i <= 21; i += 7) {
const uint32 cur = *ptr++;
val += cur << i;
if (cur < 128) {
break;
}
}
return val;
}
};
CR_NAMESPACE_END

212
ext/crlib/cr-uniqueptr.h Normal file
View file

@ -0,0 +1,212 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-movable.h>
CR_NAMESPACE_BEGIN
// simple unique ptr
template <typename T> class UniquePtr final : public DenyCopying {
private:
T *ptr_ {};
public:
UniquePtr () = default;
explicit UniquePtr (T *ptr) : ptr_ (ptr)
{ }
UniquePtr (UniquePtr &&rhs) noexcept : ptr_ (rhs.release ())
{ }
template <typename U> UniquePtr (UniquePtr <U> &&rhs) noexcept : ptr_ (rhs.release ())
{ }
~UniquePtr () {
destroy ();
}
public:
T *get () const {
return ptr_;
}
T *release () {
auto ret = ptr_;
ptr_ = nullptr;
return ret;
}
void reset (T *ptr = nullptr) {
destroy ();
ptr_ = ptr;
}
private:
void destroy () {
alloc.destroy (ptr_);
ptr_ = nullptr;
}
public:
UniquePtr &operator = (UniquePtr &&rhs) noexcept {
if (this != &rhs) {
reset (rhs.release ());
}
return *this;
}
template <typename U> UniquePtr &operator = (UniquePtr <U> &&rhs) noexcept {
if (this != &rhs) {
reset (rhs.release ());
}
return *this;
}
UniquePtr &operator = (decltype (nullptr)) {
destroy ();
return *this;
}
T &operator * () const {
return *ptr_;
}
T *operator -> () const {
return ptr_;
}
explicit operator bool () const {
return ptr_ != nullptr;
}
};
template <typename T> class UniquePtr <T[]> final : public DenyCopying {
private:
T *ptr_ { };
public:
UniquePtr () = default;
explicit UniquePtr (T *ptr) : ptr_ (ptr)
{ }
UniquePtr (UniquePtr &&rhs) noexcept : ptr_ (rhs.release ())
{ }
template <typename U> UniquePtr (UniquePtr <U> &&rhs) noexcept : ptr_ (rhs.release ())
{ }
~UniquePtr () {
destroy ();
}
public:
T *get () const {
return ptr_;
}
T *release () {
auto ret = ptr_;
ptr_ = nullptr;
return ret;
}
void reset (T *ptr = nullptr) {
destroy ();
ptr_ = ptr;
}
private:
void destroy () {
alloc.destroy (ptr_);
ptr_ = nullptr;
}
public:
UniquePtr &operator = (UniquePtr &&rhs) noexcept {
if (this != &rhs) {
reset (rhs.release ());
}
return *this;
}
template <typename U> UniquePtr &operator = (UniquePtr <U> &&rhs) noexcept {
if (this != &rhs) {
reset (rhs.release ());
}
return *this;
}
UniquePtr &operator = (decltype (nullptr)) {
destroy ();
return *this;
}
T &operator [] (size_t index) {
return ptr_[index];
}
const T &operator [] (size_t index) const {
return ptr_[index];
}
explicit operator bool () const {
return ptr_ != nullptr;
}
};
namespace detail {
template <typename T> struct ClearExtent {
using Type = T;
};
template <typename T> struct ClearExtent <T[]> {
using Type = T;
};
template <typename T, size_t N> struct ClearExtent <T[N]> {
using Type = T;
};
template <typename T> struct UniqueIf {
using SingleObject = UniquePtr <T>;
};
template <typename T> struct UniqueIf<T[]> {
using UnknownBound = UniquePtr <T[]>;
};
template <typename T, size_t N> struct UniqueIf <T[N]> {
using KnownBound = void;
};
}
template <typename T, typename... Args> typename detail::UniqueIf <T>::SingleObject makeUnique (Args &&... args) {
return UniquePtr <T> (alloc.create <T> (cr::forward <Args> (args)...));
}
template <typename T> typename detail::UniqueIf <T>::UnknownBound makeUnique (size_t size) {
return UniquePtr <T> (alloc.createArray <typename detail::ClearExtent <T>::Type> (size));
}
template <typename T, typename... Args> typename detail::UniqueIf <T>::KnownBound makeUnique (Args &&...) = delete;
CR_NAMESPACE_END

335
ext/crlib/cr-vector.h Normal file
View file

@ -0,0 +1,335 @@
//
// CRLib - Simple library for STL replacement in private projects.
// Copyright © 2020 YaPB Development Team <team@yapb.ru>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#pragma once
#include <crlib/cr-math.h>
CR_NAMESPACE_BEGIN
// small simd operations for 3d vector
#if defined (CR_HAS_SSE)
template <typename T> class CR_ALIGN16 SimdWrap {
private:
__m128 wrap_dp_sse2 (__m128 v1, __m128 v2) {
auto mul = _mm_mul_ps (v1, v2);
auto res = _mm_add_ps (_mm_shuffle_ps (v2, mul, _MM_SHUFFLE (1, 0, 0, 0)), mul);
mul = _mm_add_ps (_mm_shuffle_ps (mul, res, _MM_SHUFFLE (0, 3, 0, 0)), res);
return _mm_shuffle_ps (mul, mul, _MM_SHUFFLE (2, 2, 2, 2));
}
public:
union {
__m128 m;
struct {
T x, y, z;
} vec;
};
SimdWrap (const T &x, const T &y, const T &z) {
m = _mm_set_ps (0.0f, z, y, x);
}
SimdWrap (const T &x, const T &y) {
m = _mm_set_ps (0.0f, 0.0f, y, x);
}
SimdWrap (__m128 m) : m (m)
{ }
public:
SimdWrap normalize () {
return { _mm_div_ps (m, _mm_sqrt_ps (wrap_dp_sse2 (m, m))) };
}
};
#endif
// 3dmath vector
template <typename T> class Vec3D {
public:
T x {};
T y {};
T z {};
public:
Vec3D (const T &scaler = 0.0f) : x (scaler), y (scaler), z (scaler)
{ }
Vec3D (const T &x, const T &y, const T &z) : x (x), y (y), z (z)
{ }
Vec3D (T *rhs) : x (rhs[0]), y (rhs[1]), z (rhs[2])
{ }
#if defined (CR_HAS_SSE)
Vec3D (const SimdWrap <T> &rhs) : x (rhs.vec.x), y (rhs.vec.y), z (rhs.vec.z)
{ }
#endif
Vec3D (const Vec3D &) = default;
Vec3D (decltype (nullptr)) {
clear ();
}
public:
operator T * () {
return &x;
}
operator const T * () const {
return &x;
}
Vec3D operator + (const Vec3D &rhs) const {
return { x + rhs.x, y + rhs.y, z + rhs.z };
}
Vec3D operator - (const Vec3D &rhs) const {
return { x - rhs.x, y - rhs.y, z - rhs.z };
}
Vec3D operator - () const {
return { -x, -y, -z };
}
friend Vec3D operator * (const T &scale, const Vec3D &rhs) {
return { rhs.x * scale, rhs.y * scale, rhs.z * scale };
}
Vec3D operator * (const T &scale) const {
return { scale * x, scale * y, scale * z };
}
Vec3D operator / (const T &rhs) const {
const auto inv = 1 / (rhs + kFloatEqualEpsilon);
return { inv * x, inv * y, inv * z };
}
// cross product
Vec3D operator ^ (const Vec3D &rhs) const {
return { y * rhs.z - z * rhs.y, z * rhs.x - x * rhs.z, x * rhs.y - y * rhs.x };
}
// dot product
T operator | (const Vec3D &rhs) const {
return x * rhs.x + y * rhs.y + z * rhs.z;
}
const Vec3D &operator += (const Vec3D &rhs) {
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
const Vec3D &operator -= (const Vec3D &rhs) {
x -= rhs.x;
y -= rhs.y;
z -= rhs.z;
return *this;
}
const Vec3D &operator *= (const T &rhs) {
x *= rhs;
y *= rhs;
z *= rhs;
return *this;
}
const Vec3D &operator /= (const T &rhs) {
const auto inv = 1 / (rhs + kFloatEqualEpsilon);
x *= inv;
y *= inv;
z *= inv;
return *this;
}
bool operator == (const Vec3D &rhs) const {
return cr::fequal (x, rhs.x) && cr::fequal (y, rhs.y) && cr::fequal (z, rhs.z);
}
bool operator != (const Vec3D &rhs) const {
return !operator == (rhs);
}
void operator = (decltype (nullptr)) {
clear ();
}
const float &operator [] (const int i) const {
return &(x)[i];
}
float &operator [] (const int i) {
return &(x)[i];
}
Vec3D &operator = (const Vec3D &) = default;
public:
T length () const {
return cr::sqrtf (lengthSq ());
}
T length2d () const {
return cr::sqrtf (cr::square (x) + cr::square (y));
}
T lengthSq () const {
return cr::square (x) + cr::square (y) + cr::square (z);
}
Vec3D get2d () const {
return { x, y, 0.0f };
}
Vec3D normalize () const {
#if defined (CR_HAS_SSE)
return SimdWrap <T> { x, y, z }.normalize ();
#else
auto len = length () + cr::kFloatCmpEpsilon;
if (cr::fzero (len)) {
return { 0.0f, 0.0f, 1.0f };
}
len = 1.0f / len;
return { x * len, y * len, z * len };
#endif
}
Vec3D normalize2d () const {
#if defined (CR_HAS_SSE)
return SimdWrap <T> { x, y }.normalize ();
#else
auto len = length2d () + cr::kFloatCmpEpsilon;
if (cr::fzero (len)) {
return { 0.0f, 1.0f, 0.0f };
}
len = 1.0f / len;
return { x * len, y * len, 0.0f };
#endif
}
bool empty () const {
return cr::fzero (x) && cr::fzero (y) && cr::fzero (z);
}
void clear () {
x = y = z = 0.0f;
}
Vec3D clampAngles () {
x = cr::normalizeAngles (x);
y = cr::normalizeAngles (y);
z = 0.0f;
return *this;
}
T pitch () const {
if (cr::fzero (z)) {
return 0.0f;
}
return cr::deg2rad (cr::atan2f (z, length2d ()));
}
T yaw () const {
if (cr::fzero (x) && cr::fzero (y)) {
return 0.0f;
}
return cr::rad2deg (cr:: atan2f (y, x));
}
Vec3D angles () const {
if (cr::fzero (x) && cr::fzero (y)) {
return { z > 0.0f ? 90.0f : 270.0f, 0.0, 0.0f };
}
return { cr::rad2deg (cr::atan2f (z, length2d ())), cr::rad2deg (cr::atan2f (y, x)), 0.0f };
}
void angleVectors (Vec3D *forward, Vec3D *right, Vec3D *upward) const {
enum { pitch, yaw, roll, unused, max };
T sines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
T cosines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
// compute the sine and cosine compontents
cr::sincosf (cr::deg2rad (x), cr::deg2rad (y), cr::deg2rad (z), sines, cosines);
if (forward) {
*forward = {
cosines[pitch] * cosines[yaw],
cosines[pitch] * sines[yaw],
-sines[pitch]
};
}
if (right) {
*right = {
-sines[roll] * sines[pitch] * cosines[yaw] + cosines[roll] * sines[yaw],
-sines[roll] * sines[pitch] * sines[yaw] - cosines[roll] * cosines[yaw],
-sines[roll] * cosines[pitch]
};
}
if (upward) {
*upward = {
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]
};
}
}
const Vec3D &forward () {
static Vec3D s_fwd {};
angleVectors (&s_fwd, nullptr, nullptr);
return s_fwd;
}
const Vec3D &upward () {
static Vec3D s_up {};
angleVectors (nullptr, nullptr, &s_up);
return s_up;
}
const Vec3D &right () {
static Vec3D s_right {};
angleVectors (nullptr, &s_right, nullptr);
return s_right;
}
};
// default is float
using Vector = Vec3D <float>;
CR_NAMESPACE_END