Initial commit

This commit is contained in:
2022-10-08 17:16:13 -04:00
commit 385638c5e1
1925 changed files with 872504 additions and 0 deletions

19
src/util/CMakeLists.txt Normal file
View File

@@ -0,0 +1,19 @@
set(UTIL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ieee_float.cpp
${CMAKE_CURRENT_SOURCE_DIR}/metricsbackend.cpp
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/quicktune.cpp
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sha256.c
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
${CMAKE_CURRENT_SOURCE_DIR}/png.cpp
PARENT_SCOPE)

105
src/util/Optional.h Normal file
View File

@@ -0,0 +1,105 @@
/*
Minetest
Copyright (C) 2021 rubenwardy
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <utility>
#include "debug.h"
struct nullopt_t
{
};
constexpr nullopt_t nullopt{};
/**
* An implementation of optional for C++11, which aims to be
* compatible with a subset of std::optional features.
*
* Unfortunately, Minetest doesn't use C++17 yet.
*
* @tparam T The type to be stored
*/
template <typename T>
class Optional
{
bool m_has_value = false;
T m_value;
public:
Optional() noexcept {}
Optional(nullopt_t) noexcept {}
Optional(const T &value) noexcept : m_has_value(true), m_value(value) {}
Optional(T &&value) noexcept : m_has_value(true), m_value(std::move(value)) {}
Optional(const Optional<T> &other) noexcept :
m_has_value(other.m_has_value), m_value(other.m_value)
{}
Optional(Optional<T> &&other) noexcept :
m_has_value(other.m_has_value), m_value(std::move(other.m_value))
{
other.m_has_value = false;
}
Optional<T> &operator=(nullopt_t) noexcept { m_has_value = false; return *this; }
Optional<T> &operator=(const Optional<T> &other) noexcept
{
if (&other == this)
return *this;
m_has_value = other.m_has_value;
m_value = other.m_value;
return *this;
}
Optional<T> &operator=(Optional<T> &&other) noexcept
{
if (&other == this)
return *this;
m_has_value = other.m_has_value;
m_value = std::move(other.m_value);
other.m_has_value = false;
return *this;
}
T &value()
{
FATAL_ERROR_IF(!m_has_value, "optional doesn't have value");
return m_value;
}
const T &value() const
{
FATAL_ERROR_IF(!m_has_value, "optional doesn't have value");
return m_value;
}
const T &value_or(const T &def) const { return m_has_value ? m_value : def; }
// Unchecked access consistent with std::optional
T* operator->() { return &m_value; }
const T* operator->() const { return &m_value; }
T& operator*() { return m_value; }
const T& operator*() const { return m_value; }
bool has_value() const noexcept { return m_has_value; }
explicit operator bool() const { return m_has_value; }
};

329
src/util/areastore.cpp Normal file
View File

@@ -0,0 +1,329 @@
/*
Minetest
Copyright (C) 2015 est31 <mtest31@outlook.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "util/areastore.h"
#include "util/serialize.h"
#include "util/container.h"
#if USE_SPATIAL
#include <spatialindex/SpatialIndex.h>
#include <spatialindex/RTree.h>
#include <spatialindex/Point.h>
#endif
#define AST_SMALLER_EQ_AS(p, q) (((p).X <= (q).X) && ((p).Y <= (q).Y) && ((p).Z <= (q).Z))
#define AST_OVERLAPS_IN_DIMENSION(amine, amaxe, b, d) \
(!(((amine).d > (b)->maxedge.d) || ((amaxe).d < (b)->minedge.d)))
#define AST_CONTAINS_PT(a, p) (AST_SMALLER_EQ_AS((a)->minedge, (p)) && \
AST_SMALLER_EQ_AS((p), (a)->maxedge))
#define AST_CONTAINS_AREA(amine, amaxe, b) \
(AST_SMALLER_EQ_AS((amine), (b)->minedge) \
&& AST_SMALLER_EQ_AS((b)->maxedge, (amaxe)))
#define AST_AREAS_OVERLAP(amine, amaxe, b) \
(AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), X) && \
AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Y) && \
AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Z))
AreaStore *AreaStore::getOptimalImplementation()
{
#if USE_SPATIAL
return new SpatialAreaStore();
#else
return new VectorAreaStore();
#endif
}
const Area *AreaStore::getArea(u32 id) const
{
AreaMap::const_iterator it = areas_map.find(id);
if (it == areas_map.end())
return nullptr;
return &it->second;
}
void AreaStore::serialize(std::ostream &os) const
{
// WARNING:
// Before 5.1.0-dev: version != 0 throws SerializationError
// After 5.1.0-dev: version >= 5 throws SerializationError
// Forwards-compatibility is assumed before version 5.
writeU8(os, 0); // Serialisation version
// TODO: Compression?
writeU16(os, areas_map.size());
for (const auto &it : areas_map) {
const Area &a = it.second;
writeV3S16(os, a.minedge);
writeV3S16(os, a.maxedge);
writeU16(os, a.data.size());
os.write(a.data.data(), a.data.size());
}
// Serialize IDs
for (const auto &it : areas_map)
writeU32(os, it.second.id);
}
void AreaStore::deserialize(std::istream &is)
{
u8 ver = readU8(is);
// Assume forwards-compatibility before version 5
if (ver >= 5)
throw SerializationError("Unknown AreaStore "
"serialization version!");
u16 num_areas = readU16(is);
std::vector<Area> areas;
areas.reserve(num_areas);
for (u32 i = 0; i < num_areas; ++i) {
Area a(U32_MAX);
a.minedge = readV3S16(is);
a.maxedge = readV3S16(is);
u16 data_len = readU16(is);
a.data = std::string(data_len, '\0');
is.read(&a.data[0], data_len);
areas.emplace_back(std::move(a));
}
bool read_ids = is.good(); // EOF for old formats
for (auto &area : areas) {
if (read_ids)
area.id = readU32(is);
insertArea(&area);
}
}
void AreaStore::invalidateCache()
{
if (m_cache_enabled) {
m_res_cache.invalidate();
}
}
u32 AreaStore::getNextId() const
{
u32 free_id = 0;
for (const auto &area : areas_map) {
if (area.first > free_id)
return free_id; // Found gap
free_id = area.first + 1;
}
// End of map
return free_id;
}
void AreaStore::setCacheParams(bool enabled, u8 block_radius, size_t limit)
{
m_cache_enabled = enabled;
m_cacheblock_radius = MYMAX(block_radius, 16);
m_res_cache.setLimit(MYMAX(limit, 20));
invalidateCache();
}
void AreaStore::cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *dest)
{
AreaStore *as = (AreaStore *)data;
u8 r = as->m_cacheblock_radius;
// get the points at the edges of the mapblock
v3s16 minedge(mpos.X * r, mpos.Y * r, mpos.Z * r);
v3s16 maxedge(
minedge.X + r - 1,
minedge.Y + r - 1,
minedge.Z + r - 1);
as->getAreasInArea(dest, minedge, maxedge, true);
/* infostream << "Cache miss with " << dest->size() << " areas, between ("
<< minedge.X << ", " << minedge.Y << ", " << minedge.Z
<< ") and ("
<< maxedge.X << ", " << maxedge.Y << ", " << maxedge.Z
<< ")" << std::endl; // */
}
void AreaStore::getAreasForPos(std::vector<Area *> *result, v3s16 pos)
{
if (m_cache_enabled) {
v3s16 mblock = getContainerPos(pos, m_cacheblock_radius);
const std::vector<Area *> *pre_list = m_res_cache.lookupCache(mblock);
size_t s_p_l = pre_list->size();
for (size_t i = 0; i < s_p_l; i++) {
Area *b = (*pre_list)[i];
if (AST_CONTAINS_PT(b, pos)) {
result->push_back(b);
}
}
} else {
return getAreasForPosImpl(result, pos);
}
}
////
// VectorAreaStore
////
bool VectorAreaStore::insertArea(Area *a)
{
if (a->id == U32_MAX)
a->id = getNextId();
std::pair<AreaMap::iterator, bool> res =
areas_map.insert(std::make_pair(a->id, *a));
if (!res.second)
// ID is not unique
return false;
m_areas.push_back(&res.first->second);
invalidateCache();
return true;
}
bool VectorAreaStore::removeArea(u32 id)
{
AreaMap::iterator it = areas_map.find(id);
if (it == areas_map.end())
return false;
Area *a = &it->second;
for (std::vector<Area *>::iterator v_it = m_areas.begin();
v_it != m_areas.end(); ++v_it) {
if (*v_it == a) {
m_areas.erase(v_it);
break;
}
}
areas_map.erase(it);
invalidateCache();
return true;
}
void VectorAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
{
for (Area *area : m_areas) {
if (AST_CONTAINS_PT(area, pos)) {
result->push_back(area);
}
}
}
void VectorAreaStore::getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap)
{
for (Area *area : m_areas) {
if (accept_overlap ? AST_AREAS_OVERLAP(minedge, maxedge, area) :
AST_CONTAINS_AREA(minedge, maxedge, area)) {
result->push_back(area);
}
}
}
#if USE_SPATIAL
static inline SpatialIndex::Region get_spatial_region(const v3s16 minedge,
const v3s16 maxedge)
{
const double p_low[] = {(double)minedge.X,
(double)minedge.Y, (double)minedge.Z};
const double p_high[] = {(double)maxedge.X, (double)maxedge.Y,
(double)maxedge.Z};
return SpatialIndex::Region(p_low, p_high, 3);
}
static inline SpatialIndex::Point get_spatial_point(const v3s16 pos)
{
const double p[] = {(double)pos.X, (double)pos.Y, (double)pos.Z};
return SpatialIndex::Point(p, 3);
}
bool SpatialAreaStore::insertArea(Area *a)
{
if (a->id == U32_MAX)
a->id = getNextId();
if (!areas_map.insert(std::make_pair(a->id, *a)).second)
// ID is not unique
return false;
m_tree->insertData(0, nullptr, get_spatial_region(a->minedge, a->maxedge), a->id);
invalidateCache();
return true;
}
bool SpatialAreaStore::removeArea(u32 id)
{
std::map<u32, Area>::iterator itr = areas_map.find(id);
if (itr != areas_map.end()) {
Area *a = &itr->second;
bool result = m_tree->deleteData(get_spatial_region(a->minedge,
a->maxedge), id);
areas_map.erase(itr);
invalidateCache();
return result;
} else {
return false;
}
}
void SpatialAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
{
VectorResultVisitor visitor(result, this);
m_tree->pointLocationQuery(get_spatial_point(pos), visitor);
}
void SpatialAreaStore::getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap)
{
VectorResultVisitor visitor(result, this);
if (accept_overlap) {
m_tree->intersectsWithQuery(get_spatial_region(minedge, maxedge),
visitor);
} else {
m_tree->containsWhatQuery(get_spatial_region(minedge, maxedge), visitor);
}
}
SpatialAreaStore::~SpatialAreaStore()
{
delete m_tree;
delete m_storagemanager;
}
SpatialAreaStore::SpatialAreaStore()
{
m_storagemanager =
SpatialIndex::StorageManager::createNewMemoryStorageManager();
SpatialIndex::id_type id;
m_tree = SpatialIndex::RTree::createNewRTree(
*m_storagemanager,
.7, // Fill factor
100, // Index capacity
100, // Leaf capacity
3, // dimension :)
SpatialIndex::RTree::RV_RSTAR,
id);
}
#endif

197
src/util/areastore.h Normal file
View File

@@ -0,0 +1,197 @@
/*
Minetest
Copyright (C) 2015 est31 <mtest31@outlook.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irr_v3d.h"
#include "noise.h" // for PcgRandom
#include <map>
#include <list>
#include <vector>
#include <istream>
#include "util/container.h"
#include "util/numeric.h"
#ifndef ANDROID
#include "cmake_config.h"
#endif
#if USE_SPATIAL
#include <spatialindex/SpatialIndex.h>
#include "util/serialize.h"
#endif
struct Area {
Area(u32 area_id) : id(area_id) {}
Area(const v3s16 &mine, const v3s16 &maxe, u32 area_id = U32_MAX) :
id(area_id), minedge(mine), maxedge(maxe)
{
sortBoxVerticies(minedge, maxedge);
}
u32 id;
v3s16 minedge, maxedge;
std::string data;
};
class AreaStore {
public:
AreaStore() :
m_res_cache(1000, &cacheMiss, this)
{}
virtual ~AreaStore() = default;
static AreaStore *getOptimalImplementation();
virtual void reserve(size_t count) {};
size_t size() const { return areas_map.size(); }
/// Add an area to the store.
/// Updates the area's ID if it hasn't already been set.
/// @return Whether the area insertion was successful.
virtual bool insertArea(Area *a) = 0;
/// Removes an area from the store by ID.
/// @return Whether the area was in the store and removed.
virtual bool removeArea(u32 id) = 0;
/// Finds areas that the passed position is contained in.
/// Stores output in passed vector.
void getAreasForPos(std::vector<Area *> *result, v3s16 pos);
/// Finds areas that are completely contained inside the area defined
/// by the passed edges. If @p accept_overlap is true this finds any
/// areas that intersect with the passed area at any point.
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap) = 0;
/// Sets cache parameters.
void setCacheParams(bool enabled, u8 block_radius, size_t limit);
/// Returns a pointer to the area coresponding to the passed ID,
/// or NULL if it doesn't exist.
const Area *getArea(u32 id) const;
/// Serializes the store's areas to a binary ostream.
void serialize(std::ostream &is) const;
/// Deserializes the Areas from a binary istream.
/// This does not currently clear the AreaStore before adding the
/// areas, making it possible to deserialize multiple serialized
/// AreaStores.
void deserialize(std::istream &is);
protected:
/// Invalidates the getAreasForPos cache.
/// Call after adding or removing an area.
void invalidateCache();
/// Implementation of getAreasForPos.
/// getAreasForPos calls this if the cache is disabled.
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos) = 0;
/// Returns the next area ID and increments it.
u32 getNextId() const;
// Note: This can't be an unordered_map, since all
// references would be invalidated on rehash.
typedef std::map<u32, Area> AreaMap;
AreaMap areas_map;
private:
/// Called by the cache when a value isn't found in the cache.
static void cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *dest);
bool m_cache_enabled = true;
/// Range, in nodes, of the getAreasForPos cache.
/// If you modify this, call invalidateCache()
u8 m_cacheblock_radius = 64;
LRUCache<v3s16, std::vector<Area *> > m_res_cache;
};
class VectorAreaStore : public AreaStore {
public:
virtual void reserve(size_t count) { m_areas.reserve(count); }
virtual bool insertArea(Area *a);
virtual bool removeArea(u32 id);
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap);
protected:
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
private:
std::vector<Area *> m_areas;
};
#if USE_SPATIAL
class SpatialAreaStore : public AreaStore {
public:
SpatialAreaStore();
virtual ~SpatialAreaStore();
virtual bool insertArea(Area *a);
virtual bool removeArea(u32 id);
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap);
protected:
virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
private:
SpatialIndex::ISpatialIndex *m_tree = nullptr;
SpatialIndex::IStorageManager *m_storagemanager = nullptr;
class VectorResultVisitor : public SpatialIndex::IVisitor {
public:
VectorResultVisitor(std::vector<Area *> *result, SpatialAreaStore *store) :
m_store(store),
m_result(result)
{}
~VectorResultVisitor() {}
virtual void visitNode(const SpatialIndex::INode &in) {}
virtual void visitData(const SpatialIndex::IData &in)
{
u32 id = in.getIdentifier();
std::map<u32, Area>::iterator itr = m_store->areas_map.find(id);
assert(itr != m_store->areas_map.end());
m_result->push_back(&itr->second);
}
virtual void visitData(std::vector<const SpatialIndex::IData *> &v)
{
for (size_t i = 0; i < v.size(); i++)
visitData(*(v[i]));
}
private:
SpatialAreaStore *m_store = nullptr;
std::vector<Area *> *m_result = nullptr;
};
};
#endif // USE_SPATIAL

137
src/util/auth.cpp Normal file
View File

@@ -0,0 +1,137 @@
/*
Minetest
Copyright (C) 2015, 2016 est31 <MTest31@outlook.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <algorithm>
#include <string>
#include "auth.h"
#include "base64.h"
#include "sha1.h"
#include "srp.h"
#include "util/string.h"
#include "debug.h"
// Get an sha-1 hash of the player's name combined with
// the password entered. That's what the server uses as
// their password. (Exception : if the password field is
// blank, we send a blank password - this is for backwards
// compatibility with password-less players).
std::string translate_password(const std::string &name,
const std::string &password)
{
if (password.length() == 0)
return "";
std::string slt = name + password;
SHA1 sha1;
sha1.addBytes(slt.c_str(), slt.length());
unsigned char *digest = sha1.getDigest();
std::string pwd = base64_encode(digest, 20);
free(digest);
return pwd;
}
// Call lower level SRP code to generate a verifier with the
// given pointers. Contains the preparations, call parameters
// and error checking common to all srp verifier generation code.
// See docs of srp_create_salted_verification_key for more info.
static inline void gen_srp_v(const std::string &name,
const std::string &password, char **salt, size_t *salt_len,
char **bytes_v, size_t *len_v)
{
std::string n_name = lowercase(name);
SRP_Result res = srp_create_salted_verification_key(SRP_SHA256, SRP_NG_2048,
n_name.c_str(), (const unsigned char *)password.c_str(),
password.size(), (unsigned char **)salt, salt_len,
(unsigned char **)bytes_v, len_v, NULL, NULL);
FATAL_ERROR_IF(res != SRP_OK, "Couldn't create salted SRP verifier");
}
/// Creates a verification key with given salt and password.
std::string generate_srp_verifier(const std::string &name,
const std::string &password, const std::string &salt)
{
size_t salt_len = salt.size();
// The API promises us that the salt doesn't
// get modified if &salt_ptr isn't NULL.
char *salt_ptr = (char *)salt.c_str();
char *bytes_v = nullptr;
size_t verifier_len = 0;
gen_srp_v(name, password, &salt_ptr, &salt_len, &bytes_v, &verifier_len);
std::string verifier = std::string(bytes_v, verifier_len);
free(bytes_v);
return verifier;
}
/// Creates a verification key and salt with given password.
void generate_srp_verifier_and_salt(const std::string &name,
const std::string &password, std::string *verifier,
std::string *salt)
{
char *bytes_v = nullptr;
size_t verifier_len;
char *salt_ptr = nullptr;
size_t salt_len;
gen_srp_v(name, password, &salt_ptr, &salt_len, &bytes_v, &verifier_len);
*verifier = std::string(bytes_v, verifier_len);
*salt = std::string(salt_ptr, salt_len);
free(bytes_v);
free(salt_ptr);
}
/// Gets an SRP verifier, generating a salt,
/// and encodes it as DB-ready string.
std::string get_encoded_srp_verifier(const std::string &name,
const std::string &password)
{
std::string verifier;
std::string salt;
generate_srp_verifier_and_salt(name, password, &verifier, &salt);
return encode_srp_verifier(verifier, salt);
}
/// Converts the passed SRP verifier into a DB-ready format.
std::string encode_srp_verifier(const std::string &verifier,
const std::string &salt)
{
std::ostringstream ret_str;
ret_str << "#1#"
<< base64_encode((unsigned char *)salt.c_str(), salt.size()) << "#"
<< base64_encode((unsigned char *)verifier.c_str(), verifier.size());
return ret_str.str();
}
/// Reads the DB-formatted SRP verifier and gets the verifier
/// and salt components.
bool decode_srp_verifier_and_salt(const std::string &encoded,
std::string *verifier, std::string *salt)
{
std::vector<std::string> components = str_split(encoded, '#');
if ((components.size() != 4)
|| (components[1] != "1") // 1 means srp
|| !base64_is_valid(components[2])
|| !base64_is_valid(components[3]))
return false;
*salt = base64_decode(components[2]);
*verifier = base64_decode(components[3]);
return true;
}

47
src/util/auth.h Normal file
View File

@@ -0,0 +1,47 @@
/*
Minetest
Copyright (C) 2015, 2016 est31 <MTest31@outlook.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
/// Gets the base64 encoded legacy password db entry.
std::string translate_password(const std::string &name,
const std::string &password);
/// Creates a verification key with given salt and password.
std::string generate_srp_verifier(const std::string &name,
const std::string &password, const std::string &salt);
/// Creates a verification key and salt with given password.
void generate_srp_verifier_and_salt(const std::string &name,
const std::string &password, std::string *verifier,
std::string *salt);
/// Gets an SRP verifier, generating a salt,
/// and encodes it as DB-ready string.
std::string get_encoded_srp_verifier(const std::string &name,
const std::string &password);
/// Converts the passed SRP verifier into a DB-ready format.
std::string encode_srp_verifier(const std::string &verifier,
const std::string &salt);
/// Reads the DB-formatted SRP verifier and gets the verifier
/// and salt components.
bool decode_srp_verifier_and_salt(const std::string &encoded,
std::string *verifier, std::string *salt);

154
src/util/base64.cpp Normal file
View File

@@ -0,0 +1,154 @@
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include "base64.h"
#include <iostream>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static const std::string base64_chars_padding_1 = "AEIMQUYcgkosw048";
static const std::string base64_chars_padding_2 = "AQgw";
static inline bool is_base64(unsigned char c)
{
return (c >= '0' && c <= '9')
|| (c >= 'A' && c <= 'Z')
|| (c >= 'a' && c <= 'z')
|| c == '+' || c == '/';
}
bool base64_is_valid(std::string const& s)
{
size_t i = 0;
for (; i < s.size(); ++i)
if (!is_base64(s[i]))
break;
unsigned char padding = 3 - ((i + 3) % 4);
if ((padding == 1 && base64_chars_padding_1.find(s[i - 1]) == std::string::npos)
|| (padding == 2 && base64_chars_padding_2.find(s[i - 1]) == std::string::npos)
|| padding == 3)
return false;
int actual_padding = s.size() - i;
// omission of padding characters is allowed
if (actual_padding == 0)
return true;
// remaining characters (max. 2) may only be padding
for (; i < s.size(); ++i)
if (s[i] != '=')
return false;
// number of padding characters needs to match
return padding == actual_padding;
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
// Don't pad it with =
/*while((i++ < 3))
ret += '=';*/
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}

26
src/util/base64.h Normal file
View File

