Use std::optional rather than sentinel values

This commit is contained in:
Chris Thrasher 2024-04-28 21:53:11 -06:00
parent 8acb9d9ab1
commit de8430bb29
19 changed files with 219 additions and 191 deletions

View File

@ -931,9 +931,10 @@ public:
return; return;
} }
std::vector<std::uint32_t> buffer(static_cast<std::size_t>(file.getSize()) / sizeof(std::uint32_t)); const auto fileSize = file.getSize().value();
std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t));
if (file.read(buffer.data(), file.getSize()) != file.getSize()) if (file.read(buffer.data(), fileSize) != file.getSize())
{ {
vulkanAvailable = false; vulkanAvailable = false;
return; return;
@ -959,9 +960,10 @@ public:
return; return;
} }
std::vector<std::uint32_t> buffer(static_cast<std::size_t>(file.getSize()) / sizeof(std::uint32_t)); const auto fileSize = file.getSize().value();
std::vector<std::uint32_t> buffer(fileSize / sizeof(std::uint32_t));
if (file.read(buffer.data(), file.getSize()) != file.getSize()) if (file.read(buffer.data(), fileSize) != file.getSize())
{ {
vulkanAvailable = false; vulkanAvailable = false;
return; return;

View File

@ -111,36 +111,36 @@ public:
/// \param data Buffer where to copy the read data /// \param data Buffer where to copy the read data
/// \param size Desired number of bytes to read /// \param size Desired number of bytes to read
/// ///
/// \return The number of bytes actually read, or -1 on error /// \return The number of bytes actually read, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] std::int64_t read(void* data, std::int64_t size) override; [[nodiscard]] std::optional<std::size_t> read(void* data, std::size_t size) override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Change the current reading position /// \brief Change the current reading position
/// ///
/// \param position The position to seek to, from the beginning /// \param position The position to seek to, from the beginning
/// ///
/// \return The position actually sought to, or -1 on error /// \return The position actually sought to, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] std::int64_t seek(std::int64_t position) override; [[nodiscard]] std::optional<std::size_t> seek(std::size_t position) override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the stream /// \brief Get the current reading position in the stream
/// ///
/// \return The current position, or -1 on error. /// \return The current position, or `std::nullopt` on error.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] std::int64_t tell() override; [[nodiscard]] std::optional<std::size_t> tell() override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Return the size of the stream /// \brief Return the size of the stream
/// ///
/// \return The total number of bytes available in the stream, or -1 on error /// \return The total number of bytes available in the stream, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t getSize() override; std::optional<std::size_t> getSize() override;
private: private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -31,6 +31,8 @@
#include <SFML/System/Export.hpp> #include <SFML/System/Export.hpp>
#include <optional>
#include <cstdint> #include <cstdint>
@ -58,36 +60,36 @@ public:
/// \param data Buffer where to copy the read data /// \param data Buffer where to copy the read data
/// \param size Desired number of bytes to read /// \param size Desired number of bytes to read
/// ///
/// \return The number of bytes actually read, or -1 on error /// \return The number of bytes actually read, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] virtual std::int64_t read(void* data, std::int64_t size) = 0; [[nodiscard]] virtual std::optional<std::size_t> read(void* data, std::size_t size) = 0;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Change the current reading position /// \brief Change the current reading position
/// ///
/// \param position The position to seek to, from the beginning /// \param position The position to seek to, from the beginning
/// ///
/// \return The position actually sought to, or -1 on error /// \return The position actually sought to, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] virtual std::int64_t seek(std::int64_t position) = 0; [[nodiscard]] virtual std::optional<std::size_t> seek(std::size_t position) = 0;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the stream /// \brief Get the current reading position in the stream
/// ///
/// \return The current position, or -1 on error. /// \return The current position, or `std::nullopt` on error.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] virtual std::int64_t tell() = 0; [[nodiscard]] virtual std::optional<std::size_t> tell() = 0;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Return the size of the stream /// \brief Return the size of the stream
/// ///
/// \return The total number of bytes available in the stream, or -1 on error /// \return The total number of bytes available in the stream, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
virtual std::int64_t getSize() = 0; virtual std::optional<std::size_t> getSize() = 0;
}; };
} // namespace sf } // namespace sf
@ -119,13 +121,13 @@ public:
/// ///
/// [[nodiscard]] bool open(const std::filesystem::path& filename); /// [[nodiscard]] bool open(const std::filesystem::path& filename);
/// ///
/// [[nodiscard]] std::int64_t read(void* data, std::int64_t size); /// [[nodiscard]] std::optional<std::size_t> read(void* data, std::size_t size);
/// ///
/// [[nodiscard]] std::int64_t seek(std::int64_t position); /// [[nodiscard]] std::optional<std::size_t> seek(std::size_t position);
/// ///
/// [[nodiscard]] std::int64_t tell(); /// [[nodiscard]] std::optional<std::size_t> tell();
/// ///
/// std::int64_t getSize(); /// std::optional<std::size_t> getSize();
/// ///
/// private: /// private:
/// ///

