mirror of
https://github.com/MCLx86/xtreemtest.git
synced 2025-12-16 11:35:32 +01:00
Initial commit
This commit is contained in:
19
src/util/CMakeLists.txt
Normal file
19
src/util/CMakeLists.txt
Normal 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
105
src/util/Optional.h
Normal 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
329
src/util/areastore.cpp
Normal 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
197
src/util/areastore.h
Normal 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
137
src/util/auth.cpp
Normal 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
47
src/util/auth.h
Normal 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
154
src/util/base64.cpp
Normal 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
26
src/util/base64.h
Normal 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
64
src/util/basic_macros.h
Normal 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
307
src/util/container.h
Normal 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;
|
||||
};
|
||||
120
src/util/directiontables.cpp
Normal file
120
src/util/directiontables.cpp
Normal 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
|
||||
};
|
||||
89
src/util/directiontables.h
Normal file
89
src/util/directiontables.h
Normal 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,
|
||||
};
|
||||
211
src/util/enriched_string.cpp
Normal file
211
src/util/enriched_string.cpp
Normal 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
110
src/util/enriched_string.h
Normal 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
60
src/util/hex.h
Normal 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
136
src/util/ieee_float.cpp
Normal 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
34
src/util/ieee_float.h
Normal 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
430
src/util/md32_common.h
Normal 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
204
src/util/metricsbackend.cpp
Normal 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
72
src/util/metricsbackend.h
Normal 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
223
src/util/numeric.cpp
Normal 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
471
src/util/numeric.h
Normal 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 ¤t, 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
68
src/util/png.cpp
Normal 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
27
src/util/png.h
Normal 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
134
src/util/pointedthing.cpp
Normal 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
105
src/util/pointedthing.h
Normal 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
273
src/util/pointer.h
Normal 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
104
src/util/quicktune.cpp
Normal 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
98
src/util/quicktune.h
Normal 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)
|
||||
84
src/util/quicktune_shortcutter.h
Normal file
84
src/util/quicktune_shortcutter.h
Normal 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
294
src/util/serialize.cpp
Normal 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
475
src/util/serialize.h
Normal 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
198
src/util/sha1.cpp
Normal 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
54
src/util/sha1.h
Normal 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
154
src/util/sha2.h
Normal 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
399
src/util/sha256.c
Normal 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
1040
src/util/srp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
191
src/util/srp.h
Normal file
191
src/util/srp.h
Normal 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
70
src/util/stream.h
Normal 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
78
src/util/strfnd.h
Normal 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
908
src/util/string.cpp
Normal 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
763
src/util/string.h
Normal 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
228
src/util/thread.h
Normal 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
63
src/util/timetaker.cpp
Normal 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
50
src/util/timetaker.h
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user