From 753da366725dc551f344ddc7fed2400605cadeac Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Fri, 8 Sep 2023 11:41:23 +0200 Subject: [PATCH] idrec: add support for stdex::stream Signed-off-by: Simon Rozman --- include/stdex/idrec.hpp | 250 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 228 insertions(+), 22 deletions(-) diff --git a/include/stdex/idrec.hpp b/include/stdex/idrec.hpp index 5f8440453..cc41dc911 100644 --- a/include/stdex/idrec.hpp +++ b/include/stdex/idrec.hpp @@ -6,11 +6,11 @@ #pragma once #include "sal.hpp" +#include "stream.hpp" #include #include #include - namespace stdex { namespace idrec { /// @@ -34,6 +34,26 @@ namespace stdex { return false; } + /// + /// Reads record ID + /// + /// \param[in] stream Input stream + /// \param[out] id Record ID + /// \param[in] end Position limit. Default is -1 (no limit). + /// + /// \returns + /// - \c true when succeeded + /// - \c false otherwise + /// + template + _Success_(return) bool read_id(_In_ stdex::stream::basic_file& stream, _Out_ T_ID &id, _In_opt_ stdex::stream::fpos_t end = stdex::stream::fpos_max) + { + if (end == stdex::stream::fpos_max || stream.tell() < end) { + stream >> id; + return stream.ok(); + } else + return false; + } /// /// Skips current record data @@ -50,16 +70,40 @@ namespace stdex { // Read record size. T_SIZE size; stream.read((char*)&size, sizeof(size)); - if (!stream.good()) return false; + if (!stream.good()) _Unlikely_ return false; // Skip the record data. size += (T_SIZE)(ALIGN - size) % ALIGN; stream.ignore(size); - if (!stream.good()) return false; + if (!stream.good()) _Unlikely_ return false; return true; } + /// + /// Skips current record data + /// + /// \param[in] stream Input stream + /// + /// \returns + /// - \c true when successful + /// - \c false otherwise + /// + template + bool ignore(_In_ stdex::stream::basic_file& stream) + { + // Read record size. + T_SIZE size; + stream >> size; + if (!stream.ok()) _Unlikely_ return false; + + // Skip the record data. + size += (T_SIZE)(ALIGN - size) % ALIGN; + stream.skip(size); + if (!stream.ok()) _Unlikely_ return false; + + return true; + } /// /// Finds record data @@ -76,21 +120,44 @@ namespace stdex { bool find(_In_ std::istream& stream, _In_ T_ID id, _In_opt_ std::streamoff end = (std::streamoff)-1) { T_ID _id; - while (end == (std::streamoff)-1 || stream.tellg() < end) { stream.read((char*)&_id, sizeof(_id)); - if (!stream.good()) return false; - + if (!stream.good()) _Unlikely_ return false; if (_id == id) { // The record was found. return true; } else ignore(stream); } - return false; } + /// + /// Finds record data + /// + /// \param[in] stream Input stream + /// \param[in] id Record ID + /// \param[in] end Position limit. Default is -1 (no limit). + /// + /// \returns + /// - \c true when found + /// - \c false otherwise + /// + template + bool find(_In_ stdex::stream::basic_file& stream, _In_ T_ID id, _In_opt_ stdex::stream::fpos_t end = stdex::stream::fpos_max) + { + T_ID _id; + while (end == stdex::stream::fpos_max || stream.tell() < end) { + stream >> _id; + if (!stream.ok()) _Unlikely_ return false; + if (_id == id) { + // The record was found. + return true; + } else + ignore(stream); + } + return false; + } /// /// Writes record header @@ -106,17 +173,40 @@ namespace stdex { std::streamoff start = stream.tellp(); // Write ID. - if (stream.fail()) return (std::streamoff)-1; + if (stream.fail()) _Unlikely_ return (std::streamoff)-1; stream.write((const char*)&id, sizeof(id)); // Write 0 as a placeholder for data size. - if (stream.fail()) return (std::streamoff)-1; + if (stream.fail()) _Unlikely_ return (std::streamoff)-1; T_SIZE size = 0; stream.write((const char*)&size, sizeof(size)); return start; } + /// + /// Writes record header + /// + /// \param[in] stream Output stream + /// \param[in] id Record ID + /// + /// \returns Position of the record header start in \p stream. Save for later \c close call. + /// + template + stdex::stream::fpos_t open(_In_ stdex::stream::basic_file& stream, _In_ T_ID id) + { + auto start = stream.tell(); + + // Write ID. + if (!stream.ok()) _Unlikely_ return stdex::stream::fpos_max; + stream << id; + + // Write 0 as a placeholder for data size. + if (!stream.ok()) _Unlikely_ return stdex::stream::fpos_max; + stream << (T_SIZE)0; + + return start; + } /// /// Updates record header @@ -131,8 +221,8 @@ namespace stdex { { std::streamoff end = stream.tellp(); T_SIZE - size = (T_SIZE)(end - start - sizeof(T_ID) - sizeof(T_SIZE)), - remainder = (T_SIZE)(ALIGN - size) % ALIGN; // Number of bytes we need to add, to keep the data integral number of ALIGN blocks long + size = static_cast(end - start - sizeof(T_ID) - sizeof(T_SIZE)), + remainder = static_cast((ALIGN - size) % ALIGN); // Number of bytes we need to add, to keep the data integral number of ALIGN blocks long if (remainder) { // Append padding. @@ -142,7 +232,7 @@ namespace stdex { } // Update the data size. - if (stream.fail()) return (std::streamoff)-1; + if (stream.fail()) _Unlikely_ return (std::streamoff)-1; stream.seekp(start + sizeof(T_ID)); stream.write((const char*)&size, sizeof(size)); stream.seekp(end); @@ -150,6 +240,37 @@ namespace stdex { return end; } + /// + /// Updates record header + /// + /// \param[in] stream Output stream + /// \param[in] start Start position of the record in \p stream + /// + /// \returns Position of the record end in \p stream + /// + template + stdex::stream::fpos_t close(_In_ stdex::stream::basic_file& stream, _In_ stdex::stream::fpos_t start) + { + auto end = stream.tell(); + T_SIZE + size = static_cast(end - start - sizeof(T_ID) - sizeof(T_SIZE)), + remainder = static_cast((ALIGN - size) % ALIGN); // Number of bytes we need to add, to keep the data integral number of ALIGN blocks long + + if (remainder) { + // Append padding. + static const char padding[ALIGN] = {}; + stream.write_array(padding, sizeof(char), remainder); + end += remainder; + } + + // Update the data size. + if (!stream.ok()) _Unlikely_ return stdex::stream::fpos_max; + stream.seek(start + sizeof(T_ID)); + stream << size; + stream.seek(end); + + return end; + } /// /// Helper class for read/write of records to/from memory @@ -165,7 +286,6 @@ namespace stdex { /// record(_In_ T &d) : data(d) {} - /// /// Constructs the class /// @@ -173,7 +293,6 @@ namespace stdex { /// record(_In_ const T &d) : data((T&)d) {} - /// /// Returns record id /// @@ -182,7 +301,6 @@ namespace stdex { return ID; } - /// /// Assignment operator /// @@ -196,7 +314,6 @@ namespace stdex { return *this; } - /// /// Writes record header /// @@ -209,6 +326,17 @@ namespace stdex { return stdex::idrec::open(stream, ID); } + /// + /// Writes record header + /// + /// \param[in] stream Output stream + /// + /// \returns Position of the record header start in \p stream. Save for later \c close call. + /// + static stdex::stream::foff_t open(_In_ stdex::stream::basic_file& stream) + { + return stdex::idrec::open(stream, ID); + } /// /// Updates record header @@ -223,6 +351,18 @@ namespace stdex { return stdex::idrec::close(stream, start); } + /// + /// Updates record header + /// + /// \param[in] stream Output stream + /// \param[in] start Start position of the record in \p stream + /// + /// \returns Position of the record end in \p stream + /// + static stdex::stream::foff_t close(_In_ stdex::stream::basic_file& stream, _In_ stdex::stream::foff_t start) + { + return stdex::idrec::close(stream, start); + } /// /// Finds record data @@ -239,13 +379,26 @@ namespace stdex { return stdex::idrec::find(stream, ID, end); } + /// + /// Finds record data + /// + /// \param[in] stream Input stream + /// \param[in] end Position limit. Default is -1 (no limit). + /// + /// \returns + /// - \c true when found + /// - \c false otherwise + /// + static bool find(_In_ stdex::stream::basic_file& stream, _In_opt_ stdex::stream::foff_t end = stdex::stream::foff_max) + { + return stdex::idrec::find(stream, ID, end); + } - T &data; ///< Record data reference + T &data; ///< Record data reference }; }; }; - /// /// Writes record to a stream /// @@ -259,14 +412,34 @@ std::ostream& operator <<(_In_ std::ostream& stream, _In_ const stdex::idrec::re { // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already. - std::streamoff start = r.open(stream); - if (stream.fail()) return stream; + auto start = r.open(stream); + if (stream.fail()) _Unlikely_ return stream; stream << r.data; r.close(stream, start); return stream; } +/// +/// Writes record to a stream +/// +/// \param[in] stream Output stream +/// \param[in] r Record +/// +/// \returns The stream \p stream +/// +template +stdex::stream::basic_file& operator <<(_In_ stdex::stream::basic_file& stream, _In_ const stdex::idrec::record r) +{ + // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already. + + auto start = r.open(stream); + if (!stream.ok()) _Unlikely_ return stream; + stream << r.data; + r.close(stream, start); + + return stream; +} /// /// Reads record from a stream @@ -284,14 +457,47 @@ std::istream& operator >>(_In_ std::istream& stream, _In_ stdex::idrec::record> r.data; // TODO: operator >> should not read past the record data! Make a size limited stream and read from it instead. + if (!stream.good()) _Unlikely_ return stream; - size += (T_SIZE)(ALIGN - size) % ALIGN; + size += static_cast((ALIGN - size) % ALIGN); stream.seekg(start + size); return stream; } + +/// +/// Reads record from a stream +/// +/// \param[in] stream Input stream +/// \param[out] r Record +/// +/// \returns The stream \p stream +/// +template +stdex::stream::basic_file& operator >>(_In_ stdex::stream::basic_file& stream, _In_ stdex::idrec::record r) +{ + // Parameter r does not need to be passed by reference. It has only one field (data), which is a reference itself already. + + // Read data size. + T_SIZE size; + stream >> size; + if (!stream.ok()) _Unlikely_ return stream; + + // Read data. + auto start = stream.tell(); + { + stdex::stream::limiter limiter(stream, size, size); + limiter >> r.data; + if (!limiter.ok()) _Unlikely_ return stream; + } + + size += static_cast((ALIGN - size) % ALIGN); + stream.seek(start + size); + + return stream; +}