View File

@ -64,44 +64,44 @@ public:
/// \param data Buffer where to copy the read data /// \param data Buffer where to copy the read data
/// \param size Desired number of bytes to read /// \param size Desired number of bytes to read
/// ///
/// \return The number of bytes actually read, or -1 on error /// \return The number of bytes actually read, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] std::int64_t read(void* data, std::int64_t size) override; [[nodiscard]] std::optional<std::size_t> read(void* data, std::size_t size) override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Change the current reading position /// \brief Change the current reading position
/// ///
/// \param position The position to seek to, from the beginning /// \param position The position to seek to, from the beginning
/// ///
/// \return The position actually sought to, or -1 on error /// \return The position actually sought to, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] std::int64_t seek(std::int64_t position) override; [[nodiscard]] std::optional<std::size_t> seek(std::size_t position) override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the stream /// \brief Get the current reading position in the stream
/// ///
/// \return The current position, or -1 on error. /// \return The current position, or `std::nullopt` on error.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] std::int64_t tell() override; [[nodiscard]] std::optional<std::size_t> tell() override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Return the size of the stream /// \brief Return the size of the stream
/// ///
/// \return The total number of bytes available in the stream, or -1 on error /// \return The total number of bytes available in the stream, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t getSize() override; std::optional<std::size_t> getSize() override;
private: private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
const std::byte* m_data{}; //!< Pointer to the data in memory const std::byte* m_data{}; //!< Pointer to the data in memory
std::int64_t m_size{}; //!< Total size of the data std::size_t m_size{}; //!< Total size of the data
std::int64_t m_offset{}; //!< Current reading position std::size_t m_offset{}; //!< Current reading position
}; };
} // namespace sf } // namespace sf

View File

@ -213,7 +213,7 @@ void InputSoundFile::seek(std::uint64_t sampleOffset)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void InputSoundFile::seek(Time timeOffset) void InputSoundFile::seek(Time timeOffset)
{ {
seek(static_cast<std::uint64_t>(timeOffset.asSeconds() * static_cast<float>(m_sampleRate)) * m_channelMap.size()); seek(static_cast<std::size_t>(timeOffset.asSeconds() * static_cast<float>(m_sampleRate)) * m_channelMap.size());
} }

View File

