From 0907a3eb13d36182bc75e0568aee001c6f8c1989 Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 30 Aug 2023 12:18:04 +0300 Subject: [PATCH] version 3.14.0 PIBinaryStream::wasReadError() method, remove incomplete read asserts --- CMakeLists.txt | 4 +- doc/pages/iostream.md | 8 ++ libs/main/io_devices/piiostream.h | 5 +- libs/main/serialization/pibinarystream.h | 148 ++++++++++++++++------- libs/main/text/pistring.h | 1 + 5 files changed, 118 insertions(+), 48 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b31f081..3a0fd249 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(PIP) set(PIP_MAJOR 3) -set(PIP_MINOR 13) -set(PIP_REVISION 2) +set(PIP_MINOR 14) +set(PIP_REVISION 0) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) diff --git a/doc/pages/iostream.md b/doc/pages/iostream.md index 488936ba..272ea97b 100644 --- a/doc/pages/iostream.md +++ b/doc/pages/iostream.md @@ -2,6 +2,7 @@ \~russian \page iostream Поток ввода/вывода \~english + \~russian %PIBinaryStream представляет собой интерфейс бинарной сериализации. Не может быть использован в чистом виде, только в виде миксина или @@ -115,3 +116,10 @@ for (int i = 0; i < 10; ++i) 0.8 0.9 \endcode + +\~english + +\~russian +Если при чтении из потока не хватило данных (например, закончился массив или файл), то проверка +объекта потока на wasReadError() вернёт true. Рекомендуется делать эту проверку после чтения +данных для корректной обработки ошибки. diff --git a/libs/main/io_devices/piiostream.h b/libs/main/io_devices/piiostream.h index bd969f8c..e78bee0e 100644 --- a/libs/main/io_devices/piiostream.h +++ b/libs/main/io_devices/piiostream.h @@ -44,7 +44,10 @@ public: //! \~english Assign "device" device //! \~russian Назначает устройство "device" - void setDevice(PIIODevice * device) { dev = device; } + void setDevice(PIIODevice * device) { + dev = device; + resetReadError(); + } bool binaryStreamAppendImp(const void * d, size_t s) { if (!dev) return false; diff --git a/libs/main/serialization/pibinarystream.h b/libs/main/serialization/pibinarystream.h index 2b2ab8e1..e1dbeb62 100644 --- a/libs/main/serialization/pibinarystream.h +++ b/libs/main/serialization/pibinarystream.h @@ -65,6 +65,7 @@ template class PIBinaryStream { public: + //! \~english Write data //! \~russian Записать данные bool binaryStreamAppend(const void * d, size_t s) { if (!static_cast

(this)->binaryStreamAppendImp(d, s)) { @@ -73,32 +74,50 @@ public: } return true; } + + //! \~english Read data //! \~russian Прочитать данные bool binaryStreamTake(void * d, size_t s) { if (!static_cast

(this)->binaryStreamTakeImp(d, s)) { + _was_read_error_ = true; return false; printf("[PIBinaryStream] binaryStreamTake() error\n"); } return true; } - //! \~russian Узнать оставшийся размер - //!\~\details - //!\~russian Возвращает -1 если нет информации о размере + //! \~english Returns remain size + //! \~russian Возвращает оставшийся размер + //! \~\details + //! \~english Returns -1 if no information about size + //! \~russian Возвращает -1 если нет информации о размере ssize_t binaryStreamSize() const { return static_cast(this)->binaryStreamSizeImp(); } + //! \~english Write data //! \~russian Записать данные template void binaryStreamAppend(T v) { binaryStreamAppend(&v, sizeof(v)); } + //! \~english Read int //! \~russian Прочитать int int binaryStreamTakeInt() { int r = 0; binaryStreamTake(&r, sizeof(r)); return r; } + + //! \~english Returns whether there has been an incomplete read since last \a resetReadError() or after the stream was created + //! \~russian Возвращает было ли неполное чтение с момента последнего вызова \a resetReadError() + bool wasReadError() const { return _was_read_error_; } + + //! \~english Reset incomplete read flag + //! \~russian Сбрасывает флаг неполного чтения + void resetReadError() { _was_read_error_ = false; } + +private: + bool _was_read_error_ = false; }; @@ -305,7 +324,6 @@ template operator>>(PIBinaryStream

