This commit is contained in:
2022-11-25 21:35:39 +03:00
parent a2256a3872
commit dc6fbbf7ec
37 changed files with 1498 additions and 1321 deletions

View 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

View 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);
}

View 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

View 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";
}

View 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

View 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