diff --git a/include/stdex/idrec.h b/include/stdex/idrec similarity index 96% rename from include/stdex/idrec.h rename to include/stdex/idrec index 7fa1f6dd2..b35c8180f 100644 --- a/include/stdex/idrec.h +++ b/include/stdex/idrec @@ -1,288 +1,288 @@ -/* - SPDX-License-Identifier: GPL-3.0-or-later - Copyright © 2016-2022 Amebis -*/ - -#pragma once - -#include -#include -#include - - -namespace stdex { - namespace idrec { - /// - /// 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 - inline _Success_(return) bool read_id(_In_ std::istream& stream, _Out_ T_ID &id, _In_opt_ std::streamoff end = (std::streamoff)-1) - { - if (end == (std::streamoff)-1 || stream.tellg() < end) { - stream.read((char*)&id, sizeof(id)); - return stream.good(); - } else - return false; - } - - - /// - /// Skips current record data - /// - /// \param[in] stream Input stream - /// - /// \returns - /// - \c true when successful - /// - \c false otherwise - /// - template - inline bool ignore(_In_ std::istream& stream) - { - // Read record size. - T_SIZE size; - stream.read((char*)&size, sizeof(size)); - if (!stream.good()) return false; - - // Skip the record data. - size += (T_SIZE)(ALIGN - size) % ALIGN; - stream.ignore(size); - if (!stream.good()) return false; - - return true; - } - - - /// - /// 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 - inline 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 (_id == id) { - // The record was found. - return true; - } else - ignore(stream); - } - - return false; - } - - - /// - /// 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 - inline std::streamoff open(_In_ std::ostream& stream, _In_ T_ID id) - { - std::streamoff start = stream.tellp(); - - // Write ID. - if (stream.fail()) 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; - T_SIZE size = 0; - stream.write((const char*)&size, sizeof(size)); - - return 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 - /// - template - inline std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start) - { - 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 - - if (remainder) { - // Append padding. - static const char padding[ALIGN] = {}; - stream.write(padding, remainder); - end += remainder; - } - - // Update the data size. - if (stream.fail()) return (std::streamoff)-1; - stream.seekp(start + sizeof(T_ID)); - stream.write((const char*)&size, sizeof(size)); - stream.seekp(end); - - return end; - } - - - /// - /// Helper class for read/write of records to/from memory - /// - template - class record - { - public: - /// - /// Constructs the class - /// - /// \param[in] d Reference to record data - /// - inline record(_In_ T &d) : data(d) {} - - - /// - /// Constructs the class - /// - /// \param[in] d Reference to record data - /// - inline record(_In_ const T &d) : data((T&)d) {} - - - /// - /// Assignment operator - /// - /// \param[in] r Source record - /// - /// \returns A const reference to this struct - /// - inline const record& operator =(_In_ const record &r) - { - data = r.data; - return *this; - } - - - /// - /// 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 inline std::streamoff open(_In_ std::ostream& stream) - { - return stdex::idrec::open(stream, id); - } - - - /// - /// 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 inline std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start) - { - return stdex::idrec::close(stream, start); - } - - - /// - /// 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 inline bool find(_In_ std::istream& stream, _In_opt_ std::streamoff end = (std::streamoff)-1) - { - return stdex::idrec::find(stream, id, end); - } - - - static const T_ID id; ///< Record id - T &data; ///< Record data reference - }; - }; -}; - - -/// -/// Writes record to a stream -/// -/// \param[in] stream Output stream -/// \param[in] r Record -/// -/// \returns The stream \p stream -/// -template -inline std::ostream& operator <<(_In_ std::ostream& 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. The id field is static anyway. - - std::streamoff start = r.open(stream); - if (stream.fail()) return stream; - stream << r.data; - r.close(stream, start); - - return stream; -} - - -/// -/// Reads record from a stream -/// -/// \param[in] stream Input stream -/// \param[out] r Record -/// -/// \returns The stream \p stream -/// -template -inline std::istream& operator >>(_In_ std::istream& 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. The id field is static anyway. - - // Read data size. - T_SIZE size; - stream.read((char*)&size, sizeof(size)); - if (!stream.good()) return stream; - - // Read data. - std::streamoff start = stream.tellg(); - stream >> r.data; // TODO: operator >> should not read past the record data! Make a size limited stream and read from it instead. - - size += (T_SIZE)(ALIGN - size) % ALIGN; - stream.seekg(start + size); - - return stream; -} +/* + SPDX-License-Identifier: GPL-3.0-or-later + Copyright © 2016-2022 Amebis +*/ + +#pragma once + +#include +#include +#include + + +namespace stdex { + namespace idrec { + /// + /// 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 + inline _Success_(return) bool read_id(_In_ std::istream& stream, _Out_ T_ID &id, _In_opt_ std::streamoff end = (std::streamoff)-1) + { + if (end == (std::streamoff)-1 || stream.tellg() < end) { + stream.read((char*)&id, sizeof(id)); + return stream.good(); + } else + return false; + } + + + /// + /// Skips current record data + /// + /// \param[in] stream Input stream + /// + /// \returns + /// - \c true when successful + /// - \c false otherwise + /// + template + inline bool ignore(_In_ std::istream& stream) + { + // Read record size. + T_SIZE size; + stream.read((char*)&size, sizeof(size)); + if (!stream.good()) return false; + + // Skip the record data. + size += (T_SIZE)(ALIGN - size) % ALIGN; + stream.ignore(size); + if (!stream.good()) return false; + + return true; + } + + + /// + /// 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 + inline 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 (_id == id) { + // The record was found. + return true; + } else + ignore(stream); + } + + return false; + } + + + /// + /// 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 + inline std::streamoff open(_In_ std::ostream& stream, _In_ T_ID id) + { + std::streamoff start = stream.tellp(); + + // Write ID. + if (stream.fail()) 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; + T_SIZE size = 0; + stream.write((const char*)&size, sizeof(size)); + + return 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 + /// + template + inline std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start) + { + 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 + + if (remainder) { + // Append padding. + static const char padding[ALIGN] = {}; + stream.write(padding, remainder); + end += remainder; + } + + // Update the data size. + if (stream.fail()) return (std::streamoff)-1; + stream.seekp(start + sizeof(T_ID)); + stream.write((const char*)&size, sizeof(size)); + stream.seekp(end); + + return end; + } + + + /// + /// Helper class for read/write of records to/from memory + /// + template + class record + { + public: + /// + /// Constructs the class + /// + /// \param[in] d Reference to record data + /// + inline record(_In_ T &d) : data(d) {} + + + /// + /// Constructs the class + /// + /// \param[in] d Reference to record data + /// + inline record(_In_ const T &d) : data((T&)d) {} + + + /// + /// Assignment operator + /// + /// \param[in] r Source record + /// + /// \returns A const reference to this struct + /// + inline const record& operator =(_In_ const record &r) + { + data = r.data; + return *this; + } + + + /// + /// 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 inline std::streamoff open(_In_ std::ostream& stream) + { + return stdex::idrec::open(stream, id); + } + + + /// + /// 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 inline std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start) + { + return stdex::idrec::close(stream, start); + } + + + /// + /// 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 inline bool find(_In_ std::istream& stream, _In_opt_ std::streamoff end = (std::streamoff)-1) + { + return stdex::idrec::find(stream, id, end); + } + + + static const T_ID id; ///< Record id + T &data; ///< Record data reference + }; + }; +}; + + +/// +/// Writes record to a stream +/// +/// \param[in] stream Output stream +/// \param[in] r Record +/// +/// \returns The stream \p stream +/// +template +inline std::ostream& operator <<(_In_ std::ostream& 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. The id field is static anyway. + + std::streamoff start = r.open(stream); + if (stream.fail()) return stream; + stream << r.data; + r.close(stream, start); + + return stream; +} + + +/// +/// Reads record from a stream +/// +/// \param[in] stream Input stream +/// \param[out] r Record +/// +/// \returns The stream \p stream +/// +template +inline std::istream& operator >>(_In_ std::istream& 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. The id field is static anyway. + + // Read data size. + T_SIZE size; + stream.read((char*)&size, sizeof(size)); + if (!stream.good()) return stream; + + // Read data. + std::streamoff start = stream.tellg(); + stream >> r.data; // TODO: operator >> should not read past the record data! Make a size limited stream and read from it instead. + + size += (T_SIZE)(ALIGN - size) % ALIGN; + stream.seekg(start + size); + + return stream; +}