folders
This commit is contained in:
534
libs/main/serialization/pibinarystream.h
Normal file
534
libs/main/serialization/pibinarystream.h
Normal file
@@ -0,0 +1,534 @@
|
||||
/*! \file pibinarystream.h
|
||||
* \ingroup Serialization
|
||||
* \~\brief
|
||||
* \~english Binary serialization interface
|
||||
* \~russian Интерфейс бинарной сериализации
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Binary serialization interface
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIBINARYSTREAM_H
|
||||
#define PIBINARYSTREAM_H
|
||||
|
||||
#include "pimemoryblock.h"
|
||||
#include "pibitarray.h"
|
||||
#include "pimap.h"
|
||||
#include "pivector2d.h"
|
||||
|
||||
#define PIP_BINARY_STREAM
|
||||
|
||||
#define BINARY_STREAM_FRIEND(T) \
|
||||
template<typename P> friend PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const T & v); \
|
||||
template<typename P> friend PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, T & v);
|
||||
#define BINARY_STREAM_WRITE(T) \
|
||||
template<typename P> inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const T & v)
|
||||
#define BINARY_STREAM_READ(T) \
|
||||
template<typename P> inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, T & v)
|
||||
|
||||
|
||||
//! \ingroup Serialization
|
||||
//! \~\brief
|
||||
//! \~english Binary serialization interface.
|
||||
//! \~russian Интерфейс бинарной сериализации.
|
||||
//! \~\details
|
||||
//! \~english In your class you should implement this methods:
|
||||
//! \~russian В производном классе вы должны реализовать следующие методы:
|
||||
//! \~\code
|
||||
//! bool binaryStreamAppendImp (const void * d, size_t s);
|
||||
//! bool binaryStreamTakeImp (void * d, size_t s);
|
||||
//! ssize_t binaryStreamSizeImp () const;
|
||||
//! \endcode
|
||||
//! \~english Function binaryStreamSizeImp should return -1 if size unknown.
|
||||
//! \~russian Функция binaryStreamSizeImp должна возвращать -1 если нет информации о размере.
|
||||
//! \~english See details \ref iostream.
|
||||
//! \~russian Подробнее \ref iostream.
|
||||
template<typename P>
|
||||
class PIBinaryStream {
|
||||
public:
|
||||
//! \~russian Записать данные
|
||||
bool binaryStreamAppend(const void * d, size_t s) {
|
||||
if (!static_cast<P*>(this)->binaryStreamAppendImp(d, s)) {
|
||||
return false;
|
||||
printf("[PIBinaryStream] binaryStreamAppend() error\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//! \~russian Прочитать данные
|
||||
bool binaryStreamTake(void * d, size_t s) {
|
||||
if (!static_cast<P*>(this)->binaryStreamTakeImp(d, s)) {
|
||||
return false;
|
||||
printf("[PIBinaryStream] binaryStreamTake() error\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//! \~russian Узнать оставшийся размер
|
||||
//!\~\details
|
||||
//!\~russian Возвращает -1 если нет информации о размере
|
||||
ssize_t binaryStreamSize() const {
|
||||
return static_cast<const P*>(this)->binaryStreamSizeImp();
|
||||
}
|
||||
|
||||
//! \~russian Записать данные
|
||||
template<typename T>
|
||||
void binaryStreamAppend(T v) {binaryStreamAppend(&v, sizeof(v));}
|
||||
|
||||
//! \~russian Прочитать int
|
||||
int binaryStreamTakeInt() {
|
||||
int r = 0;
|
||||
binaryStreamTake(&r, sizeof(r));
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// helper class to detect default operators
|
||||
template<typename P>
|
||||
class PIBinaryStreamTrivialRef {
|
||||
public:
|
||||
PIBinaryStreamTrivialRef(PIBinaryStream<P> & s): p(s) {}
|
||||
PIBinaryStream<P> & p;
|
||||
};
|
||||
|
||||
|
||||
template<typename P, typename T> inline PIBinaryStream<P> & operator <<(PIBinaryStreamTrivialRef<P> s, const T & v) {
|
||||
s.p << v;
|
||||
return s.p;
|
||||
}
|
||||
template<typename P, typename T> inline PIBinaryStream<P> & operator >>(PIBinaryStreamTrivialRef<P> s, T & v) {
|
||||
s.p >> v;
|
||||
return s.p;
|
||||
}
|
||||
|
||||
template<typename P> inline PIBinaryStream<P> & operator <<(PIBinaryStreamTrivialRef<P> s, const PIMemoryBlock v) {
|
||||
s.p << v;
|
||||
return s.p;
|
||||
}
|
||||
template<typename P> inline PIBinaryStream<P> & operator >>(PIBinaryStreamTrivialRef<P> s, PIMemoryBlock v) {
|
||||
s.p >> v;
|
||||
return s.p;
|
||||
}
|
||||
|
||||
|
||||
// specify types
|
||||
template<typename P> inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const bool v) {
|
||||
s.binaryStreamAppend((uchar)v);
|
||||
return s;
|
||||
}
|
||||
template<typename P> inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, bool & v) {
|
||||
uchar c;
|
||||
s.binaryStreamTake(&c, sizeof(c));
|
||||
v = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename P> inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIMemoryBlock v) {
|
||||
s.binaryStreamAppend(v.data(), v.size());
|
||||
return s;
|
||||
}
|
||||
template<typename P> inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIMemoryBlock v) {
|
||||
s.binaryStreamTake(v.data(), v.size());
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// store simple types
|
||||
|
||||
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const T & v) {
|
||||
//piCout << "<< enum";
|
||||
s.binaryStreamAppend((int)v);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if<!std::is_enum<T>::value, int>::type = 0,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStreamTrivialRef<P> operator <<(PIBinaryStream<P> & s, const T & v) {
|
||||
s.binaryStreamAppend(&v, sizeof(v));
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Store operator for PIVector of any trivial copyable type
|
||||
//! \~russian Оператор сохранения для PIVector тривиальных типов
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if< std::is_same<decltype(std::declval<PIBinaryStream<P>&>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIVector<T> & v) {
|
||||
//piCout << "<< vector trivial default";
|
||||
s.binaryStreamAppend((int)v.size());
|
||||
s.binaryStreamAppend(v.data(), v.size() * sizeof(T));
|
||||
return s;
|
||||
}
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if<!std::is_same<decltype(std::declval<PIBinaryStream<P>&>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIVector<T> & v) {
|
||||
//piCout << "<< vector trivial custom";
|
||||
s.binaryStreamAppend((int)v.size());
|
||||
for (size_t i = 0; i < v.size(); ++i) s << v[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Store operator for PIDeque of any trivial copyable type
|
||||
//! \~russian Оператор сохранения для PIDeque тривиальных типов
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if< std::is_same<decltype(std::declval<PIBinaryStream<P>&>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIDeque<T> & v) {
|
||||
//piCout << "<< deque trivial default";
|
||||
s.binaryStreamAppend((int)v.size());
|
||||
s.binaryStreamAppend(v.data(), v.size() * sizeof(T));
|
||||
return s;
|
||||
}
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if<!std::is_same<decltype(std::declval<PIBinaryStream<P>&>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIDeque<T> & v) {
|
||||
//piCout << "<< deque trivial custom";
|
||||
s.binaryStreamAppend((int)v.size());
|
||||
for (size_t i = 0; i < v.size(); ++i) s << v[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Store operator for PIVector2D of any trivial copyable type
|
||||
//! \~russian Оператор сохранения для PIVector2D тривиальных типов
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if< std::is_same<decltype(std::declval<PIBinaryStream<P>&>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIVector2D<T> & v) {
|
||||
//piCout << "<< vector2d trivial default";
|
||||
s.binaryStreamAppend((int)v.rows());
|
||||
s.binaryStreamAppend((int)v.cols());
|
||||
s.binaryStreamAppend(v.data(), v.size() * sizeof(T));
|
||||
return s;
|
||||
}
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if<!std::is_same<decltype(std::declval<PIBinaryStream<P>&>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIVector2D<T> & v) {
|
||||
//piCout << "<< vector2d trivial custom";
|
||||
s.binaryStreamAppend((int)v.rows());
|
||||
s.binaryStreamAppend((int)v.cols());
|
||||
s << v.toPlainVector();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Store operator
|
||||
//! \~russian Оператор сохранения
|
||||
template<typename P>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIBitArray & v) {s << v.size_ << v.data_; return s;}
|
||||
|
||||
|
||||
//! \~english Store operator
|
||||
//! \~russian Оператор сохранения
|
||||
template<typename P, typename Type0, typename Type1>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIPair<Type0, Type1> & v) {s << v.first << v.second; return s;}
|
||||
|
||||
|
||||
|
||||
|
||||
// restore simple types
|
||||
|
||||
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, T & v) {
|
||||
//piCout << ">> enum";
|
||||
v = (T)s.binaryStreamTakeInt();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if<!std::is_enum<T>::value, int>::type = 0,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStreamTrivialRef<P> operator >>(PIBinaryStream<P> & s, T & v) {
|
||||
if (!s.binaryStreamTake(&v, sizeof(v))) {
|
||||
printf("error with %s\n", __PIP_TYPENAME__(T));
|
||||
assert(false);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Restore operator for PIVector of any trivial copyable type
|
||||
//! \~russian Оператор извлечения для PIVector тривиальных типов
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if< std::is_same<decltype(std::declval<PIBinaryStream<P>&>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIVector<T> & v) {
|
||||
//piCout << ">> vector trivial default";
|
||||
int sz = s.binaryStreamTakeInt();
|
||||
v._resizeRaw(sz);
|
||||
if (!s.binaryStreamTake(v.data(), sz * sizeof(T))) {
|
||||
printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T));
|
||||
assert(false);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if<!std::is_same<decltype(std::declval<PIBinaryStream<P>&>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIVector<T> & v) {
|
||||
//piCout << ">> vector trivial custom";
|
||||
int sz = s.binaryStreamTakeInt();
|
||||
v._resizeRaw(sz);
|
||||
for (int i = 0; i < sz; ++i) s >> v[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Restore operator for PIDeque of any trivial copyable type
|
||||
//! \~russian Оператор извлечения для PIDeque тривиальных типов
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if< std::is_same<decltype(std::declval<PIBinaryStream<P>&>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIDeque<T> & v) {
|
||||
//piCout << ">> deque trivial default";
|
||||
int sz = s.binaryStreamTakeInt();
|
||||
v._resizeRaw(sz);
|
||||
if (!s.binaryStreamTake(v.data(), sz * sizeof(T))) {
|
||||
printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T));
|
||||
assert(false);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if<!std::is_same<decltype(std::declval<PIBinaryStream<P>&>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIDeque<T> & v) {
|
||||
//piCout << ">> deque trivial custom";
|
||||
int sz = s.binaryStreamTakeInt();
|
||||
v._resizeRaw(sz);
|
||||
for (int i = 0; i < sz; ++i) s >> v[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Restore operator for PIVector2D of any trivial copyable type
|
||||
//! \~russian Оператор извлечения для PIVector2D тривиальных типов
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if< std::is_same<decltype(std::declval<PIBinaryStream<P>&>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIVector2D<T> & v) {
|
||||
//piCout << ">> vector2d trivial default";
|
||||
int r, c;
|
||||
r = s.binaryStreamTakeInt();
|
||||
c = s.binaryStreamTakeInt();
|
||||
v._resizeRaw(r, c);
|
||||
if (!s.binaryStreamTake(v.data(), v.size() * sizeof(T))) {
|
||||
printf("error with PIVector2D<%s>\n", __PIP_TYPENAME__(T));
|
||||
assert(false);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
template<typename P, typename T,
|
||||
typename std::enable_if< std::is_trivially_copyable<T>::value, int>::type = 0,
|
||||
typename std::enable_if<!std::is_same<decltype(std::declval<PIBinaryStream<P>&>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIVector2D<T> & v) {
|
||||
//piCout << ">> vector2d trivial custom";
|
||||
int r, c;
|
||||
PIVector<T> tmp;
|
||||
r = s.binaryStreamTakeInt();
|
||||
c = s.binaryStreamTakeInt();
|
||||
s >> tmp;
|
||||
v = PIVector2D<T>(r, c, tmp);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Restore operator
|
||||
//! \~russian Оператор извлечения
|
||||
template<typename P>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIBitArray & v) {s >> v.size_ >> v.data_; return s;}
|
||||
|
||||
|
||||
//! \~english Restore operator
|
||||
//! \~russian Оператор извлечения
|
||||
template<typename P, typename Type0, typename Type1>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIPair<Type0, Type1> & v) {s >> v.first >> v.second; return s;}
|
||||
|
||||
|
||||
|
||||
|
||||
// store complex types
|
||||
|
||||
|
||||
//! \~english Store operator for PIVector of any compound type
|
||||
//! \~russian Оператор сохранения для PIVector сложных типов
|
||||
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIVector<T> & v) {
|
||||
//piCout << "<< vector complex";
|
||||
s.binaryStreamAppend(int(v.size_s()));
|
||||
for (size_t i = 0; i < v.size(); ++i) s << v[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Store operator for PIDeque of any compound type
|
||||
//! \~russian Оператор сохранения для PIDeque сложных типов
|
||||
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIDeque<T> & v) {
|
||||
//piCout << "<< deque complex";
|
||||
s.binaryStreamAppend(int(v.size_s()));
|
||||
for (size_t i = 0; i < v.size(); ++i) s << v[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Store operator for PIVector2D of any compound type
|
||||
//! \~russian Оператор сохранения для PIVector2D сложных типов
|
||||
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIVector2D<T> & v) {
|
||||
//piCout << "<< vector2d complex";
|
||||
s.binaryStreamAppend(int(v.rows()));
|
||||
s.binaryStreamAppend(int(v.cols()));
|
||||
s << v.toPlainVector();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// restore complex types
|
||||
|
||||
|
||||
//! \~english Restore operator for PIVector of any compound type
|
||||
//! \~russian Оператор извлечения для PIVector сложных типов
|
||||
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIVector<T> & v) {
|
||||
//piCout << ">> vector complex";
|
||||
/*if (s.size_s() < 4) {
|
||||
printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T));
|
||||
assert(s.size_s() >= 4);
|
||||
}*/
|
||||
v.resize(s.binaryStreamTakeInt());
|
||||
for (size_t i = 0; i < v.size(); ++i) s >> v[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Restore operator for PIDeque of any compound type
|
||||
//! \~russian Оператор извлечения для PIDeque сложных типов
|
||||
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIDeque<T> & v) {
|
||||
//piCout << ">> deque complex";
|
||||
/*if (s.size_s() < 4) {
|
||||
printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T));
|
||||
assert(s.size_s() >= 4);
|
||||
}*/
|
||||
v.resize(s.binaryStreamTakeInt());
|
||||
for (size_t i = 0; i < v.size(); ++i) s >> v[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Restore operator for PIVector2D of any compound type
|
||||
//! \~russian Оператор извлечения для PIVector2D сложных типов
|
||||
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIVector2D<T> & v) {
|
||||
//piCout << ">> vector2d complex";
|
||||
/*if (s.size_s() < 8) {
|
||||
printf("error with PIVecto2Dr<%s>\n", __PIP_TYPENAME__(T));
|
||||
assert(s.size_s() >= 8);
|
||||
}*/
|
||||
int r, c;
|
||||
PIVector<T> tmp;
|
||||
r = s.binaryStreamTakeInt();
|
||||
c = s.binaryStreamTakeInt();
|
||||
s >> tmp;
|
||||
v = PIVector2D<T>(r, c, tmp);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// other types
|
||||
|
||||
|
||||
//! \~english Store operator
|
||||
//! \~russian Оператор сохранения
|
||||
template <typename P, typename Key, typename T>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const PIMap<Key, T> & v) {
|
||||
s.binaryStreamAppend((int)v.pim_index.size_s());
|
||||
for (uint i = 0; i < v.size(); ++i) {
|
||||
s.binaryStreamAppend((int)v.pim_index[i].index);
|
||||
s << v.pim_index[i].key;
|
||||
}
|
||||
s << v.pim_content;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \~english Restore operator
|
||||
//! \~russian Оператор извлечения
|
||||
template <typename P, typename Key, typename T>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, PIMap<Key, T> & v) {
|
||||
/*if (s.size_s() < 4) {
|
||||
printf("error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T));
|
||||
assert(s.size_s() >= 4);
|
||||
}*/
|
||||
int sz = s.binaryStreamTakeInt(); v.pim_index.resize(sz);
|
||||
int ind = 0;
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
ind = s.binaryStreamTakeInt();
|
||||
s >> v.pim_index[i].key;
|
||||
v.pim_index[i].index = ind;
|
||||
}
|
||||
s >> v.pim_content;
|
||||
if (v.pim_content.size_s() != v.pim_index.size_s()) {
|
||||
piCout << "Warning: loaded invalid PIMap, clear";
|
||||
v.clear();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// non-defined complex types
|
||||
|
||||
|
||||
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator <<(PIBinaryStream<P> & s, const T & ) {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "[PIBinaryStream] Error: using undeclared operator << for complex type!");
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
|
||||
inline PIBinaryStream<P> & operator >>(PIBinaryStream<P> & s, T & ) {
|
||||
static_assert(std::is_trivially_copyable<T>::value, "[PIBinaryStream] Error: using undeclared operator >> for complex type!");
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
263
libs/main/serialization/pichunkstream.cpp
Normal file
263
libs/main/serialization/pichunkstream.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Binary markup serializator
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pichunkstream.h"
|
||||
|
||||
//! \class PIChunkStream pichunkstream.h
|
||||
//! \details
|
||||
//! \~english \section PIChunkStream_sec0 Synopsis
|
||||
//! \~russian \section PIChunkStream_sec0 Краткий обзор
|
||||
//! \~english
|
||||
//! This class provides very handly mechanism to store and restore values to and from
|
||||
//! \a PIByteArray. The main advantage of using this class is that your binary data
|
||||
//! become independent from order and collection of your values.
|
||||
//!
|
||||
//! \~russian
|
||||
//! Этот класс предоставляет очень удобный механизм для сохранения и извлечения значений
|
||||
//! в/из \a PIByteArray. Главным плюсом является то, что данные не будут зависеть от порядка
|
||||
//! и наличия значений.
|
||||
//!
|
||||
//! \~english \section PIChunkStream_sec1 Mechanism
|
||||
//! \~russian \section PIChunkStream_sec1 Механизм
|
||||
//! \~english
|
||||
//! %PIChunkStream works with items called "chunk". Chunk is an ID, size and any value that
|
||||
//! can be stored and restored to/from %PIChunkStream with stream operators << and >>.
|
||||
//!
|
||||
//! To construct %PIChunkStream for writing data use any non-default constructor. Empty constructor
|
||||
//! creates internal buffer that can be accessed by function \a data().
|
||||
//! Non-empty constructor works with given byte array.
|
||||
//!
|
||||
//! To read chunks from byte array use function \a read() that returns ID of
|
||||
//! readed chunk. Then you can get value of this chunk with functions \a getData() or \a get(),
|
||||
//! but you should definitely know type of this value. You can read from byte array
|
||||
//! while \a atEnd() if false.
|
||||
//!
|
||||
//! \~russian
|
||||
//! %PIChunkStream работает с элементами под названием "чанк". Чанк имеет ID, размер и значение,
|
||||
//! и может быть записан или прочитан в/из %PIChunkStream с помощью операторов << и >>.
|
||||
//!
|
||||
//! Для создания потока на запись используется любой не-умолчальный конструктор. Пустой конструктор
|
||||
//! создает внутренний буфер, который можно получить с помощью метода \a data().
|
||||
//! Непустой конструктор работает с переданным байтовым массивом.
|
||||
//!
|
||||
//! Для чтения чанков из байтового массива используется метод \a read(), который возвращает
|
||||
//! ID прочитанного чанка. Получить значение этого чанка далее можно с помощью методов \a getData() или get(),
|
||||
//! но тип значения должен быть известен. Читать из потока можно пока метод \a atEnd() возвращает ложь.
|
||||
//!
|
||||
//! \~english \section PIChunkStream_sec2 Examples
|
||||
//! \~russian \section PIChunkStream_sec2 Пример
|
||||
//!
|
||||
//! \~english Using simple operator and cascade serialization:
|
||||
//! \~russian Использование простого оператора и каскадная сериализация:
|
||||
//!
|
||||
//! \~english Prepare your structs to work with %PIChunkStream:
|
||||
//! \~russian Подготовка своей структуры для работы с %PIChunkStream:
|
||||
//! \~\snippet pichunkstream.cpp struct
|
||||
//! \~english Old-style writing to %PIChunkStream:
|
||||
//! \~russian Старый стиль использования %PIChunkStream:
|
||||
//! \~\snippet pichunkstream.cpp write
|
||||
//! \~english Fastest reading from %PIChunkStream:
|
||||
//! \~russian Самое быстрое чтение из %PIChunkStream:
|
||||
//! \~\snippet pichunkstream.cpp read
|
||||
//!
|
||||
//! \~english And next code show how to serialize your struct with %PIChunkStream:
|
||||
//! \~russian Следующий код показывает, как сериализовать свою структуру в %PIChunkStream:
|
||||
//! \~\snippet pichunkstream.cpp write_new
|
||||
//!
|
||||
//! \~english ... and deserialize:
|
||||
//! \~russian ... и десериализовать:
|
||||
//! \~\snippet pichunkstream.cpp read_new
|
||||
//!
|
||||
|
||||
|
||||
void PIChunkStream::setSource(const PIByteArray & data) {
|
||||
data_ = const_cast<PIByteArray*>(&data);
|
||||
_init();
|
||||
}
|
||||
|
||||
|
||||
void PIChunkStream::setSource(PIByteArray * data) {
|
||||
data_ = (data ? data : &tmp_data);
|
||||
_init();
|
||||
}
|
||||
|
||||
|
||||
PIByteArray PIChunkStream::data() const {
|
||||
if (first_byte_taken) {
|
||||
PIByteArray ret(*data_);
|
||||
ret.prepend((uchar)(0x80 | version_));
|
||||
return ret;
|
||||
}
|
||||
return *data_;
|
||||
}
|
||||
|
||||
|
||||
int PIChunkStream::read() {
|
||||
switch (version_) {
|
||||
case Version_1:
|
||||
(*data_) >> last_id >> last_data;
|
||||
break;
|
||||
case Version_2:
|
||||
last_id = readVInt(*data_);
|
||||
last_data.resize(readVInt(*data_));
|
||||
//piCout << last_id << last_data.size();
|
||||
(*data_) >> PIMemoryBlock(last_data.data(), last_data.size_s());
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return last_id;
|
||||
}
|
||||
|
||||
|
||||
int PIChunkStream::peekVInt(Version version_, uchar * data_, int sz, uint & ret) {
|
||||
switch (version_) {
|
||||
case Version_1:
|
||||
memcpy(&ret, data_, 4);
|
||||
return 4;
|
||||
case Version_2: {
|
||||
PIByteArray hdr(data_, piMini(4, sz));
|
||||
hdr.resize(4);
|
||||
uchar hsz = 0;
|
||||
ret = readVInt(hdr, &hsz);
|
||||
return hsz;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void PIChunkStream::replaceChunk(int id, const PIByteArray & v) {
|
||||
if (!data_map.contains(id)) return;
|
||||
auto & pos(data_map[id]);
|
||||
PIByteArray nsba;
|
||||
writeVInt(nsba, v.size());
|
||||
int size_mod = (v.size_s() + nsba.size_s()) - (pos.length + pos.size_bytes);
|
||||
pos.length = v.size_s();
|
||||
if (size_mod != 0) {
|
||||
auto it = data_map.makeIterator();
|
||||
while (it.next()) {
|
||||
if (it.value().start > pos.start) {
|
||||
it.value().start += size_mod;
|
||||
}
|
||||
}
|
||||
if (size_mod > 0) {
|
||||
data_->insert(pos.start, PIByteArray(size_mod));
|
||||
} else {
|
||||
data_->remove(pos.start, -size_mod);
|
||||
}
|
||||
}
|
||||
memcpy(data_->data(pos.start - pos.size_bytes), nsba.data(), nsba.size());
|
||||
pos.start += nsba.size_s() - pos.size_bytes;
|
||||
memcpy(data_->data(pos.start), v.data(), pos.length);
|
||||
}
|
||||
|
||||
|
||||
void PIChunkStream::readAll() {
|
||||
data_map.clear();
|
||||
if (!data_) return;
|
||||
int pos = 0, sz = data_->size_s(), hsz = 0;
|
||||
uint csz = 0, cid = 0;
|
||||
while (pos < sz) {
|
||||
pos += peekVInt((Version)version_, data_->data(pos), data_->size_s() - pos, cid);
|
||||
hsz = peekVInt((Version)version_, data_->data(pos), data_->size_s() - pos, csz);
|
||||
pos += hsz;
|
||||
data_map[cid] = CacheEntry(pos, csz, hsz);
|
||||
pos += csz;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIChunkStream::~PIChunkStream() {
|
||||
}
|
||||
|
||||
|
||||
bool PIChunkStream::extract(PIByteArray & data, bool read_all) {
|
||||
if (data.size_s() < 4) return false;
|
||||
data >> tmp_data;
|
||||
if (tmp_data.size_s() < 4) return false;
|
||||
data_ = &tmp_data;
|
||||
_init();
|
||||
if (read_all) readAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIChunkStream::_init() {
|
||||
first_byte_taken = false;
|
||||
last_id = -1;
|
||||
last_data.clear();
|
||||
if (!data_->isEmpty()) {
|
||||
uchar v = data_->at(0);
|
||||
if ((v & 0x80) == 0x80) {
|
||||
v &= 0x7f;
|
||||
switch (v) {
|
||||
case 2:
|
||||
version_ = (uchar)Version_2;
|
||||
data_->pop_front();
|
||||
first_byte_taken = true;
|
||||
break;
|
||||
default:
|
||||
version_ = Version_1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
version_ = Version_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint PIChunkStream::readVInt(PIByteArray & s, uchar * bytes_cnt) {
|
||||
if (s.isEmpty()) return 0;
|
||||
uchar bytes[4];
|
||||
uchar abc;
|
||||
s >> bytes[0];
|
||||
for (abc = 0; abc < 3; ++abc) {
|
||||
uchar mask = (0x80 >> abc);
|
||||
if ((bytes[0] & mask) == mask) {
|
||||
bytes[0] &= (mask - 1);
|
||||
s >> bytes[abc + 1];
|
||||
} else break;
|
||||
}
|
||||
if (bytes_cnt) *bytes_cnt = (abc + 1);
|
||||
uint ret = 0;
|
||||
for (int i = 0; i <= abc; ++i) {
|
||||
ret += (bytes[i] << (8 * ((int)abc - i)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PIChunkStream::writeVInt(PIByteArray & s, uint val) {
|
||||
if (val > 0xfffffff) return;
|
||||
if (val <= 0x7f) {
|
||||
s << uchar(val);
|
||||
return;
|
||||
}
|
||||
if (val <= 0x3fff) {
|
||||
s << uchar((val >> 8) | 0x80) << uchar(val & 0xff);
|
||||
return;
|
||||
}
|
||||
if (val <= 0x1fffff) {
|
||||
s << uchar((val >> 16) | 0xc0) << uchar((val >> 8) & 0xff) << uchar(val & 0xff);
|
||||
return;
|
||||
}
|
||||
s << uchar((val >> 24) | 0xe0) << uchar((val >> 16) & 0xff) << uchar((val >> 8) & 0xff) << uchar(val & 0xff);
|
||||
}
|
||||
220
libs/main/serialization/pichunkstream.h
Normal file
220
libs/main/serialization/pichunkstream.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/*! \file pichunkstream.h
|
||||
* \ingroup Serialization
|
||||
* \~\brief
|
||||
* \~english Binary markup de/serializator stream
|
||||
* \~russian Бинарный поток для де/сериализации с разметкой
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Binary markup serializator
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICHUNKSTREAM_H
|
||||
#define PICHUNKSTREAM_H
|
||||
|
||||
#include "pibytearray.h"
|
||||
|
||||
|
||||
//! \ingroup Serialization
|
||||
//! \~\brief
|
||||
//! \~english Class for binary de/serialization.
|
||||
//! \~russian Класс для бинарной де/сериализации.
|
||||
class PIP_EXPORT PIChunkStream
|
||||
{
|
||||
public:
|
||||
|
||||
//! \~english
|
||||
//! Version of data packing. Read-access %PIChunkStream automatic detect version, but write-access
|
||||
//! %PIChunkStream by default write in new version, be careful!
|
||||
//! \~russian
|
||||
//! Версия хранения данных. %PIChunkStream на чтение автоматически определяет версию, но для записи
|
||||
//! использует по умолчанию новую, осторожно!
|
||||
enum Version {
|
||||
Version_1 /*! \~english First, old version \~russian Первая, старая версия */,
|
||||
Version_2 /*! \~english Second, more optimized version \~russian Вторая, более оптимизированная версия */ = 2,
|
||||
};
|
||||
|
||||
//! \~english Contructs stream for read from "data"
|
||||
//! \~russian Создает поток на чтение из "data"
|
||||
PIChunkStream(const PIByteArray & data): version_(Version_2) {setSource(data);}
|
||||
|
||||
//! \~english Contructs stream for read or write to/from "data", or empty stream for write if "data" = 0
|
||||
//! \~russian Создает поток на чтение или запись из/в "data", или пустой поток на запись если "data" = 0
|
||||
PIChunkStream(PIByteArray * data = 0, Version v = Version_2): version_(v) {setSource(data);}
|
||||
|
||||
//! \~english Contructs empty stream for write with version \"v\"
|
||||
//! \~russian Создает пустой поток на запись с версией \"v\"
|
||||
PIChunkStream(Version v): version_(v) {setSource(0);}
|
||||
|
||||
~PIChunkStream();
|
||||
|
||||
template <typename T>
|
||||
struct Chunk {
|
||||
Chunk(int i, const T & d): id(i), data(d) {}
|
||||
int id;
|
||||
T data;
|
||||
};
|
||||
template <typename T>
|
||||
struct ChunkConst {
|
||||
ChunkConst(int i, const T & d): id(i), data(d) {}
|
||||
int id;
|
||||
const T & data;
|
||||
};
|
||||
|
||||
//! \~english Returns chunk with ID "id" and value "data" for write to stream
|
||||
//! \~russian Возвращает чанк с ID "id" и значением "data" для записи в поток
|
||||
template <typename T> static ChunkConst<T> chunk(int id, const T & data) {return ChunkConst<T>(id, data);}
|
||||
|
||||
//! \~english Add to this stream chunk with ID "id" and value "data"
|
||||
//! \~russian Добавляет в этот поток чанк с ID "id" и значением "data"
|
||||
template <typename T> PIChunkStream & add(int id, const T & data) {*this << ChunkConst<T>(id, data); return *this;}
|
||||
|
||||
//! \~english
|
||||
//! Extract %PIByteArray from "data" and set it current stream.
|
||||
//! If "read_all" then call \a readAll() after extract.
|
||||
//! Returns if has data to read.
|
||||
//! \~russian
|
||||
//! Извлекает %PIByteArray из "data" и инициализирует им поток.
|
||||
//! Если указан "read_all", то вызывает \a readAll() после инициализации.
|
||||
//! Возвращает если ли данные для чтения.
|
||||
bool extract(PIByteArray & data, bool read_all = false);
|
||||
|
||||
void setSource(const PIByteArray & data);
|
||||
void setSource(PIByteArray * data);
|
||||
|
||||
//! \~english Returns internal buffer with written data
|
||||
//! \~russian Возвращает внутренний буфер с записанными данными
|
||||
PIByteArray data() const;
|
||||
|
||||
//! \~english Returns if there is end of stream
|
||||
//! \~russian Возвращает достигнут ли конец потока
|
||||
bool atEnd() const {return data_->size_s() <= 1;}
|
||||
|
||||
//! \~english Returns stream version
|
||||
//! \~russian Возвращает версию потока
|
||||
Version version() const {return (Version)version_;}
|
||||
|
||||
|
||||
//! \~english Read one chunk from stream and returns its ID
|
||||
//! \~russian Читает один чанк из потока и возвращает его ID
|
||||
int read();
|
||||
|
||||
//! \~english Read all chunks from stream. This function just index input data
|
||||
//! \~russian Читает все чанки из потока. Данный метод лишь индексирует данные
|
||||
void readAll();
|
||||
|
||||
//! \~english Returns last readed chunk ID
|
||||
//! \~russian Возвращает ID последнего прочитанного чанка
|
||||
int getID() {return last_id;}
|
||||
|
||||
//! \~english Returns value of last readed chunk
|
||||
//! \~russian Возвращает значение последнего прочитанного чанка
|
||||
template <typename T>
|
||||
T getData() const {T ret{}; PIByteArray s(last_data); s >> ret; return ret;}
|
||||
|
||||
//! \~english Place value of last readed chunk into \"v\"
|
||||
//! \~russian Записывает значение последнего прочитанного чанка в \"v\"
|
||||
template <typename T>
|
||||
void get(T & v) const {v = getData<T>();}
|
||||
|
||||
//! \~english Place value of chunk with ID \"id\" into \"v\". You should call \a readAll() before using this function!
|
||||
//! \~russian Записывает значение чанка с ID \"id\" в \"v\". Необходимо вызвать \a readAll() перед использованием этого метода!
|
||||
template <typename T>
|
||||
const PIChunkStream & get(int id, T & v) const {
|
||||
CacheEntry pos = data_map.value(id);
|
||||
if (pos.start < 0 || pos.length == 0) return *this;
|
||||
PIByteArray ba(data_->data(pos.start), pos.length);
|
||||
if (!ba.isEmpty())
|
||||
ba >> v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \~english Replace value of chunk with ID \"id\" to \"v\". You should call \a readAll() before using this function!
|
||||
//! \~russian Заменяет значение чанка с ID \"id\" на \"v\". Необходимо вызвать \a readAll() перед использованием этого метода!
|
||||
template <typename T>
|
||||
PIChunkStream & set(int id, const T & v) {
|
||||
PIByteArray ba;
|
||||
ba << v;
|
||||
replaceChunk(id, ba);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void _init();
|
||||
|
||||
struct CacheEntry {
|
||||
CacheEntry(int s = 0, int l = 0, int b = 0): start(s), length(l), size_bytes(b) {}
|
||||
int start;
|
||||
int length;
|
||||
int size_bytes;
|
||||
};
|
||||
|
||||
static uint readVInt(PIByteArray & s, uchar * bytes = 0);
|
||||
static void writeVInt(PIByteArray & s, uint val);
|
||||
static int peekVInt(Version version_, uchar * data_, int sz, uint & ret);
|
||||
void replaceChunk(int id, const PIByteArray & v);
|
||||
|
||||
int last_id;
|
||||
uchar version_;
|
||||
mutable PIByteArray * data_, last_data, tmp_data;
|
||||
PIMap<int, CacheEntry> data_map;
|
||||
bool first_byte_taken;
|
||||
|
||||
template <typename T> friend PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::Chunk<T> & c);
|
||||
template <typename T> friend PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::ChunkConst<T> & c);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::Chunk<T> & c) {
|
||||
PIByteArray ba;
|
||||
ba << c.data;
|
||||
switch (s.version_) {
|
||||
case PIChunkStream::Version_1:
|
||||
(*(s.data_)) << c.id << ba;
|
||||
break;
|
||||
case PIChunkStream::Version_2:
|
||||
if (s.data_->isEmpty())
|
||||
(*(s.data_)) << uchar(uchar(s.version_) | 0x80);
|
||||
PIChunkStream::writeVInt(*(s.data_), c.id);
|
||||
PIChunkStream::writeVInt(*(s.data_), ba.size());
|
||||
s.data_->append(ba);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
template <typename T>
|
||||
PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::ChunkConst<T> & c) {
|
||||
PIByteArray ba;
|
||||
ba << c.data;
|
||||
switch (s.version_) {
|
||||
case PIChunkStream::Version_1:
|
||||
(*(s.data_)) << c.id << ba;
|
||||
break;
|
||||
case PIChunkStream::Version_2:
|
||||
if (s.data_->isEmpty())
|
||||
(*(s.data_)) << uchar(uchar(s.version_) | 0x80);
|
||||
PIChunkStream::writeVInt(*(s.data_), c.id);
|
||||
PIChunkStream::writeVInt(*(s.data_), ba.size());
|
||||
s.data_->append(ba);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // PICHUNKSTREAM_H
|
||||
567
libs/main/serialization/pijson.cpp
Normal file
567
libs/main/serialization/pijson.cpp
Normal file
@@ -0,0 +1,567 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
JSON class
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "pijson.h"
|
||||
|
||||
|
||||
//! \~\class PIJSON pijson.h
|
||||
//! \~\details
|
||||
//! \~english \section PIJSON_sec0 Synopsis
|
||||
//! \~russian \section PIJSON_sec0 Краткий обзор
|
||||
//! \~english
|
||||
//!
|
||||
//! \~russian
|
||||
//! JSON - это древовидная структура, каждый элемент которой может быть либо
|
||||
//! парой имя:значение, либо массивом, либо объектом, т.е. именованным
|
||||
//! списком элементов. Корневой элемент JSON может быть либо массивом,
|
||||
//! либо объектом.
|
||||
//!
|
||||
//! Массивы заключены в квадратные скобки [], их элементы не имеют имени
|
||||
//! и разделяются запятыми.
|
||||
//!
|
||||
//! Объекты заключены в фигурные скобки {}, их элементы имеют имя
|
||||
//! и разделяются запятыми.
|
||||
//!
|
||||
//!
|
||||
//! \~english \section PIJSON_sec1 PIJSON tree
|
||||
//! \~russian \section PIJSON_sec1 Дерево PIJSON
|
||||
//! \~english
|
||||
//!
|
||||
//! \~russian
|
||||
//! %PIJSON представляет собой элемент дерева JSON. Каждый элемент имеет тип (\a type())
|
||||
//! и может иметь имя (\a name()). Если это конечный элемент,то он будет иметь значение,
|
||||
//! доступное через \a value(), \a toBool(), \a toInt(), \a toDouble() или \a toString().
|
||||
//!
|
||||
//! Если элемент преставляет собой массив, то его размер доступен через \a size(),
|
||||
//! а элементы массива через целочисленный оператор []. Весь массив доступен через \a array().
|
||||
//!
|
||||
//! Если элемент преставляет собой объект, то его размер доступен через \a size(),
|
||||
//! а элементы объекта через строковый оператор []. Проверить наличие элемента по имени можно
|
||||
//! с помощью \a contains(). Весь объект доступен через \a object().
|
||||
//!
|
||||
//! Создать дерево из текстового представления JSON можно с помощью \a fromJSON(), а
|
||||
//! преобразовать в текст с помощью \a toJSON().
|
||||
//!
|
||||
//!
|
||||
//! \~english \section PIJSON_sec2 PIJSON creation
|
||||
//! \~russian \section PIJSON_sec2 Создание PIJSON
|
||||
//! \~english
|
||||
//!
|
||||
//! \~russian
|
||||
//! Для создания нового дерева необходимо лишь создать пустой корневой PIJSON и заполнить его
|
||||
//! значениями, массивами или объектами с помощью целочисленного или строкового оператора [].
|
||||
//! В зависимости от типа аргумента элемент преобразуется либо в массив, либо в объект.
|
||||
//!
|
||||
//! При приравнивании PIJSON к какому-либо значению, его тип автоматически установится в нужный.
|
||||
//! При обращении на запись целочисленным оператором [] размер массива автоматически увеличится
|
||||
//! при необходимости.
|
||||
//!
|
||||
//!
|
||||
//! \~english \section PIJSON_sec3 Mask/unmask
|
||||
//! \~russian \section PIJSON_sec3 Маскирование/размаскирование
|
||||
//! \~english
|
||||
//!
|
||||
//! \~russian
|
||||
//! Строковые значения в стандарте JSON могут иметь в явном виде только печатные символы,
|
||||
//! спецсимволы и юникод должны быть преобразованы маскированием, т.е., например, вместо
|
||||
//! символа новой строки должно быть "\n", а не-ASCII символы должны быть в виде "\uXXXX".
|
||||
//!
|
||||
//! Оператор вывода в PICout не выполняет маскирования строк.
|
||||
//!
|
||||
//! Методы \a toJSON() и \a fromJSON() маскируют и размаскируют строковые поля.
|
||||
//!
|
||||
//!
|
||||
//! \~english \section PIJSON_sec4 Examples
|
||||
//! \~russian \section PIJSON_sec4 Примеры
|
||||
//! \~english
|
||||
//!
|
||||
//! \~russian
|
||||
//! Чтение:
|
||||
//! \~\code
|
||||
//! PIJSON json = PIJSON::fromJSON(
|
||||
//! "["
|
||||
//! " true,"
|
||||
//! " 10,"
|
||||
//! " {"
|
||||
//! " \"num\":1.E-2,"
|
||||
//! " \"str\":\"text\""
|
||||
//! " },"
|
||||
//! "]");
|
||||
//! piCout << json;
|
||||
//! piCout << json.toJSON();
|
||||
//! \endcode
|
||||
//! \~\code
|
||||
//! [
|
||||
//! true,
|
||||
//! 10,
|
||||
//! {
|
||||
//! "num": 1.e-2,
|
||||
//! "str": "text"
|
||||
//! }
|
||||
//! ]
|
||||
//!
|
||||
//! [true,10,{"num":1.e-2,"str":"text"}]
|
||||
//! \endcode
|
||||
//! Разбор:
|
||||
//! \~\code
|
||||
//! PIJSON json = PIJSON::fromJSON(
|
||||
//! "["
|
||||
//! " true,"
|
||||
//! " 10,"
|
||||
//! " {"
|
||||
//! " \"num\":1.E-2,"
|
||||
//! " \"str\":\"text\""
|
||||
//! " },"
|
||||
//! "]");
|
||||
//! piCout << "top-level:";
|
||||
//! for (const auto & i: json.array())
|
||||
//! piCout << i.value();
|
||||
//! piCout << "[2][\"str\"]:";
|
||||
//! piCout << json[2]["str"].toString();
|
||||
//! \endcode
|
||||
//! \~\code
|
||||
//! top-level:
|
||||
//! PIVariant(Bool, 1)
|
||||
//! PIVariant(String, 10)
|
||||
//! PIVariant(Invalid)
|
||||
//! [2]["str"]:
|
||||
//! text
|
||||
//! \endcode
|
||||
//!
|
||||
//! Создание:\n
|
||||
//! Простой массив
|
||||
//! \~\code
|
||||
//! PIJSON json;
|
||||
//! json[0] = -1;
|
||||
//! json[1] = "text";
|
||||
//! json[3] = false;
|
||||
//! piCout << json.toJSON();
|
||||
//! \endcode
|
||||
//! \~\code
|
||||
//! [-1,"text","",false]
|
||||
//! \endcode
|
||||
//!
|
||||
//! Массив объектов
|
||||
//! \~\code
|
||||
//! struct {
|
||||
//! PIString name;
|
||||
//! PIString surname;
|
||||
//! PIString email;
|
||||
//! } persons[] = {
|
||||
//! {"Ivan", "Ivanov", "ivan@pip.ru"},
|
||||
//! {"Igor", "Igorevich", "igor@pip.ru"},
|
||||
//! {"Andrey", "Andreevich", "andrey@pip.ru"}
|
||||
//! };
|
||||
//! PIJSON obj;
|
||||
//! PIJSON json;
|
||||
//! int index = 0;
|
||||
//! for (const auto & p: persons) {
|
||||
//! obj["index"] = index++;
|
||||
//! obj["name"] = p.name;
|
||||
//! obj["surname"] = p.surname;
|
||||
//! obj["email"] = p.email;
|
||||
//! json << obj;
|
||||
//! }
|
||||
//! piCout << json;
|
||||
//! \endcode
|
||||
//! \~\code
|
||||
//! [
|
||||
//! {
|
||||
//! "name": "Ivan",
|
||||
//! "email": "ivan@pip.ru",
|
||||
//! "index": 0,
|
||||
//! "surname": "Ivanov"
|
||||
//! },
|
||||
//! {
|
||||
//! "name": "Igor",
|
||||
//! "email": "igor@pip.ru",
|
||||
//! "index": 1,
|
||||
//! "surname": "Igorevich"
|
||||
//! },
|
||||
//! {
|
||||
//! "name": "Andrey",
|
||||
//! "email": "andrey@pip.ru",
|
||||
//! "index": 2,
|
||||
//! "surname": "Andreevich"
|
||||
//! }
|
||||
//! ]
|
||||
//! \endcode
|
||||
//!
|
||||
|
||||
|
||||
PIJSON PIJSON::newObject() {
|
||||
PIJSON ret;
|
||||
ret.c_type = Object;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIJSON PIJSON::newArray() {
|
||||
PIJSON ret;
|
||||
ret.c_type = Array;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIJSON PIJSON::newString(const PIString & v) {
|
||||
PIJSON ret;
|
||||
ret = v;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const PIVector<PIJSON> & PIJSON::array() const {
|
||||
if (!isArray()) {
|
||||
return nullEntry().c_array;
|
||||
} else {
|
||||
return c_array;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const PIMap<PIString, PIJSON> & PIJSON::object() const {
|
||||
if (!isObject()) {
|
||||
return nullEntry().c_object;
|
||||
} else {
|
||||
return c_object;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//! \details
|
||||
//! \~english
|
||||
//! If "v" type is boolean set type to \a PIJSON::Boolean.\n
|
||||
//! If "v" type is any numeric set type to \a PIJSON::Number.\n
|
||||
//! If "v" type is string set type to \a PIJSON::String.\n
|
||||
//! In case of any other type set element type to \a PIJSON::Invalid.
|
||||
//! \~russian
|
||||
//! Если тип "v" логический, то устанавливает тип в \a PIJSON::Boolean.\n
|
||||
//! Если тип "v" любой численный, то устанавливает тип в \a PIJSON::Number.\n
|
||||
//! Если тип "v" строковый, то устанавливает тип в \a PIJSON::String.\n
|
||||
//! Если тип "v" любой другой, то устанавливает тип в \a PIJSON::Invalid.
|
||||
void PIJSON::setValue(const PIVariant & v) {
|
||||
c_value = v;
|
||||
switch (v.type()) {
|
||||
case PIVariant::pivBool: c_type = Boolean; break;
|
||||
case PIVariant::pivUChar:
|
||||
case PIVariant::pivShort:
|
||||
case PIVariant::pivUShort:
|
||||
case PIVariant::pivInt:
|
||||
case PIVariant::pivUInt:
|
||||
case PIVariant::pivLLong:
|
||||
case PIVariant::pivULLong:
|
||||
case PIVariant::pivFloat:
|
||||
case PIVariant::pivDouble:
|
||||
case PIVariant::pivLDouble: c_type = Number; break;
|
||||
case PIVariant::pivString: c_type = String; break;
|
||||
default: c_type = Invalid; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIJSON::clear() {
|
||||
c_type = Invalid;
|
||||
c_value = PIVariant();
|
||||
c_name.clear();
|
||||
c_object.clear();
|
||||
c_array.clear();
|
||||
}
|
||||
|
||||
|
||||
int PIJSON::size() const {
|
||||
if (isArray()) return c_array.size_s();
|
||||
if (isObject()) return c_object.size_s();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool PIJSON::contains(const PIString & key) const {
|
||||
if (!isObject()) return false;
|
||||
return c_object.contains(key);
|
||||
}
|
||||
|
||||
|
||||
void PIJSON::resize(int new_size) {
|
||||
c_type = Array;
|
||||
c_array.resize(new_size, newString());
|
||||
}
|
||||
|
||||
|
||||
const PIJSON & PIJSON::operator[](int index) const {
|
||||
if (!isArray()) return nullEntry();
|
||||
return c_array[index];
|
||||
}
|
||||
|
||||
|
||||
PIJSON & PIJSON::operator[](int index) {
|
||||
c_type = Array;
|
||||
if (index >= c_array.size_s()) {
|
||||
c_array.resize(index + 1, newString());
|
||||
}
|
||||
PIJSON & ret(c_array[index]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIJSON & PIJSON::operator<<(const PIJSON & element) {
|
||||
c_type = Array;
|
||||
c_array << element;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIJSON & PIJSON::operator=(const PIJSON & v) {
|
||||
c_type = v.c_type;
|
||||
c_value = v.c_value;
|
||||
c_object = v.c_object;
|
||||
c_array = v.c_array;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIJSON PIJSON::operator[](const PIString & key) const {
|
||||
if (!isObject()) return nullEntry();
|
||||
if (!c_object.contains(key)) return nullEntry();
|
||||
return c_object.value(key);
|
||||
}
|
||||
|
||||
|
||||
PIJSON & PIJSON::operator[](const PIString & key) {
|
||||
c_type = Object;
|
||||
PIJSON & ret(c_object[key]);
|
||||
ret.c_name = key;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIJSON::toJSON(PrintType print_type) const {
|
||||
PIString ret;
|
||||
print(ret, *this, "", print_type == Tree, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIJSON PIJSON::fromJSON(PIString str) {
|
||||
return parseValue(str);
|
||||
}
|
||||
|
||||
|
||||
PIJSON & PIJSON::nullEntry() {
|
||||
static PIJSON ret;
|
||||
ret.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIJSON::parseName(PIString & s) {
|
||||
//piCout << "\n\n\n parseName" << s;
|
||||
PIString ret;
|
||||
ret = s.takeRange('"', '"');
|
||||
s.trim();
|
||||
if (s.isEmpty()) return PIString();
|
||||
if (s[0] != ':') return PIString();
|
||||
s.remove(0).trim();
|
||||
return PIJSON::stringUnmask(ret);
|
||||
}
|
||||
|
||||
|
||||
PIJSON PIJSON::parseValue(PIString & s) {
|
||||
PIJSON ret;
|
||||
//piCout << "\n\n\n parseValue" << s;
|
||||
s.trim();
|
||||
if (s.isEmpty()) return ret;
|
||||
if (s[0] == '{') {
|
||||
ret = parseObject(s.takeRange('{', '}'));
|
||||
} else if (s[0] == '[') {
|
||||
ret = parseArray(s.takeRange('[', ']'));
|
||||
} else {
|
||||
s.trim();
|
||||
if (s.startsWith('"')) {
|
||||
ret.c_type = PIJSON::String;
|
||||
ret.c_value = PIJSON::stringUnmask(s.takeRange('"', '"'));
|
||||
} else {
|
||||
PIString value;
|
||||
int ind = s.find(',');
|
||||
if (ind >= 0) {
|
||||
value = s.takeLeft(ind).trim().toLowerCase();
|
||||
} else {
|
||||
value = s;
|
||||
s.clear();
|
||||
}
|
||||
//piCout << "\n\n\n parseValue value = \"" << value << "\"";
|
||||
if (value == "null") {
|
||||
ret.c_type = PIJSON::Null;
|
||||
} else if (value == "true") {
|
||||
ret.c_type = PIJSON::Boolean;
|
||||
ret.c_value = true;
|
||||
} else if (value == "false") {
|
||||
ret.c_type = PIJSON::Boolean;
|
||||
ret.c_value = false;
|
||||
} else {
|
||||
ret.c_type = PIJSON::Number;
|
||||
ret.c_value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIJSON PIJSON::parseObject(PIString s) {
|
||||
//piCout << "\n\n\n parseObject" << s;
|
||||
PIJSON ret;
|
||||
ret.c_type = PIJSON::Object;
|
||||
while (s.isNotEmpty()) {
|
||||
PIString name = PIJSON::stringUnmask(parseName(s));
|
||||
PIJSON value = parseValue(s);
|
||||
auto & child(ret.c_object[name]);
|
||||
child = value;
|
||||
child.c_name = name;
|
||||
s.trim();
|
||||
if (s.isEmpty()) break;
|
||||
if (s[0] != ',') break;
|
||||
s.remove(0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIJSON PIJSON::parseArray(PIString s) {
|
||||
//piCout << "\n\n\n parseArray" << s;
|
||||
PIJSON ret;
|
||||
ret.c_type = PIJSON::Array;
|
||||
while (s.isNotEmpty()) {
|
||||
PIJSON value = parseValue(s);
|
||||
ret.c_array << value;
|
||||
s.trim();
|
||||
if (s.isEmpty()) break;
|
||||
if (s[0] != ',') break;
|
||||
s.remove(0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIJSON::stringMask(const PIString & s) {
|
||||
PIString ret;
|
||||
for (auto c: s) {
|
||||
if (!c.isAscii()) {
|
||||
ret += "\\u" + PIString::fromNumber(c.unicode16Code(), 16).expandLeftTo(4, '0');
|
||||
} else {
|
||||
char ca = c.toAscii();
|
||||
switch (ca) {
|
||||
case '"' : ret += "\\\""; break;
|
||||
case '\\': ret += "\\\\"; break;
|
||||
case '/' : ret += "\\/" ; break;
|
||||
case '\b': ret += "\\b" ; break;
|
||||
case '\f': ret += "\\f" ; break;
|
||||
case '\n': ret += "\\n" ; break;
|
||||
case '\r': ret += "\\r" ; break;
|
||||
case '\t': ret += "\\t" ; break;
|
||||
default: ret += ca;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIString PIJSON::stringUnmask(const PIString & s) {
|
||||
PIString ret;
|
||||
for (int i = 0; i < s.size_s(); ++i) {
|
||||
char ca = s[i].toAscii();
|
||||
if (ca == '\\') {
|
||||
if (i == s.size_s() - 1) continue;
|
||||
++i;
|
||||
char na = s[i].toAscii();
|
||||
switch (na) {
|
||||
case '"' : ret += '\"'; break;
|
||||
case '\\': ret += '\\'; break;
|
||||
case '/' : ret += '/' ; break;
|
||||
case 'b' : ret += '\b'; break;
|
||||
case 'f' : ret += '\f'; break;
|
||||
case 'n' : ret += '\n'; break;
|
||||
case 'r' : ret += '\r'; break;
|
||||
case 't' : ret += '\t'; break;
|
||||
case 'u' :
|
||||
ret += PIChar(s.mid(i + 1, 4).toUShort(16));
|
||||
i += 4;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
ret += s[i];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PIJSON::print(PIString & s, const PIJSON & v, PIString tab, bool spaces, bool transform, bool comma) {
|
||||
if (spaces) s += tab;
|
||||
/*
|
||||
switch (v.c_type) {
|
||||
case JSONEntry::Invalid: s << "(Invalid)"; break;
|
||||
case JSONEntry::Null: s << "(Null)"; break;
|
||||
case JSONEntry::Boolean: s << "(Boolean)"; break;
|
||||
case JSONEntry::Number: s << "(Number)"; break;
|
||||
case JSONEntry::String: s << "(String)"; break;
|
||||
case JSONEntry::Object: s << "(Object)"; break;
|
||||
case JSONEntry::Array: s << "(Array)"; break;
|
||||
}
|
||||
*/
|
||||
if (v.name().isNotEmpty()) {
|
||||
s += '"' + (transform ? stringMask(v.name()) : v.name()) + "\":";
|
||||
if (spaces) s += ' ';
|
||||
}
|
||||
switch (v.c_type) {
|
||||
case PIJSON::Invalid: break;
|
||||
case PIJSON::Null: s += "null"; break;
|
||||
case PIJSON::Boolean: s += PIString::fromBool(v.c_value.toBool()); break;
|
||||
case PIJSON::Number: s += v.c_value.toString(); break;
|
||||
case PIJSON::String: s += '"' + (transform ? stringMask(v.c_value.toString()) : v.c_value.toString()) + '"'; break;
|
||||
case PIJSON::Object:
|
||||
s += "{";
|
||||
if (spaces) s += '\n';
|
||||
{
|
||||
PIString ntab = tab + " ";
|
||||
auto it = v.c_object.makeIterator();
|
||||
int cnt = 0;
|
||||
while (it.next()) {
|
||||
print(s, it.value(), ntab, spaces, transform, ++cnt < v.c_object.size_s());
|
||||
}
|
||||
}
|
||||
if (spaces) s += tab;
|
||||
s += "}";
|
||||
break;
|
||||
case PIJSON::Array:
|
||||
s += "[";
|
||||
if (spaces) s += '\n';
|
||||
{
|
||||
PIString ntab = tab + " ";
|
||||
for (int i = 0; i < v.c_array.size_s(); ++i) {
|
||||
print(s, v.c_array[i], ntab, spaces, transform, i < v.c_array.size_s() - 1);
|
||||
}
|
||||
}
|
||||
if (spaces) s += tab;
|
||||
s += "]";
|
||||
break;
|
||||
}
|
||||
if (comma) s += ',';
|
||||
if (spaces) s += "\n";
|
||||
}
|
||||
215
libs/main/serialization/pijson.h
Normal file
215
libs/main/serialization/pijson.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/*! \file pijson.h
|
||||
* \ingroup Serialization
|
||||
* \brief
|
||||
* \~english JSON class
|
||||
* \~russian Класс JSON
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
JSON class
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIJSON_H
|
||||
#define PIJSON_H
|
||||
|
||||
#include "pivariant.h"
|
||||
|
||||
|
||||
//! \ingroup Serialization
|
||||
//! \~\brief
|
||||
//! \~english JSON class.
|
||||
//! \~russian Класс JSON.
|
||||
class PIP_EXPORT PIJSON {
|
||||
friend PICout operator <<(PICout s, const PIJSON & v);
|
||||
public:
|
||||
|
||||
//! \~english
|
||||
//! Type of JSON tree element
|
||||
//! \~russian
|
||||
//! Тип элемента дерева JSON
|
||||
enum Type {
|
||||
Invalid /*! \~english Invalid type \~russian Недействительный тип */,
|
||||
Null /*! \~english Without value, null \~russian Без значения, null */,
|
||||
Boolean /*! \~english Boolean, /b true or /b false \~russian Логическое, /b true или /b false */,
|
||||
Number /*! \~english Integer or floating-point number \~russian Целое либо число с плавающей точкой */,
|
||||
String /*! \~english Text \~russian Текст */,
|
||||
Object /*! \~english Object, {} \~russian Объект, {} */,
|
||||
Array /*! \~english Array, [] \~russian Массив, [] */
|
||||
};
|
||||
|
||||
//! \~english
|
||||
//! Generate JSON variant
|
||||
//! \~russian
|
||||
//! Вариант генерации JSON
|
||||
enum PrintType {
|
||||
Compact /*! \~english Without spaces, minimum size \~russian Без пробелов, минимальный размер */,
|
||||
Tree /*! \~english With spaces and new-lines, human-readable \~russian С пробелами и новыми строками, читаемый человеком */
|
||||
};
|
||||
|
||||
//! \~english Contructs invalid %PIJSON.
|
||||
//! \~russian Создает недействительный %PIJSON.
|
||||
PIJSON() {}
|
||||
PIJSON(const PIJSON & o) = default;
|
||||
|
||||
//! \~english Returns name of element, or empty string if it doesn`t have name.
|
||||
//! \~russian Возвращает имя элемента, либо пустую строку, если имени нет.
|
||||
const PIString & name() const {return c_name;}
|
||||
|
||||
//! \~english Returns elements array of this element, or empty array if element is not \a PIJSON::Array.
|
||||
//! \~russian Возвращает массив элементов этого элемента, либо пустой массив, если тип элемента не \a PIJSON::Array.
|
||||
const PIVector<PIJSON> & array() const;
|
||||
|
||||
//! \~english Returns elements map of this element, or empty map if element is not \a PIJSON::Object.
|
||||
//! \~russian Возвращает словарь элементов этого элемента, либо пустой словарь, если тип элемента не \a PIJSON::Object.
|
||||
const PIMap<PIString, PIJSON> & object() const;
|
||||
|
||||
//! \~english Returns element value.
|
||||
//! \~russian Возвращает значение элемента.
|
||||
const PIVariant & value() const {return c_value;}
|
||||
|
||||
//! \~english Returns element value as bool.
|
||||
//! \~russian Возвращает значение элемента как логическое.
|
||||
bool toBool() const {return c_value.toBool();}
|
||||
|
||||
//! \~english Returns element value as integer number.
|
||||
//! \~russian Возвращает значение элемента как целое число.
|
||||
int toInt() const {return c_value.toInt();}
|
||||
|
||||
//! \~english Returns element value as floating-point number.
|
||||
//! \~russian Возвращает значение элемента как число с плавающей точкой.
|
||||
double toDouble() const {return c_value.toDouble();}
|
||||
|
||||
//! \~english Returns element value as string, valid for all types.
|
||||
//! \~russian Возвращает значение элемента как строка, действительно для всех типов.
|
||||
PIString toString() const {return c_value.toString();}
|
||||
|
||||
|
||||
//! \~english Returns element type.
|
||||
//! \~russian Возвращает тип элемента.
|
||||
Type type() const {return c_type;}
|
||||
|
||||
//! \~english Returns if element is valid.
|
||||
//! \~russian Возвращает действителен ли элемент.
|
||||
bool isValid() const {return c_type != Invalid;}
|
||||
|
||||
//! \~english Returns if element is \a PIJSON::Object.
|
||||
//! \~russian Возвращает является ли элемент \a PIJSON::Object.
|
||||
bool isObject() const {return c_type == Object;}
|
||||
|
||||
//! \~english Returns if element is \a PIJSON::Array.
|
||||
//! \~russian Возвращает является ли элемент \a PIJSON::Array.
|
||||
bool isArray() const {return c_type == Array;}
|
||||
|
||||
//! \~english Set value and type of element from "v".
|
||||
//! \~russian Устанавливает значение и тип элемента из "v".
|
||||
void setValue(const PIVariant & v);
|
||||
|
||||
//! \~english Clear element and set it to \a PIJSON::Invalid.
|
||||
//! \~russian Очищает элемент и устанавливает его в \a PIJSON::Invalid.
|
||||
void clear();
|
||||
|
||||
//! \~english Returns size of elements array if type is \a PIJSON::Array, size of elements map if type is \a PIJSON::Object, otherwise returns 0.
|
||||
//! \~russian Возвращает размер массива элементов если тип \a PIJSON::Array, размер словаря элементов если тип \a PIJSON::Object, иначе возвращает 0.
|
||||
int size() const;
|
||||
|
||||
//! \~english Returns if elements map contains key "key" if type is \a PIJSON::Object, otherwise returns \b false.
|
||||
//! \~russian Возвращает содержит ли словарь элементов ключ "key" если тип \a PIJSON::Object, иначе возвращает \b false.
|
||||
bool contains(const PIString & key) const;
|
||||
|
||||
//! \~english Set element type to \a PIJSON::Array and resize elements array to "new_size".
|
||||
//! \~russian Устанавливает тип элемента в \a PIJSON::Array и изменяет размер массива элементов на "new_size".
|
||||
void resize(int new_size);
|
||||
|
||||
|
||||
//! \~english Synonim of \a setValue().
|
||||
//! \~russian Аналог \a setValue().
|
||||
PIJSON & operator=(const PIVariant & v) {setValue(v); return *this;}
|
||||
|
||||
PIJSON & operator=(const PIJSON & v);
|
||||
|
||||
|
||||
//! \~english Returns element from array with index "index" if type is \a PIJSON::Array, otherwise returns invalid %PIJSON.
|
||||
//! \~russian Возвращает элемент из массива по индексу "index" если тип \a PIJSON::Array, иначе возвращает недействительный %PIJSON.
|
||||
const PIJSON & operator[](int index) const;
|
||||
|
||||
//! \~english Set element type to \a PIJSON::Array, resize if necessary and returns element from array with index "index".
|
||||
//! \~russian Устанавливает тип элемента в \a PIJSON::Array, изменяет размер массива при неоходимости и возвращает элемент из массива по индексу "index".
|
||||
PIJSON & operator[](int index);
|
||||
|
||||
//! \~english Set element type to \a PIJSON::Array and add element to the end of array.
|
||||
//! \~russian Устанавливает тип элемента в \a PIJSON::Array и добавляет элемент в массив.
|
||||
PIJSON & operator <<(const PIJSON & element);
|
||||
|
||||
|
||||
//! \~english Returns element from map with key "key" if type is \a PIJSON::Object, otherwise returns invalid %PIJSON.
|
||||
//! \~russian Возвращает элемент из словаря по ключу "key" если тип \a PIJSON::Object, иначе возвращает недействительный %PIJSON.
|
||||
PIJSON operator[](const PIString & key) const;
|
||||
|
||||
//! \~english Set element type to \a PIJSON::Object and returns element from map with key "key". If element with this key doesn`t exists, it will be created.
|
||||
//! \~russian Устанавливает тип элемента в \a PIJSON::Object и возвращает элемент из словаря по ключу "key". Если элемента с таким ключом не существует, он будет создан.
|
||||
PIJSON & operator[](const PIString & key);
|
||||
|
||||
PIJSON operator[](const char * key) const {return (*this)[PIString::fromUTF8(key)];}
|
||||
PIJSON & operator[](const char * key) {return (*this)[PIString::fromUTF8(key)];}
|
||||
|
||||
|
||||
//! \~english Returns text representation of JSON tree.
|
||||
//! \~russian Возвращает текстовое представление дерева JSON.
|
||||
PIString toJSON(PrintType print_type = Compact) const;
|
||||
|
||||
//! \~english Parse text representation of JSON "str" and returns it root element.
|
||||
//! \~russian Разбирает текстовое представление JSON "str" и возвращает его корневой элемент.
|
||||
static PIJSON fromJSON(PIString str);
|
||||
|
||||
static PIJSON newObject();
|
||||
static PIJSON newArray();
|
||||
static PIJSON newString(const PIString & v = PIString());
|
||||
|
||||
private:
|
||||
static PIJSON & nullEntry();
|
||||
static PIString parseName(PIString & s);
|
||||
static PIJSON parseValue(PIString & s);
|
||||
static PIJSON parseObject(PIString s);
|
||||
static PIJSON parseArray(PIString s);
|
||||
static PIString stringMask(const PIString & s);
|
||||
static PIString stringUnmask(const PIString & s);
|
||||
static void print(PIString & s, const PIJSON & v, PIString tab, bool spaces, bool transform = false, bool comma = false);
|
||||
|
||||
PIString c_name;
|
||||
PIVariant c_value;
|
||||
PIVector<PIJSON> c_array;
|
||||
PIMap<PIString, PIJSON> c_object;
|
||||
Type c_type = Invalid;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//! \relatesalso PICout
|
||||
//! \~english Output operator to \a PICout.
|
||||
//! \~russian Оператор вывода в \a PICout.
|
||||
inline PICout operator <<(PICout s, const PIJSON & v) {
|
||||
s.space();
|
||||
s.saveAndSetControls(0);
|
||||
PIString str;
|
||||
PIJSON::print(str, v, "", true);
|
||||
s << str;
|
||||
s.restoreControls();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
#endif // PICONSTCHARS_H
|
||||
58
libs/main/serialization/piserializationmodule.h
Normal file
58
libs/main/serialization/piserializationmodule.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Module includes
|
||||
Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//! \defgroup Serialization Serialization
|
||||
//! \~\brief
|
||||
//! \~english Serialization.
|
||||
//! \~russian Сериализация.
|
||||
//!
|
||||
//! \~\details
|
||||
//! \~english \section cmake_module_Serialization Building with CMake
|
||||
//! \~russian \section cmake_module_Serialization Сборка с использованием CMake
|
||||
//!
|
||||
//! \~\code
|
||||
//! find_package(PIP REQUIRED)
|
||||
//! target_link_libraries([target] PIP)
|
||||
//! \endcode
|
||||
//!
|
||||
//! \~english \par Common
|
||||
//! \~russian \par Общее
|
||||
//!
|
||||
//! \~english
|
||||
//!
|
||||
//!
|
||||
//! \~russian
|
||||
//!
|
||||
//!
|
||||
//! \~\authors
|
||||
//! \~english
|
||||
//! Ivan Pelipenko peri4ko@yandex.ru;
|
||||
//! Andrey Bychkov work.a.b@yandex.ru;
|
||||
//! \~russian
|
||||
//! Иван Пелипенко peri4ko@yandex.ru;
|
||||
//! Андрей Бычков work.a.b@yandex.ru;
|
||||
//!
|
||||
|
||||
#ifndef PISERIALIZATIONMODULE_H
|
||||
#define PISERIALIZATIONMODULE_H
|
||||
|
||||
#include "pibinarystream.h"
|
||||
#include "pichunkstream.h"
|
||||
#include "pijson.h"
|
||||
|
||||
#endif // PISERIALIZATIONMODULE_H
|
||||
Reference in New Issue
Block a user