@@ -0,0 +1,26 @@
/*
Minetest
Copyright (C) 2013-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <string>
bool base64_is_valid(std::string const& s);
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

64
src/util/basic_macros.h Normal file
View File

@@ -0,0 +1,64 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
#define MYMIN(a, b) ((a) < (b) ? (a) : (b))
#define MYMAX(a, b) ((a) > (b) ? (a) : (b))
// Requires <algorithm>
#define CONTAINS(c, v) (std::find((c).begin(), (c).end(), (v)) != (c).end())
// To disable copy constructors and assignment operations for some class
// 'Foobar', add the macro DISABLE_CLASS_COPY(Foobar) in the class definition.
// Note this also disables copying for any classes derived from 'Foobar' as well
// as classes having a 'Foobar' member.
#define DISABLE_CLASS_COPY(C) \
C(const C &) = delete; \
C &operator=(const C &) = delete;
// If you have used DISABLE_CLASS_COPY with a class but still want to permit moving
// use this macro to add the default move constructors back.
#define ALLOW_CLASS_MOVE(C) \
C(C &&other) = default; \
C &operator=(C &&) = default;
#ifndef _MSC_VER
#define UNUSED_ATTRIBUTE __attribute__ ((unused))
#else
#define UNUSED_ATTRIBUTE
#endif
// Fail compilation if condition expr is not met.
// Note that 'msg' must follow the format of a valid identifier, e.g.
// STATIC_ASSERT(sizeof(foobar_t) == 40), foobar_t_is_wrong_size);
#define STATIC_ASSERT(expr, msg) \
UNUSED_ATTRIBUTE typedef char msg[!!(expr) * 2 - 1]
// Macros to facilitate writing position vectors to a stream
// Usage:
// v3s16 pos(1,2,3);
// mystream << "message " << PP(pos) << std::endl;
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
#define PP2(x) "("<<(x).X<<","<<(x).Y<<")"

307
src/util/container.h Normal file
View File

@@ -0,0 +1,307 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes.h"
#include "exceptions.h"
#include "threading/mutex_auto_lock.h"
#include "threading/semaphore.h"
#include <list>
#include <vector>
#include <map>
#include <set>
#include <queue>
/*
Queue with unique values with fast checking of value existence
*/
template<typename Value>
class UniqueQueue
{
public:
/*
Does nothing if value is already queued.
Return value:
true: value added
false: value already exists
*/
bool push_back(const Value& value)
{
if (m_set.insert(value).second)
{
m_queue.push(value);
return true;
}
return false;
}
void pop_front()
{
m_set.erase(m_queue.front());
m_queue.pop();
}
const Value& front() const
{
return m_queue.front();
}
u32 size() const
{
return m_queue.size();
}
private:
std::set<Value> m_set;
std::queue<Value> m_queue;
};
template<typename Key, typename Value>
class MutexedMap
{
public:
MutexedMap() = default;
void set(const Key &name, const Value &value)
{
MutexAutoLock lock(m_mutex);
m_values[name] = value;
}
bool get(const Key &name, Value *result) const
{
MutexAutoLock lock(m_mutex);
auto n = m_values.find(name);
if (n == m_values.end())
return false;
if (result)
*result = n->second;
return true;
}
std::vector<Value> getValues() const
{
MutexAutoLock lock(m_mutex);
std::vector<Value> result;
result.reserve(m_values.size());
for (auto it = m_values.begin(); it != m_values.end(); ++it)
result.push_back(it->second);
return result;
}
void clear() { m_values.clear(); }
private:
std::map<Key, Value> m_values;
mutable std::mutex m_mutex;
};
// Thread-safe Double-ended queue
template<typename T>
class MutexedQueue
{
public:
template<typename Key, typename U, typename Caller, typename CallerData>
friend class RequestQueue;
MutexedQueue() = default;
bool empty() const
{
MutexAutoLock lock(m_mutex);
return m_queue.empty();
}
void push_back(const T &t)
{
MutexAutoLock lock(m_mutex);
m_queue.push_back(t);
m_signal.post();
}
void push_back(T &&t)
{
MutexAutoLock lock(m_mutex);
m_queue.push_back(std::move(t));
m_signal.post();
}
/* this version of pop_front returns a empty element of T on timeout.
* Make sure default constructor of T creates a recognizable "empty" element
*/
T pop_frontNoEx(u32 wait_time_max_ms)
{
if (m_signal.wait(wait_time_max_ms)) {
MutexAutoLock lock(m_mutex);
T t = std::move(m_queue.front());
m_queue.pop_front();
return t;
}
return T();
}
T pop_front(u32 wait_time_max_ms)
{
if (m_signal.wait(wait_time_max_ms)) {
MutexAutoLock lock(m_mutex);
T t = std::move(m_queue.front());
m_queue.pop_front();
return t;
}
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
T pop_frontNoEx()
{
m_signal.wait();
MutexAutoLock lock(m_mutex);
T t = std::move(m_queue.front());
m_queue.pop_front();
return t;
}
T pop_back(u32 wait_time_max_ms=0)
{
if (m_signal.wait(wait_time_max_ms)) {
MutexAutoLock lock(m_mutex);
T t = std::move(m_queue.back());
m_queue.pop_back();
return t;
}
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
/* this version of pop_back returns a empty element of T on timeout.
* Make sure default constructor of T creates a recognizable "empty" element
*/
T pop_backNoEx(u32 wait_time_max_ms)
{
if (m_signal.wait(wait_time_max_ms)) {
MutexAutoLock lock(m_mutex);
T t = std::move(m_queue.back());
m_queue.pop_back();
return t;
}
return T();
}
T pop_backNoEx()
{
m_signal.wait();
MutexAutoLock lock(m_mutex);
T t = std::move(m_queue.back());
m_queue.pop_back();
return t;
}
protected:
std::mutex &getMutex() { return m_mutex; }
std::deque<T> &getQueue() { return m_queue; }
std::deque<T> m_queue;
mutable std::mutex m_mutex;
Semaphore m_signal;
};
template<typename K, typename V>
class LRUCache
{
public:
LRUCache(size_t limit, void (*cache_miss)(void *data, const K &key, V *dest),
void *data)
{
m_limit = limit;
m_cache_miss = cache_miss;
m_cache_miss_data = data;
}
void setLimit(size_t limit)
{
m_limit = limit;
invalidate();
}
void invalidate()
{
m_map.clear();
m_queue.clear();
}
const V *lookupCache(K key)
{
typename cache_type::iterator it = m_map.find(key);
V *ret;
if (it != m_map.end()) {
// found!
cache_entry_t &entry = it->second;
ret = &entry.second;
// update the usage information
m_queue.erase(entry.first);
m_queue.push_front(key);
entry.first = m_queue.begin();
} else {
// cache miss -- enter into cache
cache_entry_t &entry =
m_map[key];
ret = &entry.second;
m_cache_miss(m_cache_miss_data, key, &entry.second);
// delete old entries
if (m_queue.size() == m_limit) {
const K &id = m_queue.back();
m_map.erase(id);
m_queue.pop_back();
}
m_queue.push_front(key);
entry.first = m_queue.begin();
}
return ret;
}
private:
void (*m_cache_miss)(void *data, const K &key, V *dest);
void *m_cache_miss_data;
size_t m_limit;
typedef typename std::template pair<typename std::template list<K>::iterator, V> cache_entry_t;
typedef std::template map<K, cache_entry_t> cache_type;
cache_type m_map;
// we can't use std::deque here, because its iterators get invalidated
std::list<K> m_queue;
};

View File

@@ -0,0 +1,120 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "directiontables.h"
const v3s16 g_6dirs[6] =
{
// +right, +top, +back
v3s16( 0, 0, 1), // back
v3s16( 0, 1, 0), // top
v3s16( 1, 0, 0), // right
v3s16( 0, 0,-1), // front
v3s16( 0,-1, 0), // bottom
v3s16(-1, 0, 0) // left
};
const v3s16 g_7dirs[7] =
{
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
v3s16(0,0,0), // self
};
const v3s16 g_26dirs[26] =
{
// +right, +top, +back
v3s16( 0, 0, 1), // back
v3s16( 0, 1, 0), // top
v3s16( 1, 0, 0), // right
v3s16( 0, 0,-1), // front
v3s16( 0,-1, 0), // bottom
v3s16(-1, 0, 0), // left
// 6
v3s16(-1, 1, 0), // top left
v3s16( 1, 1, 0), // top right
v3s16( 0, 1, 1), // top back
v3s16( 0, 1,-1), // top front
v3s16(-1, 0, 1), // back left
v3s16( 1, 0, 1), // back right
v3s16(-1, 0,-1), // front left
v3s16( 1, 0,-1), // front right
v3s16(-1,-1, 0), // bottom left
v3s16( 1,-1, 0), // bottom right
v3s16( 0,-1, 1), // bottom back
v3s16( 0,-1,-1), // bottom front
// 18
v3s16(-1, 1, 1), // top back-left
v3s16( 1, 1, 1), // top back-right
v3s16(-1, 1,-1), // top front-left
v3s16( 1, 1,-1), // top front-right
v3s16(-1,-1, 1), // bottom back-left
v3s16( 1,-1, 1), // bottom back-right
v3s16(-1,-1,-1), // bottom front-left
v3s16( 1,-1,-1), // bottom front-right
// 26
};
const v3s16 g_27dirs[27] =
{
// +right, +top, +back
v3s16( 0, 0, 1), // back
v3s16( 0, 1, 0), // top
v3s16( 1, 0, 0), // right
v3s16( 0, 0,-1), // front
v3s16( 0,-1, 0), // bottom
v3s16(-1, 0, 0), // left
// 6
v3s16(-1, 1, 0), // top left
v3s16( 1, 1, 0), // top right
v3s16( 0, 1, 1), // top back
v3s16( 0, 1,-1), // top front
v3s16(-1, 0, 1), // back left
v3s16( 1, 0, 1), // back right
v3s16(-1, 0,-1), // front left
v3s16( 1, 0,-1), // front right
v3s16(-1,-1, 0), // bottom left
v3s16( 1,-1, 0), // bottom right
v3s16( 0,-1, 1), // bottom back
v3s16( 0,-1,-1), // bottom front
// 18
v3s16(-1, 1, 1), // top back-left
v3s16( 1, 1, 1), // top back-right
v3s16(-1, 1,-1), // top front-left
v3s16( 1, 1,-1), // top front-right
v3s16(-1,-1, 1), // bottom back-left
v3s16( 1,-1, 1), // bottom back-right
v3s16(-1,-1,-1), // bottom front-left
v3s16( 1,-1,-1), // bottom front-right
// 26
v3s16(0,0,0),
};
const u8 wallmounted_to_facedir[6] = {
20,
0,
16 + 1,
12 + 3,
8,
4 + 2
};

View File

@@ -0,0 +1,89 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes.h"
#include "irr_v3d.h"
extern const v3s16 g_6dirs[6];
extern const v3s16 g_7dirs[7];
extern const v3s16 g_26dirs[26];
// 26th is (0,0,0)
extern const v3s16 g_27dirs[27];
extern const u8 wallmounted_to_facedir[6];
/// Direction in the 6D format. g_27dirs contains corresponding vectors.
/// Here P means Positive, N stands for Negative.
enum Direction6D {
// 0
D6D_ZP,
D6D_YP,
D6D_XP,
D6D_ZN,
D6D_YN,
D6D_XN,
// 6
D6D_XN_YP,
D6D_XP_YP,
D6D_YP_ZP,
D6D_YP_ZN,
D6D_XN_ZP,
D6D_XP_ZP,
D6D_XN_ZN,
D6D_XP_ZN,
D6D_XN_YN,
D6D_XP_YN,
D6D_YN_ZP,
D6D_YN_ZN,
// 18
D6D_XN_YP_ZP,
D6D_XP_YP_ZP,
D6D_XN_YP_ZN,
D6D_XP_YP_ZN,
D6D_XN_YN_ZP,
D6D_XP_YN_ZP,
D6D_XN_YN_ZN,
D6D_XP_YN_ZN,
// 26
D6D,
// aliases
D6D_BACK = D6D_ZP,
D6D_TOP = D6D_YP,
D6D_RIGHT = D6D_XP,
D6D_FRONT = D6D_ZN,
D6D_BOTTOM = D6D_YN,
D6D_LEFT = D6D_XN,
};
/// Direction in the wallmounted format.
/// P is Positive, N is Negative.
enum DirectionWallmounted {
DWM_YP,
DWM_YN,
DWM_XP,
DWM_XN,
DWM_ZP,
DWM_ZN,
};

View File

@@ -0,0 +1,211 @@
/*
Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
Copyright (C) 2016 Nore, Nathanaëlle Courant <nore@mesecons.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "enriched_string.h"
#include "util/string.h"
#include "debug.h"
#include "log.h"
using namespace irr::video;
EnrichedString::EnrichedString()
{
clear();
}
EnrichedString::EnrichedString(const std::wstring &string,
const std::vector<SColor> &colors)
{
clear();
m_string = string;
m_colors = colors;
}
EnrichedString::EnrichedString(const std::wstring &s, const SColor &color)
{
clear();
addAtEnd(translate_string(s), color);
}
EnrichedString::EnrichedString(const wchar_t *str, const SColor &color)
{
clear();
addAtEnd(translate_string(std::wstring(str)), color);
}
void EnrichedString::clear()
{
m_string.clear();
m_colors.clear();
m_has_background = false;
m_default_length = 0;
m_default_color = irr::video::SColor(255, 255, 255, 255);
m_background = irr::video::SColor(0, 0, 0, 0);
}
void EnrichedString::operator=(const wchar_t *str)
{
clear();
addAtEnd(translate_string(std::wstring(str)), m_default_color);
}
void EnrichedString::addAtEnd(const std::wstring &s, SColor initial_color)
{
SColor color(initial_color);
bool use_default = (m_default_length == m_string.size() &&
color == m_default_color);
m_colors.reserve(m_colors.size() + s.size());
size_t i = 0;
while (i < s.length()) {
if (s[i] != L'\x1b') {
m_string += s[i];
m_colors.push_back(color);
++i;
continue;
}
++i;
size_t start_index = i;
size_t length;
if (i == s.length()) {
break;
}
if (s[i] == L'(') {
++i;
++start_index;
while (i < s.length() && s[i] != L')') {
if (s[i] == L'\\') {
++i;
}
++i;
}
length = i - start_index;
++i;
} else {
++i;
length = 1;
}
std::wstring escape_sequence(s, start_index, length);
std::vector<std::wstring> parts = split(escape_sequence, L'@');
if (parts[0] == L"c") {
if (parts.size() < 2) {
continue;
}
parseColorString(wide_to_utf8(parts[1]), color, true);
// No longer use default color after first escape
if (use_default) {
m_default_length = m_string.size();
use_default = false;
}
} else if (parts[0] == L"b") {
if (parts.size() < 2) {
continue;
}
parseColorString(wide_to_utf8(parts[1]), m_background, true);
m_has_background = true;
}
}
// Update if no escape character was found
if (use_default)
m_default_length = m_string.size();
}
void EnrichedString::addChar(const EnrichedString &source, size_t i)
{
m_string += source.m_string[i];
m_colors.push_back(source.m_colors[i]);
}
void EnrichedString::addCharNoColor(wchar_t c)
{
m_string += c;
if (m_colors.empty()) {
m_colors.emplace_back(m_default_color);
} else {
m_colors.push_back(m_colors[m_colors.size() - 1]);
}
}
EnrichedString EnrichedString::operator+(const EnrichedString &other) const
{
EnrichedString result = *this;
result += other;
return result;
}
void EnrichedString::operator+=(const EnrichedString &other)
{
bool update_default_color = m_default_length == m_string.size();
m_string += other.m_string;
m_colors.insert(m_colors.end(), other.m_colors.begin(), other.m_colors.end());
if (update_default_color) {
m_default_length += other.m_default_length;
updateDefaultColor();
}
}
EnrichedString EnrichedString::substr(size_t pos, size_t len) const
{
if (pos >= m_string.length())
return EnrichedString();
if (len == std::string::npos || pos + len > m_string.length())
len = m_string.length() - pos;
EnrichedString str(
m_string.substr(pos, len),
std::vector<SColor>(m_colors.begin() + pos, m_colors.begin() + pos + len)
);
str.m_has_background = m_has_background;
str.m_background = m_background;
if (pos < m_default_length)
str.m_default_length = std::min(m_default_length - pos, str.size());
str.setDefaultColor(m_default_color);
return str;
}
const wchar_t *EnrichedString::c_str() const
{
return m_string.c_str();
}
const std::vector<SColor> &EnrichedString::getColors() const
{
return m_colors;
}
const std::wstring &EnrichedString::getString() const
{
return m_string;
}
void EnrichedString::updateDefaultColor()
{
sanity_check(m_default_length <= m_colors.size());
for (size_t i = 0; i < m_default_length; ++i)
m_colors[i] = m_default_color;
}

110
src/util/enriched_string.h Normal file
View File

@@ -0,0 +1,110 @@
/*
Copyright (C) 2013 xyz, Ilya Zhuravlev <whatever@xyz.is>
Copyright (C) 2016 Nore, Nathanaëlle Courant <nore@mesecons.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <string>
#include <vector>
#include <SColor.h>
using namespace irr;
class EnrichedString {
public:
EnrichedString();
EnrichedString(const std::wstring &s,
const video::SColor &color = video::SColor(255, 255, 255, 255));
EnrichedString(const wchar_t *str,
const video::SColor &color = video::SColor(255, 255, 255, 255));
EnrichedString(const std::wstring &string,
const std::vector<video::SColor> &colors);
void operator=(const wchar_t *str);
void clear();
void addAtEnd(const std::wstring &s, video::SColor color);
// Adds the character source[i] at the end.
// An EnrichedString should always be able to be copied
// to the end of an existing EnrichedString that way.
void addChar(const EnrichedString &source, size_t i);
// Adds a single character at the end, without specifying its
// color. The color used will be the one from the last character.
void addCharNoColor(wchar_t c);
EnrichedString substr(size_t pos = 0, size_t len = std::string::npos) const;
EnrichedString operator+(const EnrichedString &other) const;
void operator+=(const EnrichedString &other);
const wchar_t *c_str() const;
const std::vector<video::SColor> &getColors() const;
const std::wstring &getString() const;
inline void setDefaultColor(video::SColor color)
{
m_default_color = color;
updateDefaultColor();
}
void updateDefaultColor();
inline const video::SColor &getDefaultColor() const
{
return m_default_color;
}
inline bool operator==(const EnrichedString &other) const
{
return (m_string == other.m_string && m_colors == other.m_colors);
}
inline bool operator!=(const EnrichedString &other) const
{
return !(*this == other);
}
inline bool empty() const
{
return m_string.empty();
}
inline size_t size() const
{
return m_string.size();
}
inline bool hasBackground() const
{
return m_has_background;
}
inline video::SColor getBackground() const
{
return m_background;
}
inline void setBackground(video::SColor color)
{
m_background = color;
m_has_background = true;
}
private:
std::wstring m_string;
std::vector<video::SColor> m_colors;
bool m_has_background;
video::SColor m_default_color;
video::SColor m_background;
// This variable defines the length of the default-colored text.
// Change this to a std::vector if an "end coloring" tag is wanted.
size_t m_default_length = 0;
};

60
src/util/hex.h Normal file
View File

@@ -0,0 +1,60 @@
/*
Minetest
Copyright (C) 2013 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <string>
static const char hex_chars[] = "0123456789abcdef";
static inline std::string hex_encode(const char *data, unsigned int data_size)
{
std::string ret;
ret.reserve(data_size * 2);
char buf2[3];
buf2[2] = '\0';
for (unsigned int i = 0; i < data_size; i++) {
unsigned char c = (unsigned char)data[i];
buf2[0] = hex_chars[(c & 0xf0) >> 4];
buf2[1] = hex_chars[c & 0x0f];
ret.append(buf2);
}
return ret;
}
static inline std::string hex_encode(const std::string &data)
{
return hex_encode(data.c_str(), data.size());
}
static inline bool hex_digit_decode(char hexdigit, unsigned char &value)
{
if (hexdigit >= '0' && hexdigit <= '9')
value = hexdigit - '0';
else if (hexdigit >= 'A' && hexdigit <= 'F')
value = hexdigit - 'A' + 10;
else if (hexdigit >= 'a' && hexdigit <= 'f')
value = hexdigit - 'a' + 10;
else
return false;
return true;
}

136
src/util/ieee_float.cpp Normal file
View File

@@ -0,0 +1,136 @@
/*
* Conversion of f32 to IEEE-754 and vice versa.
*
* © Copyright 2018 Pedro Gimeno Fortea.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "ieee_float.h"
#include "log.h"
#include "porting.h"
#include <limits>
#include <cmath>
// Given an unsigned 32-bit integer representing an IEEE-754 single-precision
// float, return the float.
f32 u32Tof32Slow(u32 i)
{
// clang-format off
int exp = (i >> 23) & 0xFF;
u32 sign = i & 0x80000000UL;
u32 imant = i & 0x7FFFFFUL;
if (exp == 0xFF) {
// Inf/NaN
if (imant == 0) {
if (std::numeric_limits<f32>::has_infinity)
return sign ? -std::numeric_limits<f32>::infinity() :
std::numeric_limits<f32>::infinity();
return sign ? std::numeric_limits<f32>::max() :
std::numeric_limits<f32>::lowest();
}
return std::numeric_limits<f32>::has_quiet_NaN ?
std::numeric_limits<f32>::quiet_NaN() : -0.f;
}
if (!exp) {
// Denormal or zero
return sign ? -ldexpf((f32)imant, -149) : ldexpf((f32)imant, -149);
}
return sign ? -ldexpf((f32)(imant | 0x800000UL), exp - 150) :
ldexpf((f32)(imant | 0x800000UL), exp - 150);
// clang-format on
}
// Given a float, return an unsigned 32-bit integer representing the f32
// in IEEE-754 single-precision format.
u32 f32Tou32Slow(f32 f)
{
u32 signbit = std::copysign(1.0f, f) == 1.0f ? 0 : 0x80000000UL;
if (f == 0.f)
return signbit;
if (std::isnan(f))
return signbit | 0x7FC00000UL;
if (std::isinf(f))
return signbit | 0x7F800000UL;
int exp = 0; // silence warning
f32 mant = frexpf(f, &exp);
u32 imant = (u32)std::floor((signbit ? -16777216.f : 16777216.f) * mant);
exp += 126;
if (exp <= 0) {
// Denormal
return signbit | (exp <= -31 ? 0 : imant >> (1 - exp));
}
if (exp >= 255) {
// Overflow due to the platform having exponents bigger than IEEE ones.
// Return signed infinity.
return signbit | 0x7F800000UL;
}
// Regular number
return signbit | (exp << 23) | (imant & 0x7FFFFFUL);
}
// This test needs the following requisites in order to work:
// - The float type must be a 32 bits IEEE-754 single-precision float.
// - The endianness of f32s and integers must match.
FloatType getFloatSerializationType()
{
// clang-format off
const f32 cf = -22220490.f;
const u32 cu = 0xCBA98765UL;
if (std::numeric_limits<f32>::is_iec559 && sizeof(cf) == 4 &&
sizeof(cu) == 4 && !memcmp(&cf, &cu, 4)) {
// u32Tof32Slow and f32Tou32Slow are not needed, use memcpy
return FLOATTYPE_SYSTEM;
}
// Run quick tests to ensure the custom functions provide acceptable results
warningstream << "floatSerialization: f32 and u32 endianness are "
"not equal or machine is not IEEE-754 compliant" << std::endl;
u32 i;
char buf[128];
// NaN checks aren't included in the main loop
if (!std::isnan(u32Tof32Slow(0x7FC00000UL))) {
porting::mt_snprintf(buf, sizeof(buf),
"u32Tof32Slow(0x7FC00000) failed to produce a NaN, actual: %.9g",
u32Tof32Slow(0x7FC00000UL));
infostream << buf << std::endl;
}
if (!std::isnan(u32Tof32Slow(0xFFC00000UL))) {
porting::mt_snprintf(buf, sizeof(buf),
"u32Tof32Slow(0xFFC00000) failed to produce a NaN, actual: %.9g",
u32Tof32Slow(0xFFC00000UL));
infostream << buf << std::endl;
}
i = f32Tou32Slow(std::numeric_limits<f32>::quiet_NaN());
// check that it corresponds to a NaN encoding
if ((i & 0x7F800000UL) != 0x7F800000UL || (i & 0x7FFFFFUL) == 0) {
porting::mt_snprintf(buf, sizeof(buf),
"f32Tou32Slow(NaN) failed to encode NaN, actual: 0x%X", i);
infostream << buf << std::endl;
}
return FLOATTYPE_SLOW;
// clang-format on
}

34
src/util/ieee_float.h Normal file
View File

@@ -0,0 +1,34 @@
/*
Minetest
Copyright (C) 2018 SmallJoker <mk939@ymail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes.h"
enum FloatType
{
FLOATTYPE_UNKNOWN,
FLOATTYPE_SLOW,
FLOATTYPE_SYSTEM
};
f32 u32Tof32Slow(u32 i);
u32 f32Tou32Slow(f32 f);
FloatType getFloatSerializationType();

430
src/util/md32_common.h Normal file
View File

@@ -0,0 +1,430 @@
/* md32_common.h file used by sha256 implementation */
/* ====================================================================
* Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* licensing@OpenSSL.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
*/
/*-
* This is a generic 32 bit "collector" for message digest algorithms.
* Whenever needed it collects input character stream into chunks of
* 32 bit values and invokes a block function that performs actual hash
* calculations.
*
* Porting guide.
*
* Obligatory macros:
*
* DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN
* this macro defines byte order of input stream.
* HASH_CBLOCK
* size of a unit chunk HASH_BLOCK operates on.
* HASH_LONG
* has to be at lest 32 bit wide, if it's wider, then
* HASH_LONG_LOG2 *has to* be defined along
* HASH_CTX
* context structure that at least contains following
* members:
* typedef struct {
* ...
* HASH_LONG Nl,Nh;
* either {
* HASH_LONG data[HASH_LBLOCK];
* unsigned char data[HASH_CBLOCK];
* };
* unsigned int num;
* ...
* } HASH_CTX;
* data[] vector is expected to be zeroed upon first call to
* HASH_UPDATE.
* HASH_UPDATE
* name of "Update" function, implemented here.
* HASH_TRANSFORM
* name of "Transform" function, implemented here.
* HASH_FINAL
* name of "Final" function, implemented here.
* HASH_BLOCK_DATA_ORDER
* name of "block" function capable of treating *unaligned* input
* message in original (data) byte order, implemented externally.
* HASH_MAKE_STRING
* macro convering context variables to an ASCII hash string.
*
* MD5 example:
*
* #define DATA_ORDER_IS_LITTLE_ENDIAN
*
* #define HASH_LONG MD5_LONG
* #define HASH_LONG_LOG2 MD5_LONG_LOG2
* #define HASH_CTX MD5_CTX
* #define HASH_CBLOCK MD5_CBLOCK
* #define HASH_UPDATE MD5_Update
* #define HASH_TRANSFORM MD5_Transform
* #define HASH_FINAL MD5_Final
* #define HASH_BLOCK_DATA_ORDER md5_block_data_order
*
* <appro@fy.chalmers.se>
*/
#pragma once
#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
# error "DATA_ORDER must be defined!"
#endif
#ifndef HASH_CBLOCK
# error "HASH_CBLOCK must be defined!"
#endif
#ifndef HASH_LONG
# error "HASH_LONG must be defined!"
#endif
#ifndef HASH_CTX
# error "HASH_CTX must be defined!"
#endif
#ifndef HASH_UPDATE
# error "HASH_UPDATE must be defined!"
#endif
#ifndef HASH_TRANSFORM
# error "HASH_TRANSFORM must be defined!"
#endif
#ifndef HASH_FINAL
# error "HASH_FINAL must be defined!"
#endif
#ifndef HASH_BLOCK_DATA_ORDER
# error "HASH_BLOCK_DATA_ORDER must be defined!"
#endif
/*
* Engage compiler specific rotate intrinsic function if available.
*/
#undef ROTATE
#ifndef PEDANTIC
# if defined(_MSC_VER)
# define ROTATE(a,n) _lrotl(a,n)
# elif defined(__ICC)
# define ROTATE(a,n) _rotl(a,n)
# elif defined(__MWERKS__)
# if defined(__POWERPC__)
# define ROTATE(a,n) __rlwinm(a,n,0,31)
# elif defined(__MC68K__)
/* Motorola specific tweak. <appro@fy.chalmers.se> */
# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) )
# else
# define ROTATE(a,n) __rol(a,n)
# endif
# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
/*
* Some GNU C inline assembler templates. Note that these are
* rotates by *constant* number of bits! But that's exactly
* what we need here...
* <appro@fy.chalmers.se>
*/
# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
# define ROTATE(a,n) ({ register unsigned int ret; \
asm ( \
"roll %1,%0" \
: "=r"(ret) \
: "I"(n), "0"((unsigned int)(a)) \
: "cc"); \
ret; \
})
# elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__)
# define ROTATE(a,n) ({ register unsigned int ret; \
asm ( \
"rlwinm %0,%1,%2,0,31" \
: "=r"(ret) \
: "r"(a), "I"(n)); \
ret; \
})
# elif defined(__s390x__)
# define ROTATE(a,n) ({ register unsigned int ret; \
asm ("rll %0,%1,%2" \
: "=r"(ret) \
: "r"(a), "I"(n)); \
ret; \
})
# endif
# endif
#endif /* PEDANTIC */
#ifndef ROTATE
# define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
#endif
#if defined(DATA_ORDER_IS_BIG_ENDIAN)
# ifndef PEDANTIC
# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
# if ((defined(__i386) || defined(__i386__)) && !defined(I386_ONLY)) || \
(defined(__x86_64) || defined(__x86_64__))
# if !defined(B_ENDIAN)
/*
* This gives ~30-40% performance improvement in SHA-256 compiled
* with gcc [on P4]. Well, first macro to be frank. We can pull
* this trick on x86* platforms only, because these CPUs can fetch
* unaligned data without raising an exception.
*/
# define HOST_c2l(c,l) ({ unsigned int r=*((const unsigned int *)(c)); \
asm ("bswapl %0":"=r"(r):"0"(r)); \
(c)+=4; (l)=r; })
# define HOST_l2c(l,c) ({ unsigned int r=(l); \
asm ("bswapl %0":"=r"(r):"0"(r)); \
*((unsigned int *)(c))=r; (c)+=4; r; })
# endif
# elif defined(__aarch64__)
# if defined(__BYTE_ORDER__)
# if defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
# define HOST_c2l(c,l) ({ unsigned int r; \
asm ("rev %w0,%w1" \
:"=r"(r) \
:"r"(*((const unsigned int *)(c))));\
(c)+=4; (l)=r; })
# define HOST_l2c(l,c) ({ unsigned int r; \
asm ("rev %w0,%w1" \
:"=r"(r) \
:"r"((unsigned int)(l)));\
*((unsigned int *)(c))=r; (c)+=4; r; })
# elif defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l))
# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l))
# endif
# endif
# endif
# endif
# if defined(__s390__) || defined(__s390x__)
# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l))
# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l))
# endif
# endif
# ifndef HOST_c2l
# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++))) ) )
# endif
# ifndef HOST_l2c
# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
*((c)++)=(unsigned char)(((l) )&0xff), \
l)
# endif
#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
# ifndef PEDANTIC
# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
# if defined(__s390x__)
# define HOST_c2l(c,l) ({ asm ("lrv %0,%1" \
:"=d"(l) :"m"(*(const unsigned int *)(c)));\
(c)+=4; (l); })
# define HOST_l2c(l,c) ({ asm ("strv %1,%0" \
:"=m"(*(unsigned int *)(c)) :"d"(l));\
(c)+=4; (l); })
# endif
# endif
# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
# ifndef B_ENDIAN
/* See comment in DATA_ORDER_IS_BIG_ENDIAN section. */
# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, l)
# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, l)
# endif
# endif
# endif
# ifndef HOST_c2l
# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<<24) )
# endif
# ifndef HOST_l2c
# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
*((c)++)=(unsigned char)(((l)>>24)&0xff), \
l)
# endif
#endif
/*
* Time for some action:-)
*/
int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len)
{
const unsigned char *data = (const unsigned char *)data_;
unsigned char *p;
HASH_LONG l;
size_t n;
if (len == 0)
return 1;
l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL;
/*
* 95-05-24 eay Fixed a bug with the overflow handling, thanks to Wei Dai
* <weidai@eskimo.com> for pointing it out.
*/
if (l < c->Nl) /* overflow */
c->Nh++;
c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on
* 16-bit */
c->Nl = l;
n = c->num;
if (n != 0) {
p = (unsigned char *)c->data;
if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) {
memcpy(p + n, data, HASH_CBLOCK - n);
HASH_BLOCK_DATA_ORDER(c, p, 1);
n = HASH_CBLOCK - n;
data += n;
len -= n;
c->num = 0;
memset(p, 0, HASH_CBLOCK); /* keep it zeroed */
} else {
memcpy(p + n, data, len);
c->num += (unsigned int)len;
return 1;
}
}
n = len / HASH_CBLOCK;
if (n > 0) {
HASH_BLOCK_DATA_ORDER(c, data, n);
n *= HASH_CBLOCK;
data += n;
len -= n;
}
if (len != 0) {
p = (unsigned char *)c->data;
c->num = (unsigned int)len;
memcpy(p, data, len);
}
return 1;
}
void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data)
{
HASH_BLOCK_DATA_ORDER(c, data, 1);
}
int HASH_FINAL(unsigned char *md, HASH_CTX *c)
{
unsigned char *p = (unsigned char *)c->data;
size_t n = c->num;
p[n] = 0x80; /* there is always room for one */
n++;
if (n > (HASH_CBLOCK - 8)) {
memset(p + n, 0, HASH_CBLOCK - n);
n = 0;
HASH_BLOCK_DATA_ORDER(c, p, 1);
}
memset(p + n, 0, HASH_CBLOCK - 8 - n);
p += HASH_CBLOCK - 8;
#if defined(DATA_ORDER_IS_BIG_ENDIAN)
(void)HOST_l2c(c->Nh, p);
(void)HOST_l2c(c->Nl, p);
#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
(void)HOST_l2c(c->Nl, p);
(void)HOST_l2c(c->Nh, p);
#endif
p -= HASH_CBLOCK;
HASH_BLOCK_DATA_ORDER(c, p, 1);
c->num = 0;
memset(p, 0, HASH_CBLOCK);
#ifndef HASH_MAKE_STRING
# error "HASH_MAKE_STRING must be defined!"
#else
HASH_MAKE_STRING(c, md);
#endif
return 1;
}
#ifndef MD32_REG_T
# if defined(__alpha) || defined(__sparcv9) || defined(__mips)
# define MD32_REG_T long
/*
* This comment was originaly written for MD5, which is why it
* discusses A-D. But it basically applies to all 32-bit digests,
* which is why it was moved to common header file.
*
* In case you wonder why A-D are declared as long and not
* as MD5_LONG. Doing so results in slight performance
* boost on LP64 architectures. The catch is we don't
* really care if 32 MSBs of a 64-bit register get polluted
* with eventual overflows as we *save* only 32 LSBs in
* *either* case. Now declaring 'em long excuses the compiler
* from keeping 32 MSBs zeroed resulting in 13% performance
* improvement under SPARC Solaris7/64 and 5% under AlphaLinux.
* Well, to be honest it should say that this *prevents*
* performance degradation.
* <appro@fy.chalmers.se>
*/
# else
/*
* Above is not absolute and there are LP64 compilers that
* generate better code if MD32_REG_T is defined int. The above
* pre-processor condition reflects the circumstances under which
* the conclusion was made and is subject to further extension.
* <appro@fy.chalmers.se>
*/
# define MD32_REG_T int
# endif
#endif

