summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/corefile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/corefile.cpp')
-rw-r--r--src/lib/util/corefile.cpp581
1 files changed, 374 insertions, 207 deletions
diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp
index 0c72e1337be..6406aea49f4 100644
--- a/src/lib/util/corefile.cpp
+++ b/src/lib/util/corefile.cpp
@@ -21,6 +21,7 @@
#include <cctype>
#include <cstring>
#include <iterator>
+#include <limits>
namespace util {
@@ -45,26 +46,30 @@ class core_proxy_file : public core_file
{
public:
core_proxy_file(core_file &file) noexcept : m_file(file) { }
- virtual ~core_proxy_file() override { }
- virtual int seek(std::int64_t offset, int whence) override { return m_file.seek(offset, whence); }
- virtual std::uint64_t tell() const override { return m_file.tell(); }
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override { return m_file.seek(offset, whence); }
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override { return m_file.tell(result); }
+ virtual std::error_condition length(std::uint64_t &result) noexcept override { return m_file.length(result); }
+
+ virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read(buffer, length, actual); }
+ virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read_at(offset, buffer, length, actual); }
+
+ virtual std::error_condition finalize() noexcept override { return m_file.finalize(); }
+ virtual std::error_condition flush() noexcept override { return m_file.flush(); }
+ virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write(buffer, length, actual); }
+ virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write_at(offset, buffer, length, actual); }
+
virtual bool eof() const override { return m_file.eof(); }
- virtual std::uint64_t size() const override { return m_file.size(); }
- virtual std::uint32_t read(void *buffer, std::uint32_t length) override { return m_file.read(buffer, length); }
virtual int getc() override { return m_file.getc(); }
virtual int ungetc(int c) override { return m_file.ungetc(c); }
virtual char *gets(char *s, int n) override { return m_file.gets(s, n); }
- virtual const void *buffer() override { return m_file.buffer(); }
+ virtual void const *buffer() override { return m_file.buffer(); }
- virtual std::uint32_t write(const void *buffer, std::uint32_t length) override { return m_file.write(buffer, length); }
virtual int puts(std::string_view s) override { return m_file.puts(s); }
virtual int vprintf(util::format_argument_pack<std::ostream> const &args) override { return m_file.vprintf(args); }
virtual std::error_condition truncate(std::uint64_t offset) override { return m_file.truncate(offset); }
- virtual std::error_condition flush() override { return m_file.flush(); }
-
private:
core_file &m_file;
};
@@ -99,12 +104,12 @@ protected:
{
}
- bool read_access() const { return 0U != (m_openflags & OPEN_FLAG_READ); }
- bool write_access() const { return 0U != (m_openflags & OPEN_FLAG_WRITE); }
- bool no_bom() const { return 0U != (m_openflags & OPEN_FLAG_NO_BOM); }
+ bool read_access() const noexcept { return 0U != (m_openflags & OPEN_FLAG_READ); }
+ bool write_access() const noexcept { return 0U != (m_openflags & OPEN_FLAG_WRITE); }
+ bool no_bom() const noexcept { return 0U != (m_openflags & OPEN_FLAG_NO_BOM); }
- bool has_putback() const { return m_back_char_head != m_back_char_tail; }
- void clear_putback() { m_back_char_head = m_back_char_tail = 0; }
+ bool has_putback() const noexcept { return m_back_char_head != m_back_char_tail; }
+ void clear_putback() noexcept { m_back_char_head = m_back_char_tail = 0; }
private:
std::uint32_t const m_openflags; // flags we were opened with
@@ -119,7 +124,7 @@ private:
class core_in_memory_file : public core_text_file
{
public:
- core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy)
+ core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) noexcept
: core_text_file(openflags)
, m_data_allocated(false)
, m_data(copy ? nullptr : data)
@@ -136,20 +141,26 @@ public:
~core_in_memory_file() override { purge(); }
- virtual int seek(std::int64_t offset, int whence) override;
- virtual std::uint64_t tell() const override { return m_offset; }
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override;
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override { result = m_offset; return std::error_condition(); }
+ virtual std::error_condition length(std::uint64_t &result) noexcept override { result = m_length; return std::error_condition(); }
+
+ virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+
+ virtual std::error_condition finalize() noexcept override { return std::error_condition(); }
+ virtual std::error_condition flush() noexcept override { clear_putback(); return std::error_condition(); }
+ virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; }
+ virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; }
+
virtual bool eof() const override;
- virtual std::uint64_t size() const override { return m_length; }
- virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
virtual void const *buffer() override { return m_data; }
- virtual std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; }
virtual std::error_condition truncate(std::uint64_t offset) override;
- virtual std::error_condition flush() override { clear_putback(); return std::error_condition(); }
protected:
- core_in_memory_file(std::uint32_t openflags, std::uint64_t length)
+ core_in_memory_file(std::uint32_t openflags, std::uint64_t length) noexcept
: core_text_file(openflags)
, m_data_allocated(false)
, m_data(nullptr)
@@ -158,10 +169,11 @@ protected:
{
}
- bool is_loaded() const { return nullptr != m_data; }
- void *allocate()
+ bool is_loaded() const noexcept { return nullptr != m_data; }
+
+ void *allocate() noexcept
{
- if (m_data)
+ if (m_data || (std::numeric_limits<std::size_t>::max() < m_length))
return nullptr;
void *data = malloc(m_length);
if (data)
@@ -171,7 +183,8 @@ protected:
}
return data;
}
- void purge()
+
+ void purge() noexcept
{
if (m_data && m_data_allocated)
free(const_cast<void *>(m_data));
@@ -179,14 +192,14 @@ protected:
m_data = nullptr;
}
- std::uint64_t offset() const { return m_offset; }
- void add_offset(std::uint32_t increment) { m_offset += increment; m_length = (std::max)(m_length, m_offset); }
- std::uint64_t length() const { return m_length; }
- void set_length(std::uint64_t value) { m_length = value; m_offset = (std::min)(m_offset, m_length); }
+ std::uint64_t offset() const noexcept { return m_offset; }
+ void add_offset(std::size_t increment) noexcept { m_offset += increment; m_length = (std::max)(m_length, m_offset); }
+ std::uint64_t length() const noexcept { return m_length; }
+ void set_length(std::uint64_t value) noexcept { m_length = value; }
static std::size_t safe_buffer_copy(
void const *source, std::size_t sourceoffs, std::size_t sourcelen,
- void *dest, std::size_t destoffs, std::size_t destlen);
+ void *dest, std::size_t destoffs, std::size_t destlen) noexcept;
private:
bool m_data_allocated; // was the data allocated by us?
@@ -196,35 +209,38 @@ private:
};
-class core_osd_file : public core_in_memory_file
+class core_osd_file final : public core_in_memory_file
{
public:
core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length)
: core_in_memory_file(openmode, length)
, m_file(std::move(file))
- , m_bufferbase(0)
- , m_bufferbytes(0)
{
}
~core_osd_file() override;
- virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
+ virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+
+ virtual std::error_condition finalize() noexcept override;
+ virtual std::error_condition flush() noexcept override;
+ virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
+
virtual void const *buffer() override;
- virtual std::uint32_t write(void const *buffer, std::uint32_t length) override;
virtual std::error_condition truncate(std::uint64_t offset) override;
- virtual std::error_condition flush() override;
protected:
- bool is_buffered() const { return (offset() >= m_bufferbase) && (offset() < (m_bufferbase + m_bufferbytes)); }
+ bool is_buffered(std::uint64_t offset) const noexcept { return (offset >= m_bufferbase) && (offset < (m_bufferbase + m_bufferbytes)); }
private:
static constexpr std::size_t FILE_BUFFER_SIZE = 512;
osd_file::ptr m_file; // OSD file handle
- std::uint64_t m_bufferbase; // base offset of internal buffer
- std::uint32_t m_bufferbytes; // bytes currently loaded into buffer
+ std::uint64_t m_bufferbase = 0U; // base offset of internal buffer
+ std::uint32_t m_bufferbytes = 0U; // bytes currently loaded into buffer
std::uint8_t m_buffer[FILE_BUFFER_SIZE]; // buffer data
};
@@ -240,54 +256,57 @@ private:
int core_text_file::getc()
{
- int result;
-
// refresh buffer, if necessary
if (m_back_char_head == m_back_char_tail)
{
// do we need to check the byte order marks?
- if (tell() == 0)
+ std::uint64_t pos;
+ if (!tell(pos))
{
- std::uint8_t bom[4];
- int pos = 0;
-
- if (read(bom, 4) == 4)
+ if (!pos)
{
- if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
- {
- m_text_type = text_file_type::UTF8;
- pos = 3;
- }
- else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff)
- {
- m_text_type = text_file_type::UTF32BE;
- pos = 4;
- }
- else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00)
+ std::size_t readlen;
+ std::uint8_t bom[4];
+ read(bom, 4, readlen);
+ if (readlen == 4)
{
- m_text_type = text_file_type::UTF32LE;
- pos = 4;
- }
- else if (bom[0] == 0xfe && bom[1] == 0xff)
- {
- m_text_type = text_file_type::UTF16BE;
- pos = 2;
- }
- else if (bom[0] == 0xff && bom[1] == 0xfe)
- {
- m_text_type = text_file_type::UTF16LE;
- pos = 2;
- }
- else
- {
- m_text_type = text_file_type::OSD;
- pos = 0;
+ if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
+ {
+ m_text_type = text_file_type::UTF8;
+ pos = 3;
+ }
+ else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff)
+ {
+ m_text_type = text_file_type::UTF32BE;
+ pos = 4;
+ }
+ else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00)
+ {
+ m_text_type = text_file_type::UTF32LE;
+ pos = 4;
+ }
+ else if (bom[0] == 0xfe && bom[1] == 0xff)
+ {
+ m_text_type = text_file_type::UTF16BE;
+ pos = 2;
+ }
+ else if (bom[0] == 0xff && bom[1] == 0xfe)
+ {
+ m_text_type = text_file_type::UTF16LE;
+ pos = 2;
+ }
+ else
+ {
+ m_text_type = text_file_type::OSD;
+ pos = 0;
+ }
}
+ seek(pos, SEEK_SET); // FIXME: don't assume seeking is possible, check for errors
}
- seek(pos, SEEK_SET);
}
// fetch the next character
+ // FIXME: all of this plays fast and loose with error checking and seeks backwards far too frequently
char16_t utf16_buffer[UTF16_CHAR_MAX];
auto uchar = char32_t(~0);
switch (m_text_type)
@@ -296,7 +315,8 @@ int core_text_file::getc()
case text_file_type::OSD:
{
char default_buffer[16];
- auto const readlen = read(default_buffer, sizeof(default_buffer));
+ std::size_t readlen;
+ read(default_buffer, sizeof(default_buffer), readlen);
if (readlen > 0)
{
auto const charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0]));
@@ -308,7 +328,8 @@ int core_text_file::getc()
case text_file_type::UTF8:
{
char utf8_buffer[UTF8_CHAR_MAX];
- auto const readlen = read(utf8_buffer, sizeof(utf8_buffer));
+ std::size_t readlen;
+ read(utf8_buffer, sizeof(utf8_buffer), readlen);
if (readlen > 0)
{
auto const charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0]));
@@ -319,7 +340,8 @@ int core_text_file::getc()
case text_file_type::UTF16BE:
{
- auto const readlen = read(utf16_buffer, sizeof(utf16_buffer));
+ std::size_t readlen;
+ read(utf16_buffer, sizeof(utf16_buffer), readlen);
if (readlen > 0)
{
auto const charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
@@ -330,7 +352,8 @@ int core_text_file::getc()
case text_file_type::UTF16LE:
{
- auto const readlen = read(utf16_buffer, sizeof(utf16_buffer));
+ std::size_t readlen;
+ read(utf16_buffer, sizeof(utf16_buffer), readlen);
if (readlen > 0)
{
auto const charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
@@ -340,13 +363,23 @@ int core_text_file::getc()
break;
case text_file_type::UTF32BE:
- if (read(&uchar, sizeof(uchar)) == sizeof(uchar))
- uchar = big_endianize_int32(uchar);
+ {
+ // FIXME: deal with read returning short
+ std::size_t readlen;
+ read(&uchar, sizeof(uchar), readlen);
+ if (sizeof(uchar) == readlen)
+ uchar = big_endianize_int32(uchar);
+ }
break;
case text_file_type::UTF32LE:
- if (read(&uchar, sizeof(uchar)) == sizeof(uchar))
- uchar = little_endianize_int32(uchar);
+ {
+ // FIXME: deal with read returning short
+ std::size_t readlen;
+ read(&uchar, sizeof(uchar), readlen);
+ if (sizeof(uchar) == readlen)
+ uchar = little_endianize_int32(uchar);
+ }
break;
}
@@ -360,6 +393,7 @@ int core_text_file::getc()
}
// now read from the ring buffer
+ int result;
if (m_back_char_head == m_back_char_tail)
result = EOF;
else
@@ -433,21 +467,32 @@ char *core_text_file::gets(char *s, int n)
/*-------------------------------------------------
- puts - write a line to a text file
+ puts - write a string to a text file
-------------------------------------------------*/
int core_text_file::puts(std::string_view s)
{
+ // TODO: what to do about write errors or short writes (interrupted)?
+ // The API doesn't lend itself to reporting the error as the return
+ // value includes extra bytes inserted like the UTF-8 marker and
+ // carriage returns.
char convbuf[1024];
char *pconvbuf = convbuf;
int count = 0;
// is this the beginning of the file? if so, write a byte order mark
- if (tell() == 0 && !no_bom())
+ if (!no_bom())
{
- *pconvbuf++ = char(0xef);
- *pconvbuf++ = char(0xbb);
- *pconvbuf++ = char(0xbf);
+ std::uint64_t offset;
+ if (!tell(offset))
+ {
+ if (!offset)
+ {
+ *pconvbuf++ = char(0xef);
+ *pconvbuf++ = char(0xbb);
+ *pconvbuf++ = char(0xbf);
+ }
+ }
}
// convert '\n' to platform dependant line endings
@@ -471,14 +516,20 @@ int core_text_file::puts(std::string_view s)
// if we overflow, break into chunks
if (pconvbuf >= convbuf + std::size(convbuf) - 10)
{
- count += write(convbuf, pconvbuf - convbuf);
+ std::size_t written;
+ write(convbuf, pconvbuf - convbuf, written); // FIXME: error ignored here
+ count += written;
pconvbuf = convbuf;
}
}
// final flush
if (pconvbuf != convbuf)
- count += write(convbuf, pconvbuf - convbuf);
+ {
+ std::size_t written;
+ write(convbuf, pconvbuf - convbuf, written); // FIXME: error ignored here
+ count += written;
+ }
return count;
}
@@ -507,27 +558,49 @@ int core_text_file::vprintf(util::format_argument_pack<std::ostream> const &args
seek - seek within a file
-------------------------------------------------*/
-int core_in_memory_file::seek(std::int64_t offset, int whence)
+std::error_condition core_in_memory_file::seek(std::int64_t offset, int whence) noexcept
{
// flush any buffered char
- clear_putback();
+ clear_putback(); // TODO: report errors; also, should the argument check happen before this?
// switch off the relative location
switch (whence)
{
case SEEK_SET:
+ if (0 > offset)
+ return std::errc::invalid_argument;
m_offset = offset;
- break;
+ return std::error_condition();
case SEEK_CUR:
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_offset)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_offset)
+ {
+ return std::errc::invalid_argument;
+ }
m_offset += offset;
- break;
+ return std::error_condition();
case SEEK_END:
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_length)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_length)
+ {
+ return std::errc::invalid_argument;
+ }
m_offset = m_length + offset;
- break;
+ return std::error_condition();
+
+ default:
+ return std::errc::invalid_argument;
}
- return 0;
}
@@ -550,14 +623,29 @@ bool core_in_memory_file::eof() const
read - read from a file
-------------------------------------------------*/
-std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length)
+std::error_condition core_in_memory_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept
{
clear_putback();
// handle RAM-based files
- auto const bytes_read = safe_buffer_copy(m_data, std::size_t(m_offset), std::size_t(m_length), buffer, 0, length);
- m_offset += bytes_read;
- return bytes_read;
+ if (m_offset < m_length)
+ actual = safe_buffer_copy(m_data, std::size_t(m_offset), std::size_t(m_length), buffer, 0, length);
+ else
+ actual = 0U;
+ m_offset += actual;
+ return std::error_condition();
+}
+
+std::error_condition core_in_memory_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
+{
+ clear_putback();
+
+ // handle RAM-based files
+ if (offset < m_length)
+ actual = safe_buffer_copy(m_data, std::size_t(offset), std::size_t(m_length), buffer, 0, length);
+ else
+ actual = 0U;
+ return std::error_condition();
}
@@ -583,7 +671,7 @@ std::error_condition core_in_memory_file::truncate(std::uint64_t offset)
std::size_t core_in_memory_file::safe_buffer_copy(
void const *source, std::size_t sourceoffs, std::size_t sourcelen,
- void *dest, std::size_t destoffs, std::size_t destlen)
+ void *dest, std::size_t destoffs, std::size_t destlen) noexcept
{
auto const sourceavail = sourcelen - sourceoffs;
auto const destavail = destlen - destoffs;
@@ -618,45 +706,65 @@ core_osd_file::~core_osd_file()
read - read from a file
-------------------------------------------------*/
-std::uint32_t core_osd_file::read(void *buffer, std::uint32_t length)
+std::error_condition core_osd_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept
+{
+ // since osd_file works like pread/pwrite, implement in terms of read_at
+ // core_osd_file is delcared final, so a derived class can't interfere
+ std::error_condition err = read_at(offset(), buffer, length, actual);
+ add_offset(actual);
+ return err;
+}
+
+
+std::error_condition core_osd_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
{
if (!m_file || is_loaded())
- return core_in_memory_file::read(buffer, length);
+ return core_in_memory_file::read_at(offset, buffer, length, actual);
// flush any buffered char
clear_putback();
- std::uint32_t bytes_read = 0;
+ actual = 0U;
+ std::error_condition err;
// if we're within the buffer, consume that first
- if (is_buffered())
- bytes_read += safe_buffer_copy(m_buffer, offset() - m_bufferbase, m_bufferbytes, buffer, bytes_read, length);
+ if (is_buffered(offset))
+ actual += safe_buffer_copy(m_buffer, offset - m_bufferbase, m_bufferbytes, buffer, actual, length);
// if we've got a small amount left, read it into the buffer first
- if (bytes_read < length)
+ if (actual < length)
{
- if ((length - bytes_read) < (sizeof(m_buffer) / 2))
+ if ((length - actual) < (sizeof(m_buffer) / 2))
{
// read as much as makes sense into the buffer
- m_bufferbase = offset() + bytes_read;
- m_bufferbytes = 0;
- m_file->read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes);
-
- // do a bounded copy from the buffer to the destination
- bytes_read += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, bytes_read, length);
+ m_bufferbase = offset + actual;
+ err = m_file->read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes);
+
+ // do a bounded copy from the buffer to the destination if it succeeded
+ if (!err)
+ actual += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, actual, length);
+ else
+ m_bufferbytes = 0U;
}
else
{
// read the remainder directly from the file
- std::uint32_t new_bytes_read = 0;
- m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read);
- bytes_read += new_bytes_read;
+ do
+ {
+ // may need to split into chunks if size_t is larger than 32 bits
+ 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 - actual);
+ std::uint32_t bytes_read;
+ err = m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + actual, offset + actual, chunk, bytes_read);
+ if (err || !bytes_read)
+ break;
+ actual += bytes_read;
+ }
+ while (actual < length);
}
}
- // return the number of bytes read
- add_offset(bytes_read);
- return bytes_read;
+ // return any errors
+ return err;
}
@@ -672,17 +780,29 @@ void const *core_osd_file::buffer()
if (!is_loaded() && length())
{
// allocate some memory
- void *buf = allocate();
+ void *const buf = allocate();
if (!buf)
return nullptr;
// read the file
- std::uint32_t read_length = 0;
- auto const filerr = m_file->read(buf, 0, length(), read_length);
- if (filerr || (read_length != length()))
- purge();
- else
- m_file.reset(); // close the file because we don't need it anymore
+ std::uint64_t bytes_read = 0;
+ std::uint64_t remaining = length();
+ std::uint8_t *ptr = reinterpret_cast<std::uint8_t *>(buf);
+ while (remaining)
+ {
+ std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), remaining);
+ std::uint32_t read_length;
+ std::error_condition const filerr = m_file->read(ptr, bytes_read, chunk, read_length);
+ if (filerr || !read_length)
+ {
+ purge();
+ return core_in_memory_file::buffer();
+ }
+ bytes_read += read_length;
+ remaining -= read_length;
+ ptr += read_length;
+ }
+ m_file.reset(); // close the file because we don't need it anymore
}
return core_in_memory_file::buffer();
}
@@ -692,25 +812,46 @@ void const *core_osd_file::buffer()
write - write to a file
-------------------------------------------------*/
-std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length)
+std::error_condition core_osd_file::write(void const *buffer, std::size_t length, std::size_t &actual) noexcept
+{
+ // since osd_file works like pread/pwrite, implement in terms of write_at
+ // core_osd_file is delcared final, so a derived class can't interfere
+ std::error_condition err = write_at(offset(), buffer, length, actual);
+ add_offset(actual);
+ return err;
+}
+
+
+std::error_condition core_osd_file::write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept
{
// can't write to RAM-based stuff
if (is_loaded())
- return core_in_memory_file::write(buffer, length);
+ return core_in_memory_file::write_at(offset, buffer, length, actual);
// flush any buffered char
clear_putback();
// invalidate any buffered data
- m_bufferbytes = 0;
+ m_bufferbytes = 0U;
- // do the write
- std::uint32_t bytes_written = 0;
- m_file->write(buffer, offset(), length, bytes_written);
-
- // return the number of bytes written
- add_offset(bytes_written);
- return bytes_written;
+ // do the write - may need to split into chunks if size_t is larger than 32 bits
+ actual = 0U;
+ while (length)
+ {
+ // bytes written 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 bytes_written;
+ std::error_condition err = m_file->write(buffer, offset, chunk, bytes_written);
+ if (err)
+ return err;
+ assert(chunk >= bytes_written);
+ offset += bytes_written;
+ buffer = reinterpret_cast<std::uint8_t const *>(buffer) + bytes_written;
+ length -= bytes_written;
+ actual += bytes_written;
+ set_length((std::max)(this->length(), offset));
+ }
+ return std::error_condition();
}
@@ -724,7 +865,7 @@ std::error_condition core_osd_file::truncate(std::uint64_t offset)
return core_in_memory_file::truncate(offset);
// truncate file
- auto const err = m_file->truncate(offset);
+ std::error_condition err = m_file->truncate(offset);
if (err)
return err;
@@ -738,7 +879,16 @@ std::error_condition core_osd_file::truncate(std::uint64_t offset)
flush - flush file buffers
-------------------------------------------------*/
-std::error_condition core_osd_file::flush()
+std::error_condition core_osd_file::finalize() noexcept
+{
+ if (is_loaded())
+ return core_in_memory_file::finalize();
+
+ return std::error_condition();
+}
+
+
+std::error_condition core_osd_file::flush() noexcept
{
if (is_loaded())
return core_in_memory_file::flush();
@@ -747,7 +897,7 @@ std::error_condition core_osd_file::flush()
clear_putback();
// invalidate any buffered data
- m_bufferbytes = 0;
+ m_bufferbytes = 0U;
return m_file->flush();
}
@@ -765,24 +915,19 @@ std::error_condition core_osd_file::flush()
return an error code
-------------------------------------------------*/
-std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file)
+std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file) noexcept
{
- try
- {
- // attempt to open the file
- osd_file::ptr f;
- std::uint64_t length = 0;
- auto const filerr = osd_file::open(std::string(filename), openflags, f, length); // FIXME: allow osd_file to accept std::string_view
- if (filerr)
- return filerr;
-
- file = std::make_unique<core_osd_file>(openflags, std::move(f), length);
- return std::error_condition();
- }
- catch (...)
- {
- return std::errc::not_enough_memory;
- }
+ // attempt to open the file
+ osd_file::ptr f;
+ std::uint64_t length = 0;
+ auto const filerr = osd_file::open(std::string(filename), openflags, f, length); // FIXME: allow osd_file to accept std::string_view
+ if (filerr)
+ return filerr;
+
+ try { file = std::make_unique<core_osd_file>(openflags, std::move(f), length); }
+ catch (...) { return std::errc::not_enough_memory; }
+
+ return std::error_condition();
}
@@ -791,21 +936,26 @@ std::error_condition core_file::open(std::string_view filename, std::uint32_t op
like access and return an error code
-------------------------------------------------*/
-std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file)
+std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept
{
// can only do this for read access
if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE))
return std::errc::invalid_argument;
- try
- {
- file.reset(new core_in_memory_file(openflags, data, length, false));
- return std::error_condition();
- }
- catch (...)
- {
+ // if length is non-zero, data must be non-null
+ if (length && !data)
+ return std::errc::invalid_argument;
+
+ // platforms where size_t is larger than 64 bits are theoretically possible
+ if (std::uint64_t(length) != length)
+ return std::errc::file_too_large;
+
+ ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, false));
+ if (!result)
return std::errc::not_enough_memory;
- }
+
+ file = std::move(result);
+ return std::error_condition();
}
@@ -815,25 +965,26 @@ std::error_condition core_file::open_ram(void const *data, std::size_t length, s
error code
-------------------------------------------------*/
-std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file)
+std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept
{
// can only do this for read access
if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE))
return std::errc::invalid_argument;
- try
- {
- ptr result(new core_in_memory_file(openflags, data, length, true));
- if (!result->buffer())
- return std::errc::not_enough_memory;
+ // if length is non-zero, data must be non-null
+ if (length && !data)
+ return std::errc::invalid_argument;
- file = std::move(result);
- return std::error_condition();
- }
- catch (...)
- {
+ // platforms where size_t is larger than 64 bits are theoretically possible
+ if (std::uint64_t(length) != length)
+ return std::errc::file_too_large;
+
+ ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, true));
+ if (!result || !result->buffer())
return std::errc::not_enough_memory;
- }
+
+ file = std::move(result);
+ return std::error_condition();
}
@@ -842,7 +993,7 @@ std::error_condition core_file::open_ram_copy(void const *data, std::size_t leng
object and return an error code
-------------------------------------------------*/
-std::error_condition core_file::open_proxy(core_file &file, ptr &proxy)
+std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) noexcept
{
ptr result(new (std::nothrow) core_proxy_file(file));
if (!result)
@@ -868,75 +1019,91 @@ core_file::~core_file()
pointer
-------------------------------------------------*/
-std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length)
+std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length) noexcept
{
- ptr file;
+ std::error_condition err;
// attempt to open the file
- auto const err = open(filename, OPEN_FLAG_READ, file);
+ ptr file;
+ err = open(filename, OPEN_FLAG_READ, file);
if (err)
return err;
// get the size
- auto const size = file->size();
- if (std::uint32_t(size) != size)
+ std::uint64_t size;
+ err = file->length(size);
+ if (err)
+ return err;
+ else if (std::uint32_t(size) != size) // TODO: change interface to use size_t rather than uint32_t for output size
return std::errc::file_too_large;
// allocate memory
- *data = malloc(size);
+ *data = std::malloc(std::size_t(size));
if (!*data)
return std::errc::not_enough_memory;
length = std::uint32_t(size);
// read the data
- if (file->read(*data, size) != size)
+ if (size)
{
- free(*data);
- return std::errc::io_error; // TODO: revisit this error code
+ std::size_t actual;
+ err = file->read(*data, std::size_t(size), actual);
+ if (err || (size != actual))
+ {
+ std::free(*data);
+ *data = nullptr;
+ if (err)
+ return err;
+ else
+ return std::errc::io_error; // TODO: revisit this error code - either interrupted by an async signal or file truncated out from under us
+ }
}
// close the file and return data
return std::error_condition();
}
-std::error_condition core_file::load(std::string_view filename, std::vector<uint8_t> &data)
+std::error_condition core_file::load(std::string_view filename, std::vector<uint8_t> &data) noexcept
{
- ptr file;
+ std::error_condition err;
// attempt to open the file
- auto const err = open(filename, OPEN_FLAG_READ, file);
+ ptr file;
+ err = open(filename, OPEN_FLAG_READ, file);
if (err)
return err;
// get the size
- auto const size = file->size();
- if (std::uint32_t(size) != size)
+ std::uint64_t size;
+ err = file->length(size);
+ if (err)
+ return err;
+ else if (std::size_t(size) != size)
return std::errc::file_too_large;
// allocate memory
- try { data.resize(size); }
+ try { data.resize(std::size_t(size)); }
catch (...) { return std::errc::not_enough_memory; }
// read the data
- if (file->read(&data[0], size) != size)
+ if (size)
{
- data.clear();
- return std::errc::io_error; // TODO: revisit this error code
+ std::size_t actual;
+ err = file->read(&data[0], std::size_t(size), actual);
+ if (err || (size != actual))
+ {
+ data.clear();
+ if (err)
+ return err;
+ else
+ return std::errc::io_error; // TODO: revisit this error code - either interrupted by an async signal or file truncated out from under us
+ }
}
// close the file and return data
return std::error_condition();
}
-
-/*-------------------------------------------------
- protected constructor
--------------------------------------------------*/
-
-core_file::core_file()
-{
-}
-
} // namespace util