idrec: add support for stdex::stream
Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
parent
b8cb07f456
commit
753da36672
@ -6,11 +6,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "sal.hpp"
|
#include "sal.hpp"
|
||||||
|
#include "stream.hpp"
|
||||||
#include <ios>
|
#include <ios>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
|
|
||||||
namespace stdex {
|
namespace stdex {
|
||||||
namespace idrec {
|
namespace idrec {
|
||||||
///
|
///
|
||||||
@ -34,6 +34,26 @@ namespace stdex {
|
|||||||
return false;
|
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 <class T_ID>
|
||||||
|
_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
|
/// Skips current record data
|
||||||
@ -50,16 +70,40 @@ namespace stdex {
|
|||||||
// Read record size.
|
// Read record size.
|
||||||
T_SIZE size;
|
T_SIZE size;
|
||||||
stream.read((char*)&size, sizeof(size));
|
stream.read((char*)&size, sizeof(size));
|
||||||
if (!stream.good()) return false;
|
if (!stream.good()) _Unlikely_ return false;
|
||||||
|
|
||||||
// Skip the record data.
|
// Skip the record data.
|
||||||
size += (T_SIZE)(ALIGN - size) % ALIGN;
|
size += (T_SIZE)(ALIGN - size) % ALIGN;
|
||||||
stream.ignore(size);
|
stream.ignore(size);
|
||||||
if (!stream.good()) return false;
|
if (!stream.good()) _Unlikely_ return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Skips current record data
|
||||||
|
///
|
||||||
|
/// \param[in] stream Input stream
|
||||||
|
///
|
||||||
|
/// \returns
|
||||||
|
/// - \c true when successful
|
||||||
|
/// - \c false otherwise
|
||||||
|
///
|
||||||
|
template <class T_SIZE, unsigned int ALIGN>
|
||||||
|
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
|
/// 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)
|
bool find(_In_ std::istream& stream, _In_ T_ID id, _In_opt_ std::streamoff end = (std::streamoff)-1)
|
||||||
{
|
{
|
||||||
T_ID _id;
|
T_ID _id;
|
||||||
|
|
||||||
while (end == (std::streamoff)-1 || stream.tellg() < end) {
|
while (end == (std::streamoff)-1 || stream.tellg() < end) {
|
||||||
stream.read((char*)&_id, sizeof(_id));
|
stream.read((char*)&_id, sizeof(_id));
|
||||||
if (!stream.good()) return false;
|
if (!stream.good()) _Unlikely_ return false;
|
||||||
|
|
||||||
if (_id == id) {
|
if (_id == id) {
|
||||||
// The record was found.
|
// The record was found.
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
ignore<T_SIZE, ALIGN>(stream);
|
ignore<T_SIZE, ALIGN>(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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 <class T_ID, class T_SIZE, unsigned int ALIGN>
|
||||||
|
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<T_SIZE, ALIGN>(stream);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Writes record header
|
/// Writes record header
|
||||||
@ -106,17 +173,40 @@ namespace stdex {
|
|||||||
std::streamoff start = stream.tellp();
|
std::streamoff start = stream.tellp();
|
||||||
|
|
||||||
// Write ID.
|
// Write ID.
|
||||||
if (stream.fail()) return (std::streamoff)-1;
|
if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
|
||||||
stream.write((const char*)&id, sizeof(id));
|
stream.write((const char*)&id, sizeof(id));
|
||||||
|
|
||||||
// Write 0 as a placeholder for data size.
|
// 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;
|
T_SIZE size = 0;
|
||||||
stream.write((const char*)&size, sizeof(size));
|
stream.write((const char*)&size, sizeof(size));
|
||||||
|
|
||||||
return start;
|
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 <class T_ID, class T_SIZE>
|
||||||
|
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
|
/// Updates record header
|
||||||
@ -131,8 +221,8 @@ namespace stdex {
|
|||||||
{
|
{
|
||||||
std::streamoff end = stream.tellp();
|
std::streamoff end = stream.tellp();
|
||||||
T_SIZE
|
T_SIZE
|
||||||
size = (T_SIZE)(end - start - sizeof(T_ID) - sizeof(T_SIZE)),
|
size = static_cast<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
|
remainder = static_cast<T_SIZE>((ALIGN - size) % ALIGN); // Number of bytes we need to add, to keep the data integral number of ALIGN blocks long
|
||||||
|
|
||||||
if (remainder) {
|
if (remainder) {
|
||||||
// Append padding.
|
// Append padding.
|
||||||
@ -142,7 +232,7 @@ namespace stdex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the data size.
|
// 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.seekp(start + sizeof(T_ID));
|
||||||
stream.write((const char*)&size, sizeof(size));
|
stream.write((const char*)&size, sizeof(size));
|
||||||
stream.seekp(end);
|
stream.seekp(end);
|
||||||
@ -150,6 +240,37 @@ namespace stdex {
|
|||||||
return end;
|
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 <class T_ID, class T_SIZE, unsigned int ALIGN>
|
||||||
|
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<T_SIZE>(end - start - sizeof(T_ID) - sizeof(T_SIZE)),
|
||||||
|
remainder = static_cast<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_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
|
/// Helper class for read/write of records to/from memory
|
||||||
@ -165,7 +286,6 @@ namespace stdex {
|
|||||||
///
|
///
|
||||||
record(_In_ T &d) : data(d) {}
|
record(_In_ T &d) : data(d) {}
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Constructs the class
|
/// Constructs the class
|
||||||
///
|
///
|
||||||
@ -173,7 +293,6 @@ namespace stdex {
|
|||||||
///
|
///
|
||||||
record(_In_ const T &d) : data((T&)d) {}
|
record(_In_ const T &d) : data((T&)d) {}
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns record id
|
/// Returns record id
|
||||||
///
|
///
|
||||||
@ -182,7 +301,6 @@ namespace stdex {
|
|||||||
return ID;
|
return ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Assignment operator
|
/// Assignment operator
|
||||||
///
|
///
|
||||||
@ -196,7 +314,6 @@ namespace stdex {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Writes record header
|
/// Writes record header
|
||||||
///
|
///
|
||||||
@ -209,6 +326,17 @@ namespace stdex {
|
|||||||
return stdex::idrec::open<T_ID, T_SIZE>(stream, ID);
|
return stdex::idrec::open<T_ID, T_SIZE>(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<T_ID, T_SIZE>(stream, ID);
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Updates record header
|
/// Updates record header
|
||||||
@ -223,6 +351,18 @@ namespace stdex {
|
|||||||
return stdex::idrec::close<T_ID, T_SIZE, ALIGN>(stream, start);
|
return stdex::idrec::close<T_ID, T_SIZE, ALIGN>(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<T_ID, T_SIZE, ALIGN>(stream, start);
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Finds record data
|
/// Finds record data
|
||||||
@ -239,13 +379,26 @@ namespace stdex {
|
|||||||
return stdex::idrec::find<T_ID, T_SIZE, ALIGN>(stream, ID, end);
|
return stdex::idrec::find<T_ID, T_SIZE, ALIGN>(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<T_ID, T_SIZE, ALIGN>(stream, ID, end);
|
||||||
|
}
|
||||||
|
|
||||||
T &data; ///< Record data reference
|
T &data; ///< Record data reference
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Writes record to a stream
|
/// 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.
|
// 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);
|
auto start = r.open(stream);
|
||||||
if (stream.fail()) return stream;
|
if (stream.fail()) _Unlikely_ return stream;
|
||||||
stream << r.data;
|
stream << r.data;
|
||||||
r.close(stream, start);
|
r.close(stream, start);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Writes record to a stream
|
||||||
|
///
|
||||||
|
/// \param[in] stream Output stream
|
||||||
|
/// \param[in] r Record
|
||||||
|
///
|
||||||
|
/// \returns The stream \p stream
|
||||||
|
///
|
||||||
|
template <class T, class T_ID, T_ID ID, class T_SIZE, unsigned int ALIGN>
|
||||||
|
stdex::stream::basic_file& operator <<(_In_ stdex::stream::basic_file& stream, _In_ const stdex::idrec::record<T, T_ID, ID, T_SIZE, ALIGN> 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
|
/// Reads record from a stream
|
||||||
@ -284,14 +457,47 @@ std::istream& operator >>(_In_ std::istream& stream, _In_ stdex::idrec::record<T
|
|||||||
// Read data size.
|
// Read data size.
|
||||||
T_SIZE size;
|
T_SIZE size;
|
||||||
stream.read((char*)&size, sizeof(size));
|
stream.read((char*)&size, sizeof(size));
|
||||||
if (!stream.good()) return stream;
|
if (!stream.good()) _Unlikely_ return stream;
|
||||||
|
|
||||||
// Read data.
|
// Read data.
|
||||||
std::streamoff start = stream.tellg();
|
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.
|
stream >> 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<T_SIZE>((ALIGN - size) % ALIGN);
|
||||||
stream.seekg(start + size);
|
stream.seekg(start + size);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Reads record from a stream
|
||||||
|
///
|
||||||
|
/// \param[in] stream Input stream
|
||||||
|
/// \param[out] r Record
|
||||||
|
///
|
||||||
|
/// \returns The stream \p stream
|
||||||
|
///
|
||||||
|
template <class T, class T_ID, T_ID ID, class T_SIZE, unsigned int ALIGN>
|
||||||
|
stdex::stream::basic_file& operator >>(_In_ stdex::stream::basic_file& stream, _In_ stdex::idrec::record<T, T_ID, ID, T_SIZE, ALIGN> 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<T_SIZE>((ALIGN - size) % ALIGN);
|
||||||
|
stream.seek(start + size);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user