& s, T & v) { if (!s.binaryStreamTake(&v, sizeof(v))) { printf("error with %s\n", __PIP_TYPENAME__(T)); - assert(false); } return s; } @@ -322,25 +340,41 @@ template & operator>>(PIBinaryStream

& s, PIVector & v) { // piCout << ">> vector trivial default"; int sz = s.binaryStreamTakeInt(); + if (s.wasReadError()) { + printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } v._resizeRaw(sz); if (!s.binaryStreamTake(v.data(), sz * sizeof(T))) { printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); - assert(false); + v.clear(); } return s; } template::value, int>::type = 0, - typename std::enable_if< + typename T, + typename std::enable_if::value, int>::type = 0, + typename std::enable_if< !std::is_same &>() >> std::declval()), PIBinaryStreamTrivialRef

>::value, int>::type = 0> inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector & v) { // piCout << ">> vector trivial custom"; int sz = s.binaryStreamTakeInt(); + if (s.wasReadError()) { + printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } v._resizeRaw(sz); - for (int i = 0; i < sz; ++i) + for (int i = 0; i < sz; ++i) { s >> v[i]; + if (s.wasReadError()) { + printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } + } return s; } @@ -348,33 +382,49 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector & v) { //! \~english Restore operator for PIDeque of any trivial copyable type //! \~russian Оператор извлечения для PIDeque тривиальных типов template::value, int>::type = 0, - typename std::enable_if< + typename T, + typename std::enable_if::value, int>::type = 0, + typename std::enable_if< std::is_same &>() >> std::declval()), PIBinaryStreamTrivialRef

>::value, int>::type = 0> inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & v) { // piCout << ">> deque trivial default"; int sz = s.binaryStreamTakeInt(); + if (s.wasReadError()) { + printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } v._resizeRaw(sz); if (!s.binaryStreamTake(v.data(), sz * sizeof(T))) { printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); - assert(false); + v.clear(); } return s; } template::value, int>::type = 0, - typename std::enable_if< + typename T, + typename std::enable_if::value, int>::type = 0, + typename std::enable_if< !std::is_same &>() >> std::declval()), PIBinaryStreamTrivialRef

>::value, int>::type = 0> inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & v) { // piCout << ">> deque trivial custom"; int sz = s.binaryStreamTakeInt(); + if (s.wasReadError()) { + printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } v._resizeRaw(sz); - for (int i = 0; i < sz; ++i) + for (int i = 0; i < sz; ++i) { s >> v[i]; + if (s.wasReadError()) { + printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } + } return s; } @@ -382,9 +432,9 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & v) { //! \~english Restore operator for PIVector2D of any trivial copyable type //! \~russian Оператор извлечения для PIVector2D тривиальных типов template::value, int>::type = 0, - typename std::enable_if< + typename T, + typename std::enable_if::value, int>::type = 0, + typename std::enable_if< std::is_same &>() >> std::declval()), PIBinaryStreamTrivialRef

>::value, int>::type = 0> inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector2D & v) { @@ -392,17 +442,22 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector2D & v) int r, c; r = s.binaryStreamTakeInt(); c = s.binaryStreamTakeInt(); + if (s.wasReadError()) { + printf("error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } v._resizeRaw(r, c); if (!s.binaryStreamTake(v.data(), v.size() * sizeof(T))) { printf("error with PIVector2D<%s>\n", __PIP_TYPENAME__(T)); - assert(false); + v.clear(); } return s; } template::value, int>::type = 0, - typename std::enable_if< + typename T, + typename std::enable_if::value, int>::type = 0, + typename std::enable_if< !std::is_same &>() >> std::declval()), PIBinaryStreamTrivialRef

>::value, int>::type = 0> inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector2D & v) { @@ -481,14 +536,15 @@ inline PIBinaryStream

& operator<<(PIBinaryStream

& s, const PIVector2D //! \~russian Оператор извлечения для PIVector сложных типов template::value, int>::type = 0> inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector & 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) + for (size_t i = 0; i < v.size(); ++i) { s >> v[i]; + if (s.wasReadError()) { + printf("error with PIVector<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } + } return s; } @@ -497,14 +553,15 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector & v) { //! \~russian Оператор извлечения для PIDeque сложных типов template::value, int>::type = 0> inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & 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) + for (size_t i = 0; i < v.size(); ++i) { s >> v[i]; + if (s.wasReadError()) { + printf("error with PIDeque<%s>\n", __PIP_TYPENAME__(T)); + v.clear(); + return s; + } + } return s; } @@ -513,11 +570,6 @@ inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIDeque & v) { //! \~russian Оператор извлечения для PIVector2D сложных типов template::value, int>::type = 0> inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIVector2D & 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 tmp; r = s.binaryStreamTakeInt(); @@ -549,19 +601,25 @@ inline PIBinaryStream

& operator<<(PIBinaryStream

& s, const PIMap //! \~russian Оператор извлечения template inline PIBinaryStream

& operator>>(PIBinaryStream

& s, PIMap & 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; + if (s.wasReadError()) { + printf("error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); + v.clear(); + return s; + } v.pim_index[i].index = ind; } s >> v.pim_content; + if (s.wasReadError()) { + printf("error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); + v.clear(); + return s; + } if (v.pim_content.size_s() != v.pim_index.size_s()) { piCout << "Warning: loaded invalid PIMap, clear"; v.clear(); diff --git a/libs/main/text/pistring.h b/libs/main/text/pistring.h index c720c480..ed0727f5 100644 --- a/libs/main/text/pistring.h +++ b/libs/main/text/pistring.h @@ -1820,6 +1820,7 @@ BINARY_STREAM_WRITE(PIString) { //! \~russian Оператор извлечения. BINARY_STREAM_READ(PIString) { s >> v.d; + if (s.wasReadError()) v.clear(); return s; }