From e61720598f56efcd732cd448c7064e99dd225879 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Sun, 1 Oct 2023 23:24:47 +0200 Subject: [PATCH] stream: revise string reading and writing Signed-off-by: Simon Rozman --- include/stdex/stream.hpp | 99 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/include/stdex/stream.hpp b/include/stdex/stream.hpp index 212124d27..74809cca5 100644 --- a/include/stdex/stream.hpp +++ b/include/stdex/stream.hpp @@ -473,17 +473,24 @@ namespace stdex /// \return This stream /// template, class _Ax = std::allocator<_Elem>> - inline basic& read_str(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& data) + inline basic& read_str(_Out_ std::basic_string<_Elem, _Traits, _Ax>& data) { + data.clear(); + if (!ok()) _Unlikely_ + return *this; uint32_t num_chars; read_data(num_chars); - if (!ok()) _Unlikely_ { - data.clear(); + if (!ok()) _Unlikely_ return *this; + data.reserve(num_chars); + for (;;) { + _Elem buf[0x400]; + uint32_t num_read = static_cast(read_array(buf, sizeof(_Elem), std::min(num_chars, _countof(buf)))); + data.append(buf, buf + num_read); + num_chars -= num_read; + if (!num_chars || !ok()) + return *this; } - data.resize(num_chars); - data.resize(read_array(data.data(), sizeof(_Elem), num_chars)); - return *this; } /// @@ -504,13 +511,38 @@ namespace stdex size_t num_chars = stdex::strlen(data); if (num_chars > UINT32_MAX) throw std::invalid_argument("string too long"); - write_data((uint32_t)num_chars); + write_data(static_cast(num_chars)); if (!ok()) _Unlikely_ return *this; write_array(data, sizeof(T), num_chars); return *this; } + /// + /// Writes string to the stream length-prefixed + /// + /// This method is intended for chaining: e.g. stream.write_str(a).write_str(b).write_str(c)... + /// Since it would make it impossible to detect if any of the write_str(a) or write_str(b) failed should + /// write_str(c) succeed, the method skips writing if stream state is not ok. + /// + /// \param[in] data String to write + /// + /// \return This stream + /// + template, class _Ax = std::allocator<_Elem>> + inline basic& write_str(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) + { + // Stream state will be checked in write_data. + size_t num_chars = data.size(); + if (num_chars > UINT32_MAX) + throw std::invalid_argument("string too long"); + write_data(static_cast(num_chars)); + if (!ok()) _Unlikely_ + return *this; + write_array(data.data(), sizeof(_Elem), num_chars); + return *this; + } + #ifdef _WIN32 /// /// Writes SAFEARRAY data @@ -649,9 +681,11 @@ namespace stdex inline basic& operator <<(_In_ const wchar_t data) { return write_data(data); } #endif template, class _Ax = std::allocator<_Elem>> - inline basic& operator >>(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& data) { return read_str(data); } + inline basic& operator >>(_Out_ std::basic_string<_Elem, _Traits, _Ax>& data) { return read_str(data); } template inline basic& operator <<(_In_ const T* data) { return write_str(data); } + template, class _Ax = std::allocator<_Elem>> + inline basic& operator <<(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) { return write_str(data); } template > basic& operator <<(_In_ const std::vector<_Ty, _Alloc>& data) @@ -3466,6 +3500,51 @@ namespace stdex return *this; } + /// + /// Writes string to the stream length-prefixed + /// + /// This method is intended for chaining: e.g. stream.write_str(a).write_str(b).write_str(c)... + /// Since it would make it impossible to detect if any of the write_str(a) or write_str(b) failed should + /// write_str(c) succeed, the method skips writing if stream state is not ok. + /// + /// As memory write rarely fails, a #define CHECK_STREAM_STATE 0 turns this checking off when + /// performance is paramount. + /// + /// \param[in] data String to write + /// + /// \return This stream + /// + template, class _Ax = std::allocator<_Elem>> + inline memory_file& write_str(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) + { +#if SET_FILE_OP_TIMES + m_atime = m_mtime = time_point::now(); +#endif + if (CHECK_STREAM_STATE && !ok()) _Unlikely_ + return *this; + size_t num_chars = data.size(); + if (num_chars > UINT32_MAX) + throw std::invalid_argument("string too long"); + size_t size_chars = num_chars * sizeof(_Elem); + size_t size = sizeof(uint32_t) + size_chars; + size_t end_offset = m_offset + size; + if (end_offset > m_reserved) { + reserve(end_offset); + if (!ok()) _Unlikely_ + return *this; + } + auto p = m_data + m_offset; + *reinterpret_cast(p) = HE2LE((uint32_t)num_chars); + memcpy(p + sizeof(uint32_t), data.data(), size_chars); + m_offset = end_offset; + if (m_offset > m_size) + m_size = m_offset; +#if !CHECK_STREAM_STATE + m_state = state_t::ok; +#endif + return *this; + } + /// /// Writes content of another stream /// @@ -3697,10 +3776,12 @@ namespace stdex inline memory_file& operator <<(_In_ const wchar_t data) { return write_data(data); } inline memory_file& operator >>(_Out_ wchar_t& data) { return read_data(data); } #endif + template, class _Ax = std::allocator<_Elem>> + inline memory_file& operator >>(_Out_ std::basic_string<_Elem, _Traits, _Ax>&data) { return read_str(data); } template inline memory_file& operator <<(_In_ const T * data) { return write_str(data); } template, class _Ax = std::allocator<_Elem>> - inline memory_file& operator >>(_Inout_ std::basic_string<_Elem, _Traits, _Ax>&data) { return read_str(data); } + inline memory_file& operator <<(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) { return write_str(data); } protected: uint8_t* m_data; ///< file data