@ -58,7 +58,7 @@ std::unique_ptr<SoundFileReader> SoundFileFactory::createReaderFromFilename(cons
// Test the filename in all the registered factories // Test the filename in all the registered factories
for (const auto& [fpCreate, fpCheck] : getReaderFactoryMap()) for (const auto& [fpCreate, fpCheck] : getReaderFactoryMap())
{ {
if (stream.seek(0) == -1) if (!stream.seek(0).has_value())
{ {
err() << "Failed to seek sound stream" << std::endl; err() << "Failed to seek sound stream" << std::endl;
return nullptr; return nullptr;
@ -84,7 +84,7 @@ std::unique_ptr<SoundFileReader> SoundFileFactory::createReaderFromMemory(const
// Test the stream for all the registered factories // Test the stream for all the registered factories
for (const auto& [fpCreate, fpCheck] : getReaderFactoryMap()) for (const auto& [fpCreate, fpCheck] : getReaderFactoryMap())
{ {
if (stream.seek(0) == -1) if (!stream.seek(0).has_value())
{ {
err() << "Failed to seek sound stream" << std::endl; err() << "Failed to seek sound stream" << std::endl;
return nullptr; return nullptr;
@ -106,7 +106,7 @@ std::unique_ptr<SoundFileReader> SoundFileFactory::createReaderFromStream(InputS
// Test the stream for all the registered factories // Test the stream for all the registered factories
for (const auto& [fpCreate, fpCheck] : getReaderFactoryMap()) for (const auto& [fpCreate, fpCheck] : getReaderFactoryMap())
{ {
if (stream.seek(0) == -1) if (!stream.seek(0).has_value())
{ {
err() << "Failed to seek sound stream" << std::endl; err() << "Failed to seek sound stream" << std::endl;
return nullptr; return nullptr;

View File

@ -44,15 +44,17 @@ FLAC__StreamDecoderReadStatus streamRead(const FLAC__StreamDecoder*, FLAC__byte
{ {
auto* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData); auto* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData);
const std::int64_t count = data->stream->read(buffer, static_cast<std::int64_t>(*bytes)); if (const std::optional count = data->stream->read(buffer, *bytes))
if (count > 0)
{ {
*bytes = static_cast<std::size_t>(count); if (*count > 0)
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; {
} *bytes = *count;
else if (count == 0) return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
{ }
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; else
{
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
}
} }
else else
{ {
@ -64,8 +66,7 @@ FLAC__StreamDecoderSeekStatus streamSeek(const FLAC__StreamDecoder*, FLAC__uint6
{ {
auto* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData); auto* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData);
const std::int64_t position = data->stream->seek(static_cast<std::int64_t>(absoluteByteOffset)); if (data->stream->seek(static_cast<std::size_t>(absoluteByteOffset)).has_value())
if (position >= 0)
return FLAC__STREAM_DECODER_SEEK_STATUS_OK; return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
else else
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
@ -75,10 +76,9 @@ FLAC__StreamDecoderTellStatus streamTell(const FLAC__StreamDecoder*, FLAC__uint6
{ {
auto* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData); auto* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData);
const std::int64_t position = data->stream->tell(); if (const std::optional position = data->stream->tell())
if (position >= 0)
{ {
*absoluteByteOffset = static_cast<FLAC__uint64>(position); *absoluteByteOffset = *position;
return FLAC__STREAM_DECODER_TELL_STATUS_OK; return FLAC__STREAM_DECODER_TELL_STATUS_OK;
} }
else else
@ -91,10 +91,9 @@ FLAC__StreamDecoderLengthStatus streamLength(const FLAC__StreamDecoder*, FLAC__u
{ {
auto* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData); auto* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData);
const std::int64_t count = data->stream->getSize(); if (const std::optional count = data->stream->getSize())
if (count >= 0)
{ {
*streamLength = static_cast<FLAC__uint64>(count); *streamLength = *count;
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
} }
else else

View File

@ -68,14 +68,14 @@ namespace
std::size_t readCallback(void* ptr, std::size_t size, void* data) std::size_t readCallback(void* ptr, std::size_t size, void* data)
{ {
auto* stream = static_cast<sf::InputStream*>(data); auto* stream = static_cast<sf::InputStream*>(data);
return static_cast<std::size_t>(stream->read(ptr, static_cast<std::int64_t>(size))); return stream->read(ptr, size).value_or(-1);
} }
int seekCallback(std::uint64_t offset, void* data) int seekCallback(std::uint64_t offset, void* data)
{ {
auto* stream = static_cast<sf::InputStream*>(data); auto* stream = static_cast<sf::InputStream*>(data);
const std::int64_t position = stream->seek(static_cast<std::int64_t>(offset)); const std::optional position = stream->seek(static_cast<std::size_t>(offset));
return position < 0 ? -1 : 0; return position ? 0 : -1;
} }
bool hasValidId3Tag(const std::uint8_t* header) bool hasValidId3Tag(const std::uint8_t* header)
@ -92,7 +92,7 @@ bool SoundFileReaderMp3::check(InputStream& stream)
{ {
std::uint8_t header[10]; std::uint8_t header[10];
if (static_cast<std::size_t>(stream.read(header, static_cast<std::int64_t>(sizeof(header)))) < sizeof(header)) if (stream.read(header, sizeof(header)) != sizeof(header))
return false; return false;
if (hasValidId3Tag(header)) if (hasValidId3Tag(header))

View File

@ -41,29 +41,32 @@ namespace
std::size_t read(void* ptr, std::size_t size, std::size_t nmemb, void* data) std::size_t read(void* ptr, std::size_t size, std::size_t nmemb, void* data)
{ {
auto* stream = static_cast<sf::InputStream*>(data); auto* stream = static_cast<sf::InputStream*>(data);
return static_cast<std::size_t>(stream->read(ptr, static_cast<std::int64_t>(size * nmemb))); return stream->read(ptr, size * nmemb).value_or(-1);
} }
int seek(void* data, ogg_int64_t offset, int whence) int seek(void* data, ogg_int64_t signedOffset, int whence)
{ {
auto* stream = static_cast<sf::InputStream*>(data); auto* stream = static_cast<sf::InputStream*>(data);
auto offset = static_cast<std::size_t>(signedOffset);
switch (whence) switch (whence)
{ {
case SEEK_SET: case SEEK_SET:
break; break;
case SEEK_CUR: case SEEK_CUR:
offset += stream->tell(); offset += stream->tell().value();
break; break;
case SEEK_END: case SEEK_END:
offset = stream->getSize() - offset; offset = stream->getSize().value() - offset;
} }
return static_cast<int>(stream->seek(offset)); const std::optional position = stream->seek(offset);
return position ? static_cast<int>(*position) : -1;
} }
long tell(void* data) long tell(void* data)
{ {
auto* stream = static_cast<sf::InputStream*>(data); auto* stream = static_cast<sf::InputStream*>(data);
return static_cast<long>(stream->tell()); const std::optional position = stream->tell();
return position ? static_cast<long>(*position) : -1;
} }
ov_callbacks callbacks = {&read, &seek, nullptr, &tell}; ov_callbacks callbacks = {&read, &seek, nullptr, &tell};

View File

@ -43,13 +43,13 @@ namespace
{ {
ma_result onRead(ma_decoder* decoder, void* buffer, size_t bytesToRead, size_t* bytesRead) ma_result onRead(ma_decoder* decoder, void* buffer, size_t bytesToRead, size_t* bytesRead)
{ {
auto* stream = static_cast<sf::InputStream*>(decoder->pUserData); auto* stream = static_cast<sf::InputStream*>(decoder->pUserData);
const auto count = stream->read(buffer, static_cast<std::int64_t>(bytesToRead)); const std::optional count = stream->read(buffer, bytesToRead);
if (count < 0) if (!count.has_value())
return MA_ERROR; return MA_ERROR;
*bytesRead = static_cast<size_t>(count); *bytesRead = static_cast<std::size_t>(*count);
return MA_SUCCESS; return MA_SUCCESS;
} }
@ -61,19 +61,17 @@ ma_result onSeek(ma_decoder* decoder, ma_int64 byteOffset, ma_seek_origin origin
{ {
case ma_seek_origin_start: case ma_seek_origin_start:
{ {
if (stream->seek(byteOffset) < 0) if (!stream->seek(static_cast<std::size_t>(byteOffset)).has_value())
return MA_ERROR; return MA_ERROR;
return MA_SUCCESS; return MA_SUCCESS;
} }
case ma_seek_origin_current: case ma_seek_origin_current:
{ {
const auto currentPosition = stream->tell(); if (!stream->tell().has_value())
if (currentPosition < 0)
return MA_ERROR; return MA_ERROR;
if (stream->seek(stream->tell() + byteOffset) < 0) if (!stream->seek(stream->tell().value() + static_cast<std::size_t>(byteOffset)).has_value())
return MA_ERROR; return MA_ERROR;
return MA_SUCCESS; return MA_SUCCESS;

View File

@ -55,13 +55,11 @@ namespace
// FreeType callbacks that operate on a sf::InputStream // FreeType callbacks that operate on a sf::InputStream
unsigned long read(FT_Stream rec, unsigned long offset, unsigned char* buffer, unsigned long count) unsigned long read(FT_Stream rec, unsigned long offset, unsigned char* buffer, unsigned long count)
{ {
const auto convertedOffset = static_cast<std::int64_t>(offset); auto* stream = static_cast<sf::InputStream*>(rec->descriptor.pointer);
auto* stream = static_cast<sf::InputStream*>(rec->descriptor.pointer); if (stream->seek(offset) == offset)
if (stream->seek(convertedOffset) == convertedOffset)
{ {
if (count > 0) if (count > 0)
return static_cast<unsigned long>( return static_cast<unsigned long>(stream->read(reinterpret_cast<char*>(buffer), count).value());
stream->read(reinterpret_cast<char*>(buffer), static_cast<std::int64_t>(count)));
else else
return 0; return 0;
} }
@ -244,7 +242,7 @@ std::optional<Font> Font::loadFromStream(InputStream& stream)
} }
// Make sure that the stream's reading position is at the beginning // Make sure that the stream's reading position is at the beginning
if (stream.seek(0) == -1) if (!stream.seek(0).has_value())
{ {
err() << "Failed to seek font stream" << std::endl; err() << "Failed to seek font stream" << std::endl;
return std::nullopt; return std::nullopt;
@ -252,7 +250,7 @@ std::optional<Font> Font::loadFromStream(InputStream& stream)
// Prepare a wrapper for our stream, that we'll pass to FreeType callbacks // Prepare a wrapper for our stream, that we'll pass to FreeType callbacks
fontHandles->streamRec.base = nullptr; fontHandles->streamRec.base = nullptr;
fontHandles->streamRec.size = static_cast<unsigned long>(stream.getSize()); fontHandles->streamRec.size = static_cast<unsigned long>(stream.getSize().value());
fontHandles->streamRec.pos = 0; fontHandles->streamRec.pos = 0;
fontHandles->streamRec.descriptor.pointer = &stream; fontHandles->streamRec.descriptor.pointer = &stream;
fontHandles->streamRec.read = &read; fontHandles->streamRec.read = &read;

View File

@ -55,14 +55,15 @@ namespace
// stb_image callbacks that operate on a sf::InputStream // stb_image callbacks that operate on a sf::InputStream
int read(void* user, char* data, int size) int read(void* user, char* data, int size)
{ {
auto& stream = *static_cast<sf::InputStream*>(user); auto& stream = *static_cast<sf::InputStream*>(user);
return static_cast<int>(stream.read(data, size)); const std::optional count = stream.read(data, static_cast<std::size_t>(size));
return count ? static_cast<int>(*count) : -1;
} }
void skip(void* user, int size) void skip(void* user, int size)
{ {
auto& stream = *static_cast<sf::InputStream*>(user); auto& stream = *static_cast<sf::InputStream*>(user);
if (stream.seek(stream.tell() + size) == -1) if (!stream.seek(stream.tell().value() + static_cast<std::size_t>(size)).has_value())
sf::err() << "Failed to seek image loader input stream" << std::endl; sf::err() << "Failed to seek image loader input stream" << std::endl;
} }
@ -233,7 +234,7 @@ std::optional<Image> Image::loadFromMemory(const void* data, std::size_t size)
std::optional<Image> Image::loadFromStream(InputStream& stream) std::optional<Image> Image::loadFromStream(InputStream& stream)
{ {
// Make sure that the stream's reading position is at the beginning // Make sure that the stream's reading position is at the beginning
if (stream.seek(0) == -1) if (!stream.seek(0).has_value())
{ {
err() << "Failed to seek image stream" << std::endl; err() << "Failed to seek image stream" << std::endl;
return std::nullopt; return std::nullopt;

View File

@ -104,20 +104,20 @@ bool getFileContents(const std::filesystem::path& filename, std::vector<char>& b
// Read the contents of a stream into an array of char // Read the contents of a stream into an array of char
bool getStreamContents(sf::InputStream& stream, std::vector<char>& buffer) bool getStreamContents(sf::InputStream& stream, std::vector<char>& buffer)
{ {
bool success = false; bool success = false;
const std::int64_t size = stream.getSize(); const std::optional size = stream.getSize();
if (size > 0) if (size > std::size_t{0})
{ {
buffer.resize(static_cast<std::size_t>(size)); buffer.resize(*size);
if (stream.seek(0) == -1) if (!stream.seek(0).has_value())
{ {
sf::err() << "Failed to seek shader stream" << std::endl; sf::err() << "Failed to seek shader stream" << std::endl;
return false; return false;
} }
const std::int64_t read = stream.read(buffer.data(), size); const std::optional read = stream.read(buffer.data(), *size);
success = (read == size); success = (read == size);
} }
buffer.push_back('\0'); buffer.push_back('\0');
return success; return success;

View File

@ -41,62 +41,41 @@ ResourceStream::ResourceStream(const std::filesystem::path& filename)
ActivityStates& states = getActivity(); ActivityStates& states = getActivity();
const std::lock_guard lock(states.mutex); const std::lock_guard lock(states.mutex);
m_file.reset(AAssetManager_open(states.activity->assetManager, filename.c_str(), AASSET_MODE_UNKNOWN)); m_file.reset(AAssetManager_open(states.activity->assetManager, filename.c_str(), AASSET_MODE_UNKNOWN));
assert(m_file && "Failed to initialize ResourceStream file");
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t ResourceStream::read(void* data, std::int64_t size) std::optional<std::size_t> ResourceStream::read(void* data, std::size_t size)
{ {
if (m_file) const auto numBytesRead = AAsset_read(m_file.get(), data, size);
{ if (numBytesRead < 0)
return AAsset_read(m_file.get(), data, static_cast<std::size_t>(size)); return std::nullopt;
} return numBytesRead;
else
{
return -1;
}
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t ResourceStream::seek(std::int64_t position) std::optional<std::size_t> ResourceStream::seek(std::size_t position)
{ {
if (m_file) const auto newPosition = AAsset_seek(m_file.get(), static_cast<off_t>(position), SEEK_SET);
{ if (newPosition < 0)
return AAsset_seek(m_file.get(), static_cast<off_t>(position), SEEK_SET); return std::nullopt;
} return newPosition;
else
{
return -1;
}
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t ResourceStream::tell() std::optional<std::size_t> ResourceStream::tell()
{ {
if (m_file) return getSize().value() - static_cast<std::size_t>(AAsset_getRemainingLength(m_file.get()));
{
return getSize() - AAsset_getRemainingLength(m_file.get());
}
else
{
return -1;
}
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t ResourceStream::getSize() std::optional<std::size_t> ResourceStream::getSize()
{ {
if (m_file) return AAsset_getLength(m_file.get());
{
return AAsset_getLength(m_file.get());
}
else
{
return -1;
}
} }

View File

@ -60,36 +60,36 @@ public:
/// \param data Buffer where the asset data is copied /// \param data Buffer where the asset data is copied
/// \param size Number of bytes read /// \param size Number of bytes read
/// ///
/// \return The number of bytes actually read, or -1 on error /// \return The number of bytes actually read, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t read(void* data, std::int64_t size) override; std::optional<std::size_t> read(void* data, std::size_t size) override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Change the current reading position in the asset file /// \brief Change the current reading position in the asset file
/// ///
/// \param position The position to seek to, from the beginning /// \param position The position to seek to, from the beginning
/// ///
/// \return The position actually sought to, or -1 on error /// \return The position actually sought to, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t seek(std::int64_t position) override; std::optional<std::size_t> seek(std::size_t position) override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the asset file /// \brief Get the current reading position in the asset file
/// ///
/// \return The current position, or -1 on error. /// \return The current position, or `std::nullopt` on error.
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t tell() override; std::optional<std::size_t> tell() override;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Return the size of the asset file /// \brief Return the size of the asset file
/// ///
/// \return The total number of bytes available in the asset, or -1 on error /// \return The total number of bytes available in the asset, or `std::nullopt` on error
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t getSize() override; std::optional<std::size_t> getSize() override;
private: private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -79,78 +79,79 @@ bool FileInputStream::open(const std::filesystem::path& filename)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t FileInputStream::read(void* data, std::int64_t size) std::optional<std::size_t> FileInputStream::read(void* data, std::size_t size)
{ {
#ifdef SFML_SYSTEM_ANDROID #ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr) if (priv::getActivityStatesPtr() != nullptr)
{ {
if (!m_androidFile) if (!m_androidFile)
return -1; return std::nullopt;
return m_androidFile->read(data, size); return m_androidFile->read(data, size);
} }
#endif #endif
if (!m_file) if (!m_file)
return -1; return std::nullopt;
return static_cast<std::int64_t>(std::fread(data, 1, static_cast<std::size_t>(size), m_file.get())); return std::fread(data, 1, size, m_file.get());
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t FileInputStream::seek(std::int64_t position) std::optional<std::size_t> FileInputStream::seek(std::size_t position)
{ {
#ifdef SFML_SYSTEM_ANDROID #ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr) if (priv::getActivityStatesPtr() != nullptr)
{ {
if (!m_androidFile) if (!m_androidFile)
return -1; return std::nullopt;
return m_androidFile->seek(position); return m_androidFile->seek(position);
} }
#endif #endif
if (!m_file) if (!m_file)
return -1; return std::nullopt;
if (std::fseek(m_file.get(), static_cast<long>(position), SEEK_SET)) if (std::fseek(m_file.get(), static_cast<long>(position), SEEK_SET))
return -1; return std::nullopt;
return tell(); return tell();
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t FileInputStream::tell() std::optional<std::size_t> FileInputStream::tell()
{ {
#ifdef SFML_SYSTEM_ANDROID #ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr) if (priv::getActivityStatesPtr() != nullptr)
{ {
if (!m_androidFile) if (!m_androidFile)
return -1; return std::nullopt;
return m_androidFile->tell(); return m_androidFile->tell();
} }
#endif #endif
if (!m_file) if (!m_file)
return -1; return std::nullopt;
return std::ftell(m_file.get()); const auto position = std::ftell(m_file.get());
return position < 0 ? std::nullopt : std::optional<std::size_t>(position);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t FileInputStream::getSize() std::optional<std::size_t> FileInputStream::getSize()
{ {
#ifdef SFML_SYSTEM_ANDROID #ifdef SFML_SYSTEM_ANDROID
if (priv::getActivityStatesPtr() != nullptr) if (priv::getActivityStatesPtr() != nullptr)
{ {
if (!m_androidFile) if (!m_androidFile)
return -1; return std::nullopt;
return m_androidFile->getSize(); return m_androidFile->getSize();
} }
#endif #endif
if (!m_file) if (!m_file)
return -1; return std::nullopt;
const std::int64_t position = tell(); const auto position = tell().value();
std::fseek(m_file.get(), 0, SEEK_END); std::fseek(m_file.get(), 0, SEEK_END);
const std::int64_t size = tell(); const std::optional size = tell();
if (seek(position) == -1) if (!seek(position).has_value())
return -1; return std::nullopt;
return size; return size;
} }

View File

@ -27,6 +27,8 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/System/MemoryInputStream.hpp> #include <SFML/System/MemoryInputStream.hpp>
#include <algorithm>
#include <cstring> #include <cstring>
@ -36,20 +38,18 @@ namespace sf
void MemoryInputStream::open(const void* data, std::size_t sizeInBytes) void MemoryInputStream::open(const void* data, std::size_t sizeInBytes)
{ {
m_data = static_cast<const std::byte*>(data); m_data = static_cast<const std::byte*>(data);
m_size = static_cast<std::int64_t>(sizeInBytes); m_size = sizeInBytes;
m_offset = 0; m_offset = 0;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t MemoryInputStream::read(void* data, std::int64_t size) std::optional<std::size_t> MemoryInputStream::read(void* data, std::size_t size)
{ {
if (!m_data) if (!m_data)
return -1; return std::nullopt;
const std::int64_t endPosition = m_offset + size;
const std::int64_t count = endPosition <= m_size ? size : m_size - m_offset;
const std::size_t count = std::min(size, m_size - m_offset);
if (count > 0) if (count > 0)
{ {
std::memcpy(data, m_data + m_offset, static_cast<std::size_t>(count)); std::memcpy(data, m_data + m_offset, static_cast<std::size_t>(count));
@ -61,10 +61,10 @@ std::int64_t MemoryInputStream::read(void* data, std::int64_t size)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t MemoryInputStream::seek(std::int64_t position) std::optional<std::size_t> MemoryInputStream::seek(std::size_t position)
{ {
if (!m_data) if (!m_data)
return -1; return std::nullopt;
m_offset = position < m_size ? position : m_size; m_offset = position < m_size ? position : m_size;
return m_offset; return m_offset;
@ -72,20 +72,20 @@ std::int64_t MemoryInputStream::seek(std::int64_t position)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t MemoryInputStream::tell() std::optional<std::size_t> MemoryInputStream::tell()
{ {
if (!m_data) if (!m_data)
return -1; return std::nullopt;
return m_offset; return m_offset;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::int64_t MemoryInputStream::getSize() std::optional<std::size_t> MemoryInputStream::getSize()
{ {
if (!m_data) if (!m_data)
return -1; return std::nullopt;
return m_size; return m_size;
} }

View File

@ -75,10 +75,10 @@ TEST_CASE("[System] sf::FileInputStream")
SECTION("Default constructor") SECTION("Default constructor")
{ {
sf::FileInputStream fileInputStream; sf::FileInputStream fileInputStream;
CHECK(fileInputStream.read(nullptr, 0) == -1); CHECK(fileInputStream.read(nullptr, 0) == std::nullopt);
CHECK(fileInputStream.seek(0) == -1); CHECK(fileInputStream.seek(0) == std::nullopt);
CHECK(fileInputStream.tell() == -1); CHECK(fileInputStream.tell() == std::nullopt);
CHECK(fileInputStream.getSize() == -1); CHECK(fileInputStream.getSize() == std::nullopt);
} }
const TemporaryFile temporaryFile("Hello world"); const TemporaryFile temporaryFile("Hello world");

View File

@ -16,28 +16,73 @@ TEST_CASE("[System] sf::MemoryInputStream")
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::MemoryInputStream>); STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::MemoryInputStream>);
} }
SECTION("Empty stream") SECTION("Default constructor")
{ {
sf::MemoryInputStream mis; sf::MemoryInputStream memoryInputStream;
CHECK(memoryInputStream.read(nullptr, 0) == std::nullopt);
CHECK(mis.read(nullptr, 0) == -1); CHECK(memoryInputStream.seek(0) == std::nullopt);
CHECK(mis.seek(0) == -1); CHECK(memoryInputStream.tell() == std::nullopt);
CHECK(mis.tell() == -1); CHECK(memoryInputStream.getSize() == std::nullopt);
CHECK(mis.getSize() == -1);
} }
SECTION("Open memory stream") using namespace std::literals::string_view_literals;
{
using namespace std::literals::string_view_literals;
constexpr auto memoryContents = "hello world"sv;
sf::MemoryInputStream mis;
mis.open(memoryContents.data(), sizeof(char) * memoryContents.size());
std::array<char, 32> buffer{}; SECTION("open()")
CHECK(mis.read(buffer.data(), 5) == 5); {
CHECK(std::string_view(buffer.data(), 5) == std::string_view(memoryContents.data(), 5)); sf::MemoryInputStream memoryInputStream;
CHECK(mis.seek(10) == 10); memoryInputStream.open(nullptr, 0);
CHECK(mis.tell() == 10); CHECK(memoryInputStream.tell() == std::nullopt);
CHECK(mis.getSize() == 11); CHECK(memoryInputStream.getSize() == std::nullopt);
static constexpr auto input = "hello world"sv;
memoryInputStream.open(input.data(), input.size());
CHECK(memoryInputStream.tell().value() == 0);
CHECK(memoryInputStream.getSize().value() == input.size());
}
SECTION("read()")
{
static constexpr auto input = "hello world"sv;
sf::MemoryInputStream memoryInputStream;
memoryInputStream.open(input.data(), input.size());
CHECK(memoryInputStream.tell().value() == 0);
CHECK(memoryInputStream.getSize().value() == input.size());
// Read within input
std::array<char, 32> output{};
CHECK(memoryInputStream.read(output.data(), 5).value() == 5);
CHECK(std::string_view(output.data(), 5) == "hello"sv);
CHECK(memoryInputStream.tell().value() == 5);
CHECK(memoryInputStream.getSize().value() == input.size());
// Read beyond input
CHECK(memoryInputStream.read(output.data(), 100).value() == 6);
CHECK(std::string_view(output.data(), 6) == " world"sv);
CHECK(memoryInputStream.tell().value() == 11);
CHECK(memoryInputStream.getSize().value() == input.size());
}
SECTION("seek()")
{
static constexpr auto input = "We Love SFML!"sv;
sf::MemoryInputStream memoryInputStream;
memoryInputStream.open(input.data(), input.size());
CHECK(memoryInputStream.tell().value() == 0);
CHECK(memoryInputStream.getSize().value() == input.size());
SECTION("Seek within input")
{
CHECK(memoryInputStream.seek(0).value() == 0);
CHECK(memoryInputStream.tell().value() == 0);
CHECK(memoryInputStream.seek(5).value() == 5);
CHECK(memoryInputStream.tell().value() == 5);
}
SECTION("Seek beyond input")
{
CHECK(memoryInputStream.seek(1'000).value() == input.size());
CHECK(memoryInputStream.tell().value() == input.size());
}
} }
} }