diff options
Diffstat (limited to 'src/lib/util/corefile.cpp')
-rw-r--r-- | src/lib/util/corefile.cpp | 1278 |
1 files changed, 503 insertions, 775 deletions
diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index 78a484c5511..74a742b0306 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** - corefile.c + corefile.cpp File access functions. @@ -10,17 +10,15 @@ #include "corefile.h" +#include "coretmpl.h" +#include "osdcore.h" #include "unicode.h" #include "vecstream.h" -#include <zlib.h> - -#include <algorithm> #include <cassert> #include <cstring> #include <iterator> - -#include <ctype.h> +#include <limits> namespace util { @@ -41,128 +39,32 @@ namespace { TYPE DEFINITIONS ***************************************************************************/ -class zlib_data +class core_proxy_file : public core_file { public: - typedef std::unique_ptr<zlib_data> ptr; - - static int start_compression(int level, std::uint64_t offset, ptr &data) - { - ptr result(new zlib_data(offset)); - result->reset_output(); - auto const zerr = deflateInit(&result->m_stream, level); - result->m_compress = (Z_OK == zerr); - if (result->m_compress) data = std::move(result); - return zerr; - } - static int start_decompression(std::uint64_t offset, ptr &data) - { - ptr result(new zlib_data(offset)); - auto const zerr = inflateInit(&result->m_stream); - result->m_decompress = (Z_OK == zerr); - if (result->m_decompress) data = std::move(result); - return zerr; - } - - ~zlib_data() - { - if (m_compress) deflateEnd(&m_stream); - else if (m_decompress) inflateEnd(&m_stream); - } - - std::size_t buffer_size() const { return sizeof(m_buffer); } - void const *buffer_data() const { return m_buffer; } - void *buffer_data() { return m_buffer; } - - // general-purpose output buffer manipulation - bool output_full() const { return 0 == m_stream.avail_out; } - std::size_t output_space() const { return m_stream.avail_out; } - void set_output(void *data, std::uint32_t size) - { - m_stream.next_out = reinterpret_cast<Bytef *>(data); - m_stream.avail_out = size; - } - - // working with output to the internal buffer - bool has_output() const { return m_stream.avail_out != sizeof(m_buffer); } - std::size_t output_size() const { return sizeof(m_buffer) - m_stream.avail_out; } - void reset_output() - { - m_stream.next_out = m_buffer; - m_stream.avail_out = sizeof(m_buffer); - } + core_proxy_file(core_file &file) noexcept : m_file(file) { } - // general-purpose input buffer manipulation - bool has_input() const { return 0 != m_stream.avail_in; } - std::size_t input_size() const { return m_stream.avail_in; } - void set_input(void const *data, std::uint32_t size) - { - m_stream.next_in = const_cast<Bytef *>(reinterpret_cast<Bytef const *>(data)); - m_stream.avail_in = size; - } + 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); } - // working with input from the internal buffer - void reset_input(std::uint32_t size) - { - m_stream.next_in = m_buffer; - m_stream.avail_in = size; - } + virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read_some(buffer, length, actual); } + virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read_some_at(offset, buffer, length, actual); } - int compress() { assert(m_compress); return deflate(&m_stream, Z_NO_FLUSH); } - int finalise() { assert(m_compress); return deflate(&m_stream, Z_FINISH); } - int decompress() { assert(m_decompress); return inflate(&m_stream, Z_SYNC_FLUSH); } + 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_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write_some(buffer, length, actual); } + virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write_some_at(offset, buffer, length, actual); } - std::uint64_t realoffset() const { return m_realoffset; } - void add_realoffset(std::uint32_t increment) { m_realoffset += increment; } - - bool is_nextoffset(std::uint64_t value) const { return m_nextoffset == value; } - void add_nextoffset(std::uint32_t increment) { m_nextoffset += increment; } - -private: - zlib_data(std::uint64_t offset) - : m_compress(false) - , m_decompress(false) - , m_realoffset(offset) - , m_nextoffset(offset) - { - m_stream.zalloc = Z_NULL; - m_stream.zfree = Z_NULL; - m_stream.opaque = Z_NULL; - m_stream.avail_in = m_stream.avail_out = 0; - } - - bool m_compress, m_decompress; - z_stream m_stream; - std::uint8_t m_buffer[1024]; - std::uint64_t m_realoffset; - std::uint64_t m_nextoffset; -}; - - -class core_proxy_file : public core_file -{ -public: - core_proxy_file(core_file &file) : m_file(file) { } - virtual ~core_proxy_file() override { } - virtual osd_file::error compress(int level) override { return m_file.compress(level); } - - 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 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 std::uint32_t write(const void *buffer, std::uint32_t length) override { return m_file.write(buffer, length); } - virtual int puts(const char *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 osd_file::error truncate(std::uint64_t offset) override { return m_file.truncate(offset); } - virtual osd_file::error flush() override { return m_file.flush(); } + virtual int puts(std::string_view s) override { return m_file.puts(s); } + virtual int vprintf(util::format_argument_pack<char> const &args) override { return m_file.vprintf(args); } + virtual std::error_condition truncate(std::uint64_t offset) override { return m_file.truncate(offset); } private: core_file &m_file; @@ -185,8 +87,8 @@ public: virtual int getc() override; virtual int ungetc(int c) override; virtual char *gets(char *s, int n) override; - virtual int puts(char const *s) override; - virtual int vprintf(util::format_argument_pack<std::ostream> const &args) override; + virtual int puts(std::string_view s) override; + virtual int vprintf(util::format_argument_pack<char> const &args) override; protected: core_text_file(std::uint32_t openflags) @@ -198,12 +100,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 @@ -215,53 +117,74 @@ private: }; -class core_in_memory_file : public core_text_file +class core_basic_file : public core_text_file { public: - core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) + 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_index; return std::error_condition(); } + virtual std::error_condition length(std::uint64_t &result) noexcept override { result = m_size; return std::error_condition(); } + + virtual bool eof() const override; + +protected: + core_basic_file(std::uint32_t openflags, std::uint64_t length) noexcept : core_text_file(openflags) + , m_index(0) + , m_size(length) + { + } + + std::uint64_t index() const noexcept { return m_index; } + void add_offset(std::size_t increment) noexcept { m_index += increment; m_size = (std::max)(m_size, m_index); } + std::uint64_t size() const noexcept { return m_size; } + void set_size(std::uint64_t value) noexcept { m_size = 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) noexcept; + + +private: + std::uint64_t m_index; // current file offset + std::uint64_t m_size; // total file length +}; + +class core_in_memory_file final : public core_basic_file +{ +public: + core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) noexcept + : core_basic_file(openflags, length) , m_data_allocated(false) , m_data(copy ? nullptr : data) - , m_offset(0) - , m_length(length) { if (copy) { void *const buf = allocate(); - if (buf) std::memcpy(buf, data, length); + if (buf) + std::memcpy(buf, data, length); } } ~core_in_memory_file() override { purge(); } - virtual osd_file::error compress(int level) override { return osd_file::error::INVALID_ACCESS; } - virtual int seek(std::int64_t offset, int whence) override; - virtual std::uint64_t tell() const override { return m_offset; } - virtual bool eof() const override; - virtual std::uint64_t size() const override { return m_length; } + virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override; + virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override; - virtual std::uint32_t read(void *buffer, std::uint32_t length) override; - virtual void const *buffer() override { return m_data; } + 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_some(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_some_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 std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; } - virtual osd_file::error truncate(std::uint64_t offset) override; - virtual osd_file::error flush() override { clear_putback(); return osd_file::error::NONE; } + void const *buffer() const { return m_data; } -protected: - core_in_memory_file(std::uint32_t openflags, std::uint64_t length) - : core_text_file(openflags) - , m_data_allocated(false) - , m_data(nullptr) - , m_offset(0) - , m_length(length) - { - } + virtual std::error_condition truncate(std::uint64_t offset) override; - bool is_loaded() const { return nullptr != m_data; } - void *allocate() +protected: + void *allocate() noexcept { - if (m_data) return nullptr; - void *data = malloc(m_length); + if (m_data || (std::numeric_limits<std::size_t>::max() < size())) + return nullptr; + void *data = malloc(size()); if (data) { m_data_allocated = true; @@ -269,140 +192,124 @@ protected: } return data; } - void purge() + + void purge() noexcept { - if (m_data && m_data_allocated) free(const_cast<void *>(m_data)); + if (m_data && m_data_allocated) + free(const_cast<void *>(m_data)); m_data_allocated = false; 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); } - - 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); - private: bool m_data_allocated; // was the data allocated by us? void const * m_data; // file data, if RAM-based - std::uint64_t m_offset; // current file offset - std::uint64_t m_length; // total file length }; -class core_osd_file : public core_in_memory_file +class core_osd_file final : public core_basic_file { public: core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length) - : core_in_memory_file(openmode, length) + : core_basic_file(openmode, length) , m_file(std::move(file)) - , m_zdata() - , m_bufferbase(0) - , m_bufferbytes(0) { } ~core_osd_file() override; - virtual osd_file::error compress(int level) override; + virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override; + virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override; - virtual int seek(std::int64_t offset, int whence) override; + virtual std::error_condition finalize() noexcept override; + virtual std::error_condition flush() noexcept override; + virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override; + virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override; - virtual std::uint32_t read(void *buffer, std::uint32_t length) override; - virtual void const *buffer() override; - - virtual std::uint32_t write(void const *buffer, std::uint32_t length) override; - virtual osd_file::error truncate(std::uint64_t offset) override; - virtual osd_file::error flush() override; + virtual std::error_condition truncate(std::uint64_t offset) 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::error osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); - osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); - osd_file::ptr m_file; // OSD file handle - zlib_data::ptr m_zdata; // compression data - 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 }; -/*************************************************************************** - core_text_file -***************************************************************************/ +//************************************************************************** +// core_text_file +//************************************************************************** -/*------------------------------------------------- - getc - read a character from a file --------------------------------------------------*/ +//------------------------------------------------- +// getc - read a character from a file +//------------------------------------------------- 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) + std::uint8_t bom[4]; + auto const [err, readlen] = read(*this, bom, 4); // FIXME: check for errors + if (readlen == 4) { - 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; + 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]; - char32_t uchar = char32_t(~0); + char32_t uchar = ~char32_t(0); switch (m_text_type) { default: case text_file_type::OSD: { char default_buffer[16]; - auto const readlen = read(default_buffer, sizeof(default_buffer)); + auto const [err, readlen] = read(*this, default_buffer, sizeof(default_buffer)); if (readlen > 0) { auto const charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0])); @@ -414,7 +321,7 @@ 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)); + auto const [err, readlen] = read(*this, utf8_buffer, sizeof(utf8_buffer)); if (readlen > 0) { auto const charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0])); @@ -425,7 +332,7 @@ int core_text_file::getc() case text_file_type::UTF16BE: { - auto const readlen = read(utf16_buffer, sizeof(utf16_buffer)); + auto const [err, readlen] = read(*this, utf16_buffer, sizeof(utf16_buffer)); if (readlen > 0) { auto const charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0])); @@ -436,7 +343,7 @@ int core_text_file::getc() case text_file_type::UTF16LE: { - auto const readlen = read(utf16_buffer, sizeof(utf16_buffer)); + auto const [err, readlen] = read(*this, utf16_buffer, sizeof(utf16_buffer)); if (readlen > 0) { auto const charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0])); @@ -446,13 +353,21 @@ 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 + auto const [err, readlen] = read(*this, &uchar, sizeof(uchar)); + 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 + auto const [err, readlen] = read(*this, &uchar, sizeof(uchar)); + if (sizeof(uchar) == readlen) + uchar = little_endianize_int32(uchar); + } break; } @@ -460,40 +375,41 @@ int core_text_file::getc() { // place the new character in the ring buffer m_back_char_head = 0; - m_back_char_tail = utf8_from_uchar(m_back_chars, ARRAY_LENGTH(m_back_chars), uchar); + m_back_char_tail = utf8_from_uchar(m_back_chars, std::size(m_back_chars), uchar); //assert(file->back_char_tail != -1); } } // now read from the ring buffer + int result; if (m_back_char_head == m_back_char_tail) result = EOF; else { result = m_back_chars[m_back_char_head++]; - m_back_char_head %= ARRAY_LENGTH(m_back_chars); + m_back_char_head %= std::size(m_back_chars); } return result; } -/*------------------------------------------------- - ungetc - put back a character read from a - file --------------------------------------------------*/ +//------------------------------------------------- +// ungetc - put back a character read from a +// file +//------------------------------------------------- int core_text_file::ungetc(int c) { m_back_chars[m_back_char_tail++] = char(c); - m_back_char_tail %= ARRAY_LENGTH(m_back_chars); + m_back_char_tail %= std::size(m_back_chars); return c; } -/*------------------------------------------------- - gets - read a line from a text file --------------------------------------------------*/ +//------------------------------------------------- +// gets - read a line from a text file +//------------------------------------------------- char *core_text_file::gets(char *s, int n) { @@ -538,28 +454,39 @@ 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(char const *s) +int core_text_file::puts(std::string_view s) { + // TODO: what to do about write errors? + // 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 - while (*s != '\0') + for (char ch : s) { - if (*s == '\n') + if (ch == '\n') { if (CRLF == 1) // CR only *pconvbuf++ = 13; @@ -572,126 +499,120 @@ int core_text_file::puts(char const *s) } } else - *pconvbuf++ = *s; - s++; + *pconvbuf++ = ch; // if we overflow, break into chunks - if (pconvbuf >= convbuf + ARRAY_LENGTH(convbuf) - 10) + if (pconvbuf >= convbuf + std::size(convbuf) - 10) { - count += write(convbuf, pconvbuf - convbuf); + auto const [err, written] = write(*this, convbuf, pconvbuf - convbuf); // FIXME: error ignored here + count += written; pconvbuf = convbuf; } } // final flush if (pconvbuf != convbuf) - count += write(convbuf, pconvbuf - convbuf); + { + auto const [err, written] = write(*this, convbuf, pconvbuf - convbuf); // FIXME: error ignored here + count += written; + } return count; } -/*------------------------------------------------- - vprintf - vfprintf to a text file --------------------------------------------------*/ +//------------------------------------------------- +// vprintf - vfprintf to a text file +//------------------------------------------------- -int core_text_file::vprintf(util::format_argument_pack<std::ostream> const &args) +int core_text_file::vprintf(util::format_argument_pack<char> const &args) { m_printf_buffer.clear(); m_printf_buffer.reserve(1024); m_printf_buffer.seekp(0, ovectorstream::beg); - util::stream_format<std::ostream, std::ostream>(m_printf_buffer, args); - m_printf_buffer.put('\0'); - return puts(&m_printf_buffer.vec()[0]); + util::stream_format(m_printf_buffer, args); + return puts(buf_to_string_view(m_printf_buffer)); } -/*************************************************************************** - core_in_memory_file -***************************************************************************/ +//************************************************************************** +// core_basic_file +//************************************************************************** -/*------------------------------------------------- - seek - seek within a file --------------------------------------------------*/ +//------------------------------------------------- +// seek - seek within a file +//------------------------------------------------- -int core_in_memory_file::seek(std::int64_t offset, int whence) +std::error_condition core_basic_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: - m_offset = offset; - break; + if (0 > offset) + return std::errc::invalid_argument; + m_index = offset; + return std::error_condition(); case SEEK_CUR: - m_offset += offset; - break; + if (0 > offset) + { + if (std::uint64_t(-offset) > m_index) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_index) + { + return std::errc::invalid_argument; + } + m_index += offset; + return std::error_condition(); case SEEK_END: - m_offset = m_length + offset; - break; + 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_index = m_size + offset; + return std::error_condition(); + + default: + return std::errc::invalid_argument; } - return 0; } -/*------------------------------------------------- - eof - return 1 if we're at the end of file --------------------------------------------------*/ +//------------------------------------------------- +// eof - return true if we're at the end of file +//------------------------------------------------- -bool core_in_memory_file::eof() const +bool core_basic_file::eof() const { // check for buffered chars if (has_putback()) - return 0; + return false; // if the offset == length, we're at EOF - return (m_offset >= m_length); + return (m_index >= m_size); } -/*------------------------------------------------- - read - read from a file --------------------------------------------------*/ +//------------------------------------------------- +// safe_buffer_copy - copy safely from one +// bounded buffer to another +//------------------------------------------------- -std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length) -{ - 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; -} - - -/*------------------------------------------------- - truncate - truncate a file --------------------------------------------------*/ - -osd_file::error core_in_memory_file::truncate(std::uint64_t offset) -{ - if (m_length < offset) - return osd_file::error::FAILURE; - - // adjust to new length and offset - set_length(offset); - return osd_file::error::NONE; -} - - -/*------------------------------------------------- - safe_buffer_copy - copy safely from one - bounded buffer to another --------------------------------------------------*/ - -std::size_t core_in_memory_file::safe_buffer_copy( +std::size_t core_basic_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; @@ -708,605 +629,412 @@ std::size_t core_in_memory_file::safe_buffer_copy( -/*************************************************************************** - core_osd_file -***************************************************************************/ +//************************************************************************** +// core_in_memory_file +//************************************************************************** -/*------------------------------------------------- - closes a file --------------------------------------------------*/ +//------------------------------------------------- +// read - read from a file +//------------------------------------------------- -core_osd_file::~core_osd_file() +std::error_condition core_in_memory_file::read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept { - // close files and free memory - if (m_zdata) - core_osd_file::compress(FCOMPRESS_NONE); -} - + clear_putback(); -/*------------------------------------------------- - compress - enable/disable streaming file - compression via zlib; level is 0 to disable - compression, or up to 9 for max compression --------------------------------------------------*/ + // handle RAM-based files + if (index() < size()) + actual = safe_buffer_copy(m_data, std::size_t(index()), std::size_t(size()), buffer, 0, length); + else + actual = 0U; + add_offset(actual); + return std::error_condition(); +} -osd_file::error core_osd_file::compress(int level) +std::error_condition core_in_memory_file::read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept { - osd_file::error result = osd_file::error::NONE; - - // can only do this for read-only and write-only cases - if (read_access() && write_access()) - return osd_file::error::INVALID_ACCESS; + clear_putback(); - // if we have been compressing, flush and free the data - if (m_zdata && (level == FCOMPRESS_NONE)) - { - int zerr = Z_OK; + // handle RAM-based files + if (offset < size()) + actual = safe_buffer_copy(m_data, std::size_t(offset), std::size_t(size()), buffer, 0, length); + else + actual = 0U; + return std::error_condition(); +} - // flush any remaining data if we are writing - while (write_access() && (zerr != Z_STREAM_END)) - { - // deflate some more - zerr = m_zdata->finalise(); - if ((zerr != Z_STREAM_END) && (zerr != Z_OK)) - { - result = osd_file::error::INVALID_DATA; - break; - } - // write the data - if (m_zdata->has_output()) - { - std::uint32_t actualdata; - auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata); - if (filerr != osd_file::error::NONE) - break; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_output(); - } - } +//------------------------------------------------- +// truncate - truncate a file +//------------------------------------------------- - // free memory - m_zdata.reset(); - } +std::error_condition core_in_memory_file::truncate(std::uint64_t offset) +{ + if (size() < offset) + return std::errc::io_error; // TODO: revisit this error code - // if we are just starting to compress, allocate a new buffer - if (!m_zdata && (level > FCOMPRESS_NONE)) - { - int zerr; + // adjust to new length and offset + set_size(offset); + return std::error_condition(); +} - // initialize the stream and compressor - if (write_access()) - zerr = zlib_data::start_compression(level, offset(), m_zdata); - else - zerr = zlib_data::start_decompression(offset(), m_zdata); - // on error, return an error - if (zerr != Z_OK) - return osd_file::error::OUT_OF_MEMORY; +//************************************************************************** +// core_osd_file +//************************************************************************** - // flush buffers - m_bufferbytes = 0; - } +//------------------------------------------------- +// closes a file +//------------------------------------------------- - return result; +core_osd_file::~core_osd_file() +{ + // close files and free memory } -/*------------------------------------------------- - seek - seek within a file --------------------------------------------------*/ +//------------------------------------------------- +// read - read from a file +//------------------------------------------------- -int core_osd_file::seek(std::int64_t offset, int whence) +std::error_condition core_osd_file::read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept { - // error if compressing - if (m_zdata) - return 1; - else - return core_in_memory_file::seek(offset, whence); + // since osd_file works like pread/pwrite, implement in terms of read_at + // core_osd_file is declared final, so a derived class can't interfere + std::error_condition err = read_some_at(index(), buffer, length, actual); + add_offset(actual); + return err; } -/*------------------------------------------------- - 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_some_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); + if (!m_file) + { + actual = 0U; + return std::errc::bad_file_descriptor; + } // 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; - osd_or_zlib_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; - osd_or_zlib_read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read); - bytes_read += new_bytes_read; + // read the remainder directly from the file - may need to return short 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) + actual += bytes_read; } } - // return the number of bytes read - add_offset(bytes_read); - return bytes_read; + // return any errors + return err; } -/*------------------------------------------------- - buffer - return a pointer to the file buffer; - if it doesn't yet exist, load the file into - RAM first --------------------------------------------------*/ +//------------------------------------------------- +// write - write to a file +//------------------------------------------------- -void const *core_osd_file::buffer() +std::error_condition core_osd_file::write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept { - // if we already have data, just return it - if (!is_loaded() && length()) - { - // allocate some memory - void *buf = allocate(); - if (!buf) return nullptr; - - // read the file - std::uint32_t read_length = 0; - auto const filerr = osd_or_zlib_read(buf, 0, length(), read_length); - if ((filerr != osd_file::error::NONE) || (read_length != length())) - purge(); - else - { - // close the file because we don't need it anymore - m_file.reset(); - } - } - return core_in_memory_file::buffer(); + // since osd_file works like pread/pwrite, implement in terms of write_at + // core_osd_file is declared final, so a derived class can't interfere + std::error_condition err = write_some_at(index(), buffer, length, actual); + add_offset(actual); + return err; } -/*------------------------------------------------- - 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_some_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); - // 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; - osd_or_zlib_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 return short 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); + std::uint32_t bytes_written; + std::error_condition err = m_file->write(buffer, offset, chunk, bytes_written); + if (err) + { + // bytes written not valid on error + actual = 0U; + } + else + { + assert(chunk >= bytes_written); + offset += bytes_written; + actual = bytes_written; + set_size((std::max)(size(), offset)); + } + return err; } -/*------------------------------------------------- - truncate - truncate a file --------------------------------------------------*/ +//------------------------------------------------- +// truncate - truncate a file +//------------------------------------------------- -osd_file::error core_osd_file::truncate(std::uint64_t offset) +std::error_condition core_osd_file::truncate(std::uint64_t offset) { - if (is_loaded()) - return core_in_memory_file::truncate(offset); - // truncate file - auto const err = m_file->truncate(offset); - if (err != osd_file::error::NONE) + std::error_condition err = m_file->truncate(offset); + if (err) return err; // and adjust to new length and offset - set_length(offset); - return osd_file::error::NONE; + set_size(offset); + return std::error_condition(); } -/*------------------------------------------------- - flush - flush file buffers --------------------------------------------------*/ +//------------------------------------------------- +// flush - flush file buffers +//------------------------------------------------- -osd_file::error core_osd_file::flush() +std::error_condition core_osd_file::finalize() noexcept { - if (is_loaded()) - return core_in_memory_file::flush(); + return std::error_condition(); +} + +std::error_condition core_osd_file::flush() noexcept +{ // flush any buffered char clear_putback(); // invalidate any buffered data - m_bufferbytes = 0; + m_bufferbytes = 0U; return m_file->flush(); } +} // anonymous namespace -/*------------------------------------------------- - osd_or_zlib_read - wrapper for osd_read that - handles zlib-compressed data --------------------------------------------------*/ -osd_file::error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) -{ - // if no compression, just pass through - if (!m_zdata) - return m_file->read(buffer, offset, length, actual); - - // if the offset doesn't match the next offset, fail - if (!m_zdata->is_nextoffset(offset)) - return osd_file::error::INVALID_ACCESS; - - // set up the destination - osd_file::error filerr = osd_file::error::NONE; - m_zdata->set_output(buffer, length); - while (!m_zdata->output_full()) - { - // if we didn't make progress, report an error or the end - if (m_zdata->has_input()) - { - auto const zerr = m_zdata->decompress(); - if (Z_OK != zerr) - { - if (Z_STREAM_END != zerr) filerr = osd_file::error::INVALID_DATA; - break; - } - } - // fetch more data if needed - if (!m_zdata->has_input()) - { - std::uint32_t actualdata = 0; - filerr = m_file->read(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->buffer_size(), actualdata); - if (filerr != osd_file::error::NONE) break; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_input(actualdata); - if (!m_zdata->has_input()) break; - } - } - - // adjust everything - actual = length - m_zdata->output_space(); - m_zdata->add_nextoffset(actual); - return filerr; -} +//************************************************************************** +// core_file +//************************************************************************** +//------------------------------------------------- +// open - open a file for access and +// return an error code +//------------------------------------------------- -/*------------------------------------------------- - osd_or_zlib_write - wrapper for osd_write that - handles zlib-compressed data --------------------------------------------------*/ - -/** - * @fn osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t actual) - * - * @brief OSD or zlib write. - * - * @param buffer The buffer. - * @param offset The offset. - * @param length The length. - * @param [in,out] actual The actual. - * - * @return A osd_file::error. - */ - -osd_file::error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) +std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file) noexcept { - // if no compression, just pass through - if (!m_zdata) - return m_file->write(buffer, offset, length, actual); - - // if the offset doesn't match the next offset, fail - if (!m_zdata->is_nextoffset(offset)) - return osd_file::error::INVALID_ACCESS; - - // set up the source - m_zdata->set_input(buffer, length); - while (m_zdata->has_input()) - { - // if we didn't make progress, report an error or the end - auto const zerr = m_zdata->compress(); - if (zerr != Z_OK) - { - actual = length - m_zdata->input_size(); - m_zdata->add_nextoffset(actual); - return osd_file::error::INVALID_DATA; - } + // 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; - // write more data if we are full up - if (m_zdata->output_full()) - { - std::uint32_t actualdata = 0; - auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata); - if (filerr != osd_file::error::NONE) - return filerr; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_output(); - } - } + try { file = std::make_unique<core_osd_file>(openflags, std::move(f), length); } + catch (...) { return std::errc::not_enough_memory; } - // we wrote everything - actual = length; - m_zdata->add_nextoffset(actual); - return osd_file::error::NONE; + return std::error_condition(); } -} // anonymous namespace - - - -/*************************************************************************** - core_file -***************************************************************************/ -/*------------------------------------------------- - open - open a file for access and - return an error code --------------------------------------------------*/ +//------------------------------------------------- +// open_ram - open a RAM-based buffer for file- +// like access and return an error code +//------------------------------------------------- -osd_file::error core_file::open(std::string const &filename, 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 { - try - { - // attempt to open the file - osd_file::ptr f; - std::uint64_t length = 0; - auto const filerr = osd_file::open(filename, openflags, f, length); - if (filerr != osd_file::error::NONE) - return filerr; - - file = std::make_unique<core_osd_file>(openflags, std::move(f), length); - return osd_file::error::NONE; - } - catch (...) - { - return osd_file::error::OUT_OF_MEMORY; - } -} + // can only do this for read access + if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) + return std::errc::invalid_argument; + // if length is non-zero, data must be non-null + if (length && !data) + return std::errc::invalid_argument; -/*------------------------------------------------- - open_ram - open a RAM-based buffer for file- - like access and return an error code --------------------------------------------------*/ + // platforms where size_t is larger than 64 bits are theoretically possible + if (std::uint64_t(length) != length) + return std::errc::file_too_large; -osd_file::error core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) -{ - // can only do this for read access - if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) - return osd_file::error::INVALID_ACCESS; + ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, false)); + if (!result) + return std::errc::not_enough_memory; - try - { - file.reset(new core_in_memory_file(openflags, data, length, false)); - return osd_file::error::NONE; - } - catch (...) - { - return osd_file::error::OUT_OF_MEMORY; - } + file = std::move(result); + return std::error_condition(); } -/*------------------------------------------------- - open_ram_copy - open a copy of a RAM-based - buffer for file-like access and return an - error code --------------------------------------------------*/ +//------------------------------------------------- +// open_ram_copy - open a copy of a RAM-based +// buffer for file-like access and return an +// error code +//------------------------------------------------- -osd_file::error 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 osd_file::error::INVALID_ACCESS; + return std::errc::invalid_argument; - try - { - ptr result(new core_in_memory_file(openflags, data, length, true)); - if (!result->buffer()) - return osd_file::error::OUT_OF_MEMORY; + // if length is non-zero, data must be non-null + if (length && !data) + return std::errc::invalid_argument; - file = std::move(result); - return osd_file::error::NONE; - } - catch (...) - { - return osd_file::error::OUT_OF_MEMORY; - } + // platforms where size_t is larger than 64 bits are theoretically possible + if (std::uint64_t(length) != length) + return std::errc::file_too_large; + + std::unique_ptr<core_in_memory_file> 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(); } -/*------------------------------------------------- - open_proxy - open a proxy to an existing file - object and return an error code --------------------------------------------------*/ +//------------------------------------------------- +// open_proxy - open a proxy to an existing file +// object and return an error code +//------------------------------------------------- -osd_file::error core_file::open_proxy(core_file &file, ptr &proxy) +std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) noexcept { - try - { - proxy = std::make_unique<core_proxy_file>(file); - return osd_file::error::NONE; - } - catch (...) - { - return osd_file::error::OUT_OF_MEMORY; - } + ptr result(new (std::nothrow) core_proxy_file(file)); + if (!result) + return std::errc::not_enough_memory; + + proxy = std::move(result); + return std::error_condition(); } -/*------------------------------------------------- - closes a file --------------------------------------------------*/ +//------------------------------------------------- +// closes a file +//------------------------------------------------- core_file::~core_file() { } -/*------------------------------------------------- - load - open a file with the specified - filename, read it into memory, and return a - pointer --------------------------------------------------*/ +//------------------------------------------------- +// load - open a file with the specified +// filename, read it into memory, and return a +// pointer +//------------------------------------------------- -osd_file::error core_file::load(std::string const &filename, void **data, std::uint32_t &length) +std::error_condition core_file::load(std::string_view filename, void **data, std::size_t &length) noexcept { - ptr file; + std::error_condition err; // attempt to open the file - auto const err = open(filename, OPEN_FLAG_READ, file); - if (err != osd_file::error::NONE) + 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) - return osd_file::error::OUT_OF_MEMORY; + 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 - *data = malloc(size); - length = std::uint32_t(size); + *data = std::malloc(std::size_t(size)); + if (!*data) + return std::errc::not_enough_memory; + length = std::size_t(size); // read the data - if (file->read(*data, size) != size) + if (size) { - free(*data); - return osd_file::error::FAILURE; + std::size_t actual; + std::tie(err, actual) = read(*file, *data, std::size_t(size)); + if (err || (size != actual)) + { + std::free(*data); + *data = nullptr; + if (err) + return err; + else + return std::errc::io_error; // TODO: revisit this error code - file truncated out from under us + } } // close the file and return data - return osd_file::error::NONE; + return std::error_condition(); } -osd_file::error core_file::load(std::string const &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); - if (err != osd_file::error::NONE) + 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) - return osd_file::error::OUT_OF_MEMORY; + 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 - 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 osd_file::error::FAILURE; + std::size_t actual; + std::tie(err, actual) = read(*file, &data[0], std::size_t(size)); + if (err || (size != actual)) + { + data.clear(); + if (err) + return err; + else + return std::errc::io_error; // TODO: revisit this error code - file truncated out from under us + } } // close the file and return data - return osd_file::error::NONE; -} - - -/*------------------------------------------------- - protected constructor --------------------------------------------------*/ - -core_file::core_file() -{ + return std::error_condition(); } } // namespace util - - - -/*************************************************************************** - FILENAME UTILITIES -***************************************************************************/ - -// ------------------------------------------------- -// core_filename_extract_base - extract the base -// name from a filename; note that this makes -// assumptions about path separators -// ------------------------------------------------- - -std::string core_filename_extract_base(const std::string &name, bool strip_extension) -{ - // find the start of the basename - auto const start = std::find_if(name.rbegin(), name.rend(), &util::is_directory_separator); - - // find the end of the basename - auto const chop_position = strip_extension - ? std::find(name.rbegin(), start, '.') - : start; - auto const end = ((chop_position != start) && (std::next(chop_position) != start)) - ? std::next(chop_position) - : name.rbegin(); - - // copy the result into an string - return std::string(start.base(), end.base()); -} - - -// ------------------------------------------------- -// core_filename_extract_extension -// ------------------------------------------------- - -std::string core_filename_extract_extension(const std::string &filename, bool strip_period) -{ - auto loc = filename.find_last_of('.'); - std::string result = loc != std::string::npos - ? filename.substr(loc + (strip_period ? 1 : 0)) - : ""; - return result; -} - - -// ------------------------------------------------- -// core_filename_ends_with - does the given -// filename end with the specified extension? -// ------------------------------------------------- - -bool core_filename_ends_with(const std::string &filename, const std::string &extension) -{ - auto namelen = filename.length(); - auto extlen = extension.length(); - - // first if the extension is bigger than the name, we definitely don't match - bool matches = namelen >= extlen; - - // work backwards checking for a match - while (matches && extlen > 0 && namelen > 0) - { - if (tolower((uint8_t)filename[--namelen]) != tolower((uint8_t)extension[--extlen])) - matches = false; - } - - return matches; -} |