204
src/util/metricsbackend.cpp Normal file
View File

@@ -0,0 +1,204 @@
/*
Minetest
Copyright (C) 2013-2020 Minetest core developers team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "metricsbackend.h"
#include "util/thread.h"
#if USE_PROMETHEUS
#include <prometheus/exposer.h>
#include <prometheus/registry.h>
#include <prometheus/counter.h>
#include <prometheus/gauge.h>
#include "log.h"
#include "settings.h"
#endif
/* Plain implementation */
class SimpleMetricCounter : public MetricCounter
{
public:
SimpleMetricCounter() : MetricCounter(), m_counter(0.0) {}
virtual ~SimpleMetricCounter() {}
void increment(double number) override
{
MutexAutoLock lock(m_mutex);
m_counter += number;
}
double get() const override
{
MutexAutoLock lock(m_mutex);
return m_counter;
}
private:
mutable std::mutex m_mutex;
double m_counter;
};
class SimpleMetricGauge : public MetricGauge
{
public:
SimpleMetricGauge() : MetricGauge(), m_gauge(0.0) {}
virtual ~SimpleMetricGauge() {}
void increment(double number) override
{
MutexAutoLock lock(m_mutex);
m_gauge += number;
}
void decrement(double number) override
{
MutexAutoLock lock(m_mutex);
m_gauge -= number;
}
void set(double number) override
{
MutexAutoLock lock(m_mutex);
m_gauge = number;
}
double get() const override
{
MutexAutoLock lock(m_mutex);
return m_gauge;
}
private:
mutable std::mutex m_mutex;
double m_gauge;
};
MetricCounterPtr MetricsBackend::addCounter(
const std::string &name, const std::string &help_str, Labels labels)
{
return std::make_shared<SimpleMetricCounter>();
}
MetricGaugePtr MetricsBackend::addGauge(
const std::string &name, const std::string &help_str, Labels labels)
{
return std::make_shared<SimpleMetricGauge>();
}
/* Prometheus backend */
#if USE_PROMETHEUS
class PrometheusMetricCounter : public MetricCounter
{
public:
PrometheusMetricCounter() = delete;
PrometheusMetricCounter(const std::string &name, const std::string &help_str,
MetricsBackend::Labels labels,
std::shared_ptr<prometheus::Registry> registry) :
MetricCounter(),
m_family(prometheus::BuildCounter()
.Name(name)
.Help(help_str)
.Register(*registry)),
m_counter(m_family.Add(labels))
{
}
virtual ~PrometheusMetricCounter() {}
virtual void increment(double number) { m_counter.Increment(number); }
virtual double get() const { return m_counter.Value(); }
private:
prometheus::Family<prometheus::Counter> &m_family;
prometheus::Counter &m_counter;
};
class PrometheusMetricGauge : public MetricGauge
{
public:
PrometheusMetricGauge() = delete;
PrometheusMetricGauge(const std::string &name, const std::string &help_str,
MetricsBackend::Labels labels,
std::shared_ptr<prometheus::Registry> registry) :
MetricGauge(),
m_family(prometheus::BuildGauge()
.Name(name)
.Help(help_str)
.Register(*registry)),
m_gauge(m_family.Add(labels))
{
}
virtual ~PrometheusMetricGauge() {}
virtual void increment(double number) { m_gauge.Increment(number); }
virtual void decrement(double number) { m_gauge.Decrement(number); }
virtual void set(double number) { m_gauge.Set(number); }
virtual double get() const { return m_gauge.Value(); }
private:
prometheus::Family<prometheus::Gauge> &m_family;
prometheus::Gauge &m_gauge;
};
class PrometheusMetricsBackend : public MetricsBackend
{
public:
PrometheusMetricsBackend(const std::string &addr) :
MetricsBackend(), m_exposer(std::make_unique<prometheus::Exposer>(addr)),
m_registry(std::make_shared<prometheus::Registry>())
{
m_exposer->RegisterCollectable(m_registry);
}
virtual ~PrometheusMetricsBackend() {}
MetricCounterPtr addCounter(
const std::string &name, const std::string &help_str,
Labels labels = {}) override;
MetricGaugePtr addGauge(
const std::string &name, const std::string &help_str,
Labels labels = {}) override;
private:
std::unique_ptr<prometheus::Exposer> m_exposer;
std::shared_ptr<prometheus::Registry> m_registry;
};
MetricCounterPtr PrometheusMetricsBackend::addCounter(
const std::string &name, const std::string &help_str, Labels labels)
{
return std::make_shared<PrometheusMetricCounter>(name, help_str, labels, m_registry);
}
MetricGaugePtr PrometheusMetricsBackend::addGauge(
const std::string &name, const std::string &help_str, Labels labels)
{
return std::make_shared<PrometheusMetricGauge>(name, help_str, labels, m_registry);
}
MetricsBackend *createPrometheusMetricsBackend()
{
std::string addr;
g_settings->getNoEx("prometheus_listener_address", addr);
return new PrometheusMetricsBackend(addr);
}
#endif

72
src/util/metricsbackend.h Normal file
View File

