minetest/src/utility.h

1866 lines
34 KiB
C++

/*
Minetest-c55
Copyright (C) 2010 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 General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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.
*/
#ifndef UTILITY_HEADER
#define UTILITY_HEADER
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <jthread.h>
#include <jmutex.h>
#include <jmutexautolock.h>
#include <cstring>
#include "common_irrlicht.h"
#include "debug.h"
#include "exceptions.h"
#include "porting.h"
#include "strfnd.h" // For trim()
extern const v3s16 g_6dirs[6];
extern const v3s16 g_26dirs[26];
// 26th is (0,0,0)
extern const v3s16 g_27dirs[27];
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);
}
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 writeU16(u8 *data, u16 i)
{
data[0] = ((i>> 8)&0xff);
data[1] = ((i>> 0)&0xff);
}
inline void writeU8(u8 *data, u8 i)
{
data[0] = ((i>> 0)&0xff);
}
inline u64 readU64(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 u32 readU32(u8 *data)
{
return (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]<<0);
}
inline u16 readU16(u8 *data)
{
return (data[0]<<8) | (data[1]<<0);
}
inline u8 readU8(u8 *data)
{
return (data[0]<<0);
}
inline void writeS32(u8 *data, s32 i){
writeU32(data, (u32)i);
}
inline s32 readS32(u8 *data){
return (s32)readU32(data);
}
inline void writeS16(u8 *data, s16 i){
writeU16(data, (u16)i);
}
inline s16 readS16(u8 *data){
return (s16)readU16(data);
}
inline void writeS8(u8 *data, s8 i){
writeU8(data, (u8)i);
}
inline s8 readS8(u8 *data){
return (s8)readU8(data);
}
inline void writeF1000(u8 *data, f32 i){
writeS32(data, i*1000);
}
inline f32 readF1000(u8 *data){
return (f32)readS32(data)/1000.;
}
inline void writeV3S32(u8 *data, v3s32 p)
{
writeS32(&data[0], p.X);
writeS32(&data[4], p.Y);
writeS32(&data[8], p.Z);
}
inline v3s32 readV3S32(u8 *data)
{
v3s32 p;
p.X = readS32(&data[0]);
p.Y = readS32(&data[4]);
p.Z = readS32(&data[8]);
return p;
}
inline void writeV3F1000(u8 *data, v3f p)
{
writeF1000(&data[0], p.X);
writeF1000(&data[4], p.Y);
writeF1000(&data[8], p.Z);
}
inline v3f readV3F1000(u8 *data)
{
v3f p;
p.X = (float)readF1000(&data[0]);
p.Y = (float)readF1000(&data[4]);
p.Z = (float)readF1000(&data[8]);
return p;
}
inline void writeV2F1000(u8 *data, v2f p)
{
writeF1000(&data[0], p.X);
writeF1000(&data[4], p.Y);
}
inline v2f readV2F1000(u8 *data)
{
v2f p;
p.X = (float)readF1000(&data[0]);
p.Y = (float)readF1000(&data[4]);
return p;
}
inline void writeV2S16(u8 *data, v2s16 p)
{
writeS16(&data[0], p.X);
writeS16(&data[2], p.Y);
}
inline v2s16 readV2S16(u8 *data)
{
v2s16 p;
p.X = readS16(&data[0]);
p.Y = readS16(&data[2]);
return p;
}
inline void writeV2S32(u8 *data, v2s32 p)
{
writeS32(&data[0], p.X);
writeS32(&data[2], p.Y);
}
inline v2s32 readV2S32(u8 *data)
{
v2s32 p;
p.X = readS32(&data[0]);
p.Y = readS32(&data[2]);
return p;
}
inline void writeV3S16(u8 *data, v3s16 p)
{
writeS16(&data[0], p.X);
writeS16(&data[2], p.Y);
writeS16(&data[4], p.Z);
}
inline v3s16 readV3S16(u8 *data)
{
v3s16 p;
p.X = readS16(&data[0]);
p.Y = readS16(&data[2]);
p.Z = readS16(&data[4]);
return p;
}
/*
The above stuff directly interfaced to iostream
*/
inline void writeU8(std::ostream &os, u8 p)
{
char buf[1] = {0};
writeU8((u8*)buf, p);
os.write(buf, 1);
}
inline u8 readU8(std::istream &is)
{
char buf[1] = {0};
is.read(buf, 1);
return readU8((u8*)buf);
}
inline void writeU16(std::ostream &os, u16 p)
{
char buf[2] = {0};
writeU16((u8*)buf, p);
os.write(buf, 2);
}
inline u16 readU16(std::istream &is)
{
char buf[2] = {0};
is.read(buf, 2);
return readU16((u8*)buf);
}
inline void writeU32(std::ostream &os, u32 p)
{
char buf[4] = {0};
writeU32((u8*)buf, p);
os.write(buf, 4);
}
inline u32 readU32(std::istream &is)
{
char buf[4] = {0};
is.read(buf, 4);
return readU32((u8*)buf);
}
inline void writeS32(std::ostream &os, s32 p)
{
char buf[4] = {0};
writeS32((u8*)buf, p);
os.write(buf, 4);
}
inline s32 readS32(std::istream &is)
{
char buf[4] = {0};
is.read(buf, 4);
return readS32((u8*)buf);
}
inline void writeS16(std::ostream &os, s16 p)
{
char buf[2] = {0};
writeS16((u8*)buf, p);
os.write(buf, 2);
}
inline s16 readS16(std::istream &is)
{
char buf[2] = {0};
is.read(buf, 2);
return readS16((u8*)buf);
}
inline void writeS8(std::ostream &os, s8 p)
{
char buf[1] = {0};
writeS8((u8*)buf, p);
os.write(buf, 1);
}
inline s8 readS8(std::istream &is)
{
char buf[1] = {0};
is.read(buf, 1);
return readS8((u8*)buf);
}
inline void writeF1000(std::ostream &os, f32 p)
{
char buf[4] = {0};
writeF1000((u8*)buf, p);
os.write(buf, 4);
}
inline f32 readF1000(std::istream &is)
{
char buf[4] = {0};
is.read(buf, 4);
return readF1000((u8*)buf);
}
inline void writeV3F1000(std::ostream &os, v3f p)
{
char buf[12];
writeV3F1000((u8*)buf, p);
os.write(buf, 12);
}
inline v3f readV3F1000(std::istream &is)
{
char buf[12];
is.read(buf, 12);
return readV3F1000((u8*)buf);
}
inline void writeV2F1000(std::ostream &os, v2f p)
{
char buf[8] = {0};
writeV2F1000((u8*)buf, p);
os.write(buf, 8);
}
inline v2f readV2F1000(std::istream &is)
{
char buf[8] = {0};
is.read(buf, 8);
return readV2F1000((u8*)buf);
}
inline void writeV2S16(std::ostream &os, v2s16 p)
{
char buf[4] = {0};
writeV2S16((u8*)buf, p);
os.write(buf, 4);
}
inline v2s16 readV2S16(std::istream &is)
{
char buf[4] = {0};
is.read(buf, 4);
return readV2S16((u8*)buf);
}
inline void writeV3S16(std::ostream &os, v3s16 p)
{
char buf[6] = {0};
writeV3S16((u8*)buf, p);
os.write(buf, 6);
}
inline v3s16 readV3S16(std::istream &is)
{
char buf[6] = {0};
is.read(buf, 6);
return readV3S16((u8*)buf);
}
/*
None of these are used at the moment
*/
template <typename T>
class SharedPtr
{
public:
SharedPtr(T *t=NULL)
{
refcount = new int;
*refcount = 1;
ptr = t;
}
SharedPtr(SharedPtr<T> &t)
{
//*this = t;
drop();
refcount = t.refcount;
(*refcount)++;
ptr = t.ptr;
}
~SharedPtr()
{
drop();
}
SharedPtr<T> & operator=(T *t)
{
drop();
refcount = new int;
*refcount = 1;
ptr = t;
return *this;
}
SharedPtr<T> & operator=(SharedPtr<T> &t)
{
drop();
refcount = t.refcount;
(*refcount)++;
ptr = t.ptr;
return *this;
}
T* operator->()
{
return ptr;
}
T & operator*()
{
return *ptr;
}
bool operator!=(T *t)
{
return ptr != t;
}
bool operator==(T *t)
{
return ptr == t;
}
T & operator[](unsigned int i)
{
return ptr[i];
}
private:
void drop()
{
assert((*refcount) > 0);
(*refcount)--;
if(*refcount == 0)
{
delete refcount;
if(ptr != NULL)
delete ptr;
}
}
T *ptr;
int *refcount;
};
template <typename T>
class Buffer
{
public:
Buffer()
{
m_size = 0;
data = NULL;
}
Buffer(unsigned int size)
{
m_size = size;
if(size != 0)
data = new T[size];
else
data = NULL;
}
Buffer(const Buffer &buffer)
{
m_size = buffer.m_size;
if(m_size != 0)
{
data = new T[buffer.m_size];
memcpy(data, buffer.data, buffer.m_size);
}
else
data = NULL;
}
Buffer(const T *t, unsigned int size)
{
m_size = size;
if(size != 0)
{
data = new T[size];
memcpy(data, t, size);
}
else
data = NULL;
}
~Buffer()
{
drop();
}
Buffer& operator=(const Buffer &buffer)
{
if(this == &buffer)
return *this;
drop();
m_size = buffer.m_size;
if(m_size != 0)
{
data = new T[buffer.m_size];
memcpy(data, buffer.data, buffer.m_size);
}
else
data = NULL;
return *this;
}
T & operator[](unsigned int i) const
{
return data[i];
}
T * operator*() const
{
return data;
}
unsigned int getSize() const
{
return m_size;
}
private:
void drop()
{
if(data)
delete[] data;
}
T *data;
unsigned int m_size;
};
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 = NULL;
refcount = new unsigned int;
(*refcount) = 1;
}
SharedBuffer(const SharedBuffer &buffer)
{
//std::cout<<"SharedBuffer(const SharedBuffer &buffer)"<<std::endl;
m_size = buffer.m_size;
data = buffer.data;
refcount = buffer.refcount;
(*refcount)++;
}
SharedBuffer & operator=(const SharedBuffer & buffer)
{
//std::cout<<"SharedBuffer & operator=(const SharedBuffer & buffer)"<<std::endl;
if(this == &buffer)
return *this;
drop();
m_size = buffer.m_size;
data = buffer.data;
refcount = buffer.refcount;
(*refcount)++;
return *this;
}
/*
Copies whole buffer
*/
SharedBuffer(T *t, unsigned int size)
{
m_size = size;
if(m_size != 0)
{
data = new T[m_size];
memcpy(data, t, m_size);
}
else
data = NULL;
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 = NULL;
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)
{
if(data)
delete[] data;
delete refcount;
}
}
T *data;
unsigned int m_size;
unsigned int *refcount;
};
inline SharedBuffer<u8> SharedBufferFromString(const char *string)
{
SharedBuffer<u8> b((u8*)string, strlen(string)+1);
return b;
}
template<typename T>
class MutexedVariable
{
public:
MutexedVariable(T value):
m_value(value)
{
m_mutex.Init();
}
T get()
{
JMutexAutoLock lock(m_mutex);
return m_value;
}
void set(T value)
{
JMutexAutoLock lock(m_mutex);
m_value = value;
}
// You'll want to grab this in a SharedPtr
JMutexAutoLock * getLock()
{
return new JMutexAutoLock(m_mutex);
}
// You pretty surely want to grab the lock when accessing this
T m_value;
private:
JMutex m_mutex;
};
/*
TimeTaker
*/
class TimeTaker
{
public:
TimeTaker(const char *name, u32 *result=NULL);
~TimeTaker()
{
stop();
}
u32 stop(bool quiet=false);
u32 getTime();
private:
const char *m_name;
u32 m_time1;
bool m_running;
u32 *m_result;
};
// Tests if two strings are equal, optionally case insensitive
inline bool str_equal(const std::wstring& s1, const std::wstring& s2,
bool case_insensitive = false)
{
if(case_insensitive)
{
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;
}
else
{
return s1 == s2;
}
}
// Tests if the second string is a prefix of the first, optionally case insensitive
inline bool str_starts_with(const std::wstring& str, const std::wstring& prefix,
bool case_insensitive = false)
{
if(str.size() < prefix.size())
return false;
if(case_insensitive)
{
for(size_t i = 0; i < prefix.size(); ++i)
if(tolower(str[i]) != tolower(prefix[i]))
return false;
}
else
{
for(size_t i = 0; i < prefix.size(); ++i)
if(str[i] != prefix[i])
return false;
}
return true;
}
// Calculates the borders of a "d-radius" cube
inline void getFacePositions(core::list<v3s16> &list, u16 d)
{
if(d == 0)
{
list.push_back(v3s16(0,0,0));
return;
}
if(d == 1)
{
/*
This is an optimized sequence of coordinates.
*/
list.push_back(v3s16( 0, 1, 0)); // top
list.push_back(v3s16( 0, 0, 1)); // back
list.push_back(v3s16(-1, 0, 0)); // left
list.push_back(v3s16( 1, 0, 0)); // right
list.push_back(v3s16( 0, 0,-1)); // front
list.push_back(v3s16( 0,-1, 0)); // bottom
// 6
list.push_back(v3s16(-1, 0, 1)); // back left
list.push_back(v3s16( 1, 0, 1)); // back right
list.push_back(v3s16(-1, 0,-1)); // front left
list.push_back(v3s16( 1, 0,-1)); // front right
list.push_back(v3s16(-1,-1, 0)); // bottom left
list.push_back(v3s16( 1,-1, 0)); // bottom right
list.push_back(v3s16( 0,-1, 1)); // bottom back
list.push_back(v3s16( 0,-1,-1)); // bottom front
list.push_back(v3s16(-1, 1, 0)); // top left
list.push_back(v3s16( 1, 1, 0)); // top right
list.push_back(v3s16( 0, 1, 1)); // top back
list.push_back(v3s16( 0, 1,-1)); // top front
// 18
list.push_back(v3s16(-1, 1, 1)); // top back-left
list.push_back(v3s16( 1, 1, 1)); // top back-right
list.push_back(v3s16(-1, 1,-1)); // top front-left
list.push_back(v3s16( 1, 1,-1)); // top front-right
list.push_back(v3s16(-1,-1, 1)); // bottom back-left
list.push_back(v3s16( 1,-1, 1)); // bottom back-right
list.push_back(v3s16(-1,-1,-1)); // bottom front-left
list.push_back(v3s16( 1,-1,-1)); // bottom front-right
// 26
return;
}
// Take blocks in all sides, starting from y=0 and going +-y
for(s16 y=0; y<=d-1; y++)
{
// Left and right side, including borders
for(s16 z=-d; z<=d; z++)
{
list.push_back(v3s16(d,y,z));
list.push_back(v3s16(-d,y,z));
if(y != 0)
{
list.push_back(v3s16(d,-y,z));
list.push_back(v3s16(-d,-y,z));
}
}
// Back and front side, excluding borders
for(s16 x=-d+1; x<=d-1; x++)
{
list.push_back(v3s16(x,y,d));
list.push_back(v3s16(x,y,-d));
if(y != 0)
{
list.push_back(v3s16(x,-y,d));
list.push_back(v3s16(x,-y,-d));
}
}
}
// Take the bottom and top face with borders
// -d<x<d, y=+-d, -d<z<d
for(s16 x=-d; x<=d; x++)
for(s16 z=-d; z<=d; z++)
{
list.push_back(v3s16(x,-d,z));
list.push_back(v3s16(x,d,z));
}
}
class IndentationRaiser
{
public:
IndentationRaiser(u16 *indentation)
{
m_indentation = indentation;
(*m_indentation)++;
}
~IndentationRaiser()
{
(*m_indentation)--;
}
private:
u16 *m_indentation;
};
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 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 s16 rangelim(s16 i, s16 max)
{
if(i < 0)
return 0;
if(i > max)
return max;
return i;
}
#define rangelim(d, min, max) ((d) < (min) ? (min) : ((d)>(max)?(max):(d)))
inline v3s16 arealim(v3s16 p, s16 d)
{
if(p.X < 0)
p.X = 0;
if(p.Y < 0)
p.Y = 0;
if(p.Z < 0)
p.Z = 0;
if(p.X > d-1)
p.X = d-1;
if(p.Y > d-1)
p.Y = d-1;
if(p.Z > d-1)
p.Z = d-1;
return p;
}
inline std::wstring narrow_to_wide(const std::string& mbs)
{
size_t wcl = mbs.size();
Buffer<wchar_t> wcs(wcl+1);
size_t l = mbstowcs(*wcs, mbs.c_str(), wcl);
if(l == (size_t)(-1))
return L"<invalid multibyte string>";
wcs[l] = 0;
return *wcs;
}
inline std::string wide_to_narrow(const std::wstring& wcs)
{
size_t mbl = wcs.size()*4;
SharedBuffer<char> mbs(mbl+1);
size_t l = wcstombs(*mbs, wcs.c_str(), mbl);
if(l == (size_t)(-1))
mbs[0] = 0;
else
mbs[l] = 0;
return *mbs;
}
// Split a string using the given delimiter. Returns a vector containing
// the component parts.
inline std::vector<std::wstring> str_split(const std::wstring &str, wchar_t delimiter)
{
std::vector<std::wstring> parts;
std::wstringstream sstr(str);
std::wstring part;
while(std::getline(sstr, part, delimiter))
parts.push_back(part);
return parts;
}
/*
See test.cpp for example cases.
wraps degrees to the range of -360...360
NOTE: Wrapping to 0...360 is not used because pitch needs negative values.
*/
inline float wrapDegrees(float f)
{
// Take examples of f=10, f=720.5, f=-0.5, f=-360.5
// This results in
// 10, 720, -1, -361
int i = floor(f);
// 0, 2, 0, -1
int l = i / 360;
// NOTE: This would be used for wrapping to 0...360
// 0, 2, -1, -2
/*if(i < 0)
l -= 1;*/
// 0, 720, 0, -360
int k = l * 360;
// 10, 0.5, -0.5, -0.5
f -= float(k);
return f;
}
/* Wrap to 0...360 */
inline float wrapDegrees_0_360(float f)
{
// Take examples of f=10, f=720.5, f=-0.5, f=-360.5
// This results in
// 10, 720, -1, -361
int i = floor(f);
// 0, 2, 0, -1
int l = i / 360;
// Wrap to 0...360
// 0, 2, -1, -2
if(i < 0)
l -= 1;
// 0, 720, 0, -360
int k = l * 360;
// 10, 0.5, -0.5, -0.5
f -= float(k);
return f;
}
/* Wrap to -180...180 */
inline float wrapDegrees_180(float f)
{
f += 180;
f = wrapDegrees_0_360(f);
f -= 180;
return f;
}
inline std::string lowercase(const std::string &s)
{
std::string s2;
for(size_t i=0; i<s.size(); i++)
{
char c = s[i];
if(c >= 'A' && c <= 'Z')
c -= 'A' - 'a';
s2 += c;
}
return s2;
}
inline bool is_yes(const std::string &s)
{
std::string s2 = lowercase(trim(s));
if(s2 == "y" || s2 == "yes" || s2 == "true" || s2 == "1")
return true;
return false;
}
inline s32 mystoi(const std::string &s, s32 min, s32 max)
{
s32 i = atoi(s.c_str());
if(i < min)
i = min;
if(i > max)
i = max;
return i;
}
// MSVC2010 includes it's own versions of these
//#if !defined(_MSC_VER) || _MSC_VER < 1600
inline s32 mystoi(const std::string &s)
{
return atoi(s.c_str());
}
inline s32 mystoi(const std::wstring &s)
{
return atoi(wide_to_narrow(s).c_str());
}
inline float mystof(const std::string &s)
{
// This crap causes a segfault in certain cases on MinGW
/*float f;
std::istringstream ss(s);
ss>>f;
return f;*/
// This works in that case
return atof(s.c_str());
}
//#endif
#define stoi mystoi
#define stof mystof
inline std::string itos(s32 i)
{
std::ostringstream o;
o<<i;
return o.str();
}
inline std::string ftos(float f)
{
std::ostringstream o;
o<<f;
return o.str();
}
inline void str_replace(std::string & str, std::string const & pattern,
std::string const & 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());
}
}
inline void str_replace_char(std::string & str, char from, char to)
{
for(unsigned int i=0; i<str.size(); i++)
{
if(str[i] == from)
str[i] = to;
}
}
/*
A base class for simple background thread implementation
*/
class SimpleThread : public JThread
{
bool run;
JMutex run_mutex;
public:
SimpleThread():
JThread(),
run(true)
{
run_mutex.Init();
}
virtual ~SimpleThread()
{}
virtual void * Thread() = 0;
bool getRun()
{
JMutexAutoLock lock(run_mutex);
return run;
}
void setRun(bool a_run)
{
JMutexAutoLock lock(run_mutex);
run = a_run;
}
void stop()
{
setRun(false);
while(IsRunning())
sleep_ms(100);
}
};
/*
FIFO queue (well, actually a FILO also)
*/
template<typename T>
class Queue
{
public:
void push_back(T t)
{
m_list.push_back(t);
}
T pop_front()
{
if(m_list.size() == 0)
throw ItemNotFoundException("Queue: queue is empty");
typename core::list<T>::Iterator begin = m_list.begin();
T t = *begin;
m_list.erase(begin);
return t;
}
T pop_back()
{
if(m_list.size() == 0)
throw ItemNotFoundException("Queue: queue is empty");
typename core::list<T>::Iterator last = m_list.getLast();
T t = *last;
m_list.erase(last);
return t;
}
u32 size()
{
return m_list.size();
}
protected:
core::list<T> m_list;
};
/*
Thread-safe FIFO queue (well, actually a FILO also)
*/
template<typename T>
class MutexedQueue
{
public:
MutexedQueue()
{
m_mutex.Init();
}
u32 size()
{
JMutexAutoLock lock(m_mutex);
return m_list.size();
}
void push_back(T t)
{
JMutexAutoLock lock(m_mutex);
m_list.push_back(t);
}
T pop_front(u32 wait_time_max_ms=0)
{
u32 wait_time_ms = 0;
for(;;)
{
{
JMutexAutoLock lock(m_mutex);
if(m_list.size() > 0)
{
typename core::list<T>::Iterator begin = m_list.begin();
T t = *begin;
m_list.erase(begin);
return t;
}
if(wait_time_ms >= wait_time_max_ms)
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
// Wait a while before trying again
sleep_ms(10);
wait_time_ms += 10;
}
}
T pop_back(u32 wait_time_max_ms=0)
{
u32 wait_time_ms = 0;
for(;;)
{
{
JMutexAutoLock lock(m_mutex);
if(m_list.size() > 0)
{
typename core::list<T>::Iterator last = m_list.getLast();
T t = *last;
m_list.erase(last);
return t;
}
if(wait_time_ms >= wait_time_max_ms)
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
// Wait a while before trying again
sleep_ms(10);
wait_time_ms += 10;
}
}
JMutex & getMutex()
{
return m_mutex;
}
core::list<T> & getList()
{
return m_list;
}
protected:
JMutex m_mutex;
core::list<T> m_list;
};
/*
A single worker thread - multiple client threads queue framework.
*/
template<typename Caller, typename Data>
class CallerInfo
{
public:
Caller caller;
Data data;
};
template<typename Key, typename T, typename Caller, typename CallerData>
class GetResult
{
public:
Key key;
T item;
core::list<CallerInfo<Caller, CallerData> > callers;
};
template<typename Key, typename T, typename Caller, typename CallerData>
class ResultQueue: public MutexedQueue< GetResult<Key, T, Caller, CallerData> >
{
};
template<typename Key, typename T, typename Caller, typename CallerData>
class GetRequest
{
public:
GetRequest()
{
dest = NULL;
}
GetRequest(ResultQueue<Key,T, Caller, CallerData> *a_dest)
{
dest = a_dest;
}
GetRequest(ResultQueue<Key,T, Caller, CallerData> *a_dest,
Key a_key)
{
dest = a_dest;
key = a_key;
}
~GetRequest()
{
}
Key key;
ResultQueue<Key, T, Caller, CallerData> *dest;
core::list<CallerInfo<Caller, CallerData> > callers;
};
template<typename Key, typename T, typename Caller, typename CallerData>
class RequestQueue
{
public:
u32 size()
{
return m_queue.size();
}
void add(Key key, Caller caller, CallerData callerdata,
ResultQueue<Key, T, Caller, CallerData> *dest)
{
JMutexAutoLock lock(m_queue.getMutex());
/*
If the caller is already on the list, only update CallerData
*/
for(typename core::list< GetRequest<Key, T, Caller, CallerData> >::Iterator
i = m_queue.getList().begin();
i != m_queue.getList().end(); i++)
{
GetRequest<Key, T, Caller, CallerData> &request = *i;
if(request.key == key)
{
for(typename core::list< CallerInfo<Caller, CallerData> >::Iterator
i = request.callers.begin();
i != request.callers.end(); i++)
{
CallerInfo<Caller, CallerData> &ca = *i;
if(ca.caller == caller)
{
ca.data = callerdata;
return;
}
}
CallerInfo<Caller, CallerData> ca;
ca.caller = caller;
ca.data = callerdata;
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> ca;
ca.caller = caller;
ca.data = callerdata;
request.callers.push_back(ca);
request.dest = dest;
m_queue.getList().push_back(request);
}
GetRequest<Key, T, Caller, CallerData> pop(bool wait_if_empty=false)
{
return m_queue.pop_front(wait_if_empty);
}
private:
MutexedQueue< GetRequest<Key, T, Caller, CallerData> > m_queue;
};
/*
Pseudo-random (VC++ rand() sucks)
*/
int myrand(void);
void mysrand(unsigned seed);
#define MYRAND_MAX 32767
int myrand_range(int min, int max);
/*
Miscellaneous functions
*/
bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 camera_fov, f32 range, f32 *distance_ptr=NULL);
/*
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(Value value)
{
// Check if already exists
if(m_map.find(value) != NULL)
return false;
// Add
m_map.insert(value, 0);
m_list.push_back(value);
return true;
}
Value pop_front()
{
typename core::list<Value>::Iterator i = m_list.begin();
Value value = *i;
m_map.remove(value);
m_list.erase(i);
return value;
}
u32 size()
{
assert(m_list.size() == m_map.size());
return m_list.size();
}
private:
core::map<Value, u8> m_map;
core::list<Value> m_list;
};
#if 1
template<typename Key, typename Value>
class MutexedMap
{
public:
MutexedMap()
{
m_mutex.Init();
assert(m_mutex.IsInitialized());
}
void set(const Key &name, const Value &value)
{
JMutexAutoLock lock(m_mutex);
m_values[name] = value;
}
bool get(const Key &name, Value *result)
{
JMutexAutoLock lock(m_mutex);
typename core::map<Key, Value>::Node *n;
n = m_values.find(name);
if(n == NULL)
return false;
if(result != NULL)
*result = n->getValue();
return true;
}
private:
core::map<Key, Value> m_values;
JMutex m_mutex;
};
#endif
/*
Generates ids for comparable values.
Id=0 is reserved for "no value".
Is fast at:
- Returning value by id (very fast)
- Returning id by value
- Generating a new id for a value
Is not able to:
- Remove an id/value pair (is possible to implement but slow)
*/
template<typename T>
class MutexedIdGenerator
{
public:
MutexedIdGenerator()
{
m_mutex.Init();
assert(m_mutex.IsInitialized());
}
// Returns true if found
bool getValue(u32 id, T &value)
{
if(id == 0)
return false;
JMutexAutoLock lock(m_mutex);
if(m_id_to_value.size() < id)
return false;
value = m_id_to_value[id-1];
return true;
}
// If id exists for value, returns the id.
// Otherwise generates an id for the value.
u32 getId(const T &value)
{
JMutexAutoLock lock(m_mutex);
typename core::map<T, u32>::Node *n;
n = m_value_to_id.find(value);
if(n != NULL)
return n->getValue();
m_id_to_value.push_back(value);
u32 new_id = m_id_to_value.size();
m_value_to_id.insert(value, new_id);
return new_id;
}
private:
JMutex m_mutex;
// Values are stored here at id-1 position (id 1 = [0])
core::array<T> m_id_to_value;
core::map<T, u32> m_value_to_id;
};
/*
Checks if a string contains only supplied characters
*/
inline bool string_allowed(const std::string &s, const std::string &allowed_chars)
{
for(u32 i=0; i<s.size(); i++)
{
bool confirmed = false;
for(u32 j=0; j<allowed_chars.size(); j++)
{
if(s[i] == allowed_chars[j])
{
confirmed = true;
break;
}
}
if(confirmed == false)
return false;
}
return true;
}
/*
Forcefully wraps string into rows using \n
(no word wrap, used for showing paths in gui)
*/
inline std::string wrap_rows(const std::string &from, u32 rowlen)
{
std::string to;
for(u32 i=0; i<from.size(); i++)
{
if(i != 0 && i%rowlen == 0)
to += '\n';
to += from[i];
}
return to;
}
/*
Some helper stuff
*/
#define MYMIN(a,b) ((a)<(b)?(a):(b))
#define MYMAX(a,b) ((a)>(b)?(a):(b))
/*
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 floor(f + 0.5);
}
/*
Returns integer position of node in given floating point position
*/
inline v3s16 floatToInt(v3f p, f32 d)
{
v3s16 p2(
(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);
return p2;
}
/*
Returns floating point position of node in given integer position
*/
inline v3f intToFloat(v3s16 p, f32 d)
{
v3f p2(
(f32)p.X * d,
(f32)p.Y * d,
(f32)p.Z * d
);
return p2;
}
/*
More serialization stuff
*/
// Creates a string with the length as the first two bytes
inline std::string serializeString(const std::string &plain)
{
//assert(plain.size() <= 65535);
if(plain.size() > 65535)
throw SerializationError("String too long for serializeString");
char buf[2];
writeU16((u8*)&buf[0], plain.size());
std::string s;
s.append(buf, 2);
s.append(plain);
return s;
}
// Creates a string with the length as the first two bytes from wide string
inline std::string serializeWideString(const std::wstring &plain)
{
//assert(plain.size() <= 65535);
if(plain.size() > 65535)
throw SerializationError("String too long for serializeString");
char buf[2];
writeU16((u8*)buf, plain.size());
std::string s;
s.append(buf, 2);
for(u32 i=0; i<plain.size(); i++)
{
writeU16((u8*)buf, plain[i]);
s.append(buf, 2);
}
return s;
}
// Reads a string with the length as the first two bytes
inline std::string deSerializeString(std::istream &is)
{
char buf[2];
is.read(buf, 2);
if(is.gcount() != 2)
throw SerializationError("deSerializeString: size not read");
u16 s_size = readU16((u8*)buf);
if(s_size == 0)
return "";
Buffer<char> buf2(s_size);
is.read(&buf2[0], s_size);
std::string s;
s.reserve(s_size);
s.append(&buf2[0], s_size);
return s;
}
// Reads a wide string with the length as the first two bytes
inline std::wstring deSerializeWideString(std::istream &is)
{
char buf[2];
is.read(buf, 2);
if(is.gcount() != 2)
throw SerializationError("deSerializeString: size not read");
u16 s_size = readU16((u8*)buf);
if(s_size == 0)
return L"";
std::wstring s;
s.reserve(s_size);
for(u32 i=0; i<s_size; i++)
{
is.read(&buf[0], 2);
wchar_t c16 = readU16((u8*)buf);
s.append(&c16, 1);
}
return s;
}
// Creates a string with the length as the first four bytes
inline std::string serializeLongString(const std::string &plain)
{
char buf[4];
writeU32((u8*)&buf[0], plain.size());
std::string s;
s.append(buf, 4);
s.append(plain);
return s;
}
// Reads a string with the length as the first four bytes
inline std::string deSerializeLongString(std::istream &is)
{
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 "";
Buffer<char> buf2(s_size);
is.read(&buf2[0], s_size);
std::string s;
s.reserve(s_size);
s.append(&buf2[0], s_size);
return s;
}
// 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);
//
// Random helper. Usually d=BS
inline core::aabbox3d<f32> getNodeBox(v3s16 p, float d)
{
return core::aabbox3d<f32>(
(float)p.X * d - 0.5*d,
(float)p.Y * d - 0.5*d,
(float)p.Z * d - 0.5*d,
(float)p.X * d + 0.5*d,
(float)p.Y * d + 0.5*d,
(float)p.Z * d + 0.5*d
);
}
class IntervalLimiter
{
public:
IntervalLimiter():
m_accumulator(0)
{
}
/*
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;
}
protected:
float m_accumulator;
};
/*
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;
}
}
}
std::string translatePassword(std::string playername, std::wstring password);
enum PointedThingType
{
POINTEDTHING_NOTHING,
POINTEDTHING_NODE,
POINTEDTHING_OBJECT
};
struct PointedThing
{
PointedThingType type;
v3s16 node_undersurface;
v3s16 node_abovesurface;
s16 object_id;
PointedThing();
std::string dump() const;
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
bool operator==(const PointedThing &pt2) const;
bool operator!=(const PointedThing &pt2) const;
};
#endif