stream: revise string reading and writing

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2023-10-01 23:24:47 +02:00
parent 86e7ed3690
commit e61720598f

View File

@ -473,17 +473,24 @@ namespace stdex
/// \return This stream
///
template<class _Elem, class _Traits = std::char_traits<_Elem>, 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<uint32_t>(read_array(buf, sizeof(_Elem), std::min<uint32_t>(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<uint32_t>(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 _Elem, class _Traits = std::char_traits<_Elem>, 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<uint32_t>(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 _Elem, class _Traits = std::char_traits<_Elem>, 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 <class T>
inline basic& operator <<(_In_ const T* data) { return write_str(data); }
template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
inline basic& operator <<(_In_ const std::basic_string<_Elem, _Traits, _Ax>& data) { return write_str(data); }
template <class _Ty, class _Alloc = std::allocator<_Ty>>
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 _Elem, class _Traits = std::char_traits<_Elem>, 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<uint32_t*>(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 _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
inline memory_file& operator >>(_Out_ std::basic_string<_Elem, _Traits, _Ax>&data) { return read_str(data); }
template <class T>
inline memory_file& operator <<(_In_ const T * data) { return write_str(data); }
template<class _Elem, class _Traits = std::char_traits<_Elem>, 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