@@ -0,0 +1,72 @@
/*
Minetest
Copyright (C) 2013-2020 Minetest core developers team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <memory>
#include <string>
#include <utility>
#include "config.h"
class MetricCounter
{
public:
MetricCounter() = default;
virtual ~MetricCounter() {}
virtual void increment(double number = 1.0) = 0;
virtual double get() const = 0;
};
typedef std::shared_ptr<MetricCounter> MetricCounterPtr;
class MetricGauge
{
public:
MetricGauge() = default;
virtual ~MetricGauge() {}
virtual void increment(double number = 1.0) = 0;
virtual void decrement(double number = 1.0) = 0;
virtual void set(double number) = 0;
virtual double get() const = 0;
};
typedef std::shared_ptr<MetricGauge> MetricGaugePtr;
class MetricsBackend
{
public:
MetricsBackend() = default;
virtual ~MetricsBackend() {}
typedef std::initializer_list<std::pair<const std::string, std::string>> Labels;
virtual MetricCounterPtr addCounter(
const std::string &name, const std::string &help_str,
Labels labels = {});
virtual MetricGaugePtr addGauge(
const std::string &name, const std::string &help_str,
Labels labels = {});
};
#if USE_PROMETHEUS
MetricsBackend *createPrometheusMetricsBackend();
#endif

223
src/util/numeric.cpp Normal file
View File

@@ -0,0 +1,223 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "numeric.h"
#include "log.h"
#include "constants.h" // BS, MAP_BLOCKSIZE
#include "noise.h" // PseudoRandom, PcgRandom
#include "threading/mutex_auto_lock.h"
#include <cstring>
#include <cmath>
// myrand
PcgRandom g_pcgrand;
u32 myrand()
{
return g_pcgrand.next();
}
void mysrand(unsigned int seed)
{
g_pcgrand.seed(seed);
}
void myrand_bytes(void *out, size_t len)
{
g_pcgrand.bytes(out, len);
}
float myrand_float()
{
u32 uv = g_pcgrand.next();
return (float)uv / (float)U32_MAX;
}
int myrand_range(int min, int max)
{
return g_pcgrand.range(min, max);
}
float myrand_range(float min, float max)
{
return (max-min) * myrand_float() + min;
}
/*
64-bit unaligned version of MurmurHash
*/
u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
{
const u64 m = 0xc6a4a7935bd1e995ULL;
const int r = 47;
u64 h = seed ^ (len * m);
const u8 *data = (const u8 *)key;
const u8 *end = data + (len / 8) * 8;
while (data != end) {
u64 k;
memcpy(&k, data, sizeof(u64));
data += sizeof(u64);
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
}
const unsigned char *data2 = (const unsigned char *)data;
switch (len & 7) {
case 7: h ^= (u64)data2[6] << 48;
case 6: h ^= (u64)data2[5] << 40;
case 5: h ^= (u64)data2[4] << 32;
case 4: h ^= (u64)data2[3] << 24;
case 3: h ^= (u64)data2[2] << 16;
case 2: h ^= (u64)data2[1] << 8;
case 1: h ^= (u64)data2[0];
h *= m;
}
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
}
/*
blockpos_b: position of block in block coordinates
camera_pos: position of camera in nodes
camera_dir: an unit vector pointing to camera direction
range: viewing range
distance_ptr: return location for distance from the camera
*/
bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 camera_fov, f32 range, f32 *distance_ptr)
{
v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
// Block center position
v3f blockpos(
((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
);
// Block position relative to camera
v3f blockpos_relative = blockpos - camera_pos;
// Total distance
f32 d = MYMAX(0, blockpos_relative.getLength() - BLOCK_MAX_RADIUS);
if (distance_ptr)
*distance_ptr = d;
// If block is far away, it's not in sight
if (d > range)
return false;
// If block is (nearly) touching the camera, don't
// bother validating further (that is, render it anyway)
if (d == 0)
return true;
// Adjust camera position, for purposes of computing the angle,
// such that a block that has any portion visible with the
// current camera position will have the center visible at the
// adjusted postion
f32 adjdist = BLOCK_MAX_RADIUS / cos((M_PI - camera_fov) / 2);
// Block position relative to adjusted camera
v3f blockpos_adj = blockpos - (camera_pos - camera_dir * adjdist);
// Distance in camera direction (+=front, -=back)
f32 dforward = blockpos_adj.dotProduct(camera_dir);
// Cosine of the angle between the camera direction
// and the block direction (camera_dir is an unit vector)
f32 cosangle = dforward / blockpos_adj.getLength();
// If block is not in the field of view, skip it
// HOTFIX: use sligthly increased angle (+10%) to fix too agressive
// culling. Somebody have to find out whats wrong with the math here.
// Previous value: camera_fov / 2
if (cosangle < std::cos(camera_fov * 0.55f))
return false;
return true;
}
inline float adjustDist(float dist, float zoom_fov)
{
// 1.775 ~= 72 * PI / 180 * 1.4, the default FOV on the client.
// The heuristic threshold for zooming is half of that.
static constexpr const float threshold_fov = 1.775f / 2.0f;
if (zoom_fov < 0.001f || zoom_fov > threshold_fov)
return dist;
return dist * std::cbrt((1.0f - std::cos(threshold_fov)) /
(1.0f - std::cos(zoom_fov / 2.0f)));
}
s16 adjustDist(s16 dist, float zoom_fov)
{
return std::round(adjustDist((float)dist, zoom_fov));
}
void setPitchYawRollRad(core::matrix4 &m, const v3f &rot)
{
f64 a1 = rot.Z, a2 = rot.X, a3 = rot.Y;
f64 c1 = cos(a1), s1 = sin(a1);
f64 c2 = cos(a2), s2 = sin(a2);
f64 c3 = cos(a3), s3 = sin(a3);
f32 *M = m.pointer();
M[0] = s1 * s2 * s3 + c1 * c3;
M[1] = s1 * c2;
M[2] = s1 * s2 * c3 - c1 * s3;
M[4] = c1 * s2 * s3 - s1 * c3;
M[5] = c1 * c2;
M[6] = c1 * s2 * c3 + s1 * s3;
M[8] = c2 * s3;
M[9] = -s2;
M[10] = c2 * c3;
}
v3f getPitchYawRollRad(const core::matrix4 &m)
{
const f32 *M = m.pointer();
f64 a1 = atan2(M[1], M[5]);
f32 c2 = std::sqrt((f64)M[10]*M[10] + (f64)M[8]*M[8]);
f32 a2 = atan2f(-M[9], c2);
f64 c1 = cos(a1);
f64 s1 = sin(a1);
f32 a3 = atan2f(s1*M[6] - c1*M[2], c1*M[0] - s1*M[4]);
return v3f(a2, a3, a1);
}

471
src/util/numeric.h Normal file
View File

@@ -0,0 +1,471 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "basic_macros.h"
#include "constants.h"
#include "irrlichttypes.h"
#include "irr_v2d.h"
#include "irr_v3d.h"
#include "irr_aabb3d.h"
#include "SColor.h"
#include <matrix4.h>
#define rangelim(d, min, max) ((d) < (min) ? (min) : ((d) > (max) ? (max) : (d)))
#define myfloor(x) ((x) < 0.0 ? (int)(x) - 1 : (int)(x))
// The naive swap performs better than the xor version
#define SWAP(t, x, y) do { \
t temp = x; \
x = y; \
y = temp; \
} while (0)
// Maximum radius of a block. The magic number is
// sqrt(3.0) / 2.0 in literal form.
static constexpr const f32 BLOCK_MAX_RADIUS = 0.866025403784f * MAP_BLOCKSIZE * BS;
inline s16 getContainerPos(s16 p, s16 d)
{
return (p >= 0 ? p : p - d + 1) / d;
}
inline v2s16 getContainerPos(v2s16 p, s16 d)
{
return v2s16(
getContainerPos(p.X, d),
getContainerPos(p.Y, d)
);
}
inline v3s16 getContainerPos(v3s16 p, s16 d)
{
return v3s16(
getContainerPos(p.X, d),
getContainerPos(p.Y, d),
getContainerPos(p.Z, d)
);
}
inline v2s16 getContainerPos(v2s16 p, v2s16 d)
{
return v2s16(
getContainerPos(p.X, d.X),
getContainerPos(p.Y, d.Y)
);
}
inline v3s16 getContainerPos(v3s16 p, v3s16 d)
{
return v3s16(
getContainerPos(p.X, d.X),
getContainerPos(p.Y, d.Y),
getContainerPos(p.Z, d.Z)
);
}
inline void getContainerPosWithOffset(s16 p, s16 d, s16 &container, s16 &offset)
{
container = (p >= 0 ? p : p - d + 1) / d;
offset = p & (d - 1);
}
inline void getContainerPosWithOffset(const v2s16 &p, s16 d, v2s16 &container, v2s16 &offset)
{
getContainerPosWithOffset(p.X, d, container.X, offset.X);
getContainerPosWithOffset(p.Y, d, container.Y, offset.Y);
}
inline void getContainerPosWithOffset(const v3s16 &p, s16 d, v3s16 &container, v3s16 &offset)
{
getContainerPosWithOffset(p.X, d, container.X, offset.X);
getContainerPosWithOffset(p.Y, d, container.Y, offset.Y);
getContainerPosWithOffset(p.Z, d, container.Z, offset.Z);
}
inline bool isInArea(v3s16 p, s16 d)
{
return (
p.X >= 0 && p.X < d &&
p.Y >= 0 && p.Y < d &&
p.Z >= 0 && p.Z < d
);
}
inline bool isInArea(v2s16 p, s16 d)
{
return (
p.X >= 0 && p.X < d &&
p.Y >= 0 && p.Y < d
);
}
inline bool isInArea(v3s16 p, v3s16 d)
{
return (
p.X >= 0 && p.X < d.X &&
p.Y >= 0 && p.Y < d.Y &&
p.Z >= 0 && p.Z < d.Z
);
}
inline void sortBoxVerticies(v3s16 &p1, v3s16 &p2) {
if (p1.X > p2.X)
SWAP(s16, p1.X, p2.X);
if (p1.Y > p2.Y)
SWAP(s16, p1.Y, p2.Y);
if (p1.Z > p2.Z)
SWAP(s16, p1.Z, p2.Z);
}
inline v3s16 componentwise_min(const v3s16 &a, const v3s16 &b)
{
return v3s16(MYMIN(a.X, b.X), MYMIN(a.Y, b.Y), MYMIN(a.Z, b.Z));
}
inline v3s16 componentwise_max(const v3s16 &a, const v3s16 &b)
{
return v3s16(MYMAX(a.X, b.X), MYMAX(a.Y, b.Y), MYMAX(a.Z, b.Z));
}
/** Returns \p f wrapped to the range [-360, 360]
*
* See test.cpp for example cases.
*
* \note This is also used in cases where degrees wrapped to the range [0, 360]
* is innapropriate (e.g. pitch needs negative values)
*
* \internal functionally equivalent -- although precision may vary slightly --
* to fmodf((f), 360.0f) however empirical tests indicate that this approach is
* faster.
*/
inline float modulo360f(float f)
{
int sign;
int whole;
float fraction;
if (f < 0) {
f = -f;
sign = -1;
} else {
sign = 1;
}
whole = f;
fraction = f - whole;
whole %= 360;
return sign * (whole + fraction);
}
/** Returns \p f wrapped to the range [0, 360]
*/
inline float wrapDegrees_0_360(float f)
{
float value = modulo360f(f);
return value < 0 ? value + 360 : value;
}
/** Returns \p v3f wrapped to the range [0, 360]
*/
inline v3f wrapDegrees_0_360_v3f(v3f v)
{
v3f value_v3f;
value_v3f.X = modulo360f(v.X);
value_v3f.Y = modulo360f(v.Y);
value_v3f.Z = modulo360f(v.Z);
// Now that values are wrapped, use to get values for certain ranges
value_v3f.X = value_v3f.X < 0 ? value_v3f.X + 360 : value_v3f.X;
value_v3f.Y = value_v3f.Y < 0 ? value_v3f.Y + 360 : value_v3f.Y;
value_v3f.Z = value_v3f.Z < 0 ? value_v3f.Z + 360 : value_v3f.Z;
return value_v3f;
}
/** Returns \p f wrapped to the range [-180, 180]
*/
inline float wrapDegrees_180(float f)
{
float value = modulo360f(f + 180);
if (value < 0)
value += 360;
return value - 180;
}
/*
Pseudo-random (VC++ rand() sucks)
*/
#define MYRAND_RANGE 0xffffffff
u32 myrand();
void mysrand(unsigned int seed);
void myrand_bytes(void *out, size_t len);
int myrand_range(int min, int max);
float myrand_range(float min, float max);
float myrand_float();
/*
Miscellaneous functions
*/
inline u32 get_bits(u32 x, u32 pos, u32 len)
{
u32 mask = (1 << len) - 1;
return (x >> pos) & mask;
}
inline void set_bits(u32 *x, u32 pos, u32 len, u32 val)
{
u32 mask = (1 << len) - 1;
*x &= ~(mask << pos);
*x |= (val & mask) << pos;
}
inline u32 calc_parity(u32 v)
{
v ^= v >> 16;
v ^= v >> 8;
v ^= v >> 4;
v &= 0xf;
return (0x6996 >> v) & 1;
}
u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed);
bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 camera_fov, f32 range, f32 *distance_ptr=NULL);
s16 adjustDist(s16 dist, float zoom_fov);
/*
Returns nearest 32-bit integer for given floating point number.
<cmath> and <math.h> in VC++ don't provide round().
*/
inline s32 myround(f32 f)
{
return (s32)(f < 0.f ? (f - 0.5f) : (f + 0.5f));
}
inline constexpr f32 sqr(f32 f)
{
return f * f;
}
/*
Returns integer position of node in given floating point position
*/
inline v3s16 floatToInt(v3f p, f32 d)
{
return v3s16(
(p.X + (p.X > 0 ? d / 2 : -d / 2)) / d,
(p.Y + (p.Y > 0 ? d / 2 : -d / 2)) / d,
(p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d);
}
/*
Returns integer position of node in given double precision position
*/
inline v3s16 doubleToInt(v3d p, double d)
{
return v3s16(
(p.X + (p.X > 0 ? d / 2 : -d / 2)) / d,
(p.Y + (p.Y > 0 ? d / 2 : -d / 2)) / d,
(p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d);
}
/*
Returns floating point position of node in given integer position
*/
inline v3f intToFloat(v3s16 p, f32 d)
{
return v3f(
(f32)p.X * d,
(f32)p.Y * d,
(f32)p.Z * d
);
}
// Random helper. Usually d=BS
inline aabb3f getNodeBox(v3s16 p, float d)
{
return aabb3f(
(float)p.X * d - 0.5f * d,
(float)p.Y * d - 0.5f * d,
(float)p.Z * d - 0.5f * d,
(float)p.X * d + 0.5f * d,
(float)p.Y * d + 0.5f * d,
(float)p.Z * d + 0.5f * d
);
}
class IntervalLimiter
{
public:
IntervalLimiter() = default;
/*
dtime: time from last call to this method
wanted_interval: interval wanted
return value:
true: action should be skipped
false: action should be done
*/
bool step(float dtime, float wanted_interval)
{
m_accumulator += dtime;
if (m_accumulator < wanted_interval)
return false;
m_accumulator -= wanted_interval;
return true;
}
private:
float m_accumulator = 0.0f;
};
/*
Splits a list into "pages". For example, the list [1,2,3,4,5] split
into two pages would be [1,2,3],[4,5]. This function computes the
minimum and maximum indices of a single page.
length: Length of the list that should be split
page: Page number, 1 <= page <= pagecount
pagecount: The number of pages, >= 1
minindex: Receives the minimum index (inclusive).
maxindex: Receives the maximum index (exclusive).
Ensures 0 <= minindex <= maxindex <= length.
*/
inline void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxindex)
{
if (length < 1 || pagecount < 1 || page < 1 || page > pagecount) {
// Special cases or invalid parameters
minindex = maxindex = 0;
} else if(pagecount <= length) {
// Less pages than entries in the list:
// Each page contains at least one entry
minindex = (length * (page-1) + (pagecount-1)) / pagecount;
maxindex = (length * page + (pagecount-1)) / pagecount;
} else {
// More pages than entries in the list:
// Make sure the empty pages are at the end
if (page < length) {
minindex = page-1;
maxindex = page;
} else {
minindex = 0;
maxindex = 0;
}
}
}
inline float cycle_shift(float value, float by = 0, float max = 1)
{
if (value + by < 0) return value + by + max;
if (value + by > max) return value + by - max;
return value + by;
}
inline bool is_power_of_two(u32 n)
{
return n != 0 && (n & (n - 1)) == 0;
}
// Compute next-higher power of 2 efficiently, e.g. for power-of-2 texture sizes.
// Public Domain: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
inline u32 npot2(u32 orig) {
orig--;
orig |= orig >> 1;
orig |= orig >> 2;
orig |= orig >> 4;
orig |= orig >> 8;
orig |= orig >> 16;
return orig + 1;
}
// Gradual steps towards the target value in a wrapped (circular) system
// using the shorter of both ways
template<typename T>
inline void wrappedApproachShortest(T &current, const T target, const T stepsize,
const T maximum)
{
T delta = target - current;
if (delta < 0)
delta += maximum;
if (delta > stepsize && maximum - delta > stepsize) {
current += (delta < maximum / 2) ? stepsize : -stepsize;
if (current >= maximum)
current -= maximum;
} else {
current = target;
}
}
void setPitchYawRollRad(core::matrix4 &m, const v3f &rot);
inline void setPitchYawRoll(core::matrix4 &m, const v3f &rot)
{
setPitchYawRollRad(m, rot * core::DEGTORAD64);
}
v3f getPitchYawRollRad(const core::matrix4 &m);
inline v3f getPitchYawRoll(const core::matrix4 &m)
{
return getPitchYawRollRad(m) * core::RADTODEG64;
}
// Muliply the RGB value of a color linearly, and clamp to black/white
inline irr::video::SColor multiplyColorValue(const irr::video::SColor &color, float mod)
{
return irr::video::SColor(color.getAlpha(),
core::clamp<u32>(color.getRed() * mod, 0, 255),
core::clamp<u32>(color.getGreen() * mod, 0, 255),
core::clamp<u32>(color.getBlue() * mod, 0, 255));
}
template <typename T> inline T numericAbsolute(T v) { return v < 0 ? T(-v) : v; }
template <typename T> inline T numericSign(T v) { return T(v < 0 ? -1 : (v == 0 ? 0 : 1)); }
inline v3f vecAbsolute(v3f v)
{
return v3f(
numericAbsolute(v.X),
numericAbsolute(v.Y),
numericAbsolute(v.Z)
);
}
inline v3f vecSign(v3f v)
{
return v3f(
numericSign(v.X),
numericSign(v.Y),
numericSign(v.Z)
);
}

68
src/util/png.cpp Normal file
View File

@@ -0,0 +1,68 @@
/*
Minetest
Copyright (C) 2021 hecks
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "png.h"
#include <string>
#include <sstream>
#include <zlib.h>
#include <cassert>
#include "util/serialize.h"
#include "serialization.h"
#include "irrlichttypes.h"
static void writeChunk(std::ostringstream &target, const std::string &chunk_str)
{
assert(chunk_str.size() >= 4);
assert(chunk_str.size() - 4 < U32_MAX);
writeU32(target, chunk_str.size() - 4); // Write length minus the identifier
target << chunk_str;
writeU32(target, crc32(0,(const u8*)chunk_str.data(), chunk_str.size()));
}
std::string encodePNG(const u8 *data, u32 width, u32 height, s32 compression)
{
std::ostringstream file(std::ios::binary);
file << "\x89PNG\r\n\x1a\n";
{
std::ostringstream IHDR(std::ios::binary);
IHDR << "IHDR";
writeU32(IHDR, width);
writeU32(IHDR, height);
// 8 bpp, color type 6 (RGBA)
IHDR.write("\x08\x06\x00\x00\x00", 5);
writeChunk(file, IHDR.str());
}
{
std::ostringstream IDAT(std::ios::binary);
IDAT << "IDAT";
std::ostringstream scanlines(std::ios::binary);
for(u32 i = 0; i < height; i++) {
scanlines.write("\x00", 1); // Null predictor
scanlines.write((const char*) data + width * 4 * i, width * 4);
}
compressZlib(scanlines.str(), IDAT, compression);
writeChunk(file, IDAT.str());
}
file.write("\x00\x00\x00\x00IEND\xae\x42\x60\x82", 12);
return file.str();
}

27
src/util/png.h Normal file
View File

@@ -0,0 +1,27 @@
/*
Minetest
Copyright (C) 2021 hecks
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <string>
#include "irrlichttypes.h"
/* Simple PNG encoder. Encodes an RGBA image with no predictors.
Returns a binary string. */
std::string encodePNG(const u8 *data, u32 width, u32 height, s32 compression);

134
src/util/pointedthing.cpp Normal file
View File

@@ -0,0 +1,134 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "pointedthing.h"
#include "serialize.h"
#include "exceptions.h"
#include <sstream>
PointedThing::PointedThing(const v3s16 &under, const v3s16 &above,
const v3s16 &real_under, const v3f &point, const v3s16 &normal,
u16 box_id, f32 distSq):
type(POINTEDTHING_NODE),
node_undersurface(under),
node_abovesurface(above),
node_real_undersurface(real_under),
intersection_point(point),
intersection_normal(normal),
box_id(box_id),
distanceSq(distSq)
{}
PointedThing::PointedThing(s16 id, const v3f &point, const v3s16 &normal,
f32 distSq) :
type(POINTEDTHING_OBJECT),
object_id(id),
intersection_point(point),
intersection_normal(normal),
distanceSq(distSq)
{}
std::string PointedThing::dump() const
{
std::ostringstream os(std::ios::binary);
switch (type) {
case POINTEDTHING_NOTHING:
os << "[nothing]";
break;
case POINTEDTHING_NODE:
{
const v3s16 &u = node_undersurface;
const v3s16 &a = node_abovesurface;
os << "[node under=" << u.X << "," << u.Y << "," << u.Z << " above="
<< a.X << "," << a.Y << "," << a.Z << "]";
}
break;
case POINTEDTHING_OBJECT:
os << "[object " << object_id << "]";
break;
default:
os << "[unknown PointedThing]";
}
return os.str();
}
void PointedThing::serialize(std::ostream &os) const
{
writeU8(os, 0); // version
writeU8(os, (u8)type);
switch (type) {
case POINTEDTHING_NOTHING:
break;
case POINTEDTHING_NODE:
writeV3S16(os, node_undersurface);
writeV3S16(os, node_abovesurface);
break;
case POINTEDTHING_OBJECT:
writeS16(os, object_id);
break;
}
}
void PointedThing::deSerialize(std::istream &is)
{
int version = readU8(is);
if (version != 0) throw SerializationError(
"unsupported PointedThing version");
type = (PointedThingType) readU8(is);
switch (type) {
case POINTEDTHING_NOTHING:
break;
case POINTEDTHING_NODE:
node_undersurface = readV3S16(is);
node_abovesurface = readV3S16(is);
break;
case POINTEDTHING_OBJECT:
object_id = readS16(is);
break;
default:
throw SerializationError("unsupported PointedThingType");
}
}
bool PointedThing::operator==(const PointedThing &pt2) const
{
if (type != pt2.type)
{
return false;
}
if (type == POINTEDTHING_NODE)
{
if ((node_undersurface != pt2.node_undersurface)
|| (node_abovesurface != pt2.node_abovesurface)
|| (node_real_undersurface != pt2.node_real_undersurface))
return false;
}
else if (type == POINTEDTHING_OBJECT)
{
if (object_id != pt2.object_id)
return false;
}
return true;
}
bool PointedThing::operator!=(const PointedThing &pt2) const
{
return !(*this == pt2);
}

105
src/util/pointedthing.h Normal file
View File

@@ -0,0 +1,105 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes.h"
#include "irr_v3d.h"
#include <iostream>
#include <string>
enum PointedThingType
{
POINTEDTHING_NOTHING,
POINTEDTHING_NODE,
POINTEDTHING_OBJECT
};
//! An active object or node which is selected by a ray on the map.
struct PointedThing
{
//! The type of the pointed object.
PointedThingType type = POINTEDTHING_NOTHING;
/*!
* Only valid if type is POINTEDTHING_NODE.
* The coordinates of the node which owns the
* nodebox that the ray hits first.
* This may differ from node_real_undersurface if
* a nodebox exceeds the limits of its node.
*/
v3s16 node_undersurface;
/*!
* Only valid if type is POINTEDTHING_NODE.
* The coordinates of the last node the ray intersects
* before node_undersurface. Same as node_undersurface
* if the ray starts in a nodebox.
*/
v3s16 node_abovesurface;
/*!
* Only valid if type is POINTEDTHING_NODE.
* The coordinates of the node which contains the
* point of the collision and the nodebox of the node.
*/
v3s16 node_real_undersurface;
/*!
* Only valid if type is POINTEDTHING_OBJECT.
* The ID of the object the ray hit.
*/
s16 object_id = -1;
/*!
* Only valid if type isn't POINTEDTHING_NONE.
* First intersection point of the ray and the nodebox in irrlicht
* coordinates.
*/
v3f intersection_point;
/*!
* Only valid if type isn't POINTEDTHING_NONE.
* Normal vector of the intersection.
* This is perpendicular to the face the ray hits,
* points outside of the box and it's length is 1.
*/
v3s16 intersection_normal;
/*!
* Only valid if type isn't POINTEDTHING_NONE.
* Indicates which selection box is selected, if there are more of them.
*/
u16 box_id = 0;
/*!
* Square of the distance between the pointing
* ray's start point and the intersection point in irrlicht coordinates.
*/
f32 distanceSq = 0;
//! Constructor for POINTEDTHING_NOTHING
PointedThing() = default;
//! Constructor for POINTEDTHING_NODE
PointedThing(const v3s16 &under, const v3s16 &above,
const v3s16 &real_under, const v3f &point, const v3s16 &normal,
u16 box_id, f32 distSq);
//! Constructor for POINTEDTHING_OBJECT
PointedThing(s16 id, const v3f &point, const v3s16 &normal, f32 distSq);
std::string dump() const;
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
/*!
* This function ignores the intersection point and normal.
*/
bool operator==(const PointedThing &pt2) const;
bool operator!=(const PointedThing &pt2) const;
};

273
src/util/pointer.h Normal file
View File

@@ -0,0 +1,273 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes.h"
#include "debug.h" // For assert()
#include <cstring>
#include <memory> // std::shared_ptr
template<typename T> class ConstSharedPtr {
public:
ConstSharedPtr(T *ptr) : ptr(ptr) {}
ConstSharedPtr(const std::shared_ptr<T> &ptr) : ptr(ptr) {}
const T* get() const noexcept { return ptr.get(); }
const T& operator*() const noexcept { return *ptr.get(); }
const T* operator->() const noexcept { return ptr.get(); }
private:
std::shared_ptr<T> ptr;
};
template <typename T>
class Buffer
{
public:
Buffer()
{
m_size = 0;
data = nullptr;
}
Buffer(unsigned int size)
{
m_size = size;
if(size != 0)
data = new T[size];
else
data = nullptr;
}
// Disable class copy
Buffer(const Buffer &) = delete;
Buffer &operator=(const Buffer &) = delete;
Buffer(Buffer &&buffer)
{
m_size = buffer.m_size;
if(m_size != 0)
{
data = buffer.data;
buffer.data = nullptr;
buffer.m_size = 0;
}
else
data = nullptr;
}
// Copies whole buffer
Buffer(const T *t, unsigned int size)
{
m_size = size;
if(size != 0)
{
data = new T[size];
memcpy(data, t, size);
}
else
data = nullptr;
}
~Buffer()
{
drop();
}
Buffer& operator=(Buffer &&buffer)
{
if(this == &buffer)
return *this;
drop();
m_size = buffer.m_size;
if(m_size != 0)
{
data = buffer.data;
buffer.data = nullptr;
buffer.m_size = 0;
}
else
data = nullptr;
return *this;
}
void copyTo(Buffer &buffer) const
{
buffer.drop();
buffer.m_size = m_size;
if (m_size != 0) {
buffer.data = new T[m_size];
memcpy(buffer.data, data, m_size);
} else {
buffer.data = nullptr;
}
}
T & operator[](unsigned int i) const
{
return data[i];
}
T * operator*() const
{
return data;
}
unsigned int getSize() const
{
return m_size;
}
private:
void drop()
{
delete[] data;
}
T *data;
unsigned int m_size;
};
/************************************************
* !!! W A R N I N G !!! *
* *
* This smart pointer class is NOT thread safe. *
* ONLY use in a single-threaded context! *
* *
************************************************/
template <typename T>
class SharedBuffer
{
public:
SharedBuffer()
{
m_size = 0;
data = NULL;
refcount = new unsigned int;
(*refcount) = 1;
}
SharedBuffer(unsigned int size)
{
m_size = size;
if(m_size != 0)
data = new T[m_size];
else
data = nullptr;
refcount = new unsigned int;
memset(data,0,sizeof(T)*m_size);
(*refcount) = 1;
}
SharedBuffer(const SharedBuffer &buffer)
{
m_size = buffer.m_size;
data = buffer.data;
refcount = buffer.refcount;
(*refcount)++;
}
SharedBuffer & operator=(const SharedBuffer & buffer)
{
if(this == &buffer)
return *this;
drop();
m_size = buffer.m_size;
data = buffer.data;
refcount = buffer.refcount;
(*refcount)++;
return *this;
}
/*
Copies whole buffer
*/
SharedBuffer(const T *t, unsigned int size)
{
m_size = size;
if(m_size != 0)
{
data = new T[m_size];
memcpy(data, t, m_size);
}
else
data = nullptr;
refcount = new unsigned int;
(*refcount) = 1;
}
/*
Copies whole buffer
*/
SharedBuffer(const Buffer<T> &buffer)
{
m_size = buffer.getSize();
if (m_size != 0) {
data = new T[m_size];
memcpy(data, *buffer, buffer.getSize());
}
else
data = nullptr;
refcount = new unsigned int;
(*refcount) = 1;
}
~SharedBuffer()
{
drop();
}
T & operator[](unsigned int i) const
{
assert(i < m_size);
return data[i];
}
T * operator*() const
{
return data;
}
unsigned int getSize() const
{
return m_size;
}
operator Buffer<T>() const
{
return Buffer<T>(data, m_size);
}
private:
void drop()
{
assert((*refcount) > 0);
(*refcount)--;
if(*refcount == 0)
{
delete[] data;
delete refcount;
}
}
T *data;
unsigned int m_size;
unsigned int *refcount;
};
// This class is not thread-safe!
class IntrusiveReferenceCounted {
public:
IntrusiveReferenceCounted() = default;
virtual ~IntrusiveReferenceCounted() = default;
void grab() noexcept { ++m_refcount; }
void drop() noexcept { if (--m_refcount == 0) delete this; }
// Preserve own reference count.
IntrusiveReferenceCounted(const IntrusiveReferenceCounted &) {}
IntrusiveReferenceCounted &operator=(const IntrusiveReferenceCounted &) { return *this; }
private:
u32 m_refcount = 1;
};

104
src/util/quicktune.cpp Normal file
View File

@@ -0,0 +1,104 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "quicktune.h"
#include "threading/mutex_auto_lock.h"
#include "util/string.h"
std::string QuicktuneValue::getString()
{
switch(type){
case QVT_NONE:
return "(none)";
case QVT_FLOAT:
return ftos(value_QVT_FLOAT.current);
}
return "<invalid type>";
}
void QuicktuneValue::relativeAdd(float amount)
{
switch(type){
case QVT_NONE:
break;
case QVT_FLOAT:
value_QVT_FLOAT.current += amount * (value_QVT_FLOAT.max - value_QVT_FLOAT.min);
if(value_QVT_FLOAT.current > value_QVT_FLOAT.max)
value_QVT_FLOAT.current = value_QVT_FLOAT.max;
if(value_QVT_FLOAT.current < value_QVT_FLOAT.min)
value_QVT_FLOAT.current = value_QVT_FLOAT.min;
break;
}
}
static std::map<std::string, QuicktuneValue> g_values;
static std::vector<std::string> g_names;
std::mutex *g_mutex = NULL;
static void makeMutex()
{
if(!g_mutex){
g_mutex = new std::mutex();
}
}
std::vector<std::string> getQuicktuneNames()
{
return g_names;
}
QuicktuneValue getQuicktuneValue(const std::string &name)
{
makeMutex();
MutexAutoLock lock(*g_mutex);
std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
if(i == g_values.end()){
QuicktuneValue val;
val.type = QVT_NONE;
return val;
}
return i->second;
}
void setQuicktuneValue(const std::string &name, const QuicktuneValue &val)
{
makeMutex();
MutexAutoLock lock(*g_mutex);
g_values[name] = val;
g_values[name].modified = true;
}
void updateQuicktuneValue(const std::string &name, QuicktuneValue &val)
{
makeMutex();
MutexAutoLock lock(*g_mutex);
std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
if(i == g_values.end()){
g_values[name] = val;
g_names.push_back(name);
return;
}
QuicktuneValue &ref = i->second;
if(ref.modified)
val = ref;
else{
ref = val;
ref.modified = false;
}
}

98
src/util/quicktune.h Normal file
View File

@@ -0,0 +1,98 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
Used for tuning constants when developing.
Eg. if you have this constant somewhere that you just can't get right
by changing it and recompiling all over again:
v3f wield_position = v3f(55, -35, 65);
Make it look like this:
v3f wield_position = v3f(55, -35, 65);
QUICKTUNE_AUTONAME(QVT_FLOAT, wield_position.X, 0, 100);
QUICKTUNE_AUTONAME(QVT_FLOAT, wield_position.Y, -80, 20);
QUICKTUNE_AUTONAME(QVT_FLOAT, wield_position.Z, 0, 100);
Then you can modify the values at runtime, using the keys
keymap_quicktune_prev
keymap_quicktune_next
keymap_quicktune_dec
keymap_quicktune_inc
Once you have modified the values at runtime and then quit, the game
will print out all the modified values at the end:
Modified quicktune values:
wield_position.X = 60
wield_position.Y = -30
wield_position.Z = 65
The QUICKTUNE macros shouldn't generally be left in committed code.
*/
#pragma once
#include <string>
#include <map>
#include <vector>
enum QuicktuneValueType{
QVT_NONE,
QVT_FLOAT
};
struct QuicktuneValue
{
QuicktuneValueType type = QVT_NONE;
union{
struct{
float current;
float min;
float max;
} value_QVT_FLOAT;
};
bool modified = false;
QuicktuneValue() = default;
std::string getString();
void relativeAdd(float amount);
};
std::vector<std::string> getQuicktuneNames();
QuicktuneValue getQuicktuneValue(const std::string &name);
void setQuicktuneValue(const std::string &name, const QuicktuneValue &val);
void updateQuicktuneValue(const std::string &name, QuicktuneValue &val);
#ifndef NDEBUG
#define QUICKTUNE(type_, var, min_, max_, name){\
QuicktuneValue qv;\
qv.type = type_;\
qv.value_##type_.current = var;\
qv.value_##type_.min = min_;\
qv.value_##type_.max = max_;\
updateQuicktuneValue(name, qv);\
var = qv.value_##type_.current;\
}
#else // NDEBUG
#define QUICKTUNE(type, var, min_, max_, name){}
#endif
#define QUICKTUNE_AUTONAME(type_, var, min_, max_)\
QUICKTUNE(type_, var, min_, max_, #var)

View File

@@ -0,0 +1,84 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "quicktune.h"
class QuicktuneShortcutter
{
private:
std::vector<std::string> m_names;
u32 m_selected_i;
std::string m_message;
public:
bool hasMessage() const
{
return !m_message.empty();
}
std::string getMessage()
{
std::string s = m_message;
m_message = "";
if (!s.empty())
return std::string("[quicktune] ") + s;
return "";
}
std::string getSelectedName()
{
if(m_selected_i < m_names.size())
return m_names[m_selected_i];
return "(nothing)";
}
void next()
{
m_names = getQuicktuneNames();
if(m_selected_i < m_names.size()-1)
m_selected_i++;
else
m_selected_i = 0;
m_message = std::string("Selected \"")+getSelectedName()+"\"";
}
void prev()
{
m_names = getQuicktuneNames();
if(m_selected_i > 0)
m_selected_i--;
else
m_selected_i = m_names.size()-1;
m_message = std::string("Selected \"")+getSelectedName()+"\"";
}
void inc()
{
QuicktuneValue val = getQuicktuneValue(getSelectedName());
val.relativeAdd(0.05);
m_message = std::string("\"")+getSelectedName()
+"\" = "+val.getString();
setQuicktuneValue(getSelectedName(), val);
}
void dec()
{
QuicktuneValue val = getQuicktuneValue(getSelectedName());
val.relativeAdd(-0.05);
m_message = std::string("\"")+getSelectedName()
+"\" = "+val.getString();
setQuicktuneValue(getSelectedName(), val);
}
};

294
src/util/serialize.cpp Normal file
View File

@@ -0,0 +1,294 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "serialize.h"
#include "porting.h"
#include "util/string.h"
#include "util/hex.h"
#include "exceptions.h"
#include "irrlichttypes.h"
#include <iostream>
#include <cassert>
FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN;
////
//// String
////
std::string serializeString16(const std::string &plain)
{
std::string s;
char buf[2];
if (plain.size() > STRING_MAX_LEN)
throw SerializationError("String too long for serializeString16");
s.reserve(2 + plain.size());
writeU16((u8 *)&buf[0], plain.size());
s.append(buf, 2);
s.append(plain);
return s;
}
std::string deSerializeString16(std::istream &is)
{
std::string s;
char buf[2];
is.read(buf, 2);
if (is.gcount() != 2)
throw SerializationError("deSerializeString16: size not read");
u16 s_size = readU16((u8 *)buf);
if (s_size == 0)
return s;
s.resize(s_size);
is.read(&s[0], s_size);
if (is.gcount() != s_size)
throw SerializationError("deSerializeString16: couldn't read all chars");
return s;
}
////
//// Long String
////
std::string serializeString32(const std::string &plain)
{
std::string s;
char buf[4];
if (plain.size() > LONG_STRING_MAX_LEN)
throw SerializationError("String too long for serializeLongString");
s.reserve(4 + plain.size());
writeU32((u8*)&buf[0], plain.size());
s.append(buf, 4);
s.append(plain);
return s;
}
std::string deSerializeString32(std::istream &is)
{
std::string s;
char buf[4];
is.read(buf, 4);
if (is.gcount() != 4)
throw SerializationError("deSerializeLongString: size not read");
u32 s_size = readU32((u8 *)buf);
if (s_size == 0)
return s;
// We don't really want a remote attacker to force us to allocate 4GB...
if (s_size > LONG_STRING_MAX_LEN) {
throw SerializationError("deSerializeLongString: "
"string too long: " + itos(s_size) + " bytes");
}
s.resize(s_size);
is.read(&s[0], s_size);
if ((u32)is.gcount() != s_size)
throw SerializationError("deSerializeLongString: couldn't read all chars");
return s;
}
////
//// JSON-like strings
////
std::string serializeJsonString(const std::string &plain)
{
std::string tmp;
tmp.reserve(plain.size() + 2);
tmp.push_back('"');
for (char c : plain) {
switch (c) {
case '"':
tmp.append("\\\"");
break;
case '\\':
tmp.append("\\\\");
break;
case '\b':
tmp.append("\\b");
break;
case '\f':
tmp.append("\\f");
break;
case '\n':
tmp.append("\\n");
break;
case '\r':
tmp.append("\\r");
break;
case '\t':
tmp.append("\\t");
break;
default: {
if (c >= 32 && c <= 126) {
tmp.push_back(c);
} else {
// We pretend that Unicode codepoints map to bytes (they don't)
u8 cnum = static_cast<u8>(c);
tmp.append("\\u00");
tmp.push_back(hex_chars[cnum >> 4]);
tmp.push_back(hex_chars[cnum & 0xf]);
}
break;
}
}
}
tmp.push_back('"');
return tmp;
}
static void deSerializeJsonString(std::string &s)
{
assert(s.size() >= 2);
assert(s.front() == '"' && s.back() == '"');
size_t w = 0; // write index
size_t i = 1; // read index
const size_t len = s.size() - 1; // string length with trailing quote removed
while (i < len) {
char c = s[i++];
assert(c != '"');
if (c != '\\') {
s[w++] = c;
continue;
}
if (i >= len)
throw SerializationError("JSON string ended prematurely");
char c2 = s[i++];
switch (c2) {
case 'b':
s[w++] = '\b';
break;
case 'f':
s[w++] = '\f';
break;
case 'n':
s[w++] = '\n';
break;
case 'r':
s[w++] = '\r';
break;
case 't':
s[w++] = '\t';
break;
case 'u': {
if (i + 3 >= len)
throw SerializationError("JSON string ended prematurely");
unsigned char v[4] = {};
for (int j = 0; j < 4; j++)
hex_digit_decode(s[i+j], v[j]);
i += 4;
u32 hexnumber = (v[0] << 12) | (v[1] << 8) | (v[2] << 4) | v[3];
// Note that this does not work for anything other than ASCII
// but these functions do not actually interact with real JSON input.
s[w++] = (int) hexnumber;
break;
}
default:
s[w++] = c2;
break;
}
}
assert(w <= i && i <= len);
// Truncate string to current write index
s.resize(w);
}
std::string deSerializeJsonString(std::istream &is)
{
std::string tmp;
char c;
bool was_backslash = false;
// Parse initial doublequote
c = is.get();
if (c != '"')
throw SerializationError("JSON string must start with doublequote");
tmp.push_back(c);
// Grab the entire json string
for (;;) {
c = is.get();
if (is.eof())
throw SerializationError("JSON string ended prematurely");
tmp.push_back(c);
if (was_backslash)
was_backslash = false;
else if (c == '\\')
was_backslash = true;
else if (c == '"')
break; // found end of string
}
deSerializeJsonString(tmp);
return tmp;
}
std::string serializeJsonStringIfNeeded(const std::string &s)
{
for (size_t i = 0; i < s.size(); ++i) {
if (s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
return serializeJsonString(s);
}
return s;
}
std::string deSerializeJsonStringIfNeeded(std::istream &is)
{
// Check for initial quote
char c = is.peek();
if (is.eof())
return "";
if (c == '"') {
// json string: defer to the right implementation
return deSerializeJsonString(is);
}
// not a json string:
std::string tmp;
std::getline(is, tmp, ' ');
if (!is.eof())
is.unget(); // we hit a space, put it back
return tmp;
}

475
src/util/serialize.h Normal file
View File

@@ -0,0 +1,475 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_bloated.h"
#include "exceptions.h" // for SerializationError
#include "debug.h" // for assert
#include "ieee_float.h"
#include "config.h"
#if HAVE_ENDIAN_H
#ifdef _WIN32
#define __BYTE_ORDER 0
#define __LITTLE_ENDIAN 0
#define __BIG_ENDIAN 1
#elif defined(__MACH__) && defined(__APPLE__)
#include <machine/endian.h>
#elif defined(__FreeBSD__) || defined(__DragonFly__)
#include <sys/endian.h>
#else
#include <endian.h>
#endif
#endif
#include <cstring> // for memcpy
#include <iostream>
#include <string>
#include <vector>
#define FIXEDPOINT_FACTOR 1000.0f
// 0x7FFFFFFF / 1000.0f is not serializable.
// The limited float precision at this magnitude may cause the result to round
// to a greater value than can be represented by a 32 bit integer when increased
// by a factor of FIXEDPOINT_FACTOR. As a result, [F1000_MIN..F1000_MAX] does
// not represent the full range, but rather the largest safe range, of values on
// all supported architectures. Note: This definition makes assumptions on
// platform float-to-int conversion behavior.
#define F1000_MIN ((float)(s32)((float)(-0x7FFFFFFF - 1) / FIXEDPOINT_FACTOR))
#define F1000_MAX ((float)(s32)((float)(0x7FFFFFFF) / FIXEDPOINT_FACTOR))
#define STRING_MAX_LEN 0xFFFF
#define WIDE_STRING_MAX_LEN 0xFFFF
// 64 MB ought to be enough for anybody - Billy G.
#define LONG_STRING_MAX_LEN (64 * 1024 * 1024)
extern FloatType g_serialize_f32_type;
#if HAVE_ENDIAN_H
// use machine native byte swapping routines
// Note: memcpy below is optimized out by modern compilers
inline u16 readU16(const u8 *data)
{
u16 val;
memcpy(&val, data, 2);
return be16toh(val);
}
inline u32 readU32(const u8 *data)
{
u32 val;
memcpy(&val, data, 4);
return be32toh(val);
}
inline u64 readU64(const u8 *data)
{
u64 val;
memcpy(&val, data, 8);
return be64toh(val);
}
inline void writeU16(u8 *data, u16 i)
{
u16 val = htobe16(i);
memcpy(data, &val, 2);
}
inline void writeU32(u8 *data, u32 i)
{
u32 val = htobe32(i);
memcpy(data, &val, 4);
}
inline void writeU64(u8 *data, u64 i)
{
u64 val = htobe64(i);
memcpy(data, &val, 8);
}
#else
// generic byte-swapping implementation
inline u16 readU16(const u8 *data)
{
return
((u16)data[0] << 8) | ((u16)data[1] << 0);
}
inline u32 readU32(const u8 *data)
{
return
((u32)data[0] << 24) | ((u32)data[1] << 16) |
((u32)data[2] << 8) | ((u32)data[3] << 0);
}
inline u64 readU64(const u8 *data)
{
return
((u64)data[0] << 56) | ((u64)data[1] << 48) |
((u64)data[2] << 40) | ((u64)data[3] << 32) |
((u64)data[4] << 24) | ((u64)data[5] << 16) |
((u64)data[6] << 8) | ((u64)data[7] << 0);
}
inline void writeU16(u8 *data, u16 i)
{
data[0] = (i >> 8) & 0xFF;
data[1] = (i >> 0) & 0xFF;
}
inline void writeU32(u8 *data, u32 i)
{
data[0] = (i >> 24) & 0xFF;
data[1] = (i >> 16) & 0xFF;
data[2] = (i >> 8) & 0xFF;
data[3] = (i >> 0) & 0xFF;
}
inline void writeU64(u8 *data, u64 i)
{
data[0] = (i >> 56) & 0xFF;
data[1] = (i >> 48) & 0xFF;
data[2] = (i >> 40) & 0xFF;
data[3] = (i >> 32) & 0xFF;
data[4] = (i >> 24) & 0xFF;
data[5] = (i >> 16) & 0xFF;
data[6] = (i >> 8) & 0xFF;
data[7] = (i >> 0) & 0xFF;
}
#endif // HAVE_ENDIAN_H
//////////////// read routines ////////////////
inline u8 readU8(const u8 *data)
{
return ((u8)data[0] << 0);
}
inline s8 readS8(const u8 *data)
{
return (s8)readU8(data);
}
inline s16 readS16(const u8 *data)
{
return (s16)readU16(data);
}
inline s32 readS32(const u8 *data)
{
return (s32)readU32(data);
}
inline s64 readS64(const u8 *data)
{
return (s64)readU64(data);
}
inline f32 readF1000(const u8 *data)
{
return (f32)readS32(data) / FIXEDPOINT_FACTOR;
}
inline f32 readF32(const u8 *data)
{
u32 u = readU32(data);
switch (g_serialize_f32_type) {
case FLOATTYPE_SYSTEM: {
f32 f;
memcpy(&f, &u, 4);
return f;
}
case FLOATTYPE_SLOW:
return u32Tof32Slow(u);
case FLOATTYPE_UNKNOWN: // First initialization
g_serialize_f32_type = getFloatSerializationType();
return readF32(data);
}
throw SerializationError("readF32: Unreachable code");
}
inline video::SColor readARGB8(const u8 *data)
{
video::SColor p(readU32(data));
return p;
}
inline v2s16 readV2S16(const u8 *data)
{
v2s16 p;
p.X = readS16(&data[0]);
p.Y = readS16(&data[2]);
return p;
}
inline v3s16 readV3S16(const u8 *data)
{
v3s16 p;
p.X = readS16(&data[0]);
p.Y = readS16(&data[2]);
p.Z = readS16(&data[4]);
return p;
}
inline v2s32 readV2S32(const u8 *data)
{
v2s32 p;
p.X = readS32(&data[0]);
p.Y = readS32(&data[4]);
return p;
}
inline v3s32 readV3S32(const u8 *data)
{
v3s32 p;
p.X = readS32(&data[0]);
p.Y = readS32(&data[4]);
p.Z = readS32(&data[8]);
return p;
}
inline v3f readV3F1000(const u8 *data)
{
v3f p;
p.X = readF1000(&data[0]);
p.Y = readF1000(&data[4]);
p.Z = readF1000(&data[8]);
return p;
}
inline v2f readV2F32(const u8 *data)
{
v2f p;
p.X = readF32(&data[0]);
p.Y = readF32(&data[4]);
return p;
}
inline v3f readV3F32(const u8 *data)
{
v3f p;
p.X = readF32(&data[0]);
p.Y = readF32(&data[4]);
p.Z = readF32(&data[8]);
return p;
}
/////////////// write routines ////////////////
inline void writeU8(u8 *data, u8 i)
{
data[0] = (i >> 0) & 0xFF;
}
inline void writeS8(u8 *data, s8 i)
{
writeU8(data, (u8)i);
}
inline void writeS16(u8 *data, s16 i)
{
writeU16(data, (u16)i);
}
inline void writeS32(u8 *data, s32 i)
{
writeU32(data, (u32)i);
}
inline void writeS64(u8 *data, s64 i)
{
writeU64(data, (u64)i);
}
inline void writeF1000(u8 *data, f32 i)
{
assert(i >= F1000_MIN && i <= F1000_MAX);
writeS32(data, i * FIXEDPOINT_FACTOR);
}
inline void writeF32(u8 *data, f32 i)
{
switch (g_serialize_f32_type) {
case FLOATTYPE_SYSTEM: {
u32 u;
memcpy(&u, &i, 4);
return writeU32(data, u);
}
case FLOATTYPE_SLOW:
return writeU32(data, f32Tou32Slow(i));
case FLOATTYPE_UNKNOWN: // First initialization
g_serialize_f32_type = getFloatSerializationType();
return writeF32(data, i);
}
throw SerializationError("writeF32: Unreachable code");
}
inline void writeARGB8(u8 *data, video::SColor p)
{
writeU32(data, p.color);
}
inline void writeV2S16(u8 *data, v2s16 p)
{
writeS16(&data[0], p.X);
writeS16(&data[2], p.Y);
}
inline void writeV3S16(u8 *data, v3s16 p)
{
writeS16(&data[0], p.X);
writeS16(&data[2], p.Y);
writeS16(&data[4], p.Z);
}
inline void writeV2S32(u8 *data, v2s32 p)
{
writeS32(&data[0], p.X);
writeS32(&data[4], p.Y);
}
inline void writeV3S32(u8 *data, v3s32 p)
{
writeS32(&data[0], p.X);
writeS32(&data[4], p.Y);
writeS32(&data[8], p.Z);
}
inline void writeV3F1000(u8 *data, v3f p)
{
writeF1000(&data[0], p.X);
writeF1000(&data[4], p.Y);
writeF1000(&data[8], p.Z);
}
inline void writeV2F32(u8 *data, v2f p)
{
writeF32(&data[0], p.X);
writeF32(&data[4], p.Y);
}
inline void writeV3F32(u8 *data, v3f p)
{
writeF32(&data[0], p.X);
writeF32(&data[4], p.Y);
writeF32(&data[8], p.Z);
}
////
//// Iostream wrapper for data read/write
////
#define MAKE_STREAM_READ_FXN(T, N, S) \
inline T read ## N(std::istream &is) \
{ \
char buf[S] = {0}; \
is.read(buf, sizeof(buf)); \
return read ## N((u8 *)buf); \
}
#define MAKE_STREAM_WRITE_FXN(T, N, S) \
inline void write ## N(std::ostream &os, T val) \
{ \
char buf[S]; \
write ## N((u8 *)buf, val); \
os.write(buf, sizeof(buf)); \
}
MAKE_STREAM_READ_FXN(u8, U8, 1);
MAKE_STREAM_READ_FXN(u16, U16, 2);
MAKE_STREAM_READ_FXN(u32, U32, 4);
MAKE_STREAM_READ_FXN(u64, U64, 8);
MAKE_STREAM_READ_FXN(s8, S8, 1);
MAKE_STREAM_READ_FXN(s16, S16, 2);
MAKE_STREAM_READ_FXN(s32, S32, 4);
MAKE_STREAM_READ_FXN(s64, S64, 8);
MAKE_STREAM_READ_FXN(f32, F1000, 4);
MAKE_STREAM_READ_FXN(f32, F32, 4);
MAKE_STREAM_READ_FXN(v2s16, V2S16, 4);
MAKE_STREAM_READ_FXN(v3s16, V3S16, 6);
MAKE_STREAM_READ_FXN(v2s32, V2S32, 8);
MAKE_STREAM_READ_FXN(v3s32, V3S32, 12);
MAKE_STREAM_READ_FXN(v3f, V3F1000, 12);
MAKE_STREAM_READ_FXN(v2f, V2F32, 8);
MAKE_STREAM_READ_FXN(v3f, V3F32, 12);
MAKE_STREAM_READ_FXN(video::SColor, ARGB8, 4);
MAKE_STREAM_WRITE_FXN(u8, U8, 1);
MAKE_STREAM_WRITE_FXN(u16, U16, 2);
MAKE_STREAM_WRITE_FXN(u32, U32, 4);
MAKE_STREAM_WRITE_FXN(u64, U64, 8);
MAKE_STREAM_WRITE_FXN(s8, S8, 1);
MAKE_STREAM_WRITE_FXN(s16, S16, 2);
MAKE_STREAM_WRITE_FXN(s32, S32, 4);
MAKE_STREAM_WRITE_FXN(s64, S64, 8);
MAKE_STREAM_WRITE_FXN(f32, F1000, 4);
MAKE_STREAM_WRITE_FXN(f32, F32, 4);
MAKE_STREAM_WRITE_FXN(v2s16, V2S16, 4);
MAKE_STREAM_WRITE_FXN(v3s16, V3S16, 6);
MAKE_STREAM_WRITE_FXN(v2s32, V2S32, 8);
MAKE_STREAM_WRITE_FXN(v3s32, V3S32, 12);
MAKE_STREAM_WRITE_FXN(v3f, V3F1000, 12);
MAKE_STREAM_WRITE_FXN(v2f, V2F32, 8);
MAKE_STREAM_WRITE_FXN(v3f, V3F32, 12);
MAKE_STREAM_WRITE_FXN(video::SColor, ARGB8, 4);
////
//// More serialization stuff
////
inline float clampToF1000(float v)
{
return core::clamp(v, F1000_MIN, F1000_MAX);
}
inline v3f clampToF1000(v3f v)
{
return {clampToF1000(v.X), clampToF1000(v.Y), clampToF1000(v.Z)};
}
// Creates a string with the length as the first two bytes
std::string serializeString16(const std::string &plain);
// Reads a string with the length as the first two bytes
std::string deSerializeString16(std::istream &is);
// Creates a string with the length as the first four bytes
std::string serializeString32(const std::string &plain);
// Reads a string with the length as the first four bytes
std::string deSerializeString32(std::istream &is);
// Creates a string encoded in JSON format (almost equivalent to a C string literal)
std::string serializeJsonString(const std::string &plain);
// Reads a string encoded in JSON format
std::string deSerializeJsonString(std::istream &is);
// If the string contains spaces, quotes or control characters, encodes as JSON.
// Else returns the string unmodified.
std::string serializeJsonStringIfNeeded(const std::string &s);
// Parses a string serialized by serializeJsonStringIfNeeded.
std::string deSerializeJsonStringIfNeeded(std::istream &is);

198
src/util/sha1.cpp Normal file
View File

@@ -0,0 +1,198 @@
/* sha1.cpp
Copyright (c) 2005 Michael D. Leonhard
http://tamale.net/
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include "sha1.h"
// print out memory in hexadecimal
void SHA1::hexPrinter( unsigned char* c, int l )
{
assert( c );
assert( l > 0 );
while( l > 0 )
{
printf( " %02x", *c );
l--;
c++;
}
}
// circular left bit rotation. MSB wraps around to LSB
Uint32 SHA1::lrot( Uint32 x, int bits )
{
return (x<<bits) | (x>>(32 - bits));
};
// Save a 32-bit unsigned integer to memory, in big-endian order
void SHA1::storeBigEndianUint32( unsigned char* byte, Uint32 num )
{
assert( byte );
byte[0] = (unsigned char)(num>>24);
byte[1] = (unsigned char)(num>>16);
byte[2] = (unsigned char)(num>>8);
byte[3] = (unsigned char)num;
}
// Constructor *******************************************************
SHA1::SHA1()
{
// make sure that the data type is the right size
assert( sizeof( Uint32 ) * 5 == 20 );
}
// Destructor ********************************************************
SHA1::~SHA1()
{
// erase data
H0 = H1 = H2 = H3 = H4 = 0;
for( int c = 0; c < 64; c++ ) bytes[c] = 0;
unprocessedBytes = size = 0;
}
// process ***********************************************************
void SHA1::process()
{
assert( unprocessedBytes == 64 );
//printf( "process: " ); hexPrinter( bytes, 64 ); printf( "\n" );
int t;
Uint32 a, b, c, d, e, K, f, W[80];
// starting values
a = H0;
b = H1;
c = H2;
d = H3;
e = H4;
// copy and expand the message block
for( t = 0; t < 16; t++ ) W[t] = (bytes[t*4] << 24)
+(bytes[t*4 + 1] << 16)
+(bytes[t*4 + 2] << 8)
+ bytes[t*4 + 3];
for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 );
/* main loop */
Uint32 temp;
for( t = 0; t < 80; t++ )
{
if( t < 20 ) {
K = 0x5a827999;
f = (b & c) | ((b ^ 0xFFFFFFFF) & d);//TODO: try using ~
} else if( t < 40 ) {
K = 0x6ed9eba1;
f = b ^ c ^ d;
} else if( t < 60 ) {
K = 0x8f1bbcdc;
f = (b & c) | (b & d) | (c & d);
} else {
K = 0xca62c1d6;
f = b ^ c ^ d;
}
temp = lrot(a,5) + f + e + W[t] + K;
e = d;
d = c;
c = lrot(b,30);
b = a;
a = temp;
//printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e );
}
/* add variables */
H0 += a;
H1 += b;
H2 += c;
H3 += d;
H4 += e;
//printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 );
/* all bytes have been processed */
unprocessedBytes = 0;
}
// addBytes **********************************************************
void SHA1::addBytes( const char* data, int num )
{
assert( data );
assert( num >= 0 );
// add these bytes to the running total
size += num;
// repeat until all data is processed
while( num > 0 )
{
// number of bytes required to complete block
int needed = 64 - unprocessedBytes;
assert( needed > 0 );
// number of bytes to copy (use smaller of two)
int toCopy = (num < needed) ? num : needed;
// Copy the bytes
memcpy( bytes + unprocessedBytes, data, toCopy );
// Bytes have been copied
num -= toCopy;
data += toCopy;
unprocessedBytes += toCopy;
// there is a full block
if( unprocessedBytes == 64 ) process();
}
}
// digest ************************************************************
unsigned char* SHA1::getDigest()
{
// save the message size
Uint32 totalBitsL = size << 3;
Uint32 totalBitsH = size >> 29;
// add 0x80 to the message
addBytes( "\x80", 1 );
unsigned char footer[64] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// block has no room for 8-byte filesize, so finish it
if( unprocessedBytes > 56 )
addBytes( (char*)footer, 64 - unprocessedBytes);
assert( unprocessedBytes <= 56 );
// how many zeros do we need
int neededZeros = 56 - unprocessedBytes;
// store file size (in bits) in big-endian format
storeBigEndianUint32( footer + neededZeros , totalBitsH );
storeBigEndianUint32( footer + neededZeros + 4, totalBitsL );
// finish the final block
addBytes( (char*)footer, neededZeros + 8 );
// allocate memory for the digest bytes
unsigned char* digest = (unsigned char*)malloc( 20 );
// copy the digest bytes
storeBigEndianUint32( digest, H0 );
storeBigEndianUint32( digest + 4, H1 );
storeBigEndianUint32( digest + 8, H2 );
storeBigEndianUint32( digest + 12, H3 );
storeBigEndianUint32( digest + 16, H4 );
// return the digest
return digest;
}

54
src/util/sha1.h Normal file
View File

@@ -0,0 +1,54 @@
/* sha1.h
Copyright (c) 2005 Michael D. Leonhard
http://tamale.net/
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
typedef unsigned int Uint32;
class SHA1
{
private:
// fields
Uint32 H0 = 0x67452301;
Uint32 H1 = 0xefcdab89;
Uint32 H2 = 0x98badcfe;
Uint32 H3 = 0x10325476;
Uint32 H4 = 0xc3d2e1f0;
unsigned char bytes[64];
int unprocessedBytes = 0;
Uint32 size = 0;
void process();
public:
SHA1();
~SHA1();
void addBytes(const char *data, int num);
unsigned char *getDigest();
// utility methods
static Uint32 lrot(Uint32 x, int bits);
static void storeBigEndianUint32(unsigned char *byte, Uint32 num);
static void hexPrinter(unsigned char *c, int l);
};

154
src/util/sha2.h Normal file
View File

@@ -0,0 +1,154 @@
/* crypto/sha/sha.h */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* "This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)"
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(OPENSSL_NO_SHA) || (defined(OPENSSL_NO_SHA0) && defined(OPENSSL_NO_SHA1))
#error SHA is disabled.
#endif
#if defined(OPENSSL_FIPS)
#define FIPS_SHA_SIZE_T size_t
#endif
/*
Compat stuff from OpenSSL land
*/
/* crypto.h */
#define fips_md_init(alg) fips_md_init_ctx(alg, alg)
#define fips_md_init_ctx(alg, cx) int alg##_Init(cx##_CTX *c)
#define fips_cipher_abort(alg) while (0)
/*-
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* ! SHA_LONG has to be at least 32 bits wide. If it's wider, then !
* ! SHA_LONG_LOG2 has to be defined along. !
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
#if defined(__LP32__)
#define SHA_LONG unsigned long
#elif defined(__ILP64__)
#define SHA_LONG unsigned long
#define SHA_LONG_LOG2 3
#else
#define SHA_LONG unsigned int
#endif
#define SHA_LBLOCK 16
#define SHA_CBLOCK \
(SHA_LBLOCK * 4) /* SHA treats input data as a \
* contiguous array of 32 bit wide \
* big-endian values. */
#define SHA_LAST_BLOCK (SHA_CBLOCK - 8)
#define SHA_DIGEST_LENGTH 20
typedef struct SHAstate_st
{
SHA_LONG h0, h1, h2, h3, h4;
SHA_LONG Nl, Nh;
SHA_LONG data[SHA_LBLOCK];
unsigned int num;
} SHA_CTX;
#define SHA256_CBLOCK \
(SHA_LBLOCK * 4) /* SHA-256 treats input data as a \
* contiguous array of 32 bit wide \
* big-endian values. */
#define SHA224_DIGEST_LENGTH 28
#define SHA256_DIGEST_LENGTH 32
typedef struct SHA256state_st
{
SHA_LONG h[8];
SHA_LONG Nl, Nh;
SHA_LONG data[SHA_LBLOCK];
unsigned int num, md_len;
} SHA256_CTX;
#ifndef OPENSSL_NO_SHA256
#ifdef OPENSSL_FIPS
int private_SHA224_Init(SHA256_CTX *c);
int private_SHA256_Init(SHA256_CTX *c);
#endif
int SHA224_Init(SHA256_CTX *c);
int SHA224_Update(SHA256_CTX *c, const void *data, size_t len);
int SHA224_Final(unsigned char *md, SHA256_CTX *c);
unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md);
int SHA256_Init(SHA256_CTX *c);
int SHA256_Update(SHA256_CTX *c, const void *data, size_t len);
int SHA256_Final(unsigned char *md, SHA256_CTX *c);
unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md);
void SHA256_Transform(SHA256_CTX *c, const unsigned char *data);
#endif
#define SHA384_DIGEST_LENGTH 48
#define SHA512_DIGEST_LENGTH 64
#ifdef __cplusplus
}
#endif

