diff --git a/include/bx/error.h b/include/bx/error.h new file mode 100644 index 0000000..1d30a90 --- /dev/null +++ b/include/bx/error.h @@ -0,0 +1,108 @@ +/* + * Copyright 2010-2016 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#ifndef BX_ERROR_H_HEADER_GUARD +#define BX_ERROR_H_HEADER_GUARD + +#include "bx.h" + +#define BX_ERROR_SET(_ptr, _result, _msg) \ + BX_MACRO_BLOCK_BEGIN \ + BX_TRACE("Error %d: %s", _result.code, "" _msg); \ + _ptr->setError(_result, "" _msg); \ + BX_MACRO_BLOCK_END + +#define BX_ERROR_SCOPE(_ptr) \ + const bx::Error tmpError /* It should not be used directly! */; \ + _ptr = NULL == _ptr ? const_cast(&tmpError) : _ptr; \ + bx::ErrorScope bxErrorScope(const_cast(&tmpError)) + +#define BX_ERROR_RESULT(_err, _code) \ + BX_STATIC_ASSERT(_code != 0, "ErrorCode 0 is reserved!"); \ + static const bx::ErrorResult _err = { _code } + +namespace bx +{ + /// + struct ErrorResult + { + uint32_t code; + }; + + /// + class Error + { + BX_CLASS(Error + , NO_COPY + , NO_ASSIGNMENT + ); + + public: + Error() + : m_code(0) + { + } + + void setError(ErrorResult _errorResult, const char* _msg) + { + BX_CHECK(0 != _errorResult.code, "Invalid ErrorResult passed to setError!"); + + if (!isOk() ) + { + return; + } + + m_code = _errorResult.code; + m_msg = _msg; + } + + bool isOk() const + { + return 0 == m_code; + } + + ErrorResult get() const + { + ErrorResult result = { m_code }; + return result; + } + + bool operator==(ErrorResult _rhs) const + { + return _rhs.code == m_code; + } + + private: + const char* m_msg; + uint32_t m_code; + }; + + /// + class ErrorScope + { + BX_CLASS(ErrorScope + , NO_COPY + , NO_ASSIGNMENT + ); + + public: + ErrorScope(Error* _err) + : m_err(_err) + { + BX_CHECK(NULL != _err, "_err can't be NULL"); + } + + ~ErrorScope() + { + BX_CHECK(m_err->isOk(), "Error: %d", m_err->get().code); + } + + private: + Error* m_err; + }; + +} // namespace bx + +#endif // BX_ERROR_H_HEADER_GUARD diff --git a/include/bx/readerwriter.h b/include/bx/readerwriter.h index c673eed..faa44ed 100644 --- a/include/bx/readerwriter.h +++ b/include/bx/readerwriter.h @@ -12,6 +12,7 @@ #include "bx.h" #include "allocator.h" +#include "error.h" #include "uint32_t.h" #if BX_COMPILER_MSVC_COMPATIBLE @@ -22,6 +23,10 @@ # define ftello64 ftello #endif // BX_ +BX_ERROR_RESULT(BX_ERROR_READERWRITER_OPEN, BX_MAKEFOURCC('R', 'W', 0, 1) ); +BX_ERROR_RESULT(BX_ERROR_READERWRITER_READ, BX_MAKEFOURCC('R', 'W', 0, 2) ); +BX_ERROR_RESULT(BX_ERROR_READERWRITER_WRITE, BX_MAKEFOURCC('R', 'W', 0, 3) ); + namespace bx { struct Whence @@ -37,7 +42,7 @@ namespace bx struct BX_NO_VTABLE ReaderI { virtual ~ReaderI() = 0; - virtual int32_t read(void* _data, int32_t _size) = 0; + virtual int32_t read(void* _data, int32_t _size, Error* _err) = 0; }; inline ReaderI::~ReaderI() @@ -47,7 +52,7 @@ namespace bx struct BX_NO_VTABLE WriterI { virtual ~WriterI() = 0; - virtual int32_t write(const void* _data, int32_t _size) = 0; + virtual int32_t write(const void* _data, int32_t _size, Error* _err) = 0; }; inline WriterI::~WriterI() @@ -65,40 +70,45 @@ namespace bx } /// Read data. - inline int32_t read(ReaderI* _reader, void* _data, int32_t _size) + inline int32_t read(ReaderI* _reader, void* _data, int32_t _size, Error* _err = NULL) { - return _reader->read(_data, _size); + BX_ERROR_SCOPE(_err); + return _reader->read(_data, _size, _err); } /// Write value. template - inline int32_t read(ReaderI* _reader, Ty& _value) + inline int32_t read(ReaderI* _reader, Ty& _value, Error* _err = NULL) { + BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(BX_TYPE_IS_POD(Ty) ); - return _reader->read(&_value, sizeof(Ty) ); + return _reader->read(&_value, sizeof(Ty), _err); } /// Read value and converts it to host endianess. _fromLittleEndian specifies /// underlying stream endianess. template - inline int32_t readHE(ReaderI* _reader, Ty& _value, bool _fromLittleEndian) + inline int32_t readHE(ReaderI* _reader, Ty& _value, bool _fromLittleEndian, Error* _err = NULL) { BX_STATIC_ASSERT(BX_TYPE_IS_POD(Ty) ); Ty value; - int32_t result = _reader->read(&value, sizeof(Ty) ); + int32_t result = _reader->read(&value, sizeof(Ty), _err); _value = toHostEndian(value, _fromLittleEndian); return result; } /// Write data. - inline int32_t write(WriterI* _writer, const void* _data, int32_t _size) + inline int32_t write(WriterI* _writer, const void* _data, int32_t _size, Error* _err = NULL) { - return _writer->write(_data, _size); + BX_ERROR_SCOPE(_err); + return _writer->write(_data, _size, _err); } /// Write repeat the same value. - inline int32_t writeRep(WriterI* _writer, uint8_t _byte, int32_t _size) + inline int32_t writeRep(WriterI* _writer, uint8_t _byte, int32_t _size, Error* _err = NULL) { + BX_ERROR_SCOPE(_err); + const uint32_t tmp0 = uint32_sels(64 - _size, 64, _size); const uint32_t tmp1 = uint32_sels(256 - _size, 256, tmp0); const uint32_t blockSize = uint32_sels(1024 - _size, 1024, tmp1); @@ -108,7 +118,7 @@ namespace bx int32_t size = 0; while (0 < _size) { - int32_t bytes = write(_writer, temp, uint32_min(blockSize, _size) ); + int32_t bytes = write(_writer, temp, uint32_min(blockSize, _size), _err); size += bytes; _size -= bytes; } @@ -118,29 +128,32 @@ namespace bx /// Write value. template - inline int32_t write(WriterI* _writer, const Ty& _value) + inline int32_t write(WriterI* _writer, const Ty& _value, Error* _err = NULL) { + BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(BX_TYPE_IS_POD(Ty) ); - return _writer->write(&_value, sizeof(Ty) ); + return _writer->write(&_value, sizeof(Ty), _err); } /// Write value as little endian. template - inline int32_t writeLE(WriterI* _writer, const Ty& _value) + inline int32_t writeLE(WriterI* _writer, const Ty& _value, Error* _err = NULL) { + BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(BX_TYPE_IS_POD(Ty) ); Ty value = toLittleEndian(_value); - int32_t result = _writer->write(&value, sizeof(Ty) ); + int32_t result = _writer->write(&value, sizeof(Ty), _err); return result; } /// Write value as big endian. template - inline int32_t writeBE(WriterI* _writer, const Ty& _value) + inline int32_t writeBE(WriterI* _writer, const Ty& _value, Error* _err = NULL) { + BX_ERROR_SCOPE(_err); BX_STATIC_ASSERT(BX_TYPE_IS_POD(Ty) ); Ty value = toBigEndian(_value); - int32_t result = _writer->write(&value, sizeof(Ty) ); + int32_t result = _writer->write(&value, sizeof(Ty), _err); return result; } @@ -198,34 +211,36 @@ namespace bx struct BX_NO_VTABLE FileReaderI : public ReaderSeekerI { - virtual int32_t open(const char* _filePath) = 0; - virtual int32_t close() = 0; + virtual bool open(const char* _filePath, Error* _err) = 0; + virtual void close() = 0; }; struct BX_NO_VTABLE FileWriterI : public WriterSeekerI { - virtual int32_t open(const char* _filePath, bool _append = false) = 0; - virtual int32_t close() = 0; + virtual bool open(const char* _filePath, bool _append, Error* _err) = 0; + virtual void close() = 0; }; - inline int32_t open(FileReaderI* _reader, const char* _filePath) + inline bool open(FileReaderI* _reader, const char* _filePath, Error* _err = NULL) { - return _reader->open(_filePath); + BX_ERROR_SCOPE(_err); + return _reader->open(_filePath, _err); } - inline int32_t close(FileReaderI* _reader) + inline void close(FileReaderI* _reader) { - return _reader->close(); + _reader->close(); } - inline int32_t open(FileWriterI* _writer, const char* _filePath, bool _append = false) + inline bool open(FileWriterI* _writer, const char* _filePath, bool _append = false, Error* _err = NULL) { - return _writer->open(_filePath, _append); + BX_ERROR_SCOPE(_err); + return _writer->open(_filePath, _append, _err); } - inline int32_t close(FileWriterI* _writer) + inline void close(FileWriterI* _writer) { - return _writer->close(); + _writer->close(); } struct BX_NO_VTABLE MemoryBlockI @@ -332,8 +347,10 @@ namespace bx return m_pos; } - virtual int32_t write(const void* /*_data*/, int32_t _size) BX_OVERRIDE + virtual int32_t write(const void* /*_data*/, int32_t _size, Error* _err) BX_OVERRIDE { + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + int32_t morecore = int32_t(m_pos - m_top) + _size; if (0 < morecore) @@ -344,6 +361,10 @@ namespace bx int64_t reminder = m_top-m_pos; int32_t size = uint32_min(_size, int32_t(reminder > INT32_MAX ? INT32_MAX : reminder) ); m_pos += size; + if (size != _size) + { + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_WRITE, "SizerWriter: write truncated."); + } return size; } @@ -386,12 +407,18 @@ namespace bx return m_pos; } - virtual int32_t read(void* _data, int32_t _size) BX_OVERRIDE + virtual int32_t read(void* _data, int32_t _size, Error* _err) BX_OVERRIDE { + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + int64_t reminder = m_top-m_pos; int32_t size = uint32_min(_size, int32_t(reminder > INT32_MAX ? INT32_MAX : reminder) ); memcpy(_data, &m_data[m_pos], size); m_pos += size; + if (size != _size) + { + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_READ, "MemoryReader: read truncated."); + } return size; } @@ -452,8 +479,10 @@ namespace bx return m_pos; } - virtual int32_t write(const void* _data, int32_t _size) BX_OVERRIDE + virtual int32_t write(const void* _data, int32_t _size, Error* _err) BX_OVERRIDE { + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + int32_t morecore = int32_t(m_pos - m_size) + _size; if (0 < morecore) @@ -468,6 +497,10 @@ namespace bx memcpy(&m_data[m_pos], _data, size); m_pos += size; m_top = int64_max(m_top, m_pos); + if (size != _size) + { + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_WRITE, "MemoryWriter: write truncated."); + } return size; } @@ -509,16 +542,23 @@ namespace bx { } - virtual int32_t open(const char* _filePath) BX_OVERRIDE + virtual bool open(const char* _filePath, Error* _err) BX_OVERRIDE { + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + m_file = fopen(_filePath, "rb"); - return NULL == m_file; + if (NULL == m_file) + { + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_OPEN, "CrtFileReader: Failed to open file."); + return false; + } + + return true; } - virtual int32_t close() BX_OVERRIDE + virtual void close() BX_OVERRIDE { fclose(m_file); - return 0; } virtual int64_t seek(int64_t _offset = 0, Whence::Enum _whence = Whence::Current) BX_OVERRIDE @@ -527,9 +567,18 @@ namespace bx return ftello64(m_file); } - virtual int32_t read(void* _data, int32_t _size) BX_OVERRIDE + virtual int32_t read(void* _data, int32_t _size, Error* _err) BX_OVERRIDE { - return (int32_t)fread(_data, 1, _size, m_file); + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + + int32_t size = fread(_data, 1, _size, m_file); + if (size != _size) + { + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_READ, "CrtFileReader: read failed."); + return size >= 0 ? size : 0; + } + + return size; } private: @@ -548,24 +597,22 @@ namespace bx { } - virtual int32_t open(const char* _filePath, bool _append = false) BX_OVERRIDE + virtual bool open(const char* _filePath, bool _append, Error* _err) BX_OVERRIDE { - if (_append) + m_file = fopen(_filePath, _append ? "ab" : "wb"); + + if (NULL == m_file) { - m_file = fopen(_filePath, "ab"); - } - else - { - m_file = fopen(_filePath, "wb"); + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_OPEN, "CrtFileWriter: Failed to open file."); + return false; } - return NULL == m_file; + return true; } - virtual int32_t close() BX_OVERRIDE + virtual void close() BX_OVERRIDE { fclose(m_file); - return 0; } virtual int64_t seek(int64_t _offset = 0, Whence::Enum _whence = Whence::Current) BX_OVERRIDE @@ -574,9 +621,18 @@ namespace bx return ftello64(m_file); } - virtual int32_t write(const void* _data, int32_t _size) BX_OVERRIDE + virtual int32_t write(const void* _data, int32_t _size, Error* _err) BX_OVERRIDE { - return (int32_t)fwrite(_data, 1, _size, m_file); + BX_CHECK(NULL != _err, "Reader/Writer interface calling functions must handle errors."); + + int32_t size = fwrite(_data, 1, _size, m_file); + if (size != _size) + { + BX_ERROR_SET(_err, BX_ERROR_READERWRITER_WRITE, "CrtFileWriter: write failed."); + return size >= 0 ? size : 0; + } + + return size; } private: