
* Turned `core_file` into an implementation of `random_read_write`. * Turned PNG errors into a standard error category. * Added a helper for generating what look like derived classes on-the-fly.
895 lines
22 KiB
C++
895 lines
22 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Vas Crabb
|
|
/***************************************************************************
|
|
|
|
ioprocs.h
|
|
|
|
I/O interfaces
|
|
|
|
***************************************************************************/
|
|
|
|
#include "ioprocs.h"
|
|
|
|
#include "corefile.h"
|
|
#include "ioprocsfill.h"
|
|
|
|
#include "osdfile.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cerrno>
|
|
#include <cstring>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
|
|
|
|
namespace util {
|
|
|
|
namespace {
|
|
|
|
// helper for holding a block of memory and deallocating it (or not) as necessary
|
|
|
|
template <typename T, bool Owned>
|
|
class ram_adapter_base : public virtual random_access
|
|
{
|
|
public:
|
|
virtual ~ram_adapter_base()
|
|
{
|
|
if constexpr (Owned)
|
|
std::free(m_data);
|
|
}
|
|
|
|
virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
|
|
{
|
|
switch (whence)
|
|
{
|
|
case SEEK_SET:
|
|
if (0 > offset)
|
|
return std::errc::invalid_argument;
|
|
m_pointer = std::uint64_t(offset);
|
|
return std::error_condition();
|
|
|
|
case SEEK_CUR:
|
|
if (0 > offset)
|
|
{
|
|
if (std::uint64_t(-offset) > m_pointer)
|
|
return std::errc::invalid_argument;
|
|
}
|
|
else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer)
|
|
{
|
|
return std::errc::invalid_argument;
|
|
}
|
|
m_pointer += offset;
|
|
return std::error_condition();
|
|
|
|
case SEEK_END:
|
|
if (0 > offset)
|
|
{
|
|
if (std::uint64_t(-offset) > m_size)
|
|
return std::errc::invalid_argument;
|
|
}
|
|
else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_size)
|
|
{
|
|
return std::errc::invalid_argument;
|
|
}
|
|
m_pointer = std::uint64_t(m_size) + offset;
|
|
return std::error_condition();
|
|
|
|
default:
|
|
return std::errc::invalid_argument;
|
|
}
|
|
}
|
|
|
|
virtual std::error_condition tell(std::uint64_t &result) noexcept override
|
|
{
|
|
result = m_pointer;
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition length(std::uint64_t &result) noexcept override
|
|
{
|
|
if (std::numeric_limits<uint64_t>::max() < m_size)
|
|
return std::errc::file_too_large;
|
|
|
|
result = m_size;
|
|
return std::error_condition();
|
|
}
|
|
|
|
protected:
|
|
template <typename U>
|
|
ram_adapter_base(U *data, std::size_t size) noexcept : m_data(reinterpret_cast<T>(data)), m_size(size)
|
|
{
|
|
static_assert(sizeof(*m_data) == 1U, "Element type must be byte-sized");
|
|
assert(m_data || !m_size);
|
|
}
|
|
|
|
T m_data;
|
|
std::uint64_t m_pointer = 0U;
|
|
std::size_t m_size;
|
|
};
|
|
|
|
|
|
// RAM read implementation
|
|
|
|
template <typename T, bool Owned>
|
|
class ram_read_adapter : public ram_adapter_base<T, Owned>, public virtual random_read
|
|
{
|
|
public:
|
|
template <typename U>
|
|
ram_read_adapter(U *data, std::size_t size) noexcept : ram_adapter_base<T, Owned>(data, size)
|
|
{
|
|
}
|
|
|
|
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
do_read(this->m_pointer, buffer, length, actual);
|
|
this->m_pointer += actual;
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
do_read(offset, buffer, length, actual);
|
|
return std::error_condition();
|
|
}
|
|
|
|
private:
|
|
void do_read(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) const noexcept
|
|
{
|
|
if ((offset < this->m_size) && length)
|
|
{
|
|
actual = std::min(std::size_t(this->m_size - offset), length);
|
|
if constexpr (Owned)
|
|
std::memcpy(buffer, this->m_data + offset, actual);
|
|
else
|
|
std::memmove(buffer, this->m_data + offset, actual);
|
|
}
|
|
else
|
|
{
|
|
actual = 0U;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
// helper for holding a stdio FILE and closing it (or not) as necessary
|
|
|
|
class stdio_adapter_base : public virtual random_access
|
|
{
|
|
public:
|
|
virtual ~stdio_adapter_base()
|
|
{
|
|
if (m_close)
|
|
std::fclose(m_file);
|
|
}
|
|
|
|
virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
|
|
{
|
|
if ((std::numeric_limits<long>::max() < offset) || (std::numeric_limits<long>::min() > offset))
|
|
return std::errc::invalid_argument;
|
|
else if (!std::fseek(m_file, long(offset), whence))
|
|
return std::error_condition();
|
|
else
|
|
return std::error_condition(errno, std::generic_category());
|
|
}
|
|
|
|
virtual std::error_condition tell(std::uint64_t &result) noexcept override
|
|
{
|
|
long const pos = std::ftell(m_file);
|
|
if (0 > pos)
|
|
return std::error_condition(errno, std::generic_category());
|
|
result = static_cast<unsigned long>(pos);
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition length(std::uint64_t &result) noexcept override
|
|
{
|
|
std::fpos_t oldpos;
|
|
if (std::fgetpos(m_file, &oldpos))
|
|
return std::error_condition(errno, std::generic_category());
|
|
|
|
long endpos = -1;
|
|
if (!std::fseek(m_file, 0, SEEK_END))
|
|
{
|
|
m_dangling_read = m_dangling_write = false;
|
|
endpos = std::ftell(m_file);
|
|
}
|
|
std::error_condition err;
|
|
if (0 > endpos)
|
|
err.assign(errno, std::generic_category());
|
|
else if (std::numeric_limits<std::uint64_t>::max() < static_cast<unsigned long>(endpos))
|
|
err = std::errc::file_too_large;
|
|
else
|
|
result = static_cast<unsigned long>(endpos);
|
|
|
|
if (!std::fsetpos(m_file, &oldpos))
|
|
m_dangling_read = m_dangling_write = false;
|
|
else if (!err)
|
|
err.assign(errno, std::generic_category());
|
|
|
|
return err;
|
|
}
|
|
|
|
protected:
|
|
stdio_adapter_base(FILE *file, bool close) noexcept : m_file(file), m_close(close)
|
|
{
|
|
assert(m_file);
|
|
}
|
|
|
|
FILE *file() noexcept
|
|
{
|
|
return m_file;
|
|
}
|
|
|
|
private:
|
|
FILE *const m_file;
|
|
bool const m_close;
|
|
|
|
protected:
|
|
bool m_dangling_read = true, m_dangling_write = true;
|
|
};
|
|
|
|
|
|
// stdio read implementation
|
|
|
|
class stdio_read_adapter : public stdio_adapter_base, public virtual random_read
|
|
{
|
|
public:
|
|
stdio_read_adapter(FILE *file, bool close) noexcept : stdio_adapter_base(file, close)
|
|
{
|
|
}
|
|
|
|
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
if (m_dangling_write)
|
|
{
|
|
if (std::fflush(file()))
|
|
{
|
|
std::clearerr(file());
|
|
actual = 0U;
|
|
return std::error_condition(errno, std::generic_category());
|
|
}
|
|
m_dangling_write = false;
|
|
}
|
|
|
|
actual = std::fread(buffer, sizeof(std::uint8_t), length, file());
|
|
m_dangling_read = true;
|
|
if (length != actual)
|
|
{
|
|
if (std::ferror(file()))
|
|
{
|
|
std::clearerr(file());
|
|
return std::error_condition(errno, std::generic_category());
|
|
}
|
|
else if (std::feof(file()))
|
|
{
|
|
m_dangling_read = false;
|
|
}
|
|
}
|
|
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
actual = 0U;
|
|
|
|
if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset)
|
|
return std::errc::invalid_argument;
|
|
|
|
std::fpos_t oldpos;
|
|
if (std::fgetpos(file(), &oldpos))
|
|
return std::error_condition(errno, std::generic_category());
|
|
|
|
std::error_condition err;
|
|
if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET))
|
|
{
|
|
err.assign(errno, std::generic_category());
|
|
}
|
|
else
|
|
{
|
|
m_dangling_write = false;
|
|
actual = std::fread(buffer, sizeof(std::uint8_t), length, file());
|
|
m_dangling_read = true;
|
|
if (length != actual)
|
|
{
|
|
if (std::ferror(file()))
|
|
{
|
|
err.assign(errno, std::generic_category());
|
|
std::clearerr(file());
|
|
}
|
|
else if (std::feof(file()))
|
|
{
|
|
m_dangling_read = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!std::fsetpos(file(), &oldpos))
|
|
m_dangling_read = m_dangling_write = false;
|
|
else if (!err)
|
|
err.assign(errno, std::generic_category());
|
|
|
|
return err;
|
|
}
|
|
};
|
|
|
|
|
|
// stdio read/write implementation
|
|
|
|
class stdio_read_write_adapter : public stdio_read_adapter, public random_read_write
|
|
{
|
|
public:
|
|
using stdio_read_adapter::stdio_read_adapter;
|
|
|
|
virtual std::error_condition finalize() noexcept override
|
|
{
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition flush() noexcept override
|
|
{
|
|
if (std::fflush(file()))
|
|
{
|
|
std::clearerr(file());
|
|
return std::error_condition(errno, std::generic_category());
|
|
}
|
|
m_dangling_write = false;
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
if (m_dangling_read)
|
|
{
|
|
if (std::fseek(file(), 0, SEEK_CUR))
|
|
{
|
|
actual = 0U;
|
|
return std::error_condition(errno, std::generic_category());
|
|
}
|
|
m_dangling_read = false;
|
|
}
|
|
|
|
actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file());
|
|
m_dangling_write = true;
|
|
if (length != actual)
|
|
{
|
|
std::clearerr(file());
|
|
return std::error_condition(errno, std::generic_category());
|
|
}
|
|
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
actual = 0U;
|
|
|
|
if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset)
|
|
return std::errc::invalid_argument;
|
|
|
|
std::fpos_t oldpos;
|
|
if (std::fgetpos(file(), &oldpos))
|
|
return std::error_condition(errno, std::generic_category());
|
|
|
|
std::error_condition err;
|
|
if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET))
|
|
{
|
|
err.assign(errno, std::generic_category());
|
|
}
|
|
else
|
|
{
|
|
m_dangling_read = false;
|
|
actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file());
|
|
m_dangling_write = true;
|
|
if (length != actual)
|
|
{
|
|
err.assign(errno, std::generic_category());
|
|
std::clearerr(file());
|
|
}
|
|
}
|
|
|
|
if (!std::fsetpos(file(), &oldpos))
|
|
m_dangling_read = m_dangling_write = false;
|
|
else if (!err)
|
|
err.assign(errno, std::generic_category());
|
|
|
|
return err;
|
|
}
|
|
};
|
|
|
|
|
|
// stdio helper that fills space when writing past the end-of-file
|
|
|
|
class stdio_read_write_filler : public random_read_fill_wrapper<stdio_read_write_adapter>
|
|
{
|
|
public:
|
|
stdio_read_write_filler(FILE *file, bool close, std::uint8_t fill) noexcept : random_read_fill_wrapper<stdio_read_write_adapter>(file, close)
|
|
{
|
|
set_filler(fill);
|
|
}
|
|
|
|
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
actual = 0U;
|
|
|
|
long const offset = std::ftell(file());
|
|
if (0 > offset)
|
|
return std::error_condition(errno, std::generic_category());
|
|
|
|
if (std::fseek(file(), 0, SEEK_END))
|
|
{
|
|
std::error_condition err(errno, std::generic_category());
|
|
if (!std::fseek(file(), offset, SEEK_SET))
|
|
m_dangling_read = m_dangling_write = false;
|
|
return err;
|
|
}
|
|
m_dangling_read = m_dangling_write = false;
|
|
long endpos = std::ftell(file());
|
|
if (0 > endpos)
|
|
{
|
|
std::error_condition err(errno, std::generic_category());
|
|
std::fseek(file(), offset, SEEK_SET);
|
|
return err;
|
|
}
|
|
|
|
if (offset > endpos)
|
|
{
|
|
std::uint8_t block[1024];
|
|
std::fill_n(
|
|
block,
|
|
std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos),
|
|
get_filler());
|
|
do
|
|
{
|
|
std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos);
|
|
std::size_t const filled = std::fwrite(block, sizeof(block[0]), chunk, file());
|
|
endpos += filled;
|
|
m_dangling_write = true;
|
|
if (chunk != filled)
|
|
{
|
|
std::error_condition err(errno, std::generic_category());
|
|
std::clearerr(file());
|
|
if (!std::fseek(file(), offset, SEEK_SET))
|
|
m_dangling_write = false;
|
|
return err;
|
|
}
|
|
}
|
|
while (static_cast<unsigned long>(endpos) < offset);
|
|
}
|
|
else if ((offset < endpos) && std::fseek(file(), offset, SEEK_SET))
|
|
{
|
|
return std::error_condition(errno, std::generic_category());
|
|
}
|
|
|
|
actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file());
|
|
m_dangling_write = true;
|
|
if (length != actual)
|
|
{
|
|
std::clearerr(file());
|
|
return std::error_condition(errno, std::generic_category());
|
|
}
|
|
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
actual = 0U;
|
|
|
|
if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset)
|
|
return std::errc::invalid_argument;
|
|
|
|
std::fpos_t oldpos;
|
|
if (std::fgetpos(file(), &oldpos))
|
|
return std::error_condition(errno, std::generic_category());
|
|
|
|
std::error_condition err;
|
|
if (std::fseek(file(), 0, SEEK_END))
|
|
{
|
|
err.assign(errno, std::generic_category());
|
|
}
|
|
else
|
|
{
|
|
m_dangling_read = m_dangling_write = false;
|
|
long endpos = std::ftell(file());
|
|
if (0 > endpos)
|
|
{
|
|
err.assign(errno, std::generic_category());
|
|
}
|
|
else if (static_cast<unsigned long>(endpos) > offset)
|
|
{
|
|
if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET))
|
|
err.assign(errno, std::generic_category());
|
|
}
|
|
else if (static_cast<unsigned long>(endpos) < offset)
|
|
{
|
|
std::uint8_t block[1024];
|
|
std::fill_n(
|
|
block,
|
|
std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos),
|
|
get_filler());
|
|
do
|
|
{
|
|
std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos);
|
|
std::size_t const filled = std::fwrite(block, sizeof(block[0]), chunk, file());
|
|
endpos += filled;
|
|
m_dangling_write = true;
|
|
if (chunk != filled)
|
|
{
|
|
err.assign(errno, std::generic_category());
|
|
std::clearerr(file());
|
|
}
|
|
}
|
|
while (!err && (static_cast<unsigned long>(endpos) < offset));
|
|
}
|
|
}
|
|
|
|
if (!err)
|
|
{
|
|
actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file());
|
|
m_dangling_write = true;
|
|
if (length != actual)
|
|
{
|
|
err.assign(errno, std::generic_category());
|
|
std::clearerr(file());
|
|
}
|
|
}
|
|
|
|
if (!std::fsetpos(file(), &oldpos))
|
|
m_dangling_read = m_dangling_write = false;
|
|
else if (!err)
|
|
err.assign(errno, std::generic_category());
|
|
|
|
return err;
|
|
}
|
|
};
|
|
|
|
|
|
// helper class for holding an osd_file and closing it (or not) as necessary
|
|
|
|
class osd_file_adapter_base : public virtual random_access
|
|
{
|
|
public:
|
|
virtual ~osd_file_adapter_base()
|
|
{
|
|
if (m_close)
|
|
delete m_file;
|
|
}
|
|
|
|
virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
|
|
{
|
|
switch (whence)
|
|
{
|
|
case SEEK_SET:
|
|
if (0 > offset)
|
|
return std::errc::invalid_argument;
|
|
m_pointer = std::uint64_t(offset);
|
|
return std::error_condition();
|
|
|
|
case SEEK_CUR:
|
|
if (0 > offset)
|
|
{
|
|
if (std::uint64_t(-offset) > m_pointer)
|
|
return std::errc::invalid_argument;
|
|
}
|
|
else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer)
|
|
{
|
|
return std::errc::invalid_argument;
|
|
}
|
|
m_pointer += offset;
|
|
return std::error_condition();
|
|
|
|
// TODO: add SEEK_END when osd_file can support it - should it return a different error?
|
|
default:
|
|
return std::errc::invalid_argument;
|
|
}
|
|
}
|
|
|
|
virtual std::error_condition tell(std::uint64_t &result) noexcept override
|
|
{
|
|
result = m_pointer;
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition length(std::uint64_t &result) noexcept override
|
|
{
|
|
// not supported by osd_file
|
|
return std::errc::not_supported; // TODO: revisit this error code
|
|
}
|
|
|
|
protected:
|
|
osd_file_adapter_base(osd_file::ptr &&file) noexcept : m_file(file.release()), m_close(true)
|
|
{
|
|
assert(m_file);
|
|
}
|
|
|
|
osd_file_adapter_base(osd_file &file) noexcept : m_file(&file), m_close(false)
|
|
{
|
|
}
|
|
|
|
osd_file &file() noexcept
|
|
{
|
|
return *m_file;
|
|
}
|
|
|
|
std::uint64_t m_pointer = 0U;
|
|
|
|
private:
|
|
osd_file *const m_file;
|
|
bool const m_close;
|
|
};
|
|
|
|
|
|
// osd_file read implementation
|
|
|
|
class osd_file_read_adapter : public osd_file_adapter_base, public virtual random_read
|
|
{
|
|
public:
|
|
osd_file_read_adapter(osd_file::ptr &&file) noexcept : osd_file_adapter_base(std::move(file))
|
|
{
|
|
}
|
|
|
|
osd_file_read_adapter(osd_file &file) noexcept : osd_file_adapter_base(file)
|
|
{
|
|
}
|
|
|
|
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
// TODO: should the client have to deal with reading less than expected even if EOF isn't hit?
|
|
if (std::numeric_limits<std::uint32_t>::max() < length)
|
|
{
|
|
actual = 0U;
|
|
return std::errc::invalid_argument;
|
|
}
|
|
|
|
// actual length not valid on error
|
|
std::uint32_t count;
|
|
std::error_condition err = file().read(buffer, m_pointer, std::uint32_t(length), count);
|
|
if (!err)
|
|
{
|
|
m_pointer += count;
|
|
actual = std::size_t(count);
|
|
}
|
|
else
|
|
{
|
|
actual = 0U;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
// TODO: should the client have to deal with reading less than expected even if EOF isn't hit?
|
|
if (std::numeric_limits<std::uint32_t>::max() < length)
|
|
{
|
|
actual = 0U;
|
|
return std::errc::invalid_argument;
|
|
}
|
|
|
|
// actual length not valid on error
|
|
std::uint32_t count;
|
|
std::error_condition err = file().read(buffer, offset, std::uint32_t(length), count);
|
|
if (!err)
|
|
actual = std::size_t(count);
|
|
else
|
|
actual = 0U;
|
|
return err;
|
|
}
|
|
};
|
|
|
|
|
|
// osd_file read/write implementation
|
|
|
|
class osd_file_read_write_adapter : public osd_file_read_adapter, public random_read_write
|
|
{
|
|
public:
|
|
using osd_file_read_adapter::osd_file_read_adapter;
|
|
|
|
virtual std::error_condition finalize() noexcept override
|
|
{
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition flush() noexcept override
|
|
{
|
|
return file().flush();
|
|
}
|
|
|
|
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
actual = 0U;
|
|
while (length)
|
|
{
|
|
// actual length not valid on error
|
|
std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
|
|
std::uint32_t written;
|
|
std::error_condition err = file().write(buffer, m_pointer, chunk, written);
|
|
if (err)
|
|
return err;
|
|
m_pointer += written;
|
|
buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
|
|
length -= written;
|
|
actual += written;
|
|
}
|
|
return std::error_condition();
|
|
}
|
|
|
|
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
|
{
|
|
actual = 0U;
|
|
while (length)
|
|
{
|
|
// actual length not valid on error
|
|
std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
|
|
std::uint32_t written;
|
|
std::error_condition err = file().write(buffer, offset, chunk, written);
|
|
if (err)
|
|
return err;
|
|
offset += written;
|
|
buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
|
|
length -= written;
|
|
actual += written;
|
|
}
|
|
return std::error_condition();
|
|
}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
// creating RAM read adapters
|
|
|
|
random_read::ptr ram_read(void const *data, std::size_t size) noexcept
|
|
{
|
|
random_read::ptr result;
|
|
if (data || !size)
|
|
result.reset(new (std::nothrow) ram_read_adapter<std::uint8_t const *const, false>(data, size));
|
|
return result;
|
|
}
|
|
|
|
random_read::ptr ram_read(void const *data, std::size_t size, std::uint8_t filler) noexcept
|
|
{
|
|
std::unique_ptr<random_read_fill_wrapper<ram_read_adapter<std::uint8_t const *const, false> > > result;
|
|
if (data || !size)
|
|
result.reset(new (std::nothrow) decltype(result)::element_type(data, size));
|
|
if (result)
|
|
result->set_filler(filler);
|
|
return result;
|
|
}
|
|
|
|
random_read::ptr ram_read_copy(void const *data, std::size_t size) noexcept
|
|
{
|
|
random_read::ptr result;
|
|
void *const copy = size ? std::malloc(size) : nullptr;
|
|
if (copy)
|
|
std::memcpy(copy, data, size);
|
|
if (copy || !size)
|
|
result.reset(new (std::nothrow) ram_read_adapter<std::uint8_t *const, true>(copy, size));
|
|
if (!result)
|
|
std::free(copy);
|
|
return result;
|
|
}
|
|
|
|
random_read::ptr ram_read_copy(void const *data, std::size_t size, std::uint8_t filler) noexcept
|
|
{
|
|
std::unique_ptr<random_read_fill_wrapper<ram_read_adapter<std::uint8_t *const, true> > > result;
|
|
void *const copy = size ? std::malloc(size) : nullptr;
|
|
if (copy)
|
|
std::memcpy(copy, data, size);
|
|
if (copy || !size)
|
|
result.reset(new (std::nothrow) decltype(result)::element_type(copy, size));
|
|
if (!result)
|
|
std::free(copy);
|
|
return result;
|
|
}
|
|
|
|
|
|
// creating stdio read adapters
|
|
|
|
random_read::ptr stdio_read(FILE *file) noexcept
|
|
{
|
|
random_read::ptr result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) stdio_read_adapter(file, true));
|
|
return result;
|
|
}
|
|
|
|
random_read::ptr stdio_read(FILE *file, std::uint8_t filler) noexcept
|
|
{
|
|
std::unique_ptr<random_read_fill_wrapper<stdio_read_adapter> > result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) decltype(result)::element_type(file, true));
|
|
if (result)
|
|
result->set_filler(filler);
|
|
return result;
|
|
}
|
|
|
|
random_read::ptr stdio_read_noclose(FILE *file) noexcept
|
|
{
|
|
random_read::ptr result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) stdio_read_adapter(file, false));
|
|
return result;
|
|
}
|
|
|
|
random_read::ptr stdio_read_noclose(FILE *file, std::uint8_t filler) noexcept
|
|
{
|
|
std::unique_ptr<random_read_fill_wrapper<stdio_read_adapter> > result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) decltype(result)::element_type(file, false));
|
|
if (result)
|
|
result->set_filler(filler);
|
|
return result;
|
|
}
|
|
|
|
|
|
// creating stdio read/write adapters
|
|
|
|
random_read_write::ptr stdio_read_write(FILE *file) noexcept
|
|
{
|
|
random_read_write::ptr result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) stdio_read_write_adapter(file, true));
|
|
return result;
|
|
}
|
|
|
|
random_read_write::ptr stdio_read_write(FILE *file, std::uint8_t filler) noexcept
|
|
{
|
|
random_read_write::ptr result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) stdio_read_write_filler(file, true, filler));
|
|
return result;
|
|
}
|
|
|
|
random_read_write::ptr stdio_read_write_noclose(FILE *file) noexcept
|
|
{
|
|
random_read_write::ptr result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) stdio_read_write_adapter(file, false));
|
|
return result;
|
|
}
|
|
|
|
random_read_write::ptr stdio_read_write_noclose(FILE *file, std::uint8_t filler) noexcept
|
|
{
|
|
random_read_write::ptr result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) stdio_read_write_filler(file, false, filler));
|
|
return result;
|
|
}
|
|
|
|
|
|
// creating osd_file read adapters
|
|
|
|
random_read::ptr osd_file_read(osd_file::ptr &&file) noexcept
|
|
{
|
|
random_read::ptr result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) osd_file_read_adapter(std::move(file)));
|
|
return result;
|
|
}
|
|
|
|
random_read::ptr osd_file_read(osd_file &file) noexcept
|
|
{
|
|
return random_read::ptr(new (std::nothrow) osd_file_read_adapter(file));
|
|
}
|
|
|
|
|
|
// creating osd_file read/write adapters
|
|
|
|
random_read_write::ptr osd_file_read_write(osd_file::ptr &&file) noexcept
|
|
{
|
|
random_read_write::ptr result;
|
|
if (file)
|
|
result.reset(new (std::nothrow) osd_file_read_write_adapter(std::move(file)));
|
|
return result;
|
|
}
|
|
|
|
random_read_write::ptr osd_file_read_write(osd_file &file) noexcept
|
|
{
|
|
return random_read_write::ptr(new (std::nothrow) osd_file_read_write_adapter(file));
|
|
}
|
|
|
|
} // namespace util
|