399
src/util/sha256.c Normal file
View File

@@ -0,0 +1,399 @@
/* crypto/sha/sha256.c */
/* ====================================================================
* Copyright (c) 2004 The OpenSSL Project. All rights reserved
* according to the OpenSSL license [found in ../../LICENSE].
* ====================================================================
*/
# include <stdlib.h>
# include <string.h>
# include <util/sha2.h>
# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2a 19 Mar 2015"
# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT
const char SHA256_version[] = "SHA-256" OPENSSL_VERSION_PTEXT;
/* mem_clr.c */
unsigned static char cleanse_ctr = 0;
static void OPENSSL_cleanse(void *ptr, size_t len)
{
unsigned char *p = (unsigned char *)ptr;
size_t loop = len, ctr = cleanse_ctr;
while (loop--) {
*(p++) = (unsigned char)ctr;
ctr += (17 + ((size_t)p & 0xF));
}
p = (unsigned char *)memchr(ptr, (unsigned char)ctr, len);
if (p)
ctr += (63 + (size_t)p);
cleanse_ctr = (unsigned char)ctr;
}
fips_md_init_ctx(SHA224, SHA256)
{
memset(c, 0, sizeof(*c));
c->h[0] = 0xc1059ed8UL;
c->h[1] = 0x367cd507UL;
c->h[2] = 0x3070dd17UL;
c->h[3] = 0xf70e5939UL;
c->h[4] = 0xffc00b31UL;
c->h[5] = 0x68581511UL;
c->h[6] = 0x64f98fa7UL;
c->h[7] = 0xbefa4fa4UL;
c->md_len = SHA224_DIGEST_LENGTH;
return 1;
}
fips_md_init(SHA256)
{
memset(c, 0, sizeof(*c));
c->h[0] = 0x6a09e667UL;
c->h[1] = 0xbb67ae85UL;
c->h[2] = 0x3c6ef372UL;
c->h[3] = 0xa54ff53aUL;
c->h[4] = 0x510e527fUL;
c->h[5] = 0x9b05688cUL;
c->h[6] = 0x1f83d9abUL;
c->h[7] = 0x5be0cd19UL;
c->md_len = SHA256_DIGEST_LENGTH;
return 1;
}
unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md)
{
SHA256_CTX c;
static unsigned char m[SHA224_DIGEST_LENGTH];
if (md == NULL)
md = m;
SHA224_Init(&c);
SHA256_Update(&c, d, n);
SHA256_Final(md, &c);
OPENSSL_cleanse(&c, sizeof(c));
return (md);
}
unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md)
{
SHA256_CTX c;
static unsigned char m[SHA256_DIGEST_LENGTH];
if (md == NULL)
md = m;
SHA256_Init(&c);
SHA256_Update(&c, d, n);
SHA256_Final(md, &c);
OPENSSL_cleanse(&c, sizeof(c));
return (md);
}
int SHA224_Update(SHA256_CTX *c, const void *data, size_t len)
{
return SHA256_Update(c, data, len);
}
int SHA224_Final(unsigned char *md, SHA256_CTX *c)
{
return SHA256_Final(md, c);
}
# define DATA_ORDER_IS_BIG_ENDIAN
# define HASH_LONG SHA_LONG
# define HASH_CTX SHA256_CTX
# define HASH_CBLOCK SHA_CBLOCK
/*
* Note that FIPS180-2 discusses "Truncation of the Hash Function Output."
* default: case below covers for it. It's not clear however if it's
* permitted to truncate to amount of bytes not divisible by 4. I bet not,
* but if it is, then default: case shall be extended. For reference.
* Idea behind separate cases for pre-defined lenghts is to let the
* compiler decide if it's appropriate to unroll small loops.
*/
# define HASH_MAKE_STRING(c,s) do { \
unsigned long ll; \
unsigned int nn; \
switch ((c)->md_len) \
{ case SHA224_DIGEST_LENGTH: \
for (nn=0;nn<SHA224_DIGEST_LENGTH/4;nn++) \
{ ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
break; \
case SHA256_DIGEST_LENGTH: \
for (nn=0;nn<SHA256_DIGEST_LENGTH/4;nn++) \
{ ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
break; \
default: \
if ((c)->md_len > SHA256_DIGEST_LENGTH) \
return 0; \
for (nn=0;nn<(c)->md_len/4;nn++) \
{ ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
break; \
} \
} while (0)
# define HASH_UPDATE SHA256_Update
# define HASH_TRANSFORM SHA256_Transform
# define HASH_FINAL SHA256_Final
# define HASH_BLOCK_DATA_ORDER sha256_block_data_order
# ifndef SHA256_ASM
static
# endif
void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num);
# include "md32_common.h"
# ifndef SHA256_ASM
static const SHA_LONG K256[64] = {
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
};
/*
* FIPS specification refers to right rotations, while our ROTATE macro
* is left one. This is why you might notice that rotation coefficients
* differ from those observed in FIPS document by 32-N...
*/
# define Sigma0(x) (ROTATE((x),30) ^ ROTATE((x),19) ^ ROTATE((x),10))
# define Sigma1(x) (ROTATE((x),26) ^ ROTATE((x),21) ^ ROTATE((x),7))
# define sigma0(x) (ROTATE((x),25) ^ ROTATE((x),14) ^ ((x)>>3))
# define sigma1(x) (ROTATE((x),15) ^ ROTATE((x),13) ^ ((x)>>10))
# define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
# define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
# ifdef OPENSSL_SMALL_FOOTPRINT
static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
size_t num)
{
unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1, T2;
SHA_LONG X[16], l;
int i;
const unsigned char *data = in;
while (num--) {
a = ctx->h[0];
b = ctx->h[1];
c = ctx->h[2];
d = ctx->h[3];
e = ctx->h[4];
f = ctx->h[5];
g = ctx->h[6];
h = ctx->h[7];
for (i = 0; i < 16; i++) {
HOST_c2l(data, l);
T1 = X[i] = l;
T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
T2 = Sigma0(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
}
for (; i < 64; i++) {
s0 = X[(i + 1) & 0x0f];
s0 = sigma0(s0);
s1 = X[(i + 14) & 0x0f];
s1 = sigma1(s1);
T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf];
T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
T2 = Sigma0(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
}
ctx->h[0] += a;
ctx->h[1] += b;
ctx->h[2] += c;
ctx->h[3] += d;
ctx->h[4] += e;
ctx->h[5] += f;
ctx->h[6] += g;
ctx->h[7] += h;
}
}
# else
# define ROUND_00_15(i,a,b,c,d,e,f,g,h) do { \
T1 += h + Sigma1(e) + Ch(e,f,g) + K256[i]; \
h = Sigma0(a) + Maj(a,b,c); \
d += T1; h += T1; } while (0)
# define ROUND_16_63(i,a,b,c,d,e,f,g,h,X) do { \
s0 = X[(i+1)&0x0f]; s0 = sigma0(s0); \
s1 = X[(i+14)&0x0f]; s1 = sigma1(s1); \
T1 = X[(i)&0x0f] += s0 + s1 + X[(i+9)&0x0f]; \
ROUND_00_15(i,a,b,c,d,e,f,g,h); } while (0)
static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
size_t num)
{
unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1;
SHA_LONG X[16];
int i;
const unsigned char *data = (const unsigned char *)in;
const union {
long one;
char little;
} is_endian = {
1
};
while (num--) {
a = ctx->h[0];
b = ctx->h[1];
c = ctx->h[2];
d = ctx->h[3];
e = ctx->h[4];
f = ctx->h[5];
g = ctx->h[6];
h = ctx->h[7];
if (!is_endian.little && sizeof(SHA_LONG) == 4
&& ((size_t)in % 4) == 0) {
const SHA_LONG *W = (const SHA_LONG *)data;
T1 = X[0] = W[0];
ROUND_00_15(0, a, b, c, d, e, f, g, h);
T1 = X[1] = W[1];
ROUND_00_15(1, h, a, b, c, d, e, f, g);
T1 = X[2] = W[2];
ROUND_00_15(2, g, h, a, b, c, d, e, f);
T1 = X[3] = W[3];
ROUND_00_15(3, f, g, h, a, b, c, d, e);
T1 = X[4] = W[4];
ROUND_00_15(4, e, f, g, h, a, b, c, d);
T1 = X[5] = W[5];
ROUND_00_15(5, d, e, f, g, h, a, b, c);
T1 = X[6] = W[6];
ROUND_00_15(6, c, d, e, f, g, h, a, b);
T1 = X[7] = W[7];
ROUND_00_15(7, b, c, d, e, f, g, h, a);
T1 = X[8] = W[8];
ROUND_00_15(8, a, b, c, d, e, f, g, h);
T1 = X[9] = W[9];
ROUND_00_15(9, h, a, b, c, d, e, f, g);
T1 = X[10] = W[10];
ROUND_00_15(10, g, h, a, b, c, d, e, f);
T1 = X[11] = W[11];
ROUND_00_15(11, f, g, h, a, b, c, d, e);
T1 = X[12] = W[12];
ROUND_00_15(12, e, f, g, h, a, b, c, d);
T1 = X[13] = W[13];
ROUND_00_15(13, d, e, f, g, h, a, b, c);
T1 = X[14] = W[14];
ROUND_00_15(14, c, d, e, f, g, h, a, b);
T1 = X[15] = W[15];
ROUND_00_15(15, b, c, d, e, f, g, h, a);
data += SHA256_CBLOCK;
} else {
SHA_LONG l;
HOST_c2l(data, l);
T1 = X[0] = l;
ROUND_00_15(0, a, b, c, d, e, f, g, h);
HOST_c2l(data, l);
T1 = X[1] = l;
ROUND_00_15(1, h, a, b, c, d, e, f, g);
HOST_c2l(data, l);
T1 = X[2] = l;
ROUND_00_15(2, g, h, a, b, c, d, e, f);
HOST_c2l(data, l);
T1 = X[3] = l;
ROUND_00_15(3, f, g, h, a, b, c, d, e);
HOST_c2l(data, l);
T1 = X[4] = l;
ROUND_00_15(4, e, f, g, h, a, b, c, d);
HOST_c2l(data, l);
T1 = X[5] = l;
ROUND_00_15(5, d, e, f, g, h, a, b, c);
HOST_c2l(data, l);
T1 = X[6] = l;
ROUND_00_15(6, c, d, e, f, g, h, a, b);
HOST_c2l(data, l);
T1 = X[7] = l;
ROUND_00_15(7, b, c, d, e, f, g, h, a);
HOST_c2l(data, l);
T1 = X[8] = l;
ROUND_00_15(8, a, b, c, d, e, f, g, h);
HOST_c2l(data, l);
T1 = X[9] = l;
ROUND_00_15(9, h, a, b, c, d, e, f, g);
HOST_c2l(data, l);
T1 = X[10] = l;
ROUND_00_15(10, g, h, a, b, c, d, e, f);
HOST_c2l(data, l);
T1 = X[11] = l;
ROUND_00_15(11, f, g, h, a, b, c, d, e);
HOST_c2l(data, l);
T1 = X[12] = l;
ROUND_00_15(12, e, f, g, h, a, b, c, d);
HOST_c2l(data, l);
T1 = X[13] = l;
ROUND_00_15(13, d, e, f, g, h, a, b, c);
HOST_c2l(data, l);
T1 = X[14] = l;
ROUND_00_15(14, c, d, e, f, g, h, a, b);
HOST_c2l(data, l);
T1 = X[15] = l;
ROUND_00_15(15, b, c, d, e, f, g, h, a);
}
for (i = 16; i < 64; i += 8) {
ROUND_16_63(i + 0, a, b, c, d, e, f, g, h, X);
ROUND_16_63(i + 1, h, a, b, c, d, e, f, g, X);
ROUND_16_63(i + 2, g, h, a, b, c, d, e, f, X);
ROUND_16_63(i + 3, f, g, h, a, b, c, d, e, X);
ROUND_16_63(i + 4, e, f, g, h, a, b, c, d, X);
ROUND_16_63(i + 5, d, e, f, g, h, a, b, c, X);
ROUND_16_63(i + 6, c, d, e, f, g, h, a, b, X);
ROUND_16_63(i + 7, b, c, d, e, f, g, h, a, X);
}
ctx->h[0] += a;
ctx->h[1] += b;
ctx->h[2] += c;
ctx->h[3] += d;
ctx->h[4] += e;
ctx->h[5] += f;
ctx->h[6] += g;
ctx->h[7] += h;
}
}
# endif
# endif /* SHA256_ASM */

