Loading...
Searching...
No Matches
idrec.hpp
/*
SPDX-License-Identifier: MIT
Copyright © 2016-2023 Amebis
*/
#pragma once
#include "compat.hpp"
#include "stream.hpp"
#include <ios>
#include <istream>
#include <ostream>
namespace stdex {
namespace idrec {
template <class T_ID>
_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;
}
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;
}
template <class T_SIZE, T_SIZE ALIGN>
bool ignore(_In_ std::istream& stream)
{
// Read record size.
T_SIZE size;
stream.read((char*)&size, sizeof(size));
if (!stream.good()) _Unlikely_ return false;
// Skip the record data.
size += (T_SIZE)(ALIGN - size) % ALIGN;
stream.ignore(size);
if (!stream.good()) _Unlikely_ return false;
return true;
}
template <class T_SIZE, T_SIZE ALIGN>
bool ignore(_In_ stdex::stream::basic& 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;
}
template <class T_ID, class T_SIZE, T_SIZE ALIGN>
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()) _Unlikely_ return false;
if (_id == id) {
// The record was found.
return true;
} else
ignore<T_SIZE, ALIGN>(stream);
}
return false;
}
template <class T_ID, class T_SIZE, T_SIZE 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;
}
template <class T_ID, class T_SIZE>
std::streamoff open(_In_ std::ostream& stream, _In_ T_ID id)
{
std::streamoff start = stream.tellp();
// Write ID.
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()) _Unlikely_ return (std::streamoff)-1;
T_SIZE size = 0;
stream.write((const char*)&size, sizeof(size));
return start;
}
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.
stream << id;
// Write 0 as a placeholder for data size.
stream << (T_SIZE)0;
return start;
}
template <class T_ID, class T_SIZE, T_SIZE ALIGN>
std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
{
std::streamoff end = stream.tellp();
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(padding, remainder);
end += remainder;
}
// Update the data size.
if (stream.fail()) _Unlikely_ return (std::streamoff)-1;
stream.seekp(start + sizeof(T_ID));
stream.write((const char*)&size, sizeof(size));
stream.seekp(end);
return end;
}
template <class T_ID, class T_SIZE, T_SIZE 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;
}
template <class T, class T_ID, const T_ID ID, class T_SIZE, T_SIZE ALIGN>
class record
{
public:
record(_In_ T &d) : data(d) {}
record(_In_ const T &d) : data((T&)d) {}
static const T_ID id()
{
return ID;
}
const record<T, T_ID, ID, T_SIZE, ALIGN>& operator =(_In_ const record<T, T_ID, ID, T_SIZE, ALIGN> &r)
{
data = r.data;
return *this;
}
static std::streamoff open(_In_ std::ostream& stream)
{
return stdex::idrec::open<T_ID, T_SIZE>(stream, ID);
}
static stdex::stream::foff_t open(_In_ stdex::stream::basic_file& stream)
{
return stdex::idrec::open<T_ID, T_SIZE>(stream, ID);
}
static std::streamoff close(_In_ std::ostream& stream, _In_ std::streamoff start)
{
return stdex::idrec::close<T_ID, T_SIZE, ALIGN>(stream, start);
}
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);
}
static bool find(_In_ std::istream& stream, _In_opt_ std::streamoff end = (std::streamoff)-1)
{
return stdex::idrec::find<T_ID, T_SIZE, ALIGN>(stream, ID, end);
}
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;
friend std::ostream& operator <<(_In_ std::ostream& stream, _In_ const 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.fail()) _Unlikely_ return stream;
stream << r.data;
r.close(stream, start);
return stream;
}
friend stdex::stream::basic_file& operator <<(_In_ stdex::stream::basic_file& stream, _In_ const 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;
}
friend stdex::stream::basic& operator <<(_In_ stdex::stream::basic& stream, _In_ const 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.
stdex::stream::memory_file temp;
auto start = r.open(temp);
if (!temp.ok()) _Unlikely_ return stream;
temp << r.data;
r.close(temp, start);
temp.seek(0);
stream.write_stream(temp);
return stream;
}
friend std::istream& operator >>(_In_ std::istream& stream, _In_ 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.read((char*)&size, sizeof(size));
if (!stream.good()) _Unlikely_ 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.
if (!stream.good()) _Unlikely_ return stream;
size += static_cast<T_SIZE>((ALIGN - size) % ALIGN);
stream.seekg(start + size);
return stream;
}
friend stdex::stream::basic_file& operator >>(_In_ stdex::stream::basic_file& stream, _In_ 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, 0);
limiter >> r.data;
if (!limiter.ok()) _Unlikely_ return stream;
}
size += static_cast<T_SIZE>((ALIGN - size) % ALIGN);
stream.seek(start + size);
return stream;
}
friend stdex::stream::basic& operator >>(_In_ stdex::stream::basic& stream, _In_ 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;
{
stdex::stream::limiter limiter(stream, size, 0);
limiter >> r.data;
if (!limiter.ok()) _Unlikely_ return stream;
limiter.skip(limiter.read_limit);
}
stream.skip(static_cast<T_SIZE>((ALIGN - size) % ALIGN));
return stream;
}
};
};
};
stdex::idrec::record
Helper class for read/write of records to/from memory.
Definition idrec.hpp:278
stdex::idrec::record::open
static stdex::stream::foff_t open(stdex::stream::basic_file &stream)
Writes record header.
Definition idrec.hpp:334
stdex::idrec::record::operator>>
friend std::istream & operator>>(std::istream &stream, record< T, T_ID, ID, T_SIZE, ALIGN > r)
Reads record from a stream.
Definition idrec.hpp:468
stdex::idrec::record::close
static stdex::stream::foff_t close(stdex::stream::basic_file &stream, stdex::stream::foff_t start)
Updates record header.
Definition idrec.hpp:360
stdex::idrec::record::find
static bool find(std::istream &stream, std::streamoff end=(std::streamoff) -1)
Finds record data.
Definition idrec.hpp:375
stdex::idrec::record::data
T & data
Record data reference.
Definition idrec.hpp:395
stdex::idrec::record::id
static const T_ID id()
Returns record id.
Definition idrec.hpp:297
stdex::idrec::record::open
static std::streamoff open(std::ostream &stream)
Writes record header.
Definition idrec.hpp:322
stdex::idrec::record::operator=
const record< T, T_ID, ID, T_SIZE, ALIGN > & operator=(const record< T, T_ID, ID, T_SIZE, ALIGN > &r)
Assignment operator.
Definition idrec.hpp:309
stdex::idrec::record::record
record(T &d)
Constructs the class.
Definition idrec.hpp:285
stdex::idrec::record::find
static bool find(stdex::stream::basic_file &stream, stdex::stream::foff_t end=stdex::stream::foff_max)
Finds record data.
Definition idrec.hpp:390
stdex::idrec::record::operator<<
friend std::ostream & operator<<(std::ostream &stream, const record< T, T_ID, ID, T_SIZE, ALIGN > r)
Writes record to a stream.
Definition idrec.hpp:405
stdex::idrec::record::record
record(const T &d)
Constructs the class.
Definition idrec.hpp:292
stdex::idrec::record::close
static std::streamoff close(std::ostream &stream, std::streamoff start)
Updates record header.
Definition idrec.hpp:347
stdex::stream::basic_file
Basic seekable stream operations.
Definition stream.hpp:824
stdex::stream::basic
‍UTF-8 byte-order-mark
Definition stream.hpp:79
stdex::stream::basic::ok
bool ok() const
Returns true if the stream state is clean i.e. previous operation was succesful.
Definition stream.hpp:175
stdex::stream::basic::skip
virtual void skip(fsize_t amount)
Skips given amount of bytes of data on the stream.
Definition stream.hpp:142
stdex::stream::limiter
Limits reading from/writing to stream to a predefined number of bytes.
Definition stream.hpp:1551
stdex::stream::limiter::read_limit
fsize_t read_limit
Number of bytes left that may be read from the stream.
Definition stream.hpp:1603
stdex::stream::memory_file
In-memory file.
Definition stream.hpp:3172
stdex::stream::memory_file::seek
virtual fpos_t seek(foff_t offset, seek_t how=seek_t::beg)
Seeks to specified relative file position.
Definition stream.hpp:3733
stdex::stream::memory_file::data
const void * data() const
Returns pointer to data.
Definition stream.hpp:3382