1040
src/util/srp.cpp Normal file

File diff suppressed because it is too large Load Diff

191
src/util/srp.h Normal file
View File

@@ -0,0 +1,191 @@
/*
* Secure Remote Password 6a implementation
* https://github.com/est31/csrp-gmp
*
* The MIT License (MIT)
*
* Copyright (c) 2010, 2013 Tom Cocagne, 2015 est31 <MTest31@outlook.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
/*
*
* Purpose: This is a direct implementation of the Secure Remote Password
* Protocol version 6a as described by
* http://srp.stanford.edu/design.html
*
* Author: tom.cocagne@gmail.com (Tom Cocagne)
*
* Dependencies: LibGMP
*
* Usage: Refer to test_srp.c for a demonstration
*
* Notes:
* This library allows multiple combinations of hashing algorithms and
* prime number constants. For authentication to succeed, the hash and
* prime number constants must match between
* srp_create_salted_verification_key(), srp_user_new(),
* and srp_verifier_new(). A recommended approach is to determine the
* desired level of security for an application and globally define the
* hash and prime number constants to the predetermined values.
*
* As one might suspect, more bits means more security. As one might also
* suspect, more bits also means more processing time. The test_srp.c
* program can be easily modified to profile various combinations of
* hash & prime number pairings.
*/
#pragma once
struct SRPVerifier;
struct SRPUser;
typedef enum {
SRP_NG_1024,
SRP_NG_2048,
SRP_NG_4096,
SRP_NG_8192,
SRP_NG_CUSTOM
} SRP_NGType;
typedef enum {
/*SRP_SHA1,*/
/*SRP_SHA224,*/
SRP_SHA256,
/*SRP_SHA384,
SRP_SHA512*/
} SRP_HashAlgorithm;
typedef enum {
SRP_ERR,
SRP_OK,
} SRP_Result;
// clang-format off
/* Sets the memory functions used by srp.
* Note: this doesn't set the memory functions used by gmp,
* but it is supported to have different functions for srp and gmp.
* Don't call this after you have already allocated srp structures.
*/
void srp_set_memory_functions(
void *(*new_srp_alloc) (size_t),
void *(*new_srp_realloc) (void *, size_t),
void (*new_srp_free) (void *));
/* Out: bytes_v, len_v
*
* The caller is responsible for freeing the memory allocated for bytes_v
*
* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type.
* If provided, they must contain ASCII text of the hexidecimal notation.
*
* If bytes_s == NULL, it is filled with random data.
* The caller is responsible for freeing.
*
* Returns SRP_OK on success, and SRP_ERR on error.
* bytes_s might be in this case invalid, don't free it.
*/
SRP_Result srp_create_salted_verification_key(SRP_HashAlgorithm alg,
SRP_NGType ng_type, const char *username_for_verifier,
const unsigned char *password, size_t len_password,
unsigned char **bytes_s, size_t *len_s,
unsigned char **bytes_v, size_t *len_v,
const char *n_hex, const char *g_hex);
/* Out: bytes_B, len_B.
*
* On failure, bytes_B will be set to NULL and len_B will be set to 0
*
* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type
*
* If bytes_b == NULL, random data is used for b.
*
* Returns pointer to SRPVerifier on success, and NULL on error.
*/
struct SRPVerifier* srp_verifier_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const char *username,
const unsigned char *bytes_s, size_t len_s,
const unsigned char *bytes_v, size_t len_v,
const unsigned char *bytes_A, size_t len_A,
const unsigned char *bytes_b, size_t len_b,
unsigned char** bytes_B, size_t *len_B,
const char* n_hex, const char* g_hex);
// clang-format on
void srp_verifier_delete(struct SRPVerifier *ver);
// srp_verifier_verify_session must have been called before
int srp_verifier_is_authenticated(struct SRPVerifier *ver);
const char *srp_verifier_get_username(struct SRPVerifier *ver);
/* key_length may be null */
const unsigned char *srp_verifier_get_session_key(
struct SRPVerifier *ver, size_t *key_length);
size_t srp_verifier_get_session_key_length(struct SRPVerifier *ver);
/* Verifies session, on success, it writes bytes_HAMK.
* user_M must be exactly srp_verifier_get_session_key_length() bytes in size
*/
void srp_verifier_verify_session(
struct SRPVerifier *ver, const unsigned char *user_M, unsigned char **bytes_HAMK);
/*******************************************************************************/
/* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */
struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const char *username, const char *username_for_verifier,
const unsigned char *bytes_password, size_t len_password, const char *n_hex,
const char *g_hex);
void srp_user_delete(struct SRPUser *usr);
int srp_user_is_authenticated(struct SRPUser *usr);
const char *srp_user_get_username(struct SRPUser *usr);
/* key_length may be null */
const unsigned char *srp_user_get_session_key(struct SRPUser *usr, size_t *key_length);
size_t srp_user_get_session_key_length(struct SRPUser *usr);
// clang-format off
/* Output: username, bytes_A, len_A.
* If you don't want it get written, set username to NULL.
* If bytes_a == NULL, random data is used for a. */
SRP_Result srp_user_start_authentication(struct SRPUser* usr, char **username,
const unsigned char *bytes_a, size_t len_a,
unsigned char **bytes_A, size_t* len_A);
/* Output: bytes_M, len_M (len_M may be null and will always be
* srp_user_get_session_key_length() bytes in size) */
void srp_user_process_challenge(struct SRPUser *usr,
const unsigned char *bytes_s, size_t len_s,
const unsigned char *bytes_B, size_t len_B,
unsigned char **bytes_M, size_t *len_M);
// clang-format on
/* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */
void srp_user_verify_session(struct SRPUser *usr, const unsigned char *bytes_HAMK);

70
src/util/stream.h Normal file
View File

@@ -0,0 +1,70 @@
/*
Minetest
Copyright (C) 2022 Minetest Authors
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <iostream>
#include <string>
#include <functional>
template<int BufferLength, typename Emitter = std::function<void(const std::string &)> >
class StringStreamBuffer : public std::streambuf {
public:
StringStreamBuffer(Emitter emitter) : m_emitter(emitter) {
buffer_index = 0;
}
int overflow(int c) {
push_back(c);
return c;
}
void push_back(char c) {
if (c == '\n' || c == '\r') {
if (buffer_index)
m_emitter(std::string(buffer, buffer_index));
buffer_index = 0;
} else {
buffer[buffer_index++] = c;
if (buffer_index >= BufferLength) {
m_emitter(std::string(buffer, buffer_index));
buffer_index = 0;
}
}
}
std::streamsize xsputn(const char *s, std::streamsize n) {
for (int i = 0; i < n; ++i)
push_back(s[i]);
return n;
}
private:
Emitter m_emitter;
char buffer[BufferLength];
int buffer_index;
};
class DummyStreamBuffer : public std::streambuf {
int overflow(int c) {
return c;
}
std::streamsize xsputn(const char *s, std::streamsize n) {
return n;
}
};

78
src/util/strfnd.h Normal file
View File

@@ -0,0 +1,78 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <string>
template <typename T>
class BasicStrfnd {
typedef std::basic_string<T> String;
String str;
size_t pos;
public:
BasicStrfnd(const String &s) : str(s), pos(0) {}
void start(const String &s) { str = s; pos = 0; }
size_t where() { return pos; }
void to(size_t i) { pos = i; }
bool at_end() { return pos >= str.size(); }
String what() { return str; }
String next(const String &sep)
{
if (pos >= str.size())
return String();
size_t n;
if (sep.empty() || (n = str.find(sep, pos)) == String::npos) {
n = str.size();
}
String ret = str.substr(pos, n - pos);
pos = n + sep.size();
return ret;
}
// Returns substr up to the next occurence of sep that isn't escaped with esc ('\\')
String next_esc(const String &sep, T esc=static_cast<T>('\\'))
{
if (pos >= str.size())
return String();
size_t n, old_p = pos;
do {
if (sep.empty() || (n = str.find(sep, pos)) == String::npos) {
pos = n = str.size();
break;
}
pos = n + sep.length();
} while (n > 0 && str[n - 1] == esc);
return str.substr(old_p, n - old_p);
}
void skip_over(const String &chars)
{
size_t p = str.find_first_not_of(chars, pos);
if (p != String::npos)
pos = p;
}
};
typedef BasicStrfnd<char> Strfnd;
typedef BasicStrfnd<wchar_t> WStrfnd;

908
src/util/string.cpp Normal file
View File

@@ -0,0 +1,908 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "string.h"
#include "pointer.h"
#include "numeric.h"
#include "log.h"
#include "hex.h"
#include "porting.h"
#include "translation.h"
#include <algorithm>
#include <array>
#include <sstream>
#include <iomanip>
#include <unordered_map>
#ifndef _WIN32
#include <iconv.h>
#else
#define _WIN32_WINNT 0x0501
#include <windows.h>
#endif
#ifndef _WIN32
static bool convert(const char *to, const char *from, char *outbuf,
size_t *outbuf_size, char *inbuf, size_t inbuf_size)
{
iconv_t cd = iconv_open(to, from);
char *inbuf_ptr = inbuf;
char *outbuf_ptr = outbuf;
size_t *inbuf_left_ptr = &inbuf_size;
const size_t old_outbuf_size = *outbuf_size;
size_t old_size = inbuf_size;
while (inbuf_size > 0) {
iconv(cd, &inbuf_ptr, inbuf_left_ptr, &outbuf_ptr, outbuf_size);
if (inbuf_size == old_size) {
iconv_close(cd);
return false;
}
old_size = inbuf_size;
}
iconv_close(cd);
*outbuf_size = old_outbuf_size - *outbuf_size;
return true;
}
#ifdef __ANDROID__
// On Android iconv disagrees how big a wchar_t is for whatever reason
const char *DEFAULT_ENCODING = "UTF-32LE";
#elif defined(__NetBSD__) || defined(__OpenBSD__)
// NetBSD does not allow "WCHAR_T" as a charset input to iconv.
#include <sys/endian.h>
#if BYTE_ORDER == BIG_ENDIAN
const char *DEFAULT_ENCODING = "UTF-32BE";
#else
const char *DEFAULT_ENCODING = "UTF-32LE";
#endif
#else
const char *DEFAULT_ENCODING = "WCHAR_T";
#endif
std::wstring utf8_to_wide(const std::string &input)
{
const size_t inbuf_size = input.length();
// maximum possible size, every character is sizeof(wchar_t) bytes
size_t outbuf_size = input.length() * sizeof(wchar_t);
char *inbuf = new char[inbuf_size]; // intentionally NOT null-terminated
memcpy(inbuf, input.c_str(), inbuf_size);
std::wstring out;
out.resize(outbuf_size / sizeof(wchar_t));
#if defined(__ANDROID__) || defined(__NetBSD__) || defined(__OpenBSD__)
static_assert(sizeof(wchar_t) == 4, "Unexpected wide char size");
#endif
char *outbuf = reinterpret_cast<char*>(&out[0]);
if (!convert(DEFAULT_ENCODING, "UTF-8", outbuf, &outbuf_size, inbuf, inbuf_size)) {
infostream << "Couldn't convert UTF-8 string 0x" << hex_encode(input)
<< " into wstring" << std::endl;
delete[] inbuf;
return L"<invalid UTF-8 string>";
}
delete[] inbuf;
out.resize(outbuf_size / sizeof(wchar_t));
return out;
}
std::string wide_to_utf8(const std::wstring &input)
{
const size_t inbuf_size = input.length() * sizeof(wchar_t);
// maximum possible size: utf-8 encodes codepoints using 1 up to 4 bytes
size_t outbuf_size = input.length() * 4;
char *inbuf = new char[inbuf_size]; // intentionally NOT null-terminated
memcpy(inbuf, input.c_str(), inbuf_size);
std::string out;
out.resize(outbuf_size);
if (!convert("UTF-8", DEFAULT_ENCODING, &out[0], &outbuf_size, inbuf, inbuf_size)) {
infostream << "Couldn't convert wstring 0x" << hex_encode(inbuf, inbuf_size)
<< " into UTF-8 string" << std::endl;
delete[] inbuf;
return "<invalid wide string>";
}
delete[] inbuf;
out.resize(outbuf_size);
return out;
}
#else // _WIN32
std::wstring utf8_to_wide(const std::string &input)
{
size_t outbuf_size = input.size() + 1;
wchar_t *outbuf = new wchar_t[outbuf_size];
memset(outbuf, 0, outbuf_size * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.size(),
outbuf, outbuf_size);
std::wstring out(outbuf);
delete[] outbuf;
return out;
}
std::string wide_to_utf8(const std::wstring &input)
{
size_t outbuf_size = (input.size() + 1) * 6;
char *outbuf = new char[outbuf_size];
memset(outbuf, 0, outbuf_size);
WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.size(),
outbuf, outbuf_size, NULL, NULL);
std::string out(outbuf);
delete[] outbuf;
return out;
}
#endif // _WIN32
wchar_t *utf8_to_wide_c(const char *str)
{
std::wstring ret = utf8_to_wide(std::string(str));
size_t len = ret.length();
wchar_t *ret_c = new wchar_t[len + 1];
memcpy(ret_c, ret.c_str(), (len + 1) * sizeof(wchar_t));
return ret_c;
}
std::string urlencode(const std::string &str)
{
// Encodes non-unreserved URI characters by a percent sign
// followed by two hex digits. See RFC 3986, section 2.3.
static const char url_hex_chars[] = "0123456789ABCDEF";
std::ostringstream oss(std::ios::binary);
for (unsigned char c : str) {
if (isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
oss << c;
} else {
oss << "%"
<< url_hex_chars[(c & 0xf0) >> 4]
<< url_hex_chars[c & 0x0f];
}
}
return oss.str();
}
std::string urldecode(const std::string &str)
{
// Inverse of urlencode
std::ostringstream oss(std::ios::binary);
for (u32 i = 0; i < str.size(); i++) {
unsigned char highvalue, lowvalue;
if (str[i] == '%' &&
hex_digit_decode(str[i+1], highvalue) &&
hex_digit_decode(str[i+2], lowvalue)) {
oss << (char) ((highvalue << 4) | lowvalue);
i += 2;
} else {
oss << str[i];
}
}
return oss.str();
}
u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask)
{
u32 result = 0;
u32 mask = 0;
char *s = &str[0];
char *flagstr;
char *strpos = nullptr;
while ((flagstr = strtok_r(s, ",", &strpos))) {
s = nullptr;
while (*flagstr == ' ' || *flagstr == '\t')
flagstr++;
bool flagset = true;
if (!strncasecmp(flagstr, "no", 2)) {
flagset = false;
flagstr += 2;
}
for (int i = 0; flagdesc[i].name; i++) {
if (!strcasecmp(flagstr, flagdesc[i].name)) {
mask |= flagdesc[i].flag;
if (flagset)
result |= flagdesc[i].flag;
break;
}
}
}
if (flagmask)
*flagmask = mask;
return result;
}
std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask)
{
std::string result;
for (int i = 0; flagdesc[i].name; i++) {
if (flagmask & flagdesc[i].flag) {
if (!(flags & flagdesc[i].flag))
result += "no";
result += flagdesc[i].name;
result += ", ";
}
}
size_t len = result.length();
if (len >= 2)
result.erase(len - 2, 2);
return result;
}
size_t mystrlcpy(char *dst, const char *src, size_t size)
{
size_t srclen = strlen(src) + 1;
size_t copylen = MYMIN(srclen, size);
if (copylen > 0) {
memcpy(dst, src, copylen);
dst[copylen - 1] = '\0';
}
return srclen;
}
char *mystrtok_r(char *s, const char *sep, char **lasts)
{
char *t;
if (!s)
s = *lasts;
while (*s && strchr(sep, *s))
s++;
if (!*s)
return nullptr;
t = s;
while (*t) {
if (strchr(sep, *t)) {
*t++ = '\0';
break;
}
t++;
}
*lasts = t;
return s;
}
u64 read_seed(const char *str)
{
char *endptr;
u64 num;
if (str[0] == '0' && str[1] == 'x')
num = strtoull(str, &endptr, 16);
else
num = strtoull(str, &endptr, 10);
if (*endptr)
num = murmur_hash_64_ua(str, (int)strlen(str), 0x1337);
return num;
}
static bool parseHexColorString(const std::string &value, video::SColor &color,
unsigned char default_alpha)
{
u8 components[] = {0x00, 0x00, 0x00, default_alpha}; // R,G,B,A
size_t len = value.size();
bool short_form;
if (len == 9 || len == 7) // #RRGGBBAA or #RRGGBB
short_form = false;
else if (len == 5 || len == 4) // #RGBA or #RGB
short_form = true;
else
return false;
for (size_t pos = 1, cc = 0; pos < len; pos++, cc++) {
if (short_form) {
u8 d;
if (!hex_digit_decode(value[pos], d))
return false;
components[cc] = (d & 0xf) << 4 | (d & 0xf);
} else {
u8 d1, d2;
if (!hex_digit_decode(value[pos], d1) ||
!hex_digit_decode(value[pos+1], d2))
return false;
components[cc] = (d1 & 0xf) << 4 | (d2 & 0xf);
pos++; // skip the second digit -- it's already used
}
}
color.setRed(components[0]);
color.setGreen(components[1]);
color.setBlue(components[2]);
color.setAlpha(components[3]);
return true;
}
const static std::unordered_map<std::string, u32> s_named_colors = {
{"aliceblue", 0xf0f8ff},
{"antiquewhite", 0xfaebd7},
{"aqua", 0x00ffff},
{"aquamarine", 0x7fffd4},
{"azure", 0xf0ffff},
{"beige", 0xf5f5dc},
{"bisque", 0xffe4c4},
{"black", 00000000},
{"blanchedalmond", 0xffebcd},
{"blue", 0x0000ff},
{"blueviolet", 0x8a2be2},
{"brown", 0xa52a2a},
{"burlywood", 0xdeb887},
{"cadetblue", 0x5f9ea0},
{"chartreuse", 0x7fff00},
{"chocolate", 0xd2691e},
{"coral", 0xff7f50},
{"cornflowerblue", 0x6495ed},
{"cornsilk", 0xfff8dc},
{"crimson", 0xdc143c},
{"cyan", 0x00ffff},
{"darkblue", 0x00008b},
{"darkcyan", 0x008b8b},
{"darkgoldenrod", 0xb8860b},
{"darkgray", 0xa9a9a9},
{"darkgreen", 0x006400},
{"darkgrey", 0xa9a9a9},
{"darkkhaki", 0xbdb76b},
{"darkmagenta", 0x8b008b},
{"darkolivegreen", 0x556b2f},
{"darkorange", 0xff8c00},
{"darkorchid", 0x9932cc},
{"darkred", 0x8b0000},
{"darksalmon", 0xe9967a},
{"darkseagreen", 0x8fbc8f},
{"darkslateblue", 0x483d8b},
{"darkslategray", 0x2f4f4f},
{"darkslategrey", 0x2f4f4f},
{"darkturquoise", 0x00ced1},
{"darkviolet", 0x9400d3},
{"deeppink", 0xff1493},
{"deepskyblue", 0x00bfff},
{"dimgray", 0x696969},
{"dimgrey", 0x696969},
{"dodgerblue", 0x1e90ff},
{"firebrick", 0xb22222},
{"floralwhite", 0xfffaf0},
{"forestgreen", 0x228b22},
{"fuchsia", 0xff00ff},
{"gainsboro", 0xdcdcdc},
{"ghostwhite", 0xf8f8ff},
{"gold", 0xffd700},
{"goldenrod", 0xdaa520},
{"gray", 0x808080},
{"green", 0x008000},
{"greenyellow", 0xadff2f},
{"grey", 0x808080},
{"honeydew", 0xf0fff0},
{"hotpink", 0xff69b4},
{"indianred", 0xcd5c5c},
{"indigo", 0x4b0082},
{"ivory", 0xfffff0},
{"khaki", 0xf0e68c},
{"lavender", 0xe6e6fa},
{"lavenderblush", 0xfff0f5},
{"lawngreen", 0x7cfc00},
{"lemonchiffon", 0xfffacd},
{"lightblue", 0xadd8e6},
{"lightcoral", 0xf08080},
{"lightcyan", 0xe0ffff},
{"lightgoldenrodyellow", 0xfafad2},
{"lightgray", 0xd3d3d3},
{"lightgreen", 0x90ee90},
{"lightgrey", 0xd3d3d3},
{"lightpink", 0xffb6c1},
{"lightsalmon", 0xffa07a},
{"lightseagreen", 0x20b2aa},
{"lightskyblue", 0x87cefa},
{"lightslategray", 0x778899},
{"lightslategrey", 0x778899},
{"lightsteelblue", 0xb0c4de},
{"lightyellow", 0xffffe0},
{"lime", 0x00ff00},
{"limegreen", 0x32cd32},
{"linen", 0xfaf0e6},
{"magenta", 0xff00ff},
{"maroon", 0x800000},
{"mediumaquamarine", 0x66cdaa},
{"mediumblue", 0x0000cd},
{"mediumorchid", 0xba55d3},
{"mediumpurple", 0x9370db},
{"mediumseagreen", 0x3cb371},
{"mediumslateblue", 0x7b68ee},
{"mediumspringgreen", 0x00fa9a},
{"mediumturquoise", 0x48d1cc},
{"mediumvioletred", 0xc71585},
{"midnightblue", 0x191970},
{"mintcream", 0xf5fffa},
{"mistyrose", 0xffe4e1},
{"moccasin", 0xffe4b5},
{"navajowhite", 0xffdead},
{"navy", 0x000080},
{"oldlace", 0xfdf5e6},
{"olive", 0x808000},
{"olivedrab", 0x6b8e23},
{"orange", 0xffa500},
{"orangered", 0xff4500},
{"orchid", 0xda70d6},
{"palegoldenrod", 0xeee8aa},
{"palegreen", 0x98fb98},
{"paleturquoise", 0xafeeee},
{"palevioletred", 0xdb7093},
{"papayawhip", 0xffefd5},
{"peachpuff", 0xffdab9},
{"peru", 0xcd853f},
{"pink", 0xffc0cb},
{"plum", 0xdda0dd},
{"powderblue", 0xb0e0e6},
{"purple", 0x800080},
{"rebeccapurple", 0x663399},
{"red", 0xff0000},
{"rosybrown", 0xbc8f8f},
{"royalblue", 0x4169e1},
{"saddlebrown", 0x8b4513},
{"salmon", 0xfa8072},
{"sandybrown", 0xf4a460},
{"seagreen", 0x2e8b57},
{"seashell", 0xfff5ee},
{"sienna", 0xa0522d},
{"silver", 0xc0c0c0},
{"skyblue", 0x87ceeb},
{"slateblue", 0x6a5acd},
{"slategray", 0x708090},
{"slategrey", 0x708090},
{"snow", 0xfffafa},
{"springgreen", 0x00ff7f},
{"steelblue", 0x4682b4},
{"tan", 0xd2b48c},
{"teal", 0x008080},
{"thistle", 0xd8bfd8},
{"tomato", 0xff6347},
{"turquoise", 0x40e0d0},
{"violet", 0xee82ee},
{"wheat", 0xf5deb3},
{"white", 0xffffff},
{"whitesmoke", 0xf5f5f5},
{"yellow", 0xffff00},
{"yellowgreen", 0x9acd32}
};
static bool parseNamedColorString(const std::string &value, video::SColor &color)
{
std::string color_name;
std::string alpha_string;
/* If the string has a # in it, assume this is the start of a specified
* alpha value (if it isn't the string is invalid and the error will be
* caught later on, either because the color name won't be found or the
* alpha value will fail conversion)
*/
size_t alpha_pos = value.find('#');
if (alpha_pos != std::string::npos) {
color_name = value.substr(0, alpha_pos);
alpha_string = value.substr(alpha_pos + 1);
} else {
color_name = value;
}
color_name = lowercase(color_name);
auto it = s_named_colors.find(color_name);
if (it == s_named_colors.end())
return false;
u32 color_temp = it->second;
/* An empty string for alpha is ok (none of the color table entries
* have an alpha value either). Color strings without an alpha specified
* are interpreted as fully opaque
*/
if (!alpha_string.empty()) {
if (alpha_string.size() == 1) {
u8 d;
if (!hex_digit_decode(alpha_string[0], d))
return false;
color_temp |= ((d & 0xf) << 4 | (d & 0xf)) << 24;
} else if (alpha_string.size() == 2) {
u8 d1, d2;
if (!hex_digit_decode(alpha_string[0], d1)
|| !hex_digit_decode(alpha_string[1], d2))
return false;
color_temp |= ((d1 & 0xf) << 4 | (d2 & 0xf)) << 24;
} else {
return false;
}
} else {
color_temp |= 0xff << 24; // Fully opaque
}
color = video::SColor(color_temp);
return true;
}
bool parseColorString(const std::string &value, video::SColor &color, bool quiet,
unsigned char default_alpha)
{
bool success;
if (value[0] == '#')
success = parseHexColorString(value, color, default_alpha);
else
success = parseNamedColorString(value, color);
if (!success && !quiet)
errorstream << "Invalid color: \"" << value << "\"" << std::endl;
return success;
}
void str_replace(std::string &str, char from, char to)
{
std::replace(str.begin(), str.end(), from, to);
}
/* Translated strings have the following format:
* \x1bT marks the beginning of a translated string
* \x1bE marks its end
*
* \x1bF marks the beginning of an argument, and \x1bE its end.
*
* Arguments are *not* translated, as they may contain escape codes.
* Thus, if you want a translated argument, it should be inside \x1bT/\x1bE tags as well.
*
* This representation is chosen so that clients ignoring escape codes will
* see untranslated strings.
*
* For instance, suppose we have a string such as "@1 Wool" with the argument "White"
* The string will be sent as "\x1bT\x1bF\x1bTWhite\x1bE\x1bE Wool\x1bE"
* To translate this string, we extract what is inside \x1bT/\x1bE tags.
* When we notice the \x1bF tag, we recursively extract what is there up to the \x1bE end tag,
* translating it as well.
* We get the argument "White", translated, and create a template string with "@1" instead of it.
* We finally get the template "@1 Wool" that was used in the beginning, which we translate
* before filling it again.
*/
void translate_all(const std::wstring &s, size_t &i,
Translations *translations, std::wstring &res);
void translate_string(const std::wstring &s, Translations *translations,
const std::wstring &textdomain, size_t &i, std::wstring &res)
{
std::wostringstream output;
std::vector<std::wstring> args;
int arg_number = 1;
while (i < s.length()) {
// Not an escape sequence: just add the character.
if (s[i] != '\x1b') {
output.put(s[i]);
// The character is a literal '@'; add it twice
// so that it is not mistaken for an argument.
if (s[i] == L'@')
output.put(L'@');
++i;
continue;
}
// We have an escape sequence: locate it and its data
// It is either a single character, or it begins with '('
// and extends up to the following ')', with '\' as an escape character.
++i;
size_t start_index = i;
size_t length;
if (i == s.length()) {
length = 0;
} else if (s[i] == L'(') {
++i;
++start_index;
while (i < s.length() && s[i] != L')') {
if (s[i] == L'\\')
++i;
++i;
}
length = i - start_index;
++i;
if (i > s.length())
i = s.length();
} else {
++i;
length = 1;
}
std::wstring escape_sequence(s, start_index, length);
// The escape sequence is now reconstructed.
std::vector<std::wstring> parts = split(escape_sequence, L'@');
if (parts[0] == L"E") {
// "End of translation" escape sequence. We are done locating the string to translate.
break;
} else if (parts[0] == L"F") {
// "Start of argument" escape sequence.
// Recursively translate the argument, and add it to the argument list.
// Add an "@n" instead of the argument to the template to translate.
if (arg_number >= 10) {
errorstream << "Ignoring too many arguments to translation" << std::endl;
std::wstring arg;
translate_all(s, i, translations, arg);
args.push_back(arg);
continue;
}
output.put(L'@');
output << arg_number;
++arg_number;
std::wstring arg;
translate_all(s, i, translations, arg);
args.push_back(arg);
} else {
// This is an escape sequence *inside* the template string to translate itself.
// This should not happen, show an error message.
errorstream << "Ignoring escape sequence '"
<< wide_to_utf8(escape_sequence) << "' in translation" << std::endl;
}
}
std::wstring toutput;
// Translate the template.
if (translations != nullptr)
toutput = translations->getTranslation(
textdomain, output.str());
else
toutput = output.str();
// Put back the arguments in the translated template.
std::wostringstream result;
size_t j = 0;
while (j < toutput.length()) {
// Normal character, add it to output and continue.
if (toutput[j] != L'@' || j == toutput.length() - 1) {
result.put(toutput[j]);
++j;
continue;
}
++j;
// Literal escape for '@'.
if (toutput[j] == L'@') {
result.put(L'@');
++j;
continue;
}
// Here we have an argument; get its index and add the translated argument to the output.
int arg_index = toutput[j] - L'1';
++j;
if (0 <= arg_index && (size_t)arg_index < args.size()) {
result << args[arg_index];
} else {
// This is not allowed: show an error message
errorstream << "Ignoring out-of-bounds argument escape sequence in translation" << std::endl;
}
}
res = result.str();
}
void translate_all(const std::wstring &s, size_t &i,
Translations *translations, std::wstring &res)
{
std::wostringstream output;
while (i < s.length()) {
// Not an escape sequence: just add the character.
if (s[i] != '\x1b') {
output.put(s[i]);
++i;
continue;
}
// We have an escape sequence: locate it and its data
// It is either a single character, or it begins with '('
// and extends up to the following ')', with '\' as an escape character.
size_t escape_start = i;
++i;
size_t start_index = i;
size_t length;
if (i == s.length()) {
length = 0;
} else if (s[i] == L'(') {
++i;
++start_index;
while (i < s.length() && s[i] != L')') {
if (s[i] == L'\\') {
++i;
}
++i;
}
length = i - start_index;
++i;
if (i > s.length())
i = s.length();
} else {
++i;
length = 1;
}
std::wstring escape_sequence(s, start_index, length);
// The escape sequence is now reconstructed.
std::vector<std::wstring> parts = split(escape_sequence, L'@');
if (parts[0] == L"E") {
// "End of argument" escape sequence. Exit.
break;
} else if (parts[0] == L"T") {
// Beginning of translated string.
std::wstring textdomain;
if (parts.size() > 1)
textdomain = parts[1];
std::wstring translated;
translate_string(s, translations, textdomain, i, translated);
output << translated;
} else {
// Another escape sequence, such as colors. Preserve it.
output << std::wstring(s, escape_start, i - escape_start);
}
}
res = output.str();
}
// Translate string server side
std::wstring translate_string(const std::wstring &s, Translations *translations)
{
size_t i = 0;
std::wstring res;
translate_all(s, i, translations, res);
return res;
}
// Translate string client side
std::wstring translate_string(const std::wstring &s)
{
#ifdef SERVER
return translate_string(s, nullptr);
#else
return translate_string(s, g_client_translations);
#endif
}
static const std::array<std::wstring, 30> disallowed_dir_names = {
// Problematic filenames from here:
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#file-and-directory-names
// Plus undocumented values from here:
// https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
L"CON",
L"PRN",
L"AUX",
L"NUL",
L"COM1",
L"COM2",
L"COM3",
L"COM4",
L"COM5",
L"COM6",
L"COM7",
L"COM8",
L"COM9",
L"COM\u00B2",
L"COM\u00B3",
L"COM\u00B9",
L"LPT1",
L"LPT2",
L"LPT3",
L"LPT4",
L"LPT5",
L"LPT6",
L"LPT7",
L"LPT8",
L"LPT9",
L"LPT\u00B2",
L"LPT\u00B3",
L"LPT\u00B9",
L"CONIN$",
L"CONOUT$",
};
/**
* List of characters that are blacklisted from created directories
*/
static const std::wstring disallowed_path_chars = L"<>:\"/\\|?*.";
std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix)
{
std::wstring safe_name = utf8_to_wide(str);
for (std::wstring disallowed_name : disallowed_dir_names) {
if (str_equal(safe_name, disallowed_name, true)) {
safe_name = utf8_to_wide(optional_prefix) + safe_name;
break;
}
}
// Replace leading and trailing spaces with underscores.
size_t start = safe_name.find_first_not_of(L' ');
size_t end = safe_name.find_last_not_of(L' ');
if (start == std::wstring::npos || end == std::wstring::npos)
start = end = safe_name.size();
for (size_t i = 0; i < start; i++)
safe_name[i] = L'_';
for (size_t i = end + 1; i < safe_name.size(); i++)
safe_name[i] = L'_';
// Replace other disallowed characters with underscores
for (size_t i = 0; i < safe_name.length(); i++) {
bool is_valid = true;
// Unlikely, but control characters should always be blacklisted
if (safe_name[i] < 32) {
is_valid = false;
} else if (safe_name[i] < 128) {
is_valid = disallowed_path_chars.find_first_of(safe_name[i])
== std::wstring::npos;
}
if (!is_valid)
safe_name[i] = L'_';
}
return wide_to_utf8(safe_name);
}
void safe_print_string(std::ostream &os, const std::string &str)
{
std::ostream::fmtflags flags = os.flags();
os << std::hex;
for (const char c : str) {
if (IS_ASCII_PRINTABLE_CHAR(c) || IS_UTF8_MULTB_START(c) ||
IS_UTF8_MULTB_INNER(c) || c == '\n' || c == '\t') {
os << c;
} else {
os << '<' << std::setw(2) << (int)c << '>';
}
}
os.setf(flags);
}

763
src/util/string.h Normal file
View File

@@ -0,0 +1,763 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_bloated.h"
#include "irrString.h"
#include <cstdlib>
#include <string>
#include <cstring>
#include <vector>
#include <map>
#include <sstream>
#include <iomanip>
#include <cctype>
#include <unordered_map>
class Translations;
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
// Checks whether a value is an ASCII printable character
#define IS_ASCII_PRINTABLE_CHAR(x) \
(((unsigned int)(x) >= 0x20) && \
( (unsigned int)(x) <= 0x7e))
// Checks whether a byte is an inner byte for an utf-8 multibyte sequence
#define IS_UTF8_MULTB_INNER(x) \
(((unsigned char)(x) >= 0x80) && \
( (unsigned char)(x) <= 0xbf))
// Checks whether a byte is a start byte for an utf-8 multibyte sequence
#define IS_UTF8_MULTB_START(x) \
(((unsigned char)(x) >= 0xc2) && \
( (unsigned char)(x) <= 0xf4))
// Given a start byte x for an utf-8 multibyte sequence
// it gives the length of the whole sequence in bytes.
#define UTF8_MULTB_START_LEN(x) \
(((unsigned char)(x) < 0xe0) ? 2 : \
(((unsigned char)(x) < 0xf0) ? 3 : 4))
typedef std::unordered_map<std::string, std::string> StringMap;
struct FlagDesc {
const char *name;
u32 flag;
};
// Try to avoid converting between wide and UTF-8 unless you need to
// input/output stuff via Irrlicht
std::wstring utf8_to_wide(const std::string &input);
std::string wide_to_utf8(const std::wstring &input);
// You must free the returned string!
// The returned string is allocated using new[]
wchar_t *utf8_to_wide_c(const char *str);
std::string urlencode(const std::string &str);
std::string urldecode(const std::string &str);
u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask);
std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask);
size_t mystrlcpy(char *dst, const char *src, size_t size);
char *mystrtok_r(char *s, const char *sep, char **lasts);
u64 read_seed(const char *str);
bool parseColorString(const std::string &value, video::SColor &color, bool quiet,
unsigned char default_alpha = 0xff);
/**
* Returns a copy of \p str with spaces inserted at the right hand side to ensure
* that the string is \p len characters in length. If \p str is <= \p len then the
* returned string will be identical to str.
*/
inline std::string padStringRight(std::string str, size_t len)
{
if (len > str.size())
str.insert(str.end(), len - str.size(), ' ');
return str;
}
/**
* Returns a version of \p str with the first occurrence of a string
* contained within ends[] removed from the end of the string.
*
* @param str
* @param ends A NULL- or ""- terminated array of strings to remove from s in
* the copy produced. Note that once one of these strings is removed
* that no further postfixes contained within this array are removed.
*
* @return If no end could be removed then "" is returned.
*/
inline std::string removeStringEnd(const std::string &str,
const char *ends[])
{
const char **p = ends;
for (; *p && (*p)[0] != '\0'; p++) {
std::string end = *p;
if (str.size() < end.size())
continue;
if (str.compare(str.size() - end.size(), end.size(), end) == 0)
return str.substr(0, str.size() - end.size());
}
return "";
}
/**
* Check two strings for equivalence. If \p case_insensitive is true
* then the case of the strings is ignored (default is false).
*
* @param s1
* @param s2
* @param case_insensitive
* @return true if the strings match
*/
template <typename T>
inline bool str_equal(const std::basic_string<T> &s1,
const std::basic_string<T> &s2,
bool case_insensitive = false)
{
if (!case_insensitive)
return s1 == s2;
if (s1.size() != s2.size())
return false;
for (size_t i = 0; i < s1.size(); ++i)
if(tolower(s1[i]) != tolower(s2[i]))
return false;
return true;
}
/**
* Check whether \p str begins with the string prefix. If \p case_insensitive
* is true then the check is case insensitve (default is false; i.e. case is
* significant).
*
* @param str
* @param prefix
* @param case_insensitive
* @return true if the str begins with prefix
*/
template <typename T>
inline bool str_starts_with(const std::basic_string<T> &str,
const std::basic_string<T> &prefix,
bool case_insensitive = false)
{
if (str.size() < prefix.size())
return false;
if (!case_insensitive)
return str.compare(0, prefix.size(), prefix) == 0;
for (size_t i = 0; i < prefix.size(); ++i)
if (tolower(str[i]) != tolower(prefix[i]))
return false;
return true;
}
/**
* Check whether \p str begins with the string prefix. If \p case_insensitive
* is true then the check is case insensitve (default is false; i.e. case is
* significant).
*
* @param str
* @param prefix
* @param case_insensitive
* @return true if the str begins with prefix
*/
template <typename T>
inline bool str_starts_with(const std::basic_string<T> &str,
const T *prefix,
bool case_insensitive = false)
{
return str_starts_with(str, std::basic_string<T>(prefix),
case_insensitive);
}
/**
* Check whether \p str ends with the string suffix. If \p case_insensitive
* is true then the check is case insensitve (default is false; i.e. case is
* significant).
*
* @param str
* @param suffix
* @param case_insensitive
* @return true if the str begins with suffix
*/
template <typename T>
inline bool str_ends_with(const std::basic_string<T> &str,
const std::basic_string<T> &suffix,
bool case_insensitive = false)
{
if (str.size() < suffix.size())
return false;
size_t start = str.size() - suffix.size();
if (!case_insensitive)
return str.compare(start, suffix.size(), suffix) == 0;
for (size_t i = 0; i < suffix.size(); ++i)
if (tolower(str[start + i]) != tolower(suffix[i]))
return false;
return true;
}
/**
* Check whether \p str ends with the string suffix. If \p case_insensitive
* is true then the check is case insensitve (default is false; i.e. case is
* significant).
*
* @param str
* @param suffix
* @param case_insensitive
* @return true if the str begins with suffix
*/
template <typename T>
inline bool str_ends_with(const std::basic_string<T> &str,
const T *suffix,
bool case_insensitive = false)
{
return str_ends_with(str, std::basic_string<T>(suffix),
case_insensitive);
}
/**
* Splits a string into its component parts separated by the character
* \p delimiter.
*
* @return An std::vector<std::basic_string<T> > of the component parts
*/
template <typename T>
inline std::vector<std::basic_string<T> > str_split(
const std::basic_string<T> &str,
T delimiter)
{
std::vector<std::basic_string<T> > parts;
std::basic_stringstream<T> sstr(str);
std::basic_string<T> part;
while (std::getline(sstr, part, delimiter))
parts.push_back(part);
return parts;
}
/**
* @param str
* @return A copy of \p str converted to all lowercase characters.
*/
inline std::string lowercase(const std::string &str)
{
std::string s2;
s2.reserve(str.size());
for (char i : str)
s2 += tolower(i);
return s2;
}
/**
* @param str
* @return A copy of \p str with leading and trailing whitespace removed.
*/
inline std::string trim(const std::string &str)
{
size_t front = 0;
size_t back = str.size();
while (front < back && std::isspace(str[front]))
++front;
while (back > front && std::isspace(str[back - 1]))
--back;
return str.substr(front, back - front);
}
/**
* Returns whether \p str should be regarded as (bool) true. Case and leading
* and trailing whitespace are ignored. Values that will return
* true are "y", "yes", "true" and any number that is not 0.
* @param str
*/
inline bool is_yes(const std::string &str)
{
std::string s2 = lowercase(trim(str));
return s2 == "y" || s2 == "yes" || s2 == "true" || atoi(s2.c_str()) != 0;
}
/**
* Converts the string \p str to a signed 32-bit integer. The converted value
* is constrained so that min <= value <= max.
*
* @see atoi(3) for limitations
*
* @param str
* @param min Range minimum
* @param max Range maximum
* @return The value converted to a signed 32-bit integer and constrained
* within the range defined by min and max (inclusive)
*/
inline s32 mystoi(const std::string &str, s32 min, s32 max)
{
s32 i = atoi(str.c_str());
if (i < min)
i = min;
if (i > max)
i = max;
return i;
}
/**
* Returns a 32-bit value reprensented by the string \p str (decimal).
* @see atoi(3) for further limitations
*/
inline s32 mystoi(const std::string &str)
{
return atoi(str.c_str());
}
/**
* Returns a float reprensented by the string \p str (decimal).
* @see atof(3)
*/
inline float mystof(const std::string &str)
{
return atof(str.c_str());
}
#define stoi mystoi
#define stof mystof
/// Returns a value represented by the string \p val.
template <typename T>
inline T from_string(const std::string &str)
{
std::stringstream tmp(str);
T t;
tmp >> t;
return t;
}
/// Returns a 64-bit signed value represented by the string \p str (decimal).
inline s64 stoi64(const std::string &str) { return from_string<s64>(str); }
#if __cplusplus < 201103L
namespace std {
/// Returns a string representing the value \p val.
template <typename T>
inline string to_string(T val)
{
ostringstream oss;
oss << val;
return oss.str();
}
#define DEFINE_STD_TOSTRING_FLOATINGPOINT(T) \
template <> \
inline string to_string<T>(T val) \
{ \
ostringstream oss; \
oss << std::fixed \
<< std::setprecision(6) \
<< val; \
return oss.str(); \
}
DEFINE_STD_TOSTRING_FLOATINGPOINT(float)
DEFINE_STD_TOSTRING_FLOATINGPOINT(double)
DEFINE_STD_TOSTRING_FLOATINGPOINT(long double)
#undef DEFINE_STD_TOSTRING_FLOATINGPOINT
/// Returns a wide string representing the value \p val
template <typename T>
inline wstring to_wstring(T val)
{
return utf8_to_wide(to_string(val));
}
}
#endif
/// Returns a string representing the decimal value of the 32-bit value \p i.
inline std::string itos(s32 i) { return std::to_string(i); }
/// Returns a string representing the decimal value of the 64-bit value \p i.
inline std::string i64tos(s64 i) { return std::to_string(i); }
// std::to_string uses the '%.6f' conversion, which is inconsistent with
// std::ostream::operator<<() and impractical too. ftos() uses the
// more generic and std::ostream::operator<<()-compatible '%G' format.
/// Returns a string representing the decimal value of the float value \p f.
inline std::string ftos(float f)
{
std::ostringstream oss;
oss << f;
return oss.str();
}
/**
* Replace all occurrences of \p pattern in \p str with \p replacement.
*
* @param str String to replace pattern with replacement within.
* @param pattern The pattern to replace.
* @param replacement What to replace the pattern with.
*/
inline void str_replace(std::string &str, const std::string &pattern,
const std::string &replacement)
{
std::string::size_type start = str.find(pattern, 0);
while (start != str.npos) {
str.replace(start, pattern.size(), replacement);
start = str.find(pattern, start + replacement.size());
}
}
/**
* Escapes characters [ ] \ , ; that can not be used in formspecs
*/
inline void str_formspec_escape(std::string &str)
{
str_replace(str, "\\", "\\\\");
str_replace(str, "]", "\\]");
str_replace(str, "[", "\\[");
str_replace(str, ";", "\\;");
str_replace(str, ",", "\\,");
}
/**
* Replace all occurrences of the character \p from in \p str with \p to.
*
* @param str The string to (potentially) modify.
* @param from The character in str to replace.
* @param to The replacement character.
*/
void str_replace(std::string &str, char from, char to);
/**
* Check that a string only contains whitelisted characters. This is the
* opposite of string_allowed_blacklist().
*
* @param str The string to be checked.
* @param allowed_chars A string containing permitted characters.
* @return true if the string is allowed, otherwise false.
*
* @see string_allowed_blacklist()
*/
inline bool string_allowed(const std::string &str, const std::string &allowed_chars)
{
return str.find_first_not_of(allowed_chars) == str.npos;
}
/**
* Check that a string contains no blacklisted characters. This is the
* opposite of string_allowed().
*
* @param str The string to be checked.
* @param blacklisted_chars A string containing prohibited characters.
* @return true if the string is allowed, otherwise false.
* @see string_allowed()
*/
inline bool string_allowed_blacklist(const std::string &str,
const std::string &blacklisted_chars)
{
return str.find_first_of(blacklisted_chars) == str.npos;
}
/**
* Create a string based on \p from where a newline is forcefully inserted
* every \p row_len characters.
*
* @note This function does not honour word wraps and blindy inserts a newline
* every \p row_len characters whether it breaks a word or not. It is
* intended to be used for, for example, showing paths in the GUI.
*
* @note This function doesn't wrap inside utf-8 multibyte sequences and also
* counts multibyte sequences correcly as single characters.
*
* @param from The (utf-8) string to be wrapped into rows.
* @param row_len The row length (in characters).
* @return A new string with the wrapping applied.
*/
inline std::string wrap_rows(const std::string &from,
unsigned row_len)
{
std::string to;
size_t character_idx = 0;
for (size_t i = 0; i < from.size(); i++) {
if (!IS_UTF8_MULTB_INNER(from[i])) {
// Wrap string after last inner byte of char
if (character_idx > 0 && character_idx % row_len == 0)
to += '\n';
character_idx++;
}
to += from[i];
}
return to;
}
/**
* Removes backslashes from an escaped string (FormSpec strings)
*/
template <typename T>
inline std::basic_string<T> unescape_string(const std::basic_string<T> &s)
{
std::basic_string<T> res;
for (size_t i = 0; i < s.length(); i++) {
if (s[i] == '\\') {
i++;
if (i >= s.length())
break;
}
res += s[i];
}
return res;
}
/**
* Remove all escape sequences in \p s.
*
* @param s The string in which to remove escape sequences.
* @return \p s, with escape sequences removed.
*/
template <typename T>
std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
{
std::basic_string<T> output;
size_t i = 0;
while (i < s.length()) {
if (s[i] == '\x1b') {
++i;
if (i == s.length()) continue;
if (s[i] == '(') {
++i;
while (i < s.length() && s[i] != ')') {
if (s[i] == '\\') {
++i;
}
++i;
}
++i;
} else {
++i;
}
continue;
}
output += s[i];
++i;
}
return output;
}
template <typename T>
std::vector<std::basic_string<T> > split(const std::basic_string<T> &s, T delim)
{
std::vector<std::basic_string<T> > tokens;
std::basic_string<T> current;
bool last_was_escape = false;
for (size_t i = 0; i < s.length(); i++) {
T si = s[i];
if (last_was_escape) {
current += '\\';
current += si;
last_was_escape = false;
} else {
if (si == delim) {
tokens.push_back(current);
current = std::basic_string<T>();
last_was_escape = false;
} else if (si == '\\') {
last_was_escape = true;
} else {
current += si;
last_was_escape = false;
}
}
}
//push last element
tokens.push_back(current);
return tokens;
}
std::wstring translate_string(const std::wstring &s, Translations *translations);
std::wstring translate_string(const std::wstring &s);
inline std::wstring unescape_translate(const std::wstring &s) {
return unescape_enriched(translate_string(s));
}
/**
* Checks that all characters in \p to_check are a decimal digits.
*
* @param to_check
* @return true if to_check is not empty and all characters in to_check are
* decimal digits, otherwise false
*/
inline bool is_number(const std::string &to_check)
{
for (char i : to_check)
if (!std::isdigit(i))
return false;
return !to_check.empty();
}
/**
* Returns a C-string, either "true" or "false", corresponding to \p val.
*
* @return If \p val is true, then "true" is returned, otherwise "false".
*/
inline const char *bool_to_cstr(bool val)
{
return val ? "true" : "false";
}
/**
* Converts a duration in seconds to a pretty-printed duration in
* days, hours, minutes and seconds.
*
* @param sec duration in seconds
* @return pretty-printed duration
*/
inline const std::string duration_to_string(int sec)
{
std::ostringstream ss;
const char *neg = "";
if (sec < 0) {
sec = -sec;
neg = "-";
}
int total_sec = sec;
int min = sec / 60;
sec %= 60;
int hour = min / 60;
min %= 60;
int day = hour / 24;
hour %= 24;
if (day > 0) {
ss << neg << day << "d";
if (hour > 0 || min > 0 || sec > 0)
ss << " ";
}
if (hour > 0) {
ss << neg << hour << "h";
if (min > 0 || sec > 0)
ss << " ";
}
if (min > 0) {
ss << neg << min << "min";
if (sec > 0)
ss << " ";
}
if (sec > 0 || total_sec == 0) {
ss << neg << sec << "s";
}
return ss.str();
}
/**
* Joins a vector of strings by the string \p delimiter.
*
* @return A std::string
*/
inline std::string str_join(const std::vector<std::string> &list,
const std::string &delimiter)
{
std::ostringstream oss;
bool first = true;
for (const auto &part : list) {
if (!first)
oss << delimiter;
oss << part;
first = false;
}
return oss.str();
}
/**
* Create a UTF8 std::string from a irr::core::stringw.
*/
inline std::string stringw_to_utf8(const irr::core::stringw &input)
{
std::wstring str(input.c_str());
return wide_to_utf8(str);
}
/**
* Create a irr::core:stringw from a UTF8 std::string.
*/
inline irr::core::stringw utf8_to_stringw(const std::string &input)
{
std::wstring str = utf8_to_wide(input);
return irr::core::stringw(str.c_str());
}
/**
* Sanitize the name of a new directory. This consists of two stages:
* 1. Check for 'reserved filenames' that can't be used on some filesystems
* and add a prefix to them
* 2. Remove 'unsafe' characters from the name by replacing them with '_'
*/
std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix);
/**
* Prints a sanitized version of a string without control characters.
* '\t' and '\n' are allowed, as are UTF-8 control characters (e.g. RTL).
* ASCII control characters are replaced with their hex encoding in angle
* brackets (e.g. "a\x1eb" -> "a<1e>b").
*/
void safe_print_string(std::ostream &os, const std::string &str);

228
src/util/thread.h Normal file
View File

@@ -0,0 +1,228 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes.h"
#include "threading/thread.h"
#include "threading/mutex_auto_lock.h"
#include "porting.h"
#include "log.h"
#include "container.h"
template<typename T>
class MutexedVariable
{
public:
MutexedVariable(const T &value):
m_value(value)
{}
T get()
{
MutexAutoLock lock(m_mutex);
return m_value;
}
void set(const T &value)
{
MutexAutoLock lock(m_mutex);
m_value = value;
}
// You pretty surely want to grab the lock when accessing this
T m_value;
private:
std::mutex m_mutex;
};
/*
A single worker thread - multiple client threads queue framework.
*/
template<typename Key, typename T, typename Caller, typename CallerData>
class GetResult {
public:
Key key;
T item;
std::pair<Caller, CallerData> caller;
};
template<typename Key, typename T, typename Caller, typename CallerData>
class ResultQueue : public MutexedQueue<GetResult<Key, T, Caller, CallerData> > {
};
template<typename Caller, typename Data, typename Key, typename T>
class CallerInfo {
public:
Caller caller;
Data data;
ResultQueue<Key, T, Caller, Data> *dest;
};
template<typename Key, typename T, typename Caller, typename CallerData>
class GetRequest {
public:
GetRequest() = default;
~GetRequest() = default;
GetRequest(const Key &a_key): key(a_key)
{
}
Key key;
std::list<CallerInfo<Caller, CallerData, Key, T> > callers;
};
/**
* Notes for RequestQueue usage
* @param Key unique key to identify a request for a specific resource
* @param T ?
* @param Caller unique id of calling thread
* @param CallerData data passed back to caller
*/
template<typename Key, typename T, typename Caller, typename CallerData>
class RequestQueue {
public:
bool empty()
{
return m_queue.empty();
}
void add(const Key &key, Caller caller, CallerData callerdata,
ResultQueue<Key, T, Caller, CallerData> *dest)
{
typename std::deque<GetRequest<Key, T, Caller, CallerData> >::iterator i;
typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator j;
{
MutexAutoLock lock(m_queue.getMutex());
/*
If the caller is already on the list, only update CallerData
*/
for (i = m_queue.getQueue().begin(); i != m_queue.getQueue().end(); ++i) {
GetRequest<Key, T, Caller, CallerData> &request = *i;
if (request.key != key)
continue;
for (j = request.callers.begin(); j != request.callers.end(); ++j) {
CallerInfo<Caller, CallerData, Key, T> &ca = *j;
if (ca.caller == caller) {
ca.data = callerdata;
return;
}
}
CallerInfo<Caller, CallerData, Key, T> ca;
ca.caller = caller;
ca.data = callerdata;
ca.dest = dest;
request.callers.push_back(ca);
return;
}
}
/*
Else add a new request to the queue
*/
GetRequest<Key, T, Caller, CallerData> request;
request.key = key;
CallerInfo<Caller, CallerData, Key, T> ca;
ca.caller = caller;
ca.data = callerdata;
ca.dest = dest;
request.callers.push_back(ca);
m_queue.push_back(request);
}
GetRequest<Key, T, Caller, CallerData> pop(unsigned int timeout_ms)
{
return m_queue.pop_front(timeout_ms);
}
GetRequest<Key, T, Caller, CallerData> pop()
{
return m_queue.pop_frontNoEx();
}
void pushResult(GetRequest<Key, T, Caller, CallerData> req, T res)
{
for (typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator
i = req.callers.begin();
i != req.callers.end(); ++i) {
CallerInfo<Caller, CallerData, Key, T> &ca = *i;
GetResult<Key,T,Caller,CallerData> result;
result.key = req.key;
result.item = res;
result.caller.first = ca.caller;
result.caller.second = ca.data;
ca.dest->push_back(result);
}
}
private:
MutexedQueue<GetRequest<Key, T, Caller, CallerData> > m_queue;
};
class UpdateThread : public Thread
{
public:
UpdateThread(const std::string &name) : Thread(name + "Update") {}
~UpdateThread() = default;
void deferUpdate() { m_update_sem.post(); }
void stop()
{
Thread::stop();
// give us a nudge
m_update_sem.post();
}
void *run()
{
BEGIN_DEBUG_EXCEPTION_HANDLER
while (!stopRequested()) {
m_update_sem.wait();
// Set semaphore to 0
while (m_update_sem.wait(0));
if (stopRequested()) break;
doUpdate();
}
END_DEBUG_EXCEPTION_HANDLER
return NULL;
}
protected:
virtual void doUpdate() = 0;
private:
Semaphore m_update_sem;
};

63
src/util/timetaker.cpp Normal file
View File

@@ -0,0 +1,63 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "timetaker.h"
#include "porting.h"
#include "log.h"
#include <ostream>
TimeTaker::TimeTaker(const std::string &name, u64 *result, TimePrecision prec)
{
m_name = name;
m_result = result;
m_precision = prec;
m_time1 = porting::getTime(prec);
}
u64 TimeTaker::stop(bool quiet)
{
if (m_running) {
u64 dtime = porting::getTime(m_precision) - m_time1;
if (m_result != nullptr) {
(*m_result) += dtime;
} else {
if (!quiet) {
static const char* const units[] = {
"s" /* PRECISION_SECONDS */,
"ms" /* PRECISION_MILLI */,
"us" /* PRECISION_MICRO */,
"ns" /* PRECISION_NANO */,
};
infostream << m_name << " took "
<< dtime << units[m_precision]
<< std::endl;
}
}
m_running = false;
return dtime;
}
return 0;
}
u64 TimeTaker::getTimerTime()
{
return porting::getTime(m_precision) - m_time1;
}

50
src/util/timetaker.h Normal file
View File

@@ -0,0 +1,50 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes.h"
#include "gettime.h"
/*
TimeTaker
*/
class TimeTaker
{
public:
TimeTaker(const std::string &name, u64 *result=nullptr,
TimePrecision prec=PRECISION_MILLI);
~TimeTaker()
{
stop();
}
u64 stop(bool quiet=false);
u64 getTimerTime();
private:
std::string m_name;
u64 m_time1;
bool m_running = true;
TimePrecision m_precision;
u64 *m_result = nullptr;
};