From 08e00a160fe0190fb07398e345cdca540385311d Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sun, 2 Nov 2025 18:39:34 -0600 Subject: [PATCH 01/75] Experimental C++ version of `InputSource` and company --- Package.swift | 2 +- Sources/CSFBAudioEngine/Input/DataInput.cpp | 89 ++++++++++ Sources/CSFBAudioEngine/Input/DataInput.hpp | 41 +++++ .../Input/FileContentsInput.cpp | 112 ++++++++++++ .../Input/FileContentsInput.hpp | 42 +++++ Sources/CSFBAudioEngine/Input/FileInput.cpp | 87 ++++++++++ Sources/CSFBAudioEngine/Input/FileInput.hpp | 43 +++++ Sources/CSFBAudioEngine/Input/InputSource.cpp | 128 ++++++++++++++ Sources/CSFBAudioEngine/Input/InputSource.hpp | 79 +++++++++ .../Input/MemoryMappedFileInput.cpp | 123 ++++++++++++++ .../Input/MemoryMappedFileInput.hpp | 42 +++++ .../Input/SFBDataInputSource.h | 19 --- .../Input/SFBDataInputSource.m | 104 ------------ .../Input/SFBFileContentsInputSource.h | 18 -- .../Input/SFBFileContentsInputSource.m | 23 --- .../Input/SFBFileInputSource.h | 16 -- .../Input/SFBFileInputSource.m | 144 ---------------- .../Input/SFBInputSource+Internal.h | 8 +- .../{SFBInputSource.m => SFBInputSource.mm} | 159 ++++++++++++------ .../Input/SFBMemoryMappedFileInputSource.h | 18 -- .../Input/SFBMemoryMappedFileInputSource.m | 23 --- Sources/CSFBAudioEngine/Input/scope_exit.hpp | 32 ++++ 22 files changed, 931 insertions(+), 421 deletions(-) create mode 100644 Sources/CSFBAudioEngine/Input/DataInput.cpp create mode 100644 Sources/CSFBAudioEngine/Input/DataInput.hpp create mode 100644 Sources/CSFBAudioEngine/Input/FileContentsInput.cpp create mode 100644 Sources/CSFBAudioEngine/Input/FileContentsInput.hpp create mode 100644 Sources/CSFBAudioEngine/Input/FileInput.cpp create mode 100644 Sources/CSFBAudioEngine/Input/FileInput.hpp create mode 100644 Sources/CSFBAudioEngine/Input/InputSource.cpp create mode 100644 Sources/CSFBAudioEngine/Input/InputSource.hpp create mode 100644 Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp create mode 100644 Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp delete mode 100644 Sources/CSFBAudioEngine/Input/SFBDataInputSource.h delete mode 100644 Sources/CSFBAudioEngine/Input/SFBDataInputSource.m delete mode 100644 Sources/CSFBAudioEngine/Input/SFBFileContentsInputSource.h delete mode 100644 Sources/CSFBAudioEngine/Input/SFBFileContentsInputSource.m delete mode 100644 Sources/CSFBAudioEngine/Input/SFBFileInputSource.h delete mode 100644 Sources/CSFBAudioEngine/Input/SFBFileInputSource.m rename Sources/CSFBAudioEngine/Input/{SFBInputSource.m => SFBInputSource.mm} (73%) delete mode 100644 Sources/CSFBAudioEngine/Input/SFBMemoryMappedFileInputSource.h delete mode 100644 Sources/CSFBAudioEngine/Input/SFBMemoryMappedFileInputSource.m create mode 100644 Sources/CSFBAudioEngine/Input/scope_exit.hpp diff --git a/Package.swift b/Package.swift index 51bd9458f..7807c03e3 100644 --- a/Package.swift +++ b/Package.swift @@ -107,5 +107,5 @@ let package = Package( ]) ], cLanguageStandard: .c11, - cxxLanguageStandard: .cxx17 + cxxLanguageStandard: .cxx2b ) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp new file mode 100644 index 000000000..c1eae5fd3 --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -0,0 +1,89 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#import "DataInput.hpp" + +SFB::DataInput::DataInput(CFDataRef data) noexcept +{ + if(data) + data_ = (CFDataRef)CFRetain(data); +} + +SFB::DataInput::~DataInput() +{ + if(data_) + CFRelease(data_); +} + +std::expected SFB::DataInput::_Open() noexcept +{ + if(!data_) + return std::unexpected(ENOENT); + pos_ = 0; + return {}; +} + +std::expected SFB::DataInput::_Close() noexcept +{ + return {}; +} + +std::expected SFB::DataInput::_Read(void *buffer, int64_t count) noexcept +{ + int64_t remaining = CFDataGetLength(data_) - pos_; + count = std::min(count, remaining); + + auto range = CFRangeMake(pos_, pos_ + count); + CFDataGetBytes(data_, range, static_cast(buffer)); + + pos_ += count; + + return count; +} + +std::expected SFB::DataInput::_AtEOF() const noexcept +{ + return CFDataGetLength(data_) == pos_; +} + +std::expected SFB::DataInput::_GetOffset() const noexcept +{ + return pos_; +} + +std::expected SFB::DataInput::_GetLength() const noexcept +{ + return CFDataGetLength(data_); +} + +bool SFB::DataInput::_SupportsSeeking() const noexcept +{ + return true; +} + +std::expected SFB::DataInput::_SeekToOffset(int64_t offset, int whence) noexcept +{ + auto length = CFDataGetLength(data_); + + switch(whence) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += pos_; + break; + case SEEK_END: + offset += length; + break; + default: + return std::unexpected(EINVAL); + } + + if(offset < 0 || offset > length) + return std::unexpected(EINVAL); + + pos_ = offset; + return {}; +} diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp new file mode 100644 index 000000000..ae8100bac --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -0,0 +1,41 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#pragma once + +#import "InputSource.hpp" + +namespace SFB { + +class DataInput: public InputSource +{ +public: + explicit DataInput(CFDataRef _Nonnull data) noexcept; + virtual ~DataInput(); + + // This class is non-copyable. + DataInput(const DataInput& rhs) = delete; + DataInput(DataInput&& rhs) = delete; + + // This class is non-assignable. + DataInput& operator=(const DataInput& rhs) = delete; + DataInput& operator=(DataInput&& rhs) = delete; + +private: + virtual std::expected _Open() noexcept; + virtual std::expected _Close() noexcept; + virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept; + virtual std::expected _AtEOF() const noexcept; + virtual std::expected _GetOffset() const noexcept; + virtual std::expected _GetLength() const noexcept; + virtual bool _SupportsSeeking() const noexcept; + virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + + CFDataRef _Nullable data_ = nullptr; + CFIndex pos_ = 0; +}; + +} /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp new file mode 100644 index 000000000..adca8ffdc --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -0,0 +1,112 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#import + +#import + +#import "FileContentsInput.hpp" +#import "scope_exit.hpp" + +SFB::FileContentsInput::FileContentsInput(CFURLRef url) noexcept +: InputSource(url) +{} + +std::expected SFB::FileContentsInput::_Open() noexcept +{ + CFURLRef url = GetURL(); + if(!url) + return std::unexpected(ENOENT); + + UInt8 path [PATH_MAX]; + auto success = CFURLGetFileSystemRepresentation(url, FALSE, path, PATH_MAX); + if(!success) + return std::unexpected(EIO); + + auto file = std::fopen(reinterpret_cast(path), "r"); + if(!file) + return std::unexpected(errno); + + // Ensure the file is closed + auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; + + auto fd = ::fileno(file); + + struct stat s; + if(::fstat(fd, &s)) + return std::unexpected(errno); + + buf_ = malloc(s.st_size); + if(!buf_) + return std::unexpected(ENOMEM); + + len_ = s.st_size; + pos_ = 0; + + return {};} + +std::expected SFB::FileContentsInput::_Close() noexcept +{ + free(buf_); + buf_ = nullptr; + len_ = 0; + + return {}; +} + +std::expected SFB::FileContentsInput::_Read(void *buffer, int64_t count) noexcept +{ + auto remaining = len_ - pos_; + count = std::min(count, remaining); + + memcpy(buffer, reinterpret_cast(reinterpret_cast(buf_) + pos_), count); + pos_ += count; + + return count; +} + +std::expected SFB::FileContentsInput::_AtEOF() const noexcept +{ + return len_ == pos_; +} + +std::expected SFB::FileContentsInput::_GetOffset() const noexcept +{ + return pos_; +} + +std::expected SFB::FileContentsInput::_GetLength() const noexcept +{ + return len_; +} + +bool SFB::FileContentsInput::_SupportsSeeking() const noexcept +{ + return true; +} + +std::expected SFB::FileContentsInput::_SeekToOffset(int64_t offset, int whence) noexcept +{ + switch(whence) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += pos_; + break; + case SEEK_END: + offset += len_; + break; + default: + return std::unexpected(EINVAL); + } + + if(offset < 0 || offset > len_) + return std::unexpected(EINVAL); + + pos_ = offset; + return {}; +} + diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp new file mode 100644 index 000000000..b4919263a --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -0,0 +1,42 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#pragma once + +#import "InputSource.hpp" + +namespace SFB { + +class FileContentsInput: public InputSource +{ +public: + explicit FileContentsInput(CFURLRef _Nonnull url) noexcept; + virtual ~FileContentsInput() = default; + + // This class is non-copyable. + FileContentsInput(const FileContentsInput& rhs) = delete; + FileContentsInput(FileContentsInput&& rhs) = delete; + + // This class is non-assignable. + FileContentsInput& operator=(const FileContentsInput& rhs) = delete; + FileContentsInput& operator=(FileContentsInput&& rhs) = delete; + +private: + virtual std::expected _Open() noexcept; + virtual std::expected _Close() noexcept; + virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept; + virtual std::expected _AtEOF() const noexcept; + virtual std::expected _GetOffset() const noexcept; + virtual std::expected _GetLength() const noexcept; + virtual bool _SupportsSeeking() const noexcept; + virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + + void * _Nullable buf_ = nullptr; + int64_t len_ = 0; + int64_t pos_ = 0; +}; + +} /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp new file mode 100644 index 000000000..88bd6095b --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -0,0 +1,87 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#import + +#import "FileInput.hpp" +#import "scope_exit.hpp" + +SFB::FileInput::FileInput(CFURLRef url) noexcept +: InputSource(url) +{} + +std::expected SFB::FileInput::_Open() noexcept +{ + UInt8 path [PATH_MAX]; + auto success = CFURLGetFileSystemRepresentation(GetURL(), FALSE, path, PATH_MAX); + if(!success) + return std::unexpected(EIO); + + file_ = std::fopen(reinterpret_cast(path), "r"); + if(!file_) + return std::unexpected(errno); + + struct stat s; + if(::fstat(::fileno(file_), &s)) { + std::fclose(file_); + file_ = nullptr; + return std::unexpected(errno); + } + + len_ = s.st_size; + + return {}; +} + +std::expected SFB::FileInput::_Close() noexcept +{ + auto defer = scope_exit{[&] noexcept { + file_ = nullptr; + len_ = 0; + }}; + + if(std::fclose(file_)) + return std::unexpected(errno); + return {}; +} + +std::expected SFB::FileInput::_Read(void *buffer, int64_t count) noexcept +{ + auto nitems = std::fread(buffer, 1, count, file_); + if(nitems != count && std::ferror(file_)) + return std::unexpected(errno); + return nitems; +} + +std::expected SFB::FileInput::_AtEOF() const noexcept +{ + return std::feof(file_) != 0; +} + +std::expected SFB::FileInput::_GetOffset() const noexcept +{ + auto offset = std::ftell(file_); + if(offset == -1) + return std::unexpected(errno); + return offset; +} + +std::expected SFB::FileInput::_GetLength() const noexcept +{ + return len_; +} + +bool SFB::FileInput::_SupportsSeeking() const noexcept +{ + return true; +} + +std::expected SFB::FileInput::_SeekToOffset(int64_t offset, int whence) noexcept +{ + if(std::fseek(file_, offset, whence)) + return std::unexpected(errno); + return {}; +} diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp new file mode 100644 index 000000000..99af4d473 --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#pragma once + +#import + +#import "InputSource.hpp" + +namespace SFB { + +class FileInput: public InputSource +{ +public: + explicit FileInput(CFURLRef _Nonnull url) noexcept; + virtual ~FileInput() = default; + + // This class is non-copyable. + FileInput(const FileInput& rhs) = delete; + FileInput(FileInput&& rhs) = delete; + + // This class is non-assignable. + FileInput& operator=(const FileInput& rhs) = delete; + FileInput& operator=(FileInput&& rhs) = delete; + +private: + virtual std::expected _Open() noexcept; + virtual std::expected _Close() noexcept; + virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept; + virtual std::expected _AtEOF() const noexcept; + virtual std::expected _GetOffset() const noexcept; + virtual std::expected _GetLength() const noexcept; + virtual bool _SupportsSeeking() const noexcept; + virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + + FILE * _Nullable file_ = nullptr; + int64_t len_ = 0; +}; + +} /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp new file mode 100644 index 000000000..d821ea6df --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -0,0 +1,128 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#import + +#import "InputSource.hpp" +#import "DataInput.hpp" +#import "FileInput.hpp" + +namespace SFB { + +const os_log_t InputSource::sLog = os_log_create("org.sbooth.AudioEngine", "InputSource"); + +} /* namespace SFB */ + +SFB::InputSource::InputSource(CFURLRef url) noexcept +{ + if(url) + url_ = (CFURLRef)CFRetain(url); +} + +SFB::InputSource::~InputSource() +{ + if(isOpen_) + Close(); + if(url_) + CFRelease(url_); +} + +std::expected SFB::InputSource::Open() noexcept +{ + if(IsOpen()) { + os_log_debug(sLog, "Open() called on an InputSource that is already open"); + return; + } + + auto result = _Open(); + if(result) + isOpen_ = true; + return result; +} + +std::expected SFB::InputSource::Close() noexcept +{ + if(!IsOpen()) { + os_log_debug(sLog, "Close() called on an InputSource that hasn't been opened"); + return; + } + + auto result = _Close(); + isOpen_ = false; + return result; +} + +std::expected SFB::InputSource::Read(void *buffer, int64_t count) noexcept +{ + if(!IsOpen()) { + os_log_debug(sLog, "Read() called on an InputSource that hasn't been opened"); + return std::unexpected(EINVAL); + } + + if(!buffer || count < 0) { + os_log_debug(sLog, "Read() called with null buffer or invalid count"); + return std::unexpected(EINVAL); + } + + return _Read(buffer, count); +} + +std::expected SFB::InputSource::AtEOF() const noexcept +{ + if(!IsOpen()) { + os_log_debug(sLog, "AtEOF() called on an InputSource that hasn't been opened"); + return std::unexpected(EINVAL); + } + + return _AtEOF(); +} + +std::expected SFB::InputSource::GetOffset() const noexcept +{ + if(!IsOpen()) { + os_log_debug(sLog, "GetOffset() called on an InputSource that hasn't been opened"); + return std::unexpected(EINVAL); + } + + return _GetOffset(); +} + +std::expected SFB::InputSource::GetLength() const noexcept +{ + if(!IsOpen()) { + os_log_debug(sLog, "GetLength() called on an InputSource that hasn't been opened"); + return std::unexpected(EINVAL); + } + + return _GetLength(); +} + +bool SFB::InputSource::SupportsSeeking() const noexcept +{ + return _SupportsSeeking(); +} + +std::expected SFB::InputSource::SeekToOffset(int64_t offset, int whence) noexcept +{ + if(!IsOpen()) { + os_log_debug(sLog, "SeekToOffset() called on an InputSource that hasn't been opened"); + return std::unexpected(EINVAL); + } + + return _SeekToOffset(offset, whence); +} + +// Seeking support is optional + +bool SFB::InputSource::_SupportsSeeking() const noexcept +{ + return false; +} + +std::expected SFB::InputSource::_SeekToOffset(int64_t offset, int whence) noexcept +{ + return std::unexpected(EBADF); +} diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp new file mode 100644 index 000000000..478993b20 --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -0,0 +1,79 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#pragma once + +#import +#import + +#import + +#import + +namespace SFB { + +class InputSource +{ +public: + /// The shared log for all `InputSource` instances + static const os_log_t _Nonnull sLog; + + using unique_ptr = std::unique_ptr; + + virtual ~InputSource(); + + // This class is non-copyable. + InputSource(const InputSource& rhs) = delete; + InputSource(InputSource&& rhs) = delete; + + // This class is non-assignable. + InputSource& operator=(const InputSource& rhs) = delete; + InputSource& operator=(InputSource&& rhs) = delete; + + CFURLRef _Nullable GetURL() const noexcept { return url_; } + + // Opening and closing + std::expected Open() noexcept; + std::expected Close() noexcept; + + bool IsOpen() const noexcept { return isOpen_; } + + // Reading + std::expected Read(void * _Nonnull buffer, int64_t count) noexcept; + + std::expected AtEOF() const noexcept; + + std::expected GetOffset() const noexcept; + std::expected GetLength() const noexcept; + + // Seeking + bool SupportsSeeking() const noexcept; + std::expected SeekToOffset(int64_t offset, int whence) noexcept; + +protected: + + explicit InputSource() noexcept = default; + explicit InputSource(CFURLRef _Nullable url) noexcept; + +private: + + // Subclasses must implement the following methods + virtual std::expected _Open() noexcept = 0; + virtual std::expected _Close() noexcept = 0; + virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept = 0; + virtual std::expected _AtEOF() const noexcept = 0; + virtual std::expected _GetOffset() const noexcept = 0; + virtual std::expected _GetLength() const noexcept = 0; + + // Optional seeking support + virtual bool _SupportsSeeking() const noexcept; + virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + + CFURLRef _Nullable url_ = nullptr; + bool isOpen_ = false; +}; + +} /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp new file mode 100644 index 000000000..0fb52a86e --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -0,0 +1,123 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#import + +#import +#import + +#import "MemoryMappedFileInput.hpp" +#import "scope_exit.hpp" + +SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) noexcept +: InputSource(url) +{} + +std::expected SFB::MemoryMappedFileInput::_Open() noexcept +{ + CFURLRef url = GetURL(); + if(!url) + return std::unexpected(ENOENT); + + UInt8 path [PATH_MAX]; + auto success = CFURLGetFileSystemRepresentation(url, FALSE, path, PATH_MAX); + if(!success) + return std::unexpected(EIO); + + auto file = std::fopen(reinterpret_cast(path), "r"); + if(!file) + return std::unexpected(errno); + + // Ensure the file is closed + auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; + + auto fd = ::fileno(file); + + struct stat s; + if(::fstat(fd, &s)) + return std::unexpected(errno); + + // Only regular files can be mapped + if(!S_ISREG(s.st_mode)) + return std::unexpected(EBADF); + + // Map the file to memory + auto region = mmap(nullptr, s.st_size, PROT_READ, MAP_SHARED, fd, 0); + + if(region == MAP_FAILED) + return std::unexpected(errno); + + region_ = region; + len_ = s.st_size; + pos_ = 0; + + return {}; +} + +std::expected SFB::MemoryMappedFileInput::_Close() noexcept +{ + auto defer = scope_exit{[&] noexcept { + region_ = nullptr; + len_ = 0; + }}; + + if(munmap(region_, len_)) + return std::unexpected(errno); + return {}; +} + +std::expected SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) noexcept +{ + auto remaining = len_ - pos_; + count = std::min(count, remaining); + + memcpy(buffer, reinterpret_cast(reinterpret_cast(region_) + pos_), count); + pos_ += count; + + return count; +} + +std::expected SFB::MemoryMappedFileInput::_AtEOF() const noexcept +{ + return len_ == pos_; +} + +std::expected SFB::MemoryMappedFileInput::_GetOffset() const noexcept +{ + return pos_; +} + +std::expected SFB::MemoryMappedFileInput::_GetLength() const noexcept +{ + return len_; +} + +bool SFB::MemoryMappedFileInput::_SupportsSeeking() const noexcept +{ + return true; +} + +std::expected SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, int whence) noexcept +{ + switch(whence) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += pos_; + break; + case SEEK_END: + offset += len_; + break; + default: + return std::unexpected(EINVAL); + } + + if(offset < 0 || offset > len_) + return std::unexpected(EINVAL); + + pos_ = offset; + return {}; +} diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp new file mode 100644 index 000000000..a09cef857 --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -0,0 +1,42 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#pragma once + +#import "InputSource.hpp" + +namespace SFB { + +class MemoryMappedFileInput: public InputSource +{ +public: + explicit MemoryMappedFileInput(CFURLRef _Nonnull url) noexcept; + virtual ~MemoryMappedFileInput() = default; + + // This class is non-copyable. + MemoryMappedFileInput(const MemoryMappedFileInput& rhs) = delete; + MemoryMappedFileInput(MemoryMappedFileInput&& rhs) = delete; + + // This class is non-assignable. + MemoryMappedFileInput& operator=(const MemoryMappedFileInput& rhs) = delete; + MemoryMappedFileInput& operator=(MemoryMappedFileInput&& rhs) = delete; + +private: + virtual std::expected _Open() noexcept; + virtual std::expected _Close() noexcept; + virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept; + virtual std::expected _AtEOF() const noexcept; + virtual std::expected _GetOffset() const noexcept; + virtual std::expected _GetLength() const noexcept; + virtual bool _SupportsSeeking() const noexcept; + virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + + void * _Nullable region_ = nullptr; + int64_t len_ = 0; + int64_t pos_ = 0; +}; + +} /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/SFBDataInputSource.h b/Sources/CSFBAudioEngine/Input/SFBDataInputSource.h deleted file mode 100644 index 3b2708799..000000000 --- a/Sources/CSFBAudioEngine/Input/SFBDataInputSource.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) 2010-2025 Stephen F. Booth -// Part of https://github.com/sbooth/SFBAudioEngine -// MIT license -// - -#import "SFBInputSource+Internal.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface SFBDataInputSource : SFBInputSource -+ (instancetype)new NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithURL:(nullable NSURL *)url NS_UNAVAILABLE; -- (instancetype)initWithData:(NSData *)data; -- (instancetype)initWithData:(NSData *)data url:(nullable NSURL *)url NS_DESIGNATED_INITIALIZER; -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/CSFBAudioEngine/Input/SFBDataInputSource.m b/Sources/CSFBAudioEngine/Input/SFBDataInputSource.m deleted file mode 100644 index a62e3f31a..000000000 --- a/Sources/CSFBAudioEngine/Input/SFBDataInputSource.m +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright (c) 2010-2025 Stephen F. Booth -// Part of https://github.com/sbooth/SFBAudioEngine -// MIT license -// - -#import "SFBDataInputSource.h" - -@interface SFBDataInputSource () -{ -@private - NSData *_data; - NSUInteger _pos; -} -@end - -@implementation SFBDataInputSource - -- (instancetype)initWithData:(NSData *)data -{ - return [self initWithData:data url:nil]; -} - -- (instancetype)initWithData:(NSData *)data url:(NSURL *)url -{ - NSParameterAssert(data != nil); - - if((self = [super initWithURL:url])) - _data = [data copy]; - return self; -} - -- (BOOL)openReturningError:(NSError **)error -{ - return YES; -} - -- (BOOL)closeReturningError:(NSError **)error -{ - _data = nil; - return YES; -} - -- (BOOL)isOpen -{ - return _data != nil; -} - -- (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger *)bytesRead error:(NSError **)error -{ - NSParameterAssert(buffer != NULL); - NSParameterAssert(length >= 0); - NSParameterAssert(bytesRead != NULL); - - NSUInteger count = (NSUInteger)length; - NSUInteger remaining = _data.length - _pos; - if(count > remaining) - count = remaining; - - [_data getBytes:buffer range:NSMakeRange(_pos, count)]; - _pos += count; - *bytesRead = (NSInteger)count; - - return YES; -} - -- (BOOL)atEOF -{ - return _pos == _data.length; -} - -- (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error -{ - NSParameterAssert(offset != NULL); - *offset = (NSInteger)_pos; - return YES; -} - -- (BOOL)getLength:(NSInteger *)length error:(NSError **)error -{ - NSParameterAssert(length != NULL); - *length = (NSInteger)_data.length; - return YES; -} - -- (BOOL)supportsSeeking -{ - return YES; -} - -- (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error -{ - NSParameterAssert(offset >= 0); - if((NSUInteger)offset > _data.length) { - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:EINVAL userInfo:@{ NSURLErrorKey: self.url }]; - return NO; - } - - _pos = (NSUInteger)offset; - return YES; -} - -@end diff --git a/Sources/CSFBAudioEngine/Input/SFBFileContentsInputSource.h b/Sources/CSFBAudioEngine/Input/SFBFileContentsInputSource.h deleted file mode 100644 index 8c67fc299..000000000 --- a/Sources/CSFBAudioEngine/Input/SFBFileContentsInputSource.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) 2010-2025 Stephen F. Booth -// Part of https://github.com/sbooth/SFBAudioEngine -// MIT license -// - -#import "SFBDataInputSource.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface SFBFileContentsInputSource : SFBDataInputSource -+ (instancetype)new NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithData:(NSData *)data url:(nullable NSURL *)url NS_UNAVAILABLE; -- (nullable instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)error NS_DESIGNATED_INITIALIZER; -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/CSFBAudioEngine/Input/SFBFileContentsInputSource.m b/Sources/CSFBAudioEngine/Input/SFBFileContentsInputSource.m deleted file mode 100644 index 51c32e230..000000000 --- a/Sources/CSFBAudioEngine/Input/SFBFileContentsInputSource.m +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) 2010-2025 Stephen F. Booth -// Part of https://github.com/sbooth/SFBAudioEngine -// MIT license -// - -#import "SFBFileContentsInputSource.h" - -@implementation SFBFileContentsInputSource - -- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)error -{ - NSParameterAssert(url != nil); - NSParameterAssert(url.isFileURL); - - NSData *data = [NSData dataWithContentsOfURL:url options:0 error:error]; - if(data == nil) - return nil; - - return [super initWithData:data url:url]; -} - -@end diff --git a/Sources/CSFBAudioEngine/Input/SFBFileInputSource.h b/Sources/CSFBAudioEngine/Input/SFBFileInputSource.h deleted file mode 100644 index 7d52bda14..000000000 --- a/Sources/CSFBAudioEngine/Input/SFBFileInputSource.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (c) 2010-2025 Stephen F. Booth -// Part of https://github.com/sbooth/SFBAudioEngine -// MIT license -// - -#import "SFBInputSource+Internal.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface SFBFileInputSource : SFBInputSource -+ (instancetype)new NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/CSFBAudioEngine/Input/SFBFileInputSource.m b/Sources/CSFBAudioEngine/Input/SFBFileInputSource.m deleted file mode 100644 index c3c4ebeed..000000000 --- a/Sources/CSFBAudioEngine/Input/SFBFileInputSource.m +++ /dev/null @@ -1,144 +0,0 @@ -// -// Copyright (c) 2010-2025 Stephen F. Booth -// Part of https://github.com/sbooth/SFBAudioEngine -// MIT license -// - -#import -#import - -#import "SFBFileInputSource.h" -#import "SFBInputSource+Internal.h" - -@interface SFBFileInputSource () -{ -@private - struct stat _filestats; - FILE *_file; -} -@end - -@implementation SFBFileInputSource - -#if 0 -- (instancetype)initWithURL:(NSURL *)url -{ - NSParameterAssert(url != nil); - NSParameterAssert(url.isFileURL); - - return [super initWithURL:url]; -} -#endif - -- (BOOL)openReturningError:(NSError **)error -{ - _file = fopen(self.url.fileSystemRepresentation, "r"); - if(!_file) { - int err = errno; - os_log_error(gSFBInputSourceLog, "fopen failed: %{public}s (%d)", strerror(err), err); - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:@{ NSURLErrorKey: self.url }]; - return NO; - } - - if(fstat(fileno(_file), &_filestats) == -1) { - int err = errno; - os_log_error(gSFBInputSourceLog, "fstat failed: %{public}s (%d)", strerror(err), err); - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:@{ NSURLErrorKey: self.url }]; - - if(fclose(_file)) - os_log_info(gSFBInputSourceLog, "fclose failed: %{public}s (%d)", strerror(errno), errno); - _file = NULL; - - return NO; - } - - return YES; -} - -- (BOOL)closeReturningError:(NSError **)error -{ - if(_file) { - int result = fclose(_file); - _file = NULL; - if(result) { - int err = errno; - os_log_error(gSFBInputSourceLog, "fclose failed: %{public}s (%d)", strerror(err), err); - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:@{ NSURLErrorKey: self.url }]; - return NO; - } - } - return YES; -} - -- (BOOL)isOpen -{ - return _file != NULL; -} - -- (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger *)bytesRead error:(NSError **)error -{ - NSParameterAssert(buffer != NULL); - NSParameterAssert(length >= 0); - NSParameterAssert(bytesRead != NULL); - - size_t read = fread(buffer, 1, (size_t)length, _file); - if(read != (size_t)length && ferror(_file)) { - int err = errno; - os_log_error(gSFBInputSourceLog, "fread error: %{public}s (%d)", strerror(err), err); - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:@{ NSURLErrorKey: self.url }]; - return NO; - } - *bytesRead = (NSInteger)read; - return YES; -} - -- (BOOL)atEOF -{ - return feof(_file) != 0; -} - -- (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error -{ - NSParameterAssert(offset != NULL); - off_t result = ftello(_file); - if(result == -1) { - int err = errno; - os_log_error(gSFBInputSourceLog, "ftello failed: %{public}s (%d)", strerror(err), err); - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:@{ NSURLErrorKey: self.url }]; - return NO; - } - *offset = result; - return YES; -} - -- (BOOL)getLength:(NSInteger *)length error:(NSError **)error -{ - NSParameterAssert(length != NULL); - *length = _filestats.st_size; - return YES; -} - -- (BOOL)supportsSeeking -{ - return YES; -} - -- (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error -{ - NSParameterAssert(offset >= 0); - if(fseeko(_file, offset, SEEK_SET)) { - int err = errno; - os_log_error(gSFBInputSourceLog, "fseeko(%ld, SEEK_SET) error: %{public}s (%d)", (long)offset, strerror(err), err); - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:@{ NSURLErrorKey: self.url }]; - return NO; - } - return YES; -} - -@end diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource+Internal.h b/Sources/CSFBAudioEngine/Input/SFBInputSource+Internal.h index 20a52f68b..98cbe2215 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource+Internal.h +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource+Internal.h @@ -4,20 +4,16 @@ // MIT license // -#import - #import "SFBInputSource.h" +#import "InputSource.hpp" NS_ASSUME_NONNULL_BEGIN -extern os_log_t gSFBInputSourceLog; - @interface SFBInputSource () { @package - NSURL *_url; + SFB::InputSource::unique_ptr _input; } -- (instancetype)initWithURL:(nullable NSURL *)url NS_DESIGNATED_INITIALIZER; @end NS_ASSUME_NONNULL_END diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.m b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm similarity index 73% rename from Sources/CSFBAudioEngine/Input/SFBInputSource.m rename to Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 8292f2b90..c665ba336 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.m +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -6,27 +6,16 @@ #import "SFBInputSource+Internal.h" -#import "SFBDataInputSource.h" -#import "SFBFileContentsInputSource.h" -#import "SFBFileInputSource.h" -#import "SFBMemoryMappedFileInputSource.h" +#import "DataInput.hpp" +#import "FileContentsInput.hpp" +#import "FileInput.hpp" +#import "MemoryMappedFileInput.hpp" #import "NSData+SFBExtensions.h" // NSError domain for InputSource and subclasses NSErrorDomain const SFBInputSourceErrorDomain = @"org.sbooth.AudioEngine.InputSource"; -os_log_t gSFBInputSourceLog = NULL; - -static void SFBCreateInputSourceLog(void) __attribute__ ((constructor)); -static void SFBCreateInputSourceLog(void) -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - gSFBInputSourceLog = os_log_create("org.sbooth.AudioEngine", "InputSource"); - }); -} - @implementation SFBInputSource + (void)load @@ -56,107 +45,179 @@ + (instancetype)inputSourceForURL:(NSURL *)url flags:(SFBInputSourceFlags)flags NSParameterAssert(url != nil); NSParameterAssert(url.isFileURL); + SFB::InputSource::unique_ptr up; + if(flags & SFBInputSourceFlagsMemoryMapFiles) - return [[SFBMemoryMappedFileInputSource alloc] initWithURL:url error:error]; + up = std::make_unique((__bridge CFURLRef)url); else if(flags & SFBInputSourceFlagsLoadFilesInMemory) - return [[SFBFileContentsInputSource alloc] initWithContentsOfURL:url error:error]; + up = std::make_unique((__bridge CFURLRef)url); else - return [[SFBFileInputSource alloc] initWithURL:url]; + up = std::make_unique((__bridge CFURLRef)url); + + if(up == nullptr) + return nil; - return nil; + SFBInputSource *inputSource = [[SFBInputSource alloc] init]; + if(inputSource == nil) + return nil; + + inputSource->_input = std::move(up); + return inputSource; } + (instancetype)inputSourceWithData:(NSData *)data { NSParameterAssert(data != nil); - return [[SFBDataInputSource alloc] initWithData:data]; + + auto up = std::make_unique((__bridge CFDataRef)data); + if(up == nullptr) + return nil; + + SFBInputSource *is = [[SFBInputSource alloc] init]; + if(is == nil) + return nil; + + is->_input = std::move(up); + return is; } + (instancetype)inputSourceWithBytes:(const void *)bytes length:(NSInteger)length { - NSParameterAssert(bytes != NULL); + NSParameterAssert(bytes != nullptr); NSParameterAssert(length >= 0); + NSData *data = [NSData dataWithBytes:bytes length:(NSUInteger)length]; if(data == nil) return nil; - return [[SFBDataInputSource alloc] initWithData:data]; + return [SFBInputSource inputSourceWithData:data]; } + (instancetype)inputSourceWithBytesNoCopy:(void *)bytes length:(NSInteger)length freeWhenDone:(BOOL)freeWhenDone { - NSParameterAssert(bytes != NULL); + NSParameterAssert(bytes != nullptr); NSParameterAssert(length >= 0); + NSData *data = [NSData dataWithBytesNoCopy:bytes length:(NSUInteger)length freeWhenDone:freeWhenDone]; if(data == nil) return nil; - return [[SFBDataInputSource alloc] initWithData:data]; + return [SFBInputSource inputSourceWithData:data]; } -- (instancetype)initWithURL:(NSURL *)url +- (void)dealloc { - if((self = [super init])) - _url = url; - return self; + _input.reset(); } -- (void)dealloc +- (NSURL *)url { - if(self.isOpen) - [self closeReturningError:nil]; + return (__bridge NSURL *)_input->GetURL(); } - (BOOL)openReturningError:(NSError **)error { - [self doesNotRecognizeSelector:_cmd]; - __builtin_unreachable(); + auto result = _input->Open(); + if(!result) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + return NO; + } + return YES; } - (BOOL)closeReturningError:(NSError **)error { - [self doesNotRecognizeSelector:_cmd]; - __builtin_unreachable(); + auto result = _input->Close(); + if(!result) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + return NO; + } + return YES; } - (BOOL)isOpen { - [self doesNotRecognizeSelector:_cmd]; - __builtin_unreachable(); + return _input->IsOpen(); } - (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger *)bytesRead error:(NSError **)error { - [self doesNotRecognizeSelector:_cmd]; - __builtin_unreachable(); + NSParameterAssert(bytesRead != nullptr); + + auto result = _input->Read(buffer, length); + if(!result) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + return NO; + } + + *bytesRead = result.value(); + + return YES; +} + +- (BOOL)atEOF +{ + auto result = _input->AtEOF(); + if(!result) + // FIXME + return NO; + + return result.value(); } - (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error { - [self doesNotRecognizeSelector:_cmd]; - __builtin_unreachable(); + NSParameterAssert(offset != nullptr); + + auto result = _input->GetOffset(); + if(!result) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + return NO; + } + + *offset = result.value(); + return YES; } - (BOOL)getLength:(NSInteger *)length error:(NSError **)error { - [self doesNotRecognizeSelector:_cmd]; - __builtin_unreachable(); + NSParameterAssert(length != nullptr); + + auto result = _input->GetLength(); + if(!result) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + return NO; + } + + *length = result.value(); + return YES; } - (BOOL)supportsSeeking { - [self doesNotRecognizeSelector:_cmd]; - __builtin_unreachable(); + return _input->SupportsSeeking(); } - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error { - [self doesNotRecognizeSelector:_cmd]; - __builtin_unreachable(); + auto result = _input->SeekToOffset(offset, SEEK_SET); + if(!result) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + return NO; + } + + return YES; } - (NSString *)description { - if(_url) - return [NSString stringWithFormat:@"<%@ %p: \"%@\">", [self class], self, [[NSFileManager defaultManager] displayNameAtPath:_url.path]]; + NSURL *url = (__bridge NSURL *)_input->GetURL(); + if(url) + return [NSString stringWithFormat:@"<%@ %p: \"%@\">", [self class], self, [[NSFileManager defaultManager] displayNameAtPath:url.path]]; else return [NSString stringWithFormat:@"<%@ %p>", [self class], self]; } diff --git a/Sources/CSFBAudioEngine/Input/SFBMemoryMappedFileInputSource.h b/Sources/CSFBAudioEngine/Input/SFBMemoryMappedFileInputSource.h deleted file mode 100644 index 40d2ea087..000000000 --- a/Sources/CSFBAudioEngine/Input/SFBMemoryMappedFileInputSource.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) 2010-2025 Stephen F. Booth -// Part of https://github.com/sbooth/SFBAudioEngine -// MIT license -// - -#import "SFBDataInputSource.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface SFBMemoryMappedFileInputSource : SFBDataInputSource -+ (instancetype)new NS_UNAVAILABLE; -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithData:(NSData *)data url:(nullable NSURL *)url NS_UNAVAILABLE; -- (nullable instancetype)initWithURL:(NSURL *)url error:(NSError **)error NS_DESIGNATED_INITIALIZER; -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/CSFBAudioEngine/Input/SFBMemoryMappedFileInputSource.m b/Sources/CSFBAudioEngine/Input/SFBMemoryMappedFileInputSource.m deleted file mode 100644 index e4b06a0ac..000000000 --- a/Sources/CSFBAudioEngine/Input/SFBMemoryMappedFileInputSource.m +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) 2010-2025 Stephen F. Booth -// Part of https://github.com/sbooth/SFBAudioEngine -// MIT license -// - -#import "SFBMemoryMappedFileInputSource.h" - -@implementation SFBMemoryMappedFileInputSource - -- (instancetype)initWithURL:(NSURL *)url error:(NSError **)error -{ - NSParameterAssert(url != nil); - NSParameterAssert(url.isFileURL); - - NSData *data = [NSData dataWithContentsOfURL:url options:NSDataReadingMappedAlways error:error]; - if(data == nil) - return nil; - - return [super initWithData:data url:url]; -} - -@end diff --git a/Sources/CSFBAudioEngine/Input/scope_exit.hpp b/Sources/CSFBAudioEngine/Input/scope_exit.hpp new file mode 100644 index 000000000..df3e89bd5 --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/scope_exit.hpp @@ -0,0 +1,32 @@ +// +// Copyright (c) 2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#import + +namespace SFB { + +template +concept IsNothrowInvocable = std::is_nothrow_invocable_v; + +template +class scope_exit { +public: + explicit scope_exit(F&& f) noexcept(std::is_nothrow_constructible_v) : exit_func_(f) {} + ~scope_exit() noexcept { exit_func_(); } + + // This class is non-copyable. + scope_exit(const scope_exit&) = delete; + scope_exit(scope_exit&&) = delete; + + // This class is non-assignable. + scope_exit& operator=(const scope_exit&) = delete; + scope_exit& operator=(scope_exit&&) = delete; + +private: + F exit_func_; +}; + +} /* namespace SFB */ From 358813756e4010ba0591548908363985ccb3c9e5 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sun, 2 Nov 2025 18:46:44 -0600 Subject: [PATCH 02/75] Return a value --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index d821ea6df..e320ad6e6 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -34,7 +34,7 @@ std::expected SFB::InputSource::Open() noexcept { if(IsOpen()) { os_log_debug(sLog, "Open() called on an InputSource that is already open"); - return; + return {}; } auto result = _Open(); @@ -47,7 +47,7 @@ std::expected SFB::InputSource::Close() noexcept { if(!IsOpen()) { os_log_debug(sLog, "Close() called on an InputSource that hasn't been opened"); - return; + return {}; } auto result = _Close(); From 9985e95df0ead7f92ec34b98803a10096eb82a25 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sun, 2 Nov 2025 19:49:02 -0600 Subject: [PATCH 03/75] Fix destructors --- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 5 +++++ Sources/CSFBAudioEngine/Input/FileContentsInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.cpp | 6 ++++++ Sources/CSFBAudioEngine/Input/FileInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/InputSource.cpp | 2 -- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 6 ++++++ Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp | 2 +- 7 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index adca8ffdc..c67b7f9eb 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -15,6 +15,11 @@ SFB::FileContentsInput::FileContentsInput(CFURLRef url) noexcept : InputSource(url) {} +SFB::FileContentsInput::~FileContentsInput() +{ + free(buf_); +} + std::expected SFB::FileContentsInput::_Open() noexcept { CFURLRef url = GetURL(); diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index b4919263a..168a0c2e5 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -14,7 +14,7 @@ class FileContentsInput: public InputSource { public: explicit FileContentsInput(CFURLRef _Nonnull url) noexcept; - virtual ~FileContentsInput() = default; + virtual ~FileContentsInput(); // This class is non-copyable. FileContentsInput(const FileContentsInput& rhs) = delete; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 88bd6095b..bb37b49fa 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -13,6 +13,12 @@ SFB::FileInput::FileInput(CFURLRef url) noexcept : InputSource(url) {} +SFB::FileInput::~FileInput() +{ + if(file_) + std::fclose(file_); +} + std::expected SFB::FileInput::_Open() noexcept { UInt8 path [PATH_MAX]; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 99af4d473..a704c0b56 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -16,7 +16,7 @@ class FileInput: public InputSource { public: explicit FileInput(CFURLRef _Nonnull url) noexcept; - virtual ~FileInput() = default; + virtual ~FileInput(); // This class is non-copyable. FileInput(const FileInput& rhs) = delete; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index e320ad6e6..45e48d2c0 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -24,8 +24,6 @@ SFB::InputSource::InputSource(CFURLRef url) noexcept SFB::InputSource::~InputSource() { - if(isOpen_) - Close(); if(url_) CFRelease(url_); } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 0fb52a86e..866ed358c 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -16,6 +16,12 @@ SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) noexcept : InputSource(url) {} +SFB::MemoryMappedFileInput::~MemoryMappedFileInput() +{ + if(region_) + munmap(region_, len_); +} + std::expected SFB::MemoryMappedFileInput::_Open() noexcept { CFURLRef url = GetURL(); diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index a09cef857..c4f1d3c5e 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -14,7 +14,7 @@ class MemoryMappedFileInput: public InputSource { public: explicit MemoryMappedFileInput(CFURLRef _Nonnull url) noexcept; - virtual ~MemoryMappedFileInput() = default; + virtual ~MemoryMappedFileInput(); // This class is non-copyable. MemoryMappedFileInput(const MemoryMappedFileInput& rhs) = delete; From 34636bbe7246ad54cdd789f65034eb4541b6958e Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sun, 2 Nov 2025 19:49:32 -0600 Subject: [PATCH 04/75] Correct test behavior for new input sources --- Tests/SFBAudioEngineTests/SFBAudioEngineTests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/SFBAudioEngineTests/SFBAudioEngineTests.swift b/Tests/SFBAudioEngineTests/SFBAudioEngineTests.swift index ccd6b678c..73b0e1f4f 100644 --- a/Tests/SFBAudioEngineTests/SFBAudioEngineTests.swift +++ b/Tests/SFBAudioEngineTests/SFBAudioEngineTests.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2012-2024 Stephen F. Booth +// Copyright (c) 2012-2025 Stephen F. Booth // Part of https://github.com/sbooth/SFBAudioEngine // MIT license // @@ -10,6 +10,8 @@ import XCTest final class SFBAudioEngineTests: XCTestCase { func testInputSourceFromData() throws { let input = InputSource(data: Data(repeating: 0xfe, count: 16)) + XCTAssertEqual(input.isOpen, false) + try input.open() XCTAssertEqual(input.isOpen, true) XCTAssertEqual(input.supportsSeeking, true) XCTAssertEqual(try input.offset, 0) From d98d523f1610685421b458488dc35e95b5cfda53 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 3 Nov 2025 14:11:01 -0600 Subject: [PATCH 05/75] Use `override` instead of `virtual` --- Sources/CSFBAudioEngine/Input/DataInput.hpp | 18 +++++++++--------- .../Input/FileContentsInput.hpp | 18 +++++++++--------- Sources/CSFBAudioEngine/Input/FileInput.hpp | 18 +++++++++--------- Sources/CSFBAudioEngine/Input/InputSource.hpp | 2 +- .../Input/MemoryMappedFileInput.hpp | 18 +++++++++--------- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index ae8100bac..3dfa925e8 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -14,7 +14,7 @@ class DataInput: public InputSource { public: explicit DataInput(CFDataRef _Nonnull data) noexcept; - virtual ~DataInput(); + ~DataInput() noexcept; // This class is non-copyable. DataInput(const DataInput& rhs) = delete; @@ -25,14 +25,14 @@ class DataInput: public InputSource DataInput& operator=(DataInput&& rhs) = delete; private: - virtual std::expected _Open() noexcept; - virtual std::expected _Close() noexcept; - virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept; - virtual std::expected _AtEOF() const noexcept; - virtual std::expected _GetOffset() const noexcept; - virtual std::expected _GetLength() const noexcept; - virtual bool _SupportsSeeking() const noexcept; - virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + std::expected _Open() noexcept override; + std::expected _Close() noexcept override; + std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; + std::expected _AtEOF() const noexcept override; + std::expected _GetOffset() const noexcept override; + std::expected _GetLength() const noexcept override; + bool _SupportsSeeking() const noexcept override; + std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; CFDataRef _Nullable data_ = nullptr; CFIndex pos_ = 0; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 168a0c2e5..caf77893a 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -14,7 +14,7 @@ class FileContentsInput: public InputSource { public: explicit FileContentsInput(CFURLRef _Nonnull url) noexcept; - virtual ~FileContentsInput(); + ~FileContentsInput() noexcept; // This class is non-copyable. FileContentsInput(const FileContentsInput& rhs) = delete; @@ -25,14 +25,14 @@ class FileContentsInput: public InputSource FileContentsInput& operator=(FileContentsInput&& rhs) = delete; private: - virtual std::expected _Open() noexcept; - virtual std::expected _Close() noexcept; - virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept; - virtual std::expected _AtEOF() const noexcept; - virtual std::expected _GetOffset() const noexcept; - virtual std::expected _GetLength() const noexcept; - virtual bool _SupportsSeeking() const noexcept; - virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + std::expected _Open() noexcept override; + std::expected _Close() noexcept override; + std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; + std::expected _AtEOF() const noexcept override; + std::expected _GetOffset() const noexcept override; + std::expected _GetLength() const noexcept override; + bool _SupportsSeeking() const noexcept override; + std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; void * _Nullable buf_ = nullptr; int64_t len_ = 0; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index a704c0b56..a5ba4da40 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -16,7 +16,7 @@ class FileInput: public InputSource { public: explicit FileInput(CFURLRef _Nonnull url) noexcept; - virtual ~FileInput(); + ~FileInput() noexcept; // This class is non-copyable. FileInput(const FileInput& rhs) = delete; @@ -27,14 +27,14 @@ class FileInput: public InputSource FileInput& operator=(FileInput&& rhs) = delete; private: - virtual std::expected _Open() noexcept; - virtual std::expected _Close() noexcept; - virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept; - virtual std::expected _AtEOF() const noexcept; - virtual std::expected _GetOffset() const noexcept; - virtual std::expected _GetLength() const noexcept; - virtual bool _SupportsSeeking() const noexcept; - virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + std::expected _Open() noexcept override; + std::expected _Close() noexcept override; + std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; + std::expected _AtEOF() const noexcept override; + std::expected _GetOffset() const noexcept override; + std::expected _GetLength() const noexcept override; + bool _SupportsSeeking() const noexcept override; + std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; FILE * _Nullable file_ = nullptr; int64_t len_ = 0; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 478993b20..3d5fb36e8 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -23,7 +23,7 @@ class InputSource using unique_ptr = std::unique_ptr; - virtual ~InputSource(); + virtual ~InputSource() noexcept; // This class is non-copyable. InputSource(const InputSource& rhs) = delete; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index c4f1d3c5e..f50a8393f 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -14,7 +14,7 @@ class MemoryMappedFileInput: public InputSource { public: explicit MemoryMappedFileInput(CFURLRef _Nonnull url) noexcept; - virtual ~MemoryMappedFileInput(); + ~MemoryMappedFileInput() noexcept; // This class is non-copyable. MemoryMappedFileInput(const MemoryMappedFileInput& rhs) = delete; @@ -25,14 +25,14 @@ class MemoryMappedFileInput: public InputSource MemoryMappedFileInput& operator=(MemoryMappedFileInput&& rhs) = delete; private: - virtual std::expected _Open() noexcept; - virtual std::expected _Close() noexcept; - virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept; - virtual std::expected _AtEOF() const noexcept; - virtual std::expected _GetOffset() const noexcept; - virtual std::expected _GetLength() const noexcept; - virtual bool _SupportsSeeking() const noexcept; - virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + std::expected _Open() noexcept override; + std::expected _Close() noexcept override; + std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; + std::expected _AtEOF() const noexcept override; + std::expected _GetOffset() const noexcept override; + std::expected _GetLength() const noexcept override; + bool _SupportsSeeking() const noexcept override; + std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; void * _Nullable region_ = nullptr; int64_t len_ = 0; From 405684c0edc9f710456bfd10c1372b4a8d95a058 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 3 Nov 2025 17:26:59 -0600 Subject: [PATCH 06/75] Change error code --- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 866ed358c..a1f0c31a9 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -48,7 +48,7 @@ std::expected SFB::MemoryMappedFileInput::_Open() noexcept // Only regular files can be mapped if(!S_ISREG(s.st_mode)) - return std::unexpected(EBADF); + return std::unexpected(ENOTSUP); // Map the file to memory auto region = mmap(nullptr, s.st_size, PROT_READ, MAP_SHARED, fd, 0); From be16b5c95062125f994a5e1c2e0f6cc840cb49eb Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 3 Nov 2025 17:27:23 -0600 Subject: [PATCH 07/75] Use `scope_exit` for `Close()` --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 45e48d2c0..72cdee3f8 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -10,6 +10,8 @@ #import "DataInput.hpp" #import "FileInput.hpp" +#import "scope_exit.hpp" + namespace SFB { const os_log_t InputSource::sLog = os_log_create("org.sbooth.AudioEngine", "InputSource"); @@ -48,16 +50,18 @@ std::expected SFB::InputSource::Close() noexcept return {}; } - auto result = _Close(); - isOpen_ = false; - return result; + auto defer = scope_exit{[&] noexcept { + isOpen_ = false; + }}; + + return _Close(); } std::expected SFB::InputSource::Read(void *buffer, int64_t count) noexcept { if(!IsOpen()) { os_log_debug(sLog, "Read() called on an InputSource that hasn't been opened"); - return std::unexpected(EINVAL); + return std::unexpected(EPERM); } if(!buffer || count < 0) { @@ -72,7 +76,7 @@ std::expected SFB::InputSource::AtEOF() const noexcept { if(!IsOpen()) { os_log_debug(sLog, "AtEOF() called on an InputSource that hasn't been opened"); - return std::unexpected(EINVAL); + return std::unexpected(EPERM); } return _AtEOF(); @@ -82,7 +86,7 @@ std::expected SFB::InputSource::GetOffset() const noexcept { if(!IsOpen()) { os_log_debug(sLog, "GetOffset() called on an InputSource that hasn't been opened"); - return std::unexpected(EINVAL); + return std::unexpected(EPERM); } return _GetOffset(); @@ -92,7 +96,7 @@ std::expected SFB::InputSource::GetLength() const noexcept { if(!IsOpen()) { os_log_debug(sLog, "GetLength() called on an InputSource that hasn't been opened"); - return std::unexpected(EINVAL); + return std::unexpected(EPERM); } return _GetLength(); @@ -107,7 +111,12 @@ std::expected SFB::InputSource::SeekToOffset(int64_t offset, int when { if(!IsOpen()) { os_log_debug(sLog, "SeekToOffset() called on an InputSource that hasn't been opened"); - return std::unexpected(EINVAL); + return std::unexpected(EPERM); + } + + if(!_SupportsSeeking()) { + os_log_debug(sLog, "SeekToOffset() called on an InputSource that doesn't support seeking"); + return std::unexpected(ENOTSUP); } return _SeekToOffset(offset, whence); @@ -122,5 +131,5 @@ bool SFB::InputSource::_SupportsSeeking() const noexcept std::expected SFB::InputSource::_SeekToOffset(int64_t offset, int whence) noexcept { - return std::unexpected(EBADF); + return std::unexpected(EPERM); } From a77b5b3e07e12f2c76f0e055ae412a940455f988 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 3 Nov 2025 17:32:57 -0600 Subject: [PATCH 08/75] Tweak lambda capture lists --- Sources/CSFBAudioEngine/Input/FileInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/InputSource.cpp | 2 +- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index bb37b49fa..0b6233115 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -44,7 +44,7 @@ std::expected SFB::FileInput::_Open() noexcept std::expected SFB::FileInput::_Close() noexcept { - auto defer = scope_exit{[&] noexcept { + auto defer = scope_exit{[this] noexcept { file_ = nullptr; len_ = 0; }}; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 72cdee3f8..dd8094c22 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -50,7 +50,7 @@ std::expected SFB::InputSource::Close() noexcept return {}; } - auto defer = scope_exit{[&] noexcept { + auto defer = scope_exit{[this] noexcept { isOpen_ = false; }}; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index a1f0c31a9..f3e351b10 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -65,7 +65,7 @@ std::expected SFB::MemoryMappedFileInput::_Open() noexcept std::expected SFB::MemoryMappedFileInput::_Close() noexcept { - auto defer = scope_exit{[&] noexcept { + auto defer = scope_exit{[this] noexcept { region_ = nullptr; len_ = 0; }}; From 3d93af71621c7b2d610853c58bc79f9a190a343a Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 3 Nov 2025 17:42:03 -0600 Subject: [PATCH 09/75] Add missing newline --- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index c67b7f9eb..f7726256d 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -51,7 +51,8 @@ std::expected SFB::FileContentsInput::_Open() noexcept len_ = s.st_size; pos_ = 0; - return {};} + return {}; +} std::expected SFB::FileContentsInput::_Close() noexcept { From 10044c45860d3a3970a3859cc6c7369474d07a98 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 4 Nov 2025 06:19:41 -0600 Subject: [PATCH 10/75] Add `noexcept` to dtors --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index c1eae5fd3..be56eb52d 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -12,7 +12,7 @@ SFB::DataInput::DataInput(CFDataRef data) noexcept data_ = (CFDataRef)CFRetain(data); } -SFB::DataInput::~DataInput() +SFB::DataInput::~DataInput() noexcept { if(data_) CFRelease(data_); diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index f7726256d..f769a641a 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -15,7 +15,7 @@ SFB::FileContentsInput::FileContentsInput(CFURLRef url) noexcept : InputSource(url) {} -SFB::FileContentsInput::~FileContentsInput() +SFB::FileContentsInput::~FileContentsInput() noexcept { free(buf_); } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 0b6233115..956689ef3 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -13,7 +13,7 @@ SFB::FileInput::FileInput(CFURLRef url) noexcept : InputSource(url) {} -SFB::FileInput::~FileInput() +SFB::FileInput::~FileInput() noexcept { if(file_) std::fclose(file_); diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index f3e351b10..c8ce03be3 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -16,7 +16,7 @@ SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) noexcept : InputSource(url) {} -SFB::MemoryMappedFileInput::~MemoryMappedFileInput() +SFB::MemoryMappedFileInput::~MemoryMappedFileInput() noexcept { if(region_) munmap(region_, len_); From c2f64e6e6f0e565b086bce9e56ec4e1e5c83d49c Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 4 Nov 2025 06:25:26 -0600 Subject: [PATCH 11/75] Add noexcept to dtor --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index dd8094c22..54f494578 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -24,7 +24,7 @@ SFB::InputSource::InputSource(CFURLRef url) noexcept url_ = (CFURLRef)CFRetain(url); } -SFB::InputSource::~InputSource() +SFB::InputSource::~InputSource() noexcept { if(url_) CFRelease(url_); From dcb419b520f5077168b89710dd42c788e8a7def6 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 4 Nov 2025 06:32:15 -0600 Subject: [PATCH 12/75] Make `final` --- Sources/CSFBAudioEngine/Input/scope_exit.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/scope_exit.hpp b/Sources/CSFBAudioEngine/Input/scope_exit.hpp index df3e89bd5..faa1f88cd 100644 --- a/Sources/CSFBAudioEngine/Input/scope_exit.hpp +++ b/Sources/CSFBAudioEngine/Input/scope_exit.hpp @@ -12,7 +12,7 @@ template concept IsNothrowInvocable = std::is_nothrow_invocable_v; template -class scope_exit { +class scope_exit final { public: explicit scope_exit(F&& f) noexcept(std::is_nothrow_constructible_v) : exit_func_(f) {} ~scope_exit() noexcept { exit_func_(); } From e443d17189ca96da2e57977719bfacd3d85be1b1 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 4 Nov 2025 06:36:44 -0600 Subject: [PATCH 13/75] Simplify NULL checks --- .../CSFBAudioEngine/Input/SFBInputSource.mm | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index c665ba336..ee309da3f 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -54,11 +54,11 @@ + (instancetype)inputSourceForURL:(NSURL *)url flags:(SFBInputSourceFlags)flags else up = std::make_unique((__bridge CFURLRef)url); - if(up == nullptr) + if(!up) return nil; SFBInputSource *inputSource = [[SFBInputSource alloc] init]; - if(inputSource == nil) + if(!inputSource) return nil; inputSource->_input = std::move(up); @@ -70,15 +70,15 @@ + (instancetype)inputSourceWithData:(NSData *)data NSParameterAssert(data != nil); auto up = std::make_unique((__bridge CFDataRef)data); - if(up == nullptr) + if(!up) return nil; - SFBInputSource *is = [[SFBInputSource alloc] init]; - if(is == nil) + SFBInputSource *inputSource = [[SFBInputSource alloc] init]; + if(!inputSource) return nil; - is->_input = std::move(up); - return is; + inputSource->_input = std::move(up); + return inputSource; } + (instancetype)inputSourceWithBytes:(const void *)bytes length:(NSInteger)length @@ -87,7 +87,7 @@ + (instancetype)inputSourceWithBytes:(const void *)bytes length:(NSInteger)lengt NSParameterAssert(length >= 0); NSData *data = [NSData dataWithBytes:bytes length:(NSUInteger)length]; - if(data == nil) + if(!data) return nil; return [SFBInputSource inputSourceWithData:data]; } @@ -98,7 +98,7 @@ + (instancetype)inputSourceWithBytesNoCopy:(void *)bytes length:(NSInteger)lengt NSParameterAssert(length >= 0); NSData *data = [NSData dataWithBytesNoCopy:bytes length:(NSUInteger)length freeWhenDone:freeWhenDone]; - if(data == nil) + if(!data) return nil; return [SFBInputSource inputSourceWithData:data]; } @@ -159,10 +159,9 @@ - (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger * - (BOOL)atEOF { auto result = _input->AtEOF(); + // FIXME: Is `NO` the best error return? if(!result) - // FIXME return NO; - return result.value(); } From 200fdd4ed5a4e82a7b6c2989293b719fc113e44c Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 4 Nov 2025 08:24:46 -0600 Subject: [PATCH 14/75] Replace concept with requires --- Sources/CSFBAudioEngine/Input/scope_exit.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/scope_exit.hpp b/Sources/CSFBAudioEngine/Input/scope_exit.hpp index faa1f88cd..71c9ec49e 100644 --- a/Sources/CSFBAudioEngine/Input/scope_exit.hpp +++ b/Sources/CSFBAudioEngine/Input/scope_exit.hpp @@ -8,10 +8,7 @@ namespace SFB { -template -concept IsNothrowInvocable = std::is_nothrow_invocable_v; - -template +template requires std::is_nothrow_invocable_v class scope_exit final { public: explicit scope_exit(F&& f) noexcept(std::is_nothrow_constructible_v) : exit_func_(f) {} From 7a32aab0880d65bf6c6e75306891928cd7254fb1 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 4 Nov 2025 14:35:18 -0600 Subject: [PATCH 15/75] Use `std::malloc` and `std::free` --- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index f769a641a..8b74928d6 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -5,6 +5,7 @@ // #import +#import #import @@ -17,7 +18,7 @@ SFB::FileContentsInput::FileContentsInput(CFURLRef url) noexcept SFB::FileContentsInput::~FileContentsInput() noexcept { - free(buf_); + std::free(buf_); } std::expected SFB::FileContentsInput::_Open() noexcept @@ -44,7 +45,7 @@ std::expected SFB::FileContentsInput::_Open() noexcept if(::fstat(fd, &s)) return std::unexpected(errno); - buf_ = malloc(s.st_size); + buf_ = std::malloc(s.st_size); if(!buf_) return std::unexpected(ENOMEM); From f96f06598a59b9122e6eb5469b84c86bc81e8687 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 5 Nov 2025 20:58:37 -0600 Subject: [PATCH 16/75] Fix includes --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 54f494578..c418840b2 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -4,12 +4,7 @@ // MIT license // -#import - #import "InputSource.hpp" -#import "DataInput.hpp" -#import "FileInput.hpp" - #import "scope_exit.hpp" namespace SFB { From 0545919f53d018a2a3766be8bc32ce2488902821 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 6 Nov 2025 07:29:07 -0600 Subject: [PATCH 17/75] Use uniform initialization --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 6 +++--- Sources/CSFBAudioEngine/Input/DataInput.hpp | 4 ++-- .../Input/FileContentsInput.cpp | 14 +++++++------- .../Input/FileContentsInput.hpp | 6 +++--- Sources/CSFBAudioEngine/Input/FileInput.cpp | 14 +++++++------- Sources/CSFBAudioEngine/Input/FileInput.hpp | 4 ++-- Sources/CSFBAudioEngine/Input/InputSource.cpp | 16 ++++++++-------- Sources/CSFBAudioEngine/Input/InputSource.hpp | 4 ++-- .../Input/MemoryMappedFileInput.cpp | 18 +++++++++--------- .../Input/MemoryMappedFileInput.hpp | 6 +++--- 10 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index be56eb52d..c9a64ed52 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -21,7 +21,7 @@ SFB::DataInput::~DataInput() noexcept std::expected SFB::DataInput::_Open() noexcept { if(!data_) - return std::unexpected(ENOENT); + return std::unexpected{ENOENT}; pos_ = 0; return {}; } @@ -78,11 +78,11 @@ std::expected SFB::DataInput::_SeekToOffset(int64_t offset, int whenc offset += length; break; default: - return std::unexpected(EINVAL); + return std::unexpected{EINVAL}; } if(offset < 0 || offset > length) - return std::unexpected(EINVAL); + return std::unexpected{EINVAL}; pos_ = offset; return {}; diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index 3dfa925e8..45199997e 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -34,8 +34,8 @@ class DataInput: public InputSource bool _SupportsSeeking() const noexcept override; std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; - CFDataRef _Nullable data_ = nullptr; - CFIndex pos_ = 0; + CFDataRef _Nullable data_ {nullptr}; + CFIndex pos_ {0}; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 8b74928d6..a43818711 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -25,16 +25,16 @@ std::expected SFB::FileContentsInput::_Open() noexcept { CFURLRef url = GetURL(); if(!url) - return std::unexpected(ENOENT); + return std::unexpected{ENOENT}; UInt8 path [PATH_MAX]; auto success = CFURLGetFileSystemRepresentation(url, FALSE, path, PATH_MAX); if(!success) - return std::unexpected(EIO); + return std::unexpected{EIO}; auto file = std::fopen(reinterpret_cast(path), "r"); if(!file) - return std::unexpected(errno); + return std::unexpected{errno}; // Ensure the file is closed auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; @@ -43,11 +43,11 @@ std::expected SFB::FileContentsInput::_Open() noexcept struct stat s; if(::fstat(fd, &s)) - return std::unexpected(errno); + return std::unexpected{errno}; buf_ = std::malloc(s.st_size); if(!buf_) - return std::unexpected(ENOMEM); + return std::unexpected{ENOMEM}; len_ = s.st_size; pos_ = 0; @@ -107,11 +107,11 @@ std::expected SFB::FileContentsInput::_SeekToOffset(int64_t offset, i offset += len_; break; default: - return std::unexpected(EINVAL); + return std::unexpected{EINVAL}; } if(offset < 0 || offset > len_) - return std::unexpected(EINVAL); + return std::unexpected{EINVAL}; pos_ = offset; return {}; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index caf77893a..95b18b6d2 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -34,9 +34,9 @@ class FileContentsInput: public InputSource bool _SupportsSeeking() const noexcept override; std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; - void * _Nullable buf_ = nullptr; - int64_t len_ = 0; - int64_t pos_ = 0; + void * _Nullable buf_ {nullptr}; + int64_t len_ {0}; + int64_t pos_ {0}; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 956689ef3..c069a9a02 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -24,17 +24,17 @@ std::expected SFB::FileInput::_Open() noexcept UInt8 path [PATH_MAX]; auto success = CFURLGetFileSystemRepresentation(GetURL(), FALSE, path, PATH_MAX); if(!success) - return std::unexpected(EIO); + return std::unexpected{EIO}; file_ = std::fopen(reinterpret_cast(path), "r"); if(!file_) - return std::unexpected(errno); + return std::unexpected{errno}; struct stat s; if(::fstat(::fileno(file_), &s)) { std::fclose(file_); file_ = nullptr; - return std::unexpected(errno); + return std::unexpected{errno}; } len_ = s.st_size; @@ -50,7 +50,7 @@ std::expected SFB::FileInput::_Close() noexcept }}; if(std::fclose(file_)) - return std::unexpected(errno); + return std::unexpected{errno}; return {}; } @@ -58,7 +58,7 @@ std::expected SFB::FileInput::_Read(void *buffer, int64_t count) n { auto nitems = std::fread(buffer, 1, count, file_); if(nitems != count && std::ferror(file_)) - return std::unexpected(errno); + return std::unexpected{errno}; return nitems; } @@ -71,7 +71,7 @@ std::expected SFB::FileInput::_GetOffset() const noexcept { auto offset = std::ftell(file_); if(offset == -1) - return std::unexpected(errno); + return std::unexpected{errno}; return offset; } @@ -88,6 +88,6 @@ bool SFB::FileInput::_SupportsSeeking() const noexcept std::expected SFB::FileInput::_SeekToOffset(int64_t offset, int whence) noexcept { if(std::fseek(file_, offset, whence)) - return std::unexpected(errno); + return std::unexpected{errno}; return {}; } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index a5ba4da40..6ccf75ac4 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -36,8 +36,8 @@ class FileInput: public InputSource bool _SupportsSeeking() const noexcept override; std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; - FILE * _Nullable file_ = nullptr; - int64_t len_ = 0; + FILE * _Nullable file_ {nullptr}; + int64_t len_ {0}; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index c418840b2..17a897ee8 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -56,12 +56,12 @@ std::expected SFB::InputSource::Read(void *buffer, int64_t count) { if(!IsOpen()) { os_log_debug(sLog, "Read() called on an InputSource that hasn't been opened"); - return std::unexpected(EPERM); + return std::unexpected{EPERM}; } if(!buffer || count < 0) { os_log_debug(sLog, "Read() called with null buffer or invalid count"); - return std::unexpected(EINVAL); + return std::unexpected{EINVAL}; } return _Read(buffer, count); @@ -71,7 +71,7 @@ std::expected SFB::InputSource::AtEOF() const noexcept { if(!IsOpen()) { os_log_debug(sLog, "AtEOF() called on an InputSource that hasn't been opened"); - return std::unexpected(EPERM); + return std::unexpected{EPERM}; } return _AtEOF(); @@ -81,7 +81,7 @@ std::expected SFB::InputSource::GetOffset() const noexcept { if(!IsOpen()) { os_log_debug(sLog, "GetOffset() called on an InputSource that hasn't been opened"); - return std::unexpected(EPERM); + return std::unexpected{EPERM}; } return _GetOffset(); @@ -91,7 +91,7 @@ std::expected SFB::InputSource::GetLength() const noexcept { if(!IsOpen()) { os_log_debug(sLog, "GetLength() called on an InputSource that hasn't been opened"); - return std::unexpected(EPERM); + return std::unexpected{EPERM}; } return _GetLength(); @@ -106,12 +106,12 @@ std::expected SFB::InputSource::SeekToOffset(int64_t offset, int when { if(!IsOpen()) { os_log_debug(sLog, "SeekToOffset() called on an InputSource that hasn't been opened"); - return std::unexpected(EPERM); + return std::unexpected{EPERM}; } if(!_SupportsSeeking()) { os_log_debug(sLog, "SeekToOffset() called on an InputSource that doesn't support seeking"); - return std::unexpected(ENOTSUP); + return std::unexpected{ENOTSUP}; } return _SeekToOffset(offset, whence); @@ -126,5 +126,5 @@ bool SFB::InputSource::_SupportsSeeking() const noexcept std::expected SFB::InputSource::_SeekToOffset(int64_t offset, int whence) noexcept { - return std::unexpected(EPERM); + return std::unexpected{EPERM}; } diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 3d5fb36e8..cc59c0db4 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -72,8 +72,8 @@ class InputSource virtual bool _SupportsSeeking() const noexcept; virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; - CFURLRef _Nullable url_ = nullptr; - bool isOpen_ = false; + CFURLRef _Nullable url_ {nullptr}; + bool isOpen_ {false}; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index c8ce03be3..6fe1c9924 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -26,16 +26,16 @@ std::expected SFB::MemoryMappedFileInput::_Open() noexcept { CFURLRef url = GetURL(); if(!url) - return std::unexpected(ENOENT); + return std::unexpected{ENOENT}; UInt8 path [PATH_MAX]; auto success = CFURLGetFileSystemRepresentation(url, FALSE, path, PATH_MAX); if(!success) - return std::unexpected(EIO); + return std::unexpected{EIO}; auto file = std::fopen(reinterpret_cast(path), "r"); if(!file) - return std::unexpected(errno); + return std::unexpected{errno}; // Ensure the file is closed auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; @@ -44,17 +44,17 @@ std::expected SFB::MemoryMappedFileInput::_Open() noexcept struct stat s; if(::fstat(fd, &s)) - return std::unexpected(errno); + return std::unexpected{errno}; // Only regular files can be mapped if(!S_ISREG(s.st_mode)) - return std::unexpected(ENOTSUP); + return std::unexpected{ENOTSUP}; // Map the file to memory auto region = mmap(nullptr, s.st_size, PROT_READ, MAP_SHARED, fd, 0); if(region == MAP_FAILED) - return std::unexpected(errno); + return std::unexpected{errno}; region_ = region; len_ = s.st_size; @@ -71,7 +71,7 @@ std::expected SFB::MemoryMappedFileInput::_Close() noexcept }}; if(munmap(region_, len_)) - return std::unexpected(errno); + return std::unexpected{errno}; return {}; } @@ -118,11 +118,11 @@ std::expected SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offse offset += len_; break; default: - return std::unexpected(EINVAL); + return std::unexpected{EINVAL}; } if(offset < 0 || offset > len_) - return std::unexpected(EINVAL); + return std::unexpected{EINVAL}; pos_ = offset; return {}; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index f50a8393f..742397e13 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -34,9 +34,9 @@ class MemoryMappedFileInput: public InputSource bool _SupportsSeeking() const noexcept override; std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; - void * _Nullable region_ = nullptr; - int64_t len_ = 0; - int64_t pos_ = 0; + void * _Nullable region_ {nullptr}; + int64_t len_ {0}; + int64_t pos_ {0}; }; } /* namespace SFB */ From 4694e9b9c720128cbaf34ecfb50966d5a5fdbf65 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 11 Nov 2025 22:50:58 -0600 Subject: [PATCH 18/75] Fix use of `CFRangeMake` --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index c9a64ed52..c714b7408 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -36,7 +36,7 @@ std::expected SFB::DataInput::_Read(void *buffer, int64_t count) n int64_t remaining = CFDataGetLength(data_) - pos_; count = std::min(count, remaining); - auto range = CFRangeMake(pos_, pos_ + count); + auto range = CFRangeMake(pos_, count); CFDataGetBytes(data_, range, static_cast(buffer)); pos_ += count; From 6b8144f7c68f1b3864aa3f336bc6eb0682b9d812 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 11 Nov 2025 22:51:44 -0600 Subject: [PATCH 19/75] Actually read the file contents into memory --- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index a43818711..df5829cee 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -49,7 +49,11 @@ std::expected SFB::FileContentsInput::_Open() noexcept if(!buf_) return std::unexpected{ENOMEM}; - len_ = s.st_size; + const auto nitems = std::fread(buf_, 1, s.st_size, file); + if(nitems != s.st_size && std::ferror(file)) + return std::unexpected{errno}; + + len_ = nitems; pos_ = 0; return {}; @@ -57,7 +61,7 @@ std::expected SFB::FileContentsInput::_Open() noexcept std::expected SFB::FileContentsInput::_Close() noexcept { - free(buf_); + std::free(buf_); buf_ = nullptr; len_ = 0; From 83062a85a9bf433ac33e1b77e2bdf59b1636dece Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 11 Nov 2025 22:56:13 -0600 Subject: [PATCH 20/75] Use `ftello` and `fseeko` for 64-bit support --- Sources/CSFBAudioEngine/Input/FileInput.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index c069a9a02..b20cdbede 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -69,7 +69,7 @@ std::expected SFB::FileInput::_AtEOF() const noexcept std::expected SFB::FileInput::_GetOffset() const noexcept { - auto offset = std::ftell(file_); + auto offset = ::ftello(file_); if(offset == -1) return std::unexpected{errno}; return offset; @@ -87,7 +87,7 @@ bool SFB::FileInput::_SupportsSeeking() const noexcept std::expected SFB::FileInput::_SeekToOffset(int64_t offset, int whence) noexcept { - if(std::fseek(file_, offset, whence)) + if(::fseeko(file_, offset, whence)) return std::unexpected{errno}; return {}; } From e912e74747187b8b7b8fd226cf342f553c82d4ac Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 11 Nov 2025 23:23:48 -0600 Subject: [PATCH 21/75] Verify input counts are within limits --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 11 +++++------ Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 9 +++------ Sources/CSFBAudioEngine/Input/FileInput.cpp | 9 +++++---- .../CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 6 +++--- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index c714b7408..e86ac6a6e 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -33,14 +33,13 @@ std::expected SFB::DataInput::_Close() noexcept std::expected SFB::DataInput::_Read(void *buffer, int64_t count) noexcept { - int64_t remaining = CFDataGetLength(data_) - pos_; + if(count > LONG_MAX) + return std::unexpected{EINVAL}; + const int64_t remaining = CFDataGetLength(data_) - pos_; count = std::min(count, remaining); - - auto range = CFRangeMake(pos_, count); + const auto range = CFRangeMake(pos_, count); CFDataGetBytes(data_, range, static_cast(buffer)); - pos_ += count; - return count; } @@ -66,7 +65,7 @@ bool SFB::DataInput::_SupportsSeeking() const noexcept std::expected SFB::DataInput::_SeekToOffset(int64_t offset, int whence) noexcept { - auto length = CFDataGetLength(data_); + const auto length = CFDataGetLength(data_); switch(whence) { case SEEK_SET: diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index df5829cee..44797d56f 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -62,20 +62,17 @@ std::expected SFB::FileContentsInput::_Open() noexcept std::expected SFB::FileContentsInput::_Close() noexcept { std::free(buf_); - buf_ = nullptr; - len_ = 0; - return {}; } std::expected SFB::FileContentsInput::_Read(void *buffer, int64_t count) noexcept { - auto remaining = len_ - pos_; + if(count > SIZE_T_MAX) + return std::unexpected{EINVAL}; + const auto remaining = len_ - pos_; count = std::min(count, remaining); - memcpy(buffer, reinterpret_cast(reinterpret_cast(buf_) + pos_), count); pos_ += count; - return count; } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index b20cdbede..6addcb551 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -46,7 +46,6 @@ std::expected SFB::FileInput::_Close() noexcept { auto defer = scope_exit{[this] noexcept { file_ = nullptr; - len_ = 0; }}; if(std::fclose(file_)) @@ -56,7 +55,9 @@ std::expected SFB::FileInput::_Close() noexcept std::expected SFB::FileInput::_Read(void *buffer, int64_t count) noexcept { - auto nitems = std::fread(buffer, 1, count, file_); + if(count > SIZE_T_MAX) + return std::unexpected{EINVAL}; + const auto nitems = std::fread(buffer, 1, count, file_); if(nitems != count && std::ferror(file_)) return std::unexpected{errno}; return nitems; @@ -69,7 +70,7 @@ std::expected SFB::FileInput::_AtEOF() const noexcept std::expected SFB::FileInput::_GetOffset() const noexcept { - auto offset = ::ftello(file_); + const auto offset = ::ftello(file_); if(offset == -1) return std::unexpected{errno}; return offset; @@ -87,7 +88,7 @@ bool SFB::FileInput::_SupportsSeeking() const noexcept std::expected SFB::FileInput::_SeekToOffset(int64_t offset, int whence) noexcept { - if(::fseeko(file_, offset, whence)) + if(::fseeko(file_, static_cast(offset), whence)) return std::unexpected{errno}; return {}; } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 6fe1c9924..e0556076b 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -77,12 +77,12 @@ std::expected SFB::MemoryMappedFileInput::_Close() noexcept std::expected SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) noexcept { - auto remaining = len_ - pos_; + if(count > SIZE_T_MAX) + return std::unexpected{EINVAL}; + const auto remaining = len_ - pos_; count = std::min(count, remaining); - memcpy(buffer, reinterpret_cast(reinterpret_cast(region_) + pos_), count); pos_ += count; - return count; } From cab5e3858e91dab67c6370a99794b644737fe9fb Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 11 Nov 2025 23:27:17 -0600 Subject: [PATCH 22/75] Set `buf_` to null in _Close() --- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 44797d56f..9622eddf2 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -62,6 +62,7 @@ std::expected SFB::FileContentsInput::_Open() noexcept std::expected SFB::FileContentsInput::_Close() noexcept { std::free(buf_); + buf_ = nullptr; return {}; } From 4186d30e96dfd16997a61ffc9d7348ca64148617 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 11 Nov 2025 23:30:08 -0600 Subject: [PATCH 23/75] Use const for scope_exit declarations --- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.cpp | 5 +---- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 8 ++------ 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 9622eddf2..debff23ba 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -37,7 +37,7 @@ std::expected SFB::FileContentsInput::_Open() noexcept return std::unexpected{errno}; // Ensure the file is closed - auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; + const auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; auto fd = ::fileno(file); diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 6addcb551..9c5130c70 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -44,10 +44,7 @@ std::expected SFB::FileInput::_Open() noexcept std::expected SFB::FileInput::_Close() noexcept { - auto defer = scope_exit{[this] noexcept { - file_ = nullptr; - }}; - + const auto defer = scope_exit{[this] noexcept { file_ = nullptr; }}; if(std::fclose(file_)) return std::unexpected{errno}; return {}; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index e0556076b..92c3e58d3 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -38,7 +38,7 @@ std::expected SFB::MemoryMappedFileInput::_Open() noexcept return std::unexpected{errno}; // Ensure the file is closed - auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; + const auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; auto fd = ::fileno(file); @@ -65,11 +65,7 @@ std::expected SFB::MemoryMappedFileInput::_Open() noexcept std::expected SFB::MemoryMappedFileInput::_Close() noexcept { - auto defer = scope_exit{[this] noexcept { - region_ = nullptr; - len_ = 0; - }}; - + const auto defer = scope_exit{[this] noexcept { region_ = nullptr; }}; if(munmap(region_, len_)) return std::unexpected{errno}; return {}; From 77b62f4fcba3bc4a81c2970c559ac81ee38169c8 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 11 Nov 2025 23:32:23 -0600 Subject: [PATCH 24/75] Minor cleanup --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 17a897ee8..fb2ed62df 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -45,10 +45,7 @@ std::expected SFB::InputSource::Close() noexcept return {}; } - auto defer = scope_exit{[this] noexcept { - isOpen_ = false; - }}; - + const auto defer = scope_exit{[this] noexcept { isOpen_ = false; }}; return _Close(); } From 27f52ca0d1fd93c25875a8be333054965afb8dab Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 11 Nov 2025 23:37:46 -0600 Subject: [PATCH 25/75] Fill out error on failure --- .../CSFBAudioEngine/Input/SFBInputSource.mm | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index ee309da3f..7d9cdaf83 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -54,12 +54,18 @@ + (instancetype)inputSourceForURL:(NSURL *)url flags:(SFBInputSourceFlags)flags else up = std::make_unique((__bridge CFURLRef)url); - if(!up) + if(!up) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; return nil; + } SFBInputSource *inputSource = [[SFBInputSource alloc] init]; - if(!inputSource) + if(!inputSource) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; return nil; + } inputSource->_input = std::move(up); return inputSource; @@ -115,7 +121,7 @@ - (NSURL *)url - (BOOL)openReturningError:(NSError **)error { - auto result = _input->Open(); + const auto result = _input->Open(); if(!result) { if(error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; @@ -126,7 +132,7 @@ - (BOOL)openReturningError:(NSError **)error - (BOOL)closeReturningError:(NSError **)error { - auto result = _input->Close(); + const auto result = _input->Close(); if(!result) { if(error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; @@ -144,7 +150,7 @@ - (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger * { NSParameterAssert(bytesRead != nullptr); - auto result = _input->Read(buffer, length); + const auto result = _input->Read(buffer, length); if(!result) { if(error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; @@ -158,7 +164,7 @@ - (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger * - (BOOL)atEOF { - auto result = _input->AtEOF(); + const auto result = _input->AtEOF(); // FIXME: Is `NO` the best error return? if(!result) return NO; @@ -169,7 +175,7 @@ - (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error { NSParameterAssert(offset != nullptr); - auto result = _input->GetOffset(); + const auto result = _input->GetOffset(); if(!result) { if(error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; @@ -184,7 +190,7 @@ - (BOOL)getLength:(NSInteger *)length error:(NSError **)error { NSParameterAssert(length != nullptr); - auto result = _input->GetLength(); + const auto result = _input->GetLength(); if(!result) { if(error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; @@ -202,7 +208,7 @@ - (BOOL)supportsSeeking - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error { - auto result = _input->SeekToOffset(offset, SEEK_SET); + const auto result = _input->SeekToOffset(offset, SEEK_SET); if(!result) { if(error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; From 531929760324d1f165b146dc784efc366c5c09c7 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 12 Nov 2025 06:56:31 -0600 Subject: [PATCH 26/75] Move member function definition to .cpp file --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 10 ++++++++++ Sources/CSFBAudioEngine/Input/InputSource.hpp | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index fb2ed62df..c8a898e2c 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -25,6 +25,11 @@ SFB::InputSource::~InputSource() noexcept CFRelease(url_); } +CFURLRef SFB::InputSource::GetURL() const noexcept +{ + return url_; +} + std::expected SFB::InputSource::Open() noexcept { if(IsOpen()) { @@ -49,6 +54,11 @@ std::expected SFB::InputSource::Close() noexcept return _Close(); } +bool SFB::InputSource::IsOpen() const noexcept +{ + return isOpen_; +} + std::expected SFB::InputSource::Read(void *buffer, int64_t count) noexcept { if(!IsOpen()) { diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index cc59c0db4..4e6a8569f 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -33,13 +33,13 @@ class InputSource InputSource& operator=(const InputSource& rhs) = delete; InputSource& operator=(InputSource&& rhs) = delete; - CFURLRef _Nullable GetURL() const noexcept { return url_; } + CFURLRef _Nullable GetURL() const noexcept; // Opening and closing std::expected Open() noexcept; std::expected Close() noexcept; - bool IsOpen() const noexcept { return isOpen_; } + bool IsOpen() const noexcept; // Reading std::expected Read(void * _Nonnull buffer, int64_t count) noexcept; From 36d3a026f5264b1c30af0a27fbaa43407f8328de Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 12 Nov 2025 14:35:50 -0600 Subject: [PATCH 27/75] Refactor to make some functions inline --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 45 ----------------- Sources/CSFBAudioEngine/Input/DataInput.hpp | 38 +++++++++++--- .../Input/FileContentsInput.cpp | 37 -------------- .../Input/FileContentsInput.hpp | 36 +++++++++++--- Sources/CSFBAudioEngine/Input/FileInput.cpp | 49 ------------------- Sources/CSFBAudioEngine/Input/FileInput.hpp | 46 ++++++++++++++--- Sources/CSFBAudioEngine/Input/InputSource.cpp | 34 ------------- Sources/CSFBAudioEngine/Input/InputSource.hpp | 22 ++++++--- .../Input/MemoryMappedFileInput.cpp | 39 --------------- .../Input/MemoryMappedFileInput.hpp | 34 ++++++++++--- 10 files changed, 140 insertions(+), 240 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index e86ac6a6e..abc102faa 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -6,31 +6,6 @@ #import "DataInput.hpp" -SFB::DataInput::DataInput(CFDataRef data) noexcept -{ - if(data) - data_ = (CFDataRef)CFRetain(data); -} - -SFB::DataInput::~DataInput() noexcept -{ - if(data_) - CFRelease(data_); -} - -std::expected SFB::DataInput::_Open() noexcept -{ - if(!data_) - return std::unexpected{ENOENT}; - pos_ = 0; - return {}; -} - -std::expected SFB::DataInput::_Close() noexcept -{ - return {}; -} - std::expected SFB::DataInput::_Read(void *buffer, int64_t count) noexcept { if(count > LONG_MAX) @@ -43,26 +18,6 @@ std::expected SFB::DataInput::_Read(void *buffer, int64_t count) n return count; } -std::expected SFB::DataInput::_AtEOF() const noexcept -{ - return CFDataGetLength(data_) == pos_; -} - -std::expected SFB::DataInput::_GetOffset() const noexcept -{ - return pos_; -} - -std::expected SFB::DataInput::_GetLength() const noexcept -{ - return CFDataGetLength(data_); -} - -bool SFB::DataInput::_SupportsSeeking() const noexcept -{ - return true; -} - std::expected SFB::DataInput::_SeekToOffset(int64_t offset, int whence) noexcept { const auto length = CFDataGetLength(data_); diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index 45199997e..6b1843eac 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -13,8 +13,11 @@ namespace SFB { class DataInput: public InputSource { public: - explicit DataInput(CFDataRef _Nonnull data) noexcept; - ~DataInput() noexcept; + explicit DataInput(CFDataRef _Nonnull data) noexcept + : data_(data) { if(data_) CFRetain(data_); } + + ~DataInput() noexcept + { if(data_) CFRelease(data_); } // This class is non-copyable. DataInput(const DataInput& rhs) = delete; @@ -25,15 +28,34 @@ class DataInput: public InputSource DataInput& operator=(DataInput&& rhs) = delete; private: - std::expected _Open() noexcept override; - std::expected _Close() noexcept override; + std::expected _Open() noexcept override + { + if(!data_) + return std::unexpected{ENOENT}; + pos_ = 0; + return {}; + } + + std::expected _Close() noexcept override + { return {}; } + std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; - std::expected _AtEOF() const noexcept override; - std::expected _GetOffset() const noexcept override; - std::expected _GetLength() const noexcept override; - bool _SupportsSeeking() const noexcept override; + + std::expected _AtEOF() const noexcept override + { return CFDataGetLength(data_) == pos_; } + + std::expected _GetOffset() const noexcept override + { return pos_; } + + std::expected _GetLength() const noexcept override + { return CFDataGetLength(data_); } + + bool _SupportsSeeking() const noexcept override + { return true; } + std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; + // Data members CFDataRef _Nullable data_ {nullptr}; CFIndex pos_ {0}; }; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index debff23ba..a7dfb512f 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -5,22 +5,12 @@ // #import -#import #import #import "FileContentsInput.hpp" #import "scope_exit.hpp" -SFB::FileContentsInput::FileContentsInput(CFURLRef url) noexcept -: InputSource(url) -{} - -SFB::FileContentsInput::~FileContentsInput() noexcept -{ - std::free(buf_); -} - std::expected SFB::FileContentsInput::_Open() noexcept { CFURLRef url = GetURL(); @@ -59,13 +49,6 @@ std::expected SFB::FileContentsInput::_Open() noexcept return {}; } -std::expected SFB::FileContentsInput::_Close() noexcept -{ - std::free(buf_); - buf_ = nullptr; - return {}; -} - std::expected SFB::FileContentsInput::_Read(void *buffer, int64_t count) noexcept { if(count > SIZE_T_MAX) @@ -77,26 +60,6 @@ std::expected SFB::FileContentsInput::_Read(void *buffer, int64_t return count; } -std::expected SFB::FileContentsInput::_AtEOF() const noexcept -{ - return len_ == pos_; -} - -std::expected SFB::FileContentsInput::_GetOffset() const noexcept -{ - return pos_; -} - -std::expected SFB::FileContentsInput::_GetLength() const noexcept -{ - return len_; -} - -bool SFB::FileContentsInput::_SupportsSeeking() const noexcept -{ - return true; -} - std::expected SFB::FileContentsInput::_SeekToOffset(int64_t offset, int whence) noexcept { switch(whence) { diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 95b18b6d2..570ba6a19 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -6,6 +6,8 @@ #pragma once +#import + #import "InputSource.hpp" namespace SFB { @@ -13,8 +15,11 @@ namespace SFB { class FileContentsInput: public InputSource { public: - explicit FileContentsInput(CFURLRef _Nonnull url) noexcept; - ~FileContentsInput() noexcept; + explicit FileContentsInput(CFURLRef _Nonnull url) noexcept + : InputSource(url) {} + + ~FileContentsInput() noexcept + { std::free(buf_); } // This class is non-copyable. FileContentsInput(const FileContentsInput& rhs) = delete; @@ -26,14 +31,31 @@ class FileContentsInput: public InputSource private: std::expected _Open() noexcept override; - std::expected _Close() noexcept override; + + std::expected _Close() noexcept override + { + std::free(buf_); + buf_ = nullptr; + return {}; + } + std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; - std::expected _AtEOF() const noexcept override; - std::expected _GetOffset() const noexcept override; - std::expected _GetLength() const noexcept override; - bool _SupportsSeeking() const noexcept override; + + std::expected _AtEOF() const noexcept override + { return len_ == pos_; } + + std::expected _GetOffset() const noexcept override + { return pos_; } + + std::expected _GetLength() const noexcept override + { return len_; } + + bool _SupportsSeeking() const noexcept override + { return true; } + std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; + // Data members void * _Nullable buf_ {nullptr}; int64_t len_ {0}; int64_t pos_ {0}; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 9c5130c70..b2ae4b001 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -7,17 +7,6 @@ #import #import "FileInput.hpp" -#import "scope_exit.hpp" - -SFB::FileInput::FileInput(CFURLRef url) noexcept -: InputSource(url) -{} - -SFB::FileInput::~FileInput() noexcept -{ - if(file_) - std::fclose(file_); -} std::expected SFB::FileInput::_Open() noexcept { @@ -42,14 +31,6 @@ std::expected SFB::FileInput::_Open() noexcept return {}; } -std::expected SFB::FileInput::_Close() noexcept -{ - const auto defer = scope_exit{[this] noexcept { file_ = nullptr; }}; - if(std::fclose(file_)) - return std::unexpected{errno}; - return {}; -} - std::expected SFB::FileInput::_Read(void *buffer, int64_t count) noexcept { if(count > SIZE_T_MAX) @@ -59,33 +40,3 @@ std::expected SFB::FileInput::_Read(void *buffer, int64_t count) n return std::unexpected{errno}; return nitems; } - -std::expected SFB::FileInput::_AtEOF() const noexcept -{ - return std::feof(file_) != 0; -} - -std::expected SFB::FileInput::_GetOffset() const noexcept -{ - const auto offset = ::ftello(file_); - if(offset == -1) - return std::unexpected{errno}; - return offset; -} - -std::expected SFB::FileInput::_GetLength() const noexcept -{ - return len_; -} - -bool SFB::FileInput::_SupportsSeeking() const noexcept -{ - return true; -} - -std::expected SFB::FileInput::_SeekToOffset(int64_t offset, int whence) noexcept -{ - if(::fseeko(file_, static_cast(offset), whence)) - return std::unexpected{errno}; - return {}; -} diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 6ccf75ac4..111bdf67c 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -9,14 +9,18 @@ #import #import "InputSource.hpp" +#import "scope_exit.hpp" namespace SFB { class FileInput: public InputSource { public: - explicit FileInput(CFURLRef _Nonnull url) noexcept; - ~FileInput() noexcept; + explicit FileInput(CFURLRef _Nonnull url) noexcept + : InputSource(url) {} + + ~FileInput() noexcept + { if(file_) std::fclose(file_); } // This class is non-copyable. FileInput(const FileInput& rhs) = delete; @@ -28,14 +32,40 @@ class FileInput: public InputSource private: std::expected _Open() noexcept override; - std::expected _Close() noexcept override; + + std::expected _Close() noexcept override + { + const auto defer = scope_exit{[this] noexcept { file_ = nullptr; }}; + if(std::fclose(file_)) return std::unexpected{errno}; + return {}; + } + std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; - std::expected _AtEOF() const noexcept override; - std::expected _GetOffset() const noexcept override; - std::expected _GetLength() const noexcept override; - bool _SupportsSeeking() const noexcept override; - std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; + std::expected _AtEOF() const noexcept override + { return std::feof(file_) != 0; } + + std::expected _GetOffset() const noexcept override + { + const auto offset = ::ftello(file_); + if(offset == -1) return std::unexpected{errno}; + return offset; + } + + std::expected _GetLength() const noexcept override + { return len_; } + + bool _SupportsSeeking() const noexcept override + { return true; } + + std::expected _SeekToOffset(int64_t offset, int whence) noexcept override + { + if(::fseeko(file_, static_cast(offset), whence)) + return std::unexpected{errno}; + return {}; + } + + // Data members FILE * _Nullable file_ {nullptr}; int64_t len_ {0}; }; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index c8a898e2c..b220a73a2 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -13,23 +13,6 @@ const os_log_t InputSource::sLog = os_log_create("org.sbooth.AudioEngine", "Inpu } /* namespace SFB */ -SFB::InputSource::InputSource(CFURLRef url) noexcept -{ - if(url) - url_ = (CFURLRef)CFRetain(url); -} - -SFB::InputSource::~InputSource() noexcept -{ - if(url_) - CFRelease(url_); -} - -CFURLRef SFB::InputSource::GetURL() const noexcept -{ - return url_; -} - std::expected SFB::InputSource::Open() noexcept { if(IsOpen()) { @@ -54,11 +37,6 @@ std::expected SFB::InputSource::Close() noexcept return _Close(); } -bool SFB::InputSource::IsOpen() const noexcept -{ - return isOpen_; -} - std::expected SFB::InputSource::Read(void *buffer, int64_t count) noexcept { if(!IsOpen()) { @@ -123,15 +101,3 @@ std::expected SFB::InputSource::SeekToOffset(int64_t offset, int when return _SeekToOffset(offset, whence); } - -// Seeking support is optional - -bool SFB::InputSource::_SupportsSeeking() const noexcept -{ - return false; -} - -std::expected SFB::InputSource::_SeekToOffset(int64_t offset, int whence) noexcept -{ - return std::unexpected{EPERM}; -} diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 4e6a8569f..a84b5a822 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -23,7 +23,8 @@ class InputSource using unique_ptr = std::unique_ptr; - virtual ~InputSource() noexcept; + virtual ~InputSource() noexcept + { if(url_) CFRelease(url_); } // This class is non-copyable. InputSource(const InputSource& rhs) = delete; @@ -33,13 +34,15 @@ class InputSource InputSource& operator=(const InputSource& rhs) = delete; InputSource& operator=(InputSource&& rhs) = delete; - CFURLRef _Nullable GetURL() const noexcept; + CFURLRef _Nullable GetURL() const noexcept + { return url_; } // Opening and closing std::expected Open() noexcept; std::expected Close() noexcept; - bool IsOpen() const noexcept; + bool IsOpen() const noexcept + { return isOpen_; } // Reading std::expected Read(void * _Nonnull buffer, int64_t count) noexcept; @@ -56,7 +59,9 @@ class InputSource protected: explicit InputSource() noexcept = default; - explicit InputSource(CFURLRef _Nullable url) noexcept; + + explicit InputSource(CFURLRef _Nullable url) noexcept + { if(url) url_ = (CFURLRef)CFRetain(url); } private: @@ -69,10 +74,15 @@ class InputSource virtual std::expected _GetLength() const noexcept = 0; // Optional seeking support - virtual bool _SupportsSeeking() const noexcept; - virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept; + virtual bool _SupportsSeeking() const noexcept + { return false; } + + virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept + { return std::unexpected{EPERM}; } + /// The location of the bytes to be read CFURLRef _Nullable url_ {nullptr}; + /// `true` if the input source is open bool isOpen_ {false}; }; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 92c3e58d3..9a2263587 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -10,17 +10,6 @@ #import #import "MemoryMappedFileInput.hpp" -#import "scope_exit.hpp" - -SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) noexcept -: InputSource(url) -{} - -SFB::MemoryMappedFileInput::~MemoryMappedFileInput() noexcept -{ - if(region_) - munmap(region_, len_); -} std::expected SFB::MemoryMappedFileInput::_Open() noexcept { @@ -63,14 +52,6 @@ std::expected SFB::MemoryMappedFileInput::_Open() noexcept return {}; } -std::expected SFB::MemoryMappedFileInput::_Close() noexcept -{ - const auto defer = scope_exit{[this] noexcept { region_ = nullptr; }}; - if(munmap(region_, len_)) - return std::unexpected{errno}; - return {}; -} - std::expected SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) noexcept { if(count > SIZE_T_MAX) @@ -82,26 +63,6 @@ std::expected SFB::MemoryMappedFileInput::_Read(void *buffer, int6 return count; } -std::expected SFB::MemoryMappedFileInput::_AtEOF() const noexcept -{ - return len_ == pos_; -} - -std::expected SFB::MemoryMappedFileInput::_GetOffset() const noexcept -{ - return pos_; -} - -std::expected SFB::MemoryMappedFileInput::_GetLength() const noexcept -{ - return len_; -} - -bool SFB::MemoryMappedFileInput::_SupportsSeeking() const noexcept -{ - return true; -} - std::expected SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, int whence) noexcept { switch(whence) { diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 742397e13..a4dedfe47 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -7,14 +7,18 @@ #pragma once #import "InputSource.hpp" +#import "scope_exit.hpp" namespace SFB { class MemoryMappedFileInput: public InputSource { public: - explicit MemoryMappedFileInput(CFURLRef _Nonnull url) noexcept; - ~MemoryMappedFileInput() noexcept; + explicit MemoryMappedFileInput(CFURLRef _Nonnull url) noexcept + : InputSource(url) {} + + ~MemoryMappedFileInput() noexcept + { if(region_) munmap(region_, len_); } // This class is non-copyable. MemoryMappedFileInput(const MemoryMappedFileInput& rhs) = delete; @@ -26,14 +30,30 @@ class MemoryMappedFileInput: public InputSource private: std::expected _Open() noexcept override; - std::expected _Close() noexcept override; + std::expected _Close() noexcept override + { + const auto defer = scope_exit{[this] noexcept { region_ = nullptr; }}; + if(munmap(region_, len_)) return std::unexpected{errno}; + return {}; + } + std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; - std::expected _AtEOF() const noexcept override; - std::expected _GetOffset() const noexcept override; - std::expected _GetLength() const noexcept override; - bool _SupportsSeeking() const noexcept override; + + std::expected _AtEOF() const noexcept override + { return len_ == pos_; } + + std::expected _GetOffset() const noexcept override + { return pos_; } + + std::expected _GetLength() const noexcept override + { return len_; } + + bool _SupportsSeeking() const noexcept override + { return true; } + std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; + // Data members void * _Nullable region_ {nullptr}; int64_t len_ {0}; int64_t pos_ {0}; From e700c88fc5784142f7c81eb0035840db10b7296a Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 24 Nov 2025 10:02:03 -0600 Subject: [PATCH 28/75] Replace `std::expected` with exceptions --- Package.swift | 2 +- Sources/CSFBAudioEngine/Input/DataInput.cpp | 18 +++-- Sources/CSFBAudioEngine/Input/DataInput.hpp | 30 +++---- .../Input/FileContentsInput.cpp | 28 +++---- .../Input/FileContentsInput.hpp | 20 ++--- Sources/CSFBAudioEngine/Input/FileInput.cpp | 18 ++--- Sources/CSFBAudioEngine/Input/FileInput.hpp | 30 +++---- Sources/CSFBAudioEngine/Input/InputSource.cpp | 42 +++++----- Sources/CSFBAudioEngine/Input/InputSource.hpp | 34 ++++---- .../Input/MemoryMappedFileInput.cpp | 29 +++---- .../Input/MemoryMappedFileInput.hpp | 25 +++--- .../CSFBAudioEngine/Input/SFBInputSource.mm | 78 ++++++++++--------- 12 files changed, 178 insertions(+), 176 deletions(-) diff --git a/Package.swift b/Package.swift index a43b19a36..eae319649 100644 --- a/Package.swift +++ b/Package.swift @@ -113,5 +113,5 @@ let package = Package( ]) ], cLanguageStandard: .c11, - cxxLanguageStandard: .cxx2b + cxxLanguageStandard: .cxx20 ) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index abc102faa..03ff6d659 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -6,10 +6,17 @@ #import "DataInput.hpp" -std::expected SFB::DataInput::_Read(void *buffer, int64_t count) noexcept +SFB::DataInput::DataInput(CFDataRef data) +{ + if(!data) + throw std::invalid_argument("Null data"); + data_ = static_cast(CFRetain(data)); +} + +int64_t SFB::DataInput::_Read(void *buffer, int64_t count) { if(count > LONG_MAX) - return std::unexpected{EINVAL}; + throw std::invalid_argument("Count too large"); const int64_t remaining = CFDataGetLength(data_) - pos_; count = std::min(count, remaining); const auto range = CFRangeMake(pos_, count); @@ -18,7 +25,7 @@ std::expected SFB::DataInput::_Read(void *buffer, int64_t count) n return count; } -std::expected SFB::DataInput::_SeekToOffset(int64_t offset, int whence) noexcept +void SFB::DataInput::_SeekToOffset(int64_t offset, int whence) { const auto length = CFDataGetLength(data_); @@ -32,12 +39,11 @@ std::expected SFB::DataInput::_SeekToOffset(int64_t offset, int whenc offset += length; break; default: - return std::unexpected{EINVAL}; + throw std::invalid_argument("Unknown whence"); } if(offset < 0 || offset > length) - return std::unexpected{EINVAL}; + throw std::out_of_range("Invalid seek offset"); pos_ = offset; - return {}; } diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index 6b1843eac..2b55254bb 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -13,11 +13,10 @@ namespace SFB { class DataInput: public InputSource { public: - explicit DataInput(CFDataRef _Nonnull data) noexcept - : data_(data) { if(data_) CFRetain(data_); } + explicit DataInput(CFDataRef _Nonnull data); ~DataInput() noexcept - { if(data_) CFRelease(data_); } + { CFRelease(data_); } // This class is non-copyable. DataInput(const DataInput& rhs) = delete; @@ -28,35 +27,30 @@ class DataInput: public InputSource DataInput& operator=(DataInput&& rhs) = delete; private: - std::expected _Open() noexcept override - { - if(!data_) - return std::unexpected{ENOENT}; - pos_ = 0; - return {}; - } + void _Open() noexcept override + { pos_ = 0; } - std::expected _Close() noexcept override - { return {}; } + void _Close() noexcept override + {} - std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; + int64_t _Read(void * _Nonnull buffer, int64_t count) override; - std::expected _AtEOF() const noexcept override + bool _AtEOF() const noexcept override { return CFDataGetLength(data_) == pos_; } - std::expected _GetOffset() const noexcept override + int64_t _GetOffset() const noexcept override { return pos_; } - std::expected _GetLength() const noexcept override + int64_t _GetLength() const noexcept override { return CFDataGetLength(data_); } bool _SupportsSeeking() const noexcept override { return true; } - std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; + void _SeekToOffset(int64_t offset, int whence) override; // Data members - CFDataRef _Nullable data_ {nullptr}; + CFDataRef _Nonnull data_ {nullptr}; CFIndex pos_ {0}; }; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index a7dfb512f..16a983316 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -5,26 +5,25 @@ // #import +#import #import #import "FileContentsInput.hpp" #import "scope_exit.hpp" -std::expected SFB::FileContentsInput::_Open() noexcept +void SFB::FileContentsInput::_Open() { CFURLRef url = GetURL(); - if(!url) - return std::unexpected{ENOENT}; UInt8 path [PATH_MAX]; auto success = CFURLGetFileSystemRepresentation(url, FALSE, path, PATH_MAX); if(!success) - return std::unexpected{EIO}; + throw std::runtime_error("Unable to get URL file system representation"); auto file = std::fopen(reinterpret_cast(path), "r"); if(!file) - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; // Ensure the file is closed const auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; @@ -33,26 +32,24 @@ std::expected SFB::FileContentsInput::_Open() noexcept struct stat s; if(::fstat(fd, &s)) - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; buf_ = std::malloc(s.st_size); if(!buf_) - return std::unexpected{ENOMEM}; + throw std::bad_alloc(); const auto nitems = std::fread(buf_, 1, s.st_size, file); if(nitems != s.st_size && std::ferror(file)) - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; len_ = nitems; pos_ = 0; - - return {}; } -std::expected SFB::FileContentsInput::_Read(void *buffer, int64_t count) noexcept +int64_t SFB::FileContentsInput::_Read(void *buffer, int64_t count) { if(count > SIZE_T_MAX) - return std::unexpected{EINVAL}; + throw std::invalid_argument("Count too large"); const auto remaining = len_ - pos_; count = std::min(count, remaining); memcpy(buffer, reinterpret_cast(reinterpret_cast(buf_) + pos_), count); @@ -60,7 +57,7 @@ std::expected SFB::FileContentsInput::_Read(void *buffer, int64_t return count; } -std::expected SFB::FileContentsInput::_SeekToOffset(int64_t offset, int whence) noexcept +void SFB::FileContentsInput::_SeekToOffset(int64_t offset, int whence) { switch(whence) { case SEEK_SET: @@ -72,13 +69,12 @@ std::expected SFB::FileContentsInput::_SeekToOffset(int64_t offset, i offset += len_; break; default: - return std::unexpected{EINVAL}; + throw std::invalid_argument("Unknown whence"); } if(offset < 0 || offset > len_) - return std::unexpected{EINVAL}; + throw std::out_of_range("Invalid seek offset"); pos_ = offset; - return {}; } diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 570ba6a19..e4e5696b7 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -15,8 +15,9 @@ namespace SFB { class FileContentsInput: public InputSource { public: - explicit FileContentsInput(CFURLRef _Nonnull url) noexcept - : InputSource(url) {} + explicit FileContentsInput(CFURLRef _Nonnull url) + : InputSource(url) + { if(!url) throw std::invalid_argument("Null URL"); } ~FileContentsInput() noexcept { std::free(buf_); } @@ -30,30 +31,29 @@ class FileContentsInput: public InputSource FileContentsInput& operator=(FileContentsInput&& rhs) = delete; private: - std::expected _Open() noexcept override; + void _Open() override; - std::expected _Close() noexcept override + void _Close() noexcept override { std::free(buf_); buf_ = nullptr; - return {}; } - std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; + int64_t _Read(void * _Nonnull buffer, int64_t count) override; - std::expected _AtEOF() const noexcept override + bool _AtEOF() const noexcept override { return len_ == pos_; } - std::expected _GetOffset() const noexcept override + int64_t _GetOffset() const noexcept override { return pos_; } - std::expected _GetLength() const noexcept override + int64_t _GetLength() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override { return true; } - std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; + void _SeekToOffset(int64_t offset, int whence) override; // Data members void * _Nullable buf_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index b2ae4b001..0a3c8ad49 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -4,39 +4,39 @@ // MIT license // +#import + #import #import "FileInput.hpp" -std::expected SFB::FileInput::_Open() noexcept +void SFB::FileInput::_Open() { UInt8 path [PATH_MAX]; auto success = CFURLGetFileSystemRepresentation(GetURL(), FALSE, path, PATH_MAX); if(!success) - return std::unexpected{EIO}; + throw std::runtime_error("Unable to get URL file system representation"); file_ = std::fopen(reinterpret_cast(path), "r"); if(!file_) - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; struct stat s; if(::fstat(::fileno(file_), &s)) { std::fclose(file_); file_ = nullptr; - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; } len_ = s.st_size; - - return {}; } -std::expected SFB::FileInput::_Read(void *buffer, int64_t count) noexcept +int64_t SFB::FileInput::_Read(void *buffer, int64_t count) { if(count > SIZE_T_MAX) - return std::unexpected{EINVAL}; + throw std::invalid_argument("Count too large"); const auto nitems = std::fread(buffer, 1, count, file_); if(nitems != count && std::ferror(file_)) - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; return nitems; } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 111bdf67c..32e71f69f 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -7,6 +7,7 @@ #pragma once #import +#import #import "InputSource.hpp" #import "scope_exit.hpp" @@ -16,8 +17,9 @@ namespace SFB { class FileInput: public InputSource { public: - explicit FileInput(CFURLRef _Nonnull url) noexcept - : InputSource(url) {} + explicit FileInput(CFURLRef _Nonnull url) + : InputSource(url) + { if(!url) throw std::invalid_argument("Null URL"); } ~FileInput() noexcept { if(file_) std::fclose(file_); } @@ -31,38 +33,38 @@ class FileInput: public InputSource FileInput& operator=(FileInput&& rhs) = delete; private: - std::expected _Open() noexcept override; + void _Open() override; - std::expected _Close() noexcept override + void _Close() override { const auto defer = scope_exit{[this] noexcept { file_ = nullptr; }}; - if(std::fclose(file_)) return std::unexpected{errno}; - return {}; + if(std::fclose(file_)) + throw std::system_error{errno, std::generic_category()}; } - std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; + int64_t _Read(void * _Nonnull buffer, int64_t count) override; - std::expected _AtEOF() const noexcept override + bool _AtEOF() const noexcept override { return std::feof(file_) != 0; } - std::expected _GetOffset() const noexcept override + int64_t _GetOffset() const override { const auto offset = ::ftello(file_); - if(offset == -1) return std::unexpected{errno}; + if(offset == -1) + throw std::system_error{errno, std::generic_category()}; return offset; } - std::expected _GetLength() const noexcept override + int64_t _GetLength() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override { return true; } - std::expected _SeekToOffset(int64_t offset, int whence) noexcept override + void _SeekToOffset(int64_t offset, int whence) override { if(::fseeko(file_, static_cast(offset), whence)) - return std::unexpected{errno}; - return {}; + throw std::system_error{errno, std::generic_category()}; } // Data members diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index b220a73a2..83f7dac46 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -4,6 +4,8 @@ // MIT license // +#import + #import "InputSource.hpp" #import "scope_exit.hpp" @@ -13,70 +15,68 @@ const os_log_t InputSource::sLog = os_log_create("org.sbooth.AudioEngine", "Inpu } /* namespace SFB */ -std::expected SFB::InputSource::Open() noexcept +void SFB::InputSource::Open() { if(IsOpen()) { os_log_debug(sLog, "Open() called on an InputSource that is already open"); - return {}; + return; } - auto result = _Open(); - if(result) - isOpen_ = true; - return result; + _Open(); + isOpen_ = true; } -std::expected SFB::InputSource::Close() noexcept +void SFB::InputSource::Close() { if(!IsOpen()) { os_log_debug(sLog, "Close() called on an InputSource that hasn't been opened"); - return {}; + return; } const auto defer = scope_exit{[this] noexcept { isOpen_ = false; }}; - return _Close(); + _Close(); } -std::expected SFB::InputSource::Read(void *buffer, int64_t count) noexcept +int64_t SFB::InputSource::Read(void *buffer, int64_t count) { if(!IsOpen()) { os_log_debug(sLog, "Read() called on an InputSource that hasn't been opened"); - return std::unexpected{EPERM}; + throw std::logic_error("Input source not open"); } if(!buffer || count < 0) { os_log_debug(sLog, "Read() called with null buffer or invalid count"); - return std::unexpected{EINVAL}; + throw std::out_of_range("Null buffer or negative count"); } return _Read(buffer, count); } -std::expected SFB::InputSource::AtEOF() const noexcept +bool SFB::InputSource::AtEOF() const { if(!IsOpen()) { os_log_debug(sLog, "AtEOF() called on an InputSource that hasn't been opened"); - return std::unexpected{EPERM}; + throw std::logic_error("Input source not open"); } return _AtEOF(); } -std::expected SFB::InputSource::GetOffset() const noexcept +int64_t SFB::InputSource::GetOffset() const { if(!IsOpen()) { os_log_debug(sLog, "GetOffset() called on an InputSource that hasn't been opened"); - return std::unexpected{EPERM}; + throw std::logic_error("Input source not open"); } return _GetOffset(); } -std::expected SFB::InputSource::GetLength() const noexcept +int64_t SFB::InputSource::GetLength() const { if(!IsOpen()) { os_log_debug(sLog, "GetLength() called on an InputSource that hasn't been opened"); - return std::unexpected{EPERM}; + throw std::logic_error("Input source not open"); } return _GetLength(); @@ -87,16 +87,16 @@ bool SFB::InputSource::SupportsSeeking() const noexcept return _SupportsSeeking(); } -std::expected SFB::InputSource::SeekToOffset(int64_t offset, int whence) noexcept +void SFB::InputSource::SeekToOffset(int64_t offset, int whence) { if(!IsOpen()) { os_log_debug(sLog, "SeekToOffset() called on an InputSource that hasn't been opened"); - return std::unexpected{EPERM}; + throw std::logic_error("Input source not open"); } if(!_SupportsSeeking()) { os_log_debug(sLog, "SeekToOffset() called on an InputSource that doesn't support seeking"); - return std::unexpected{ENOTSUP}; + throw std::logic_error("Seeking not supported"); } return _SeekToOffset(offset, whence); diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index a84b5a822..cfd22dae5 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -6,8 +6,8 @@ #pragma once -#import #import +#import #import @@ -38,47 +38,47 @@ class InputSource { return url_; } // Opening and closing - std::expected Open() noexcept; - std::expected Close() noexcept; + void Open(); + void Close(); bool IsOpen() const noexcept { return isOpen_; } // Reading - std::expected Read(void * _Nonnull buffer, int64_t count) noexcept; + int64_t Read(void * _Nonnull buffer, int64_t count); - std::expected AtEOF() const noexcept; + bool AtEOF() const; - std::expected GetOffset() const noexcept; - std::expected GetLength() const noexcept; + int64_t GetOffset() const; + int64_t GetLength() const; // Seeking bool SupportsSeeking() const noexcept; - std::expected SeekToOffset(int64_t offset, int whence) noexcept; + void SeekToOffset(int64_t offset, int whence); protected: explicit InputSource() noexcept = default; explicit InputSource(CFURLRef _Nullable url) noexcept - { if(url) url_ = (CFURLRef)CFRetain(url); } + { if(url) url_ = static_cast(CFRetain(url)); } private: // Subclasses must implement the following methods - virtual std::expected _Open() noexcept = 0; - virtual std::expected _Close() noexcept = 0; - virtual std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept = 0; - virtual std::expected _AtEOF() const noexcept = 0; - virtual std::expected _GetOffset() const noexcept = 0; - virtual std::expected _GetLength() const noexcept = 0; + virtual void _Open() = 0; + virtual void _Close() = 0; + virtual int64_t _Read(void * _Nonnull buffer, int64_t count) = 0; + virtual bool _AtEOF() const = 0; + virtual int64_t _GetOffset() const = 0; + virtual int64_t _GetLength() const = 0; // Optional seeking support virtual bool _SupportsSeeking() const noexcept { return false; } - virtual std::expected _SeekToOffset(int64_t offset, int whence) noexcept - { return std::unexpected{EPERM}; } + virtual void _SeekToOffset(int64_t offset, int whence) + { throw std::logic_error("Seeking not supported"); } /// The location of the bytes to be read CFURLRef _Nullable url_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 9a2263587..cadc4f31f 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -5,26 +5,25 @@ // #import +#import #import #import #import "MemoryMappedFileInput.hpp" -std::expected SFB::MemoryMappedFileInput::_Open() noexcept +void SFB::MemoryMappedFileInput::_Open() { CFURLRef url = GetURL(); - if(!url) - return std::unexpected{ENOENT}; UInt8 path [PATH_MAX]; auto success = CFURLGetFileSystemRepresentation(url, FALSE, path, PATH_MAX); if(!success) - return std::unexpected{EIO}; + throw std::runtime_error("Unable to get URL file system representation"); auto file = std::fopen(reinterpret_cast(path), "r"); if(!file) - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; // Ensure the file is closed const auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; @@ -33,29 +32,26 @@ std::expected SFB::MemoryMappedFileInput::_Open() noexcept struct stat s; if(::fstat(fd, &s)) - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; // Only regular files can be mapped if(!S_ISREG(s.st_mode)) - return std::unexpected{ENOTSUP}; + throw std::system_error{ENOTSUP, std::generic_category()}; // Map the file to memory auto region = mmap(nullptr, s.st_size, PROT_READ, MAP_SHARED, fd, 0); - if(region == MAP_FAILED) - return std::unexpected{errno}; + throw std::system_error{errno, std::generic_category()}; region_ = region; len_ = s.st_size; pos_ = 0; - - return {}; } -std::expected SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) noexcept +int64_t SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) { if(count > SIZE_T_MAX) - return std::unexpected{EINVAL}; + throw std::invalid_argument("Count too large"); const auto remaining = len_ - pos_; count = std::min(count, remaining); memcpy(buffer, reinterpret_cast(reinterpret_cast(region_) + pos_), count); @@ -63,7 +59,7 @@ std::expected SFB::MemoryMappedFileInput::_Read(void *buffer, int6 return count; } -std::expected SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, int whence) noexcept +void SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, int whence) { switch(whence) { case SEEK_SET: @@ -75,12 +71,11 @@ std::expected SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offse offset += len_; break; default: - return std::unexpected{EINVAL}; + throw std::invalid_argument("Unknown whence"); } if(offset < 0 || offset > len_) - return std::unexpected{EINVAL}; + throw std::out_of_range("Invalid seek offset"); pos_ = offset; - return {}; } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index a4dedfe47..1d3caa456 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -6,6 +6,8 @@ #pragma once +#import + #import "InputSource.hpp" #import "scope_exit.hpp" @@ -14,8 +16,9 @@ namespace SFB { class MemoryMappedFileInput: public InputSource { public: - explicit MemoryMappedFileInput(CFURLRef _Nonnull url) noexcept - : InputSource(url) {} + explicit MemoryMappedFileInput(CFURLRef _Nonnull url) + : InputSource(url) + { if(!url) throw std::invalid_argument("Null URL"); } ~MemoryMappedFileInput() noexcept { if(region_) munmap(region_, len_); } @@ -29,29 +32,29 @@ class MemoryMappedFileInput: public InputSource MemoryMappedFileInput& operator=(MemoryMappedFileInput&& rhs) = delete; private: - std::expected _Open() noexcept override; - std::expected _Close() noexcept override + void _Open() override; + void _Close() override { const auto defer = scope_exit{[this] noexcept { region_ = nullptr; }}; - if(munmap(region_, len_)) return std::unexpected{errno}; - return {}; + if(munmap(region_, len_)) + throw std::system_error{errno, std::generic_category()}; } - std::expected _Read(void * _Nonnull buffer, int64_t count) noexcept override; + int64_t _Read(void * _Nonnull buffer, int64_t count) override; - std::expected _AtEOF() const noexcept override + bool _AtEOF() const noexcept override { return len_ == pos_; } - std::expected _GetOffset() const noexcept override + int64_t _GetOffset() const noexcept override { return pos_; } - std::expected _GetLength() const noexcept override + int64_t _GetLength() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override { return true; } - std::expected _SeekToOffset(int64_t offset, int whence) noexcept override; + void _SeekToOffset(int64_t offset, int whence) override; // Data members void * _Nullable region_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 7d9cdaf83..87abe5f75 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -121,24 +121,28 @@ - (NSURL *)url - (BOOL)openReturningError:(NSError **)error { - const auto result = _input->Open(); - if(!result) { + try { + _input->Open(); + return YES; + } + catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; return NO; } - return YES; } - (BOOL)closeReturningError:(NSError **)error { - const auto result = _input->Close(); - if(!result) { + try { + _input->Close(); + return YES; + } + catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; return NO; } - return YES; } - (BOOL)isOpen @@ -150,55 +154,56 @@ - (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger * { NSParameterAssert(bytesRead != nullptr); - const auto result = _input->Read(buffer, length); - if(!result) { + try { + *bytesRead = _input->Read(buffer, length); + return YES; + } + catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; return NO; } - - *bytesRead = result.value(); - - return YES; } - (BOOL)atEOF { - const auto result = _input->AtEOF(); - // FIXME: Is `NO` the best error return? - if(!result) + try { + return _input->AtEOF(); + } + catch(const std::exception& e) { + // FIXME: Is `NO` the best error return? return NO; - return result.value(); + } } - (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error { NSParameterAssert(offset != nullptr); - const auto result = _input->GetOffset(); - if(!result) { + try { + *offset = _input->GetOffset(); + return YES; + } + catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; return NO; } - - *offset = result.value(); - return YES; } - (BOOL)getLength:(NSInteger *)length error:(NSError **)error { NSParameterAssert(length != nullptr); - const auto result = _input->GetLength(); - if(!result) { + try { + *length = _input->GetLength(); + return YES; + } + catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; return NO; } - - *length = result.value(); - return YES; } - (BOOL)supportsSeeking @@ -208,14 +213,15 @@ - (BOOL)supportsSeeking - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error { - const auto result = _input->SeekToOffset(offset, SEEK_SET); - if(!result) { + try { + _input->SeekToOffset(offset, SEEK_SET); + return YES; + } + catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:result.error() userInfo:nil]; + *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; return NO; } - - return YES; } - (NSString *)description From 917005890cf9892b4d8db2def638ee152d40068c Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 24 Nov 2025 14:23:05 -0600 Subject: [PATCH 29/75] Add empty parameter clause to lambdas --- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/InputSource.cpp | 4 ++-- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 16a983316..12aa1fcac 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -26,7 +26,7 @@ void SFB::FileContentsInput::_Open() throw std::system_error{errno, std::generic_category()}; // Ensure the file is closed - const auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; + const auto guard = scope_exit{[&file]() noexcept { std::fclose(file); }}; auto fd = ::fileno(file); diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 32e71f69f..b4880ab84 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -37,7 +37,7 @@ class FileInput: public InputSource void _Close() override { - const auto defer = scope_exit{[this] noexcept { file_ = nullptr; }}; + const auto defer = scope_exit{[this]() noexcept { file_ = nullptr; }}; if(std::fclose(file_)) throw std::system_error{errno, std::generic_category()}; } diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 83f7dac46..3e2246db3 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -33,7 +33,7 @@ void SFB::InputSource::Close() return; } - const auto defer = scope_exit{[this] noexcept { isOpen_ = false; }}; + const auto defer = scope_exit{[this]() noexcept { isOpen_ = false; }}; _Close(); } @@ -46,7 +46,7 @@ int64_t SFB::InputSource::Read(void *buffer, int64_t count) if(!buffer || count < 0) { os_log_debug(sLog, "Read() called with null buffer or invalid count"); - throw std::out_of_range("Null buffer or negative count"); + throw std::invalid_argument("Null buffer or negative count"); } return _Read(buffer, count); diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index cadc4f31f..b416ec74b 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -26,7 +26,7 @@ void SFB::MemoryMappedFileInput::_Open() throw std::system_error{errno, std::generic_category()}; // Ensure the file is closed - const auto guard = scope_exit{[&file] noexcept { std::fclose(file); }}; + const auto guard = scope_exit{[&file]() noexcept { std::fclose(file); }}; auto fd = ::fileno(file); diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 1d3caa456..f6995202c 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -35,7 +35,7 @@ class MemoryMappedFileInput: public InputSource void _Open() override; void _Close() override { - const auto defer = scope_exit{[this] noexcept { region_ = nullptr; }}; + const auto defer = scope_exit{[this]() noexcept { region_ = nullptr; }}; if(munmap(region_, len_)) throw std::system_error{errno, std::generic_category()}; } From b409d10b2582dfefb134da3c660a8396b4d0b92f Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 24 Nov 2025 15:49:08 -0600 Subject: [PATCH 30/75] Rename functions --- Sources/CSFBAudioEngine/Input/DataInput.hpp | 4 ++-- Sources/CSFBAudioEngine/Input/FileContentsInput.hpp | 4 ++-- Sources/CSFBAudioEngine/Input/FileInput.hpp | 4 ++-- Sources/CSFBAudioEngine/Input/InputSource.cpp | 8 ++++---- Sources/CSFBAudioEngine/Input/InputSource.hpp | 8 ++++---- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp | 4 ++-- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index 2b55254bb..da563e733 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -38,10 +38,10 @@ class DataInput: public InputSource bool _AtEOF() const noexcept override { return CFDataGetLength(data_) == pos_; } - int64_t _GetOffset() const noexcept override + int64_t _Offset() const noexcept override { return pos_; } - int64_t _GetLength() const noexcept override + int64_t _Length() const noexcept override { return CFDataGetLength(data_); } bool _SupportsSeeking() const noexcept override diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index e4e5696b7..98df1ef9d 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -44,10 +44,10 @@ class FileContentsInput: public InputSource bool _AtEOF() const noexcept override { return len_ == pos_; } - int64_t _GetOffset() const noexcept override + int64_t _Offset() const noexcept override { return pos_; } - int64_t _GetLength() const noexcept override + int64_t _Length() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index b4880ab84..a2a77356d 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -47,7 +47,7 @@ class FileInput: public InputSource bool _AtEOF() const noexcept override { return std::feof(file_) != 0; } - int64_t _GetOffset() const override + int64_t _Offset() const override { const auto offset = ::ftello(file_); if(offset == -1) @@ -55,7 +55,7 @@ class FileInput: public InputSource return offset; } - int64_t _GetLength() const noexcept override + int64_t _Length() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 3e2246db3..2c970d6da 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -62,24 +62,24 @@ bool SFB::InputSource::AtEOF() const return _AtEOF(); } -int64_t SFB::InputSource::GetOffset() const +int64_t SFB::InputSource::Offset() const { if(!IsOpen()) { os_log_debug(sLog, "GetOffset() called on an InputSource that hasn't been opened"); throw std::logic_error("Input source not open"); } - return _GetOffset(); + return _Offset(); } -int64_t SFB::InputSource::GetLength() const +int64_t SFB::InputSource::Length() const { if(!IsOpen()) { os_log_debug(sLog, "GetLength() called on an InputSource that hasn't been opened"); throw std::logic_error("Input source not open"); } - return _GetLength(); + return _Length(); } bool SFB::InputSource::SupportsSeeking() const noexcept diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index cfd22dae5..029d90457 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -49,8 +49,8 @@ class InputSource bool AtEOF() const; - int64_t GetOffset() const; - int64_t GetLength() const; + int64_t Offset() const; + int64_t Length() const; // Seeking bool SupportsSeeking() const noexcept; @@ -70,8 +70,8 @@ class InputSource virtual void _Close() = 0; virtual int64_t _Read(void * _Nonnull buffer, int64_t count) = 0; virtual bool _AtEOF() const = 0; - virtual int64_t _GetOffset() const = 0; - virtual int64_t _GetLength() const = 0; + virtual int64_t _Offset() const = 0; + virtual int64_t _Length() const = 0; // Optional seeking support virtual bool _SupportsSeeking() const noexcept diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index f6995202c..638128afc 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -45,10 +45,10 @@ class MemoryMappedFileInput: public InputSource bool _AtEOF() const noexcept override { return len_ == pos_; } - int64_t _GetOffset() const noexcept override + int64_t _Offset() const noexcept override { return pos_; } - int64_t _GetLength() const noexcept override + int64_t _Length() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 87abe5f75..dff34dd9e 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -181,7 +181,7 @@ - (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error NSParameterAssert(offset != nullptr); try { - *offset = _input->GetOffset(); + *offset = _input->Offset(); return YES; } catch(const std::exception& e) { @@ -196,7 +196,7 @@ - (BOOL)getLength:(NSInteger *)length error:(NSError **)error NSParameterAssert(length != nullptr); try { - *length = _input->GetLength(); + *length = _input->Length(); return YES; } catch(const std::exception& e) { From 5d26f7dc2d3e5de22a70224ed348399a52956a2a Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 25 Nov 2025 14:04:06 -0600 Subject: [PATCH 31/75] Improve log messages --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 2c970d6da..b5767c632 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -18,7 +18,7 @@ const os_log_t InputSource::sLog = os_log_create("org.sbooth.AudioEngine", "Inpu void SFB::InputSource::Open() { if(IsOpen()) { - os_log_debug(sLog, "Open() called on an InputSource that is already open"); + os_log_debug(sLog, "Open() called on that is already open", this); return; } @@ -29,7 +29,7 @@ void SFB::InputSource::Open() void SFB::InputSource::Close() { if(!IsOpen()) { - os_log_debug(sLog, "Close() called on an InputSource that hasn't been opened"); + os_log_debug(sLog, "Close() called on that hasn't been opened", this); return; } @@ -40,12 +40,12 @@ void SFB::InputSource::Close() int64_t SFB::InputSource::Read(void *buffer, int64_t count) { if(!IsOpen()) { - os_log_debug(sLog, "Read() called on an InputSource that hasn't been opened"); + os_log_error(sLog, "Read() called on that hasn't been opened", this); throw std::logic_error("Input source not open"); } if(!buffer || count < 0) { - os_log_debug(sLog, "Read() called with null buffer or invalid count"); + os_log_error(sLog, "Read() called on with null buffer or invalid count", this); throw std::invalid_argument("Null buffer or negative count"); } @@ -55,7 +55,7 @@ int64_t SFB::InputSource::Read(void *buffer, int64_t count) bool SFB::InputSource::AtEOF() const { if(!IsOpen()) { - os_log_debug(sLog, "AtEOF() called on an InputSource that hasn't been opened"); + os_log_error(sLog, "AtEOF() called on that hasn't been opened", this); throw std::logic_error("Input source not open"); } @@ -65,7 +65,7 @@ bool SFB::InputSource::AtEOF() const int64_t SFB::InputSource::Offset() const { if(!IsOpen()) { - os_log_debug(sLog, "GetOffset() called on an InputSource that hasn't been opened"); + os_log_error(sLog, "GetOffset() called on that hasn't been opened", this); throw std::logic_error("Input source not open"); } @@ -75,7 +75,7 @@ int64_t SFB::InputSource::Offset() const int64_t SFB::InputSource::Length() const { if(!IsOpen()) { - os_log_debug(sLog, "GetLength() called on an InputSource that hasn't been opened"); + os_log_error(sLog, "GetLength() called on that hasn't been opened", this); throw std::logic_error("Input source not open"); } @@ -90,12 +90,12 @@ bool SFB::InputSource::SupportsSeeking() const noexcept void SFB::InputSource::SeekToOffset(int64_t offset, int whence) { if(!IsOpen()) { - os_log_debug(sLog, "SeekToOffset() called on an InputSource that hasn't been opened"); + os_log_error(sLog, "SeekToOffset() called on that hasn't been opened", this); throw std::logic_error("Input source not open"); } if(!_SupportsSeeking()) { - os_log_debug(sLog, "SeekToOffset() called on an InputSource that doesn't support seeking"); + os_log_error(sLog, "SeekToOffset() called on that doesn't support seeking", this); throw std::logic_error("Seeking not supported"); } From 8b664723f992aa7a60a1330a9dc7446d4c633ec6 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 25 Nov 2025 14:19:02 -0600 Subject: [PATCH 32/75] Additional log messages --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 15 ++++++++++---- .../Input/FileContentsInput.cpp | 20 ++++++++++++++++--- .../Input/FileContentsInput.hpp | 4 +--- Sources/CSFBAudioEngine/Input/FileInput.cpp | 15 ++++++++++++-- Sources/CSFBAudioEngine/Input/FileInput.hpp | 4 +--- .../Input/MemoryMappedFileInput.cpp | 20 ++++++++++++++++--- .../Input/MemoryMappedFileInput.hpp | 4 +--- 7 files changed, 61 insertions(+), 21 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index 03ff6d659..86e4db3b5 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -8,15 +8,19 @@ SFB::DataInput::DataInput(CFDataRef data) { - if(!data) + if(!data) { + os_log_error(sLog, "Cannot create DataInput with null CFData"); throw std::invalid_argument("Null data"); + } data_ = static_cast(CFRetain(data)); } int64_t SFB::DataInput::_Read(void *buffer, int64_t count) { - if(count > LONG_MAX) - throw std::invalid_argument("Count too large"); + if(count > LONG_MAX) { + os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); + throw std::invalid_argument("Count greater than maximum allowable value"); + } const int64_t remaining = CFDataGetLength(data_) - pos_; count = std::min(count, remaining); const auto range = CFRangeMake(pos_, count); @@ -39,11 +43,14 @@ void SFB::DataInput::_SeekToOffset(int64_t offset, int whence) offset += length; break; default: + os_log_error(sLog, "_SeekToOffset() called on with unknown whence %d", this, whence); throw std::invalid_argument("Unknown whence"); } - if(offset < 0 || offset > length) + if(offset < 0 || offset > length) { + os_log_error(sLog, "_SeekToOffset() called on with invalid seek offset %lld", this, offset); throw std::out_of_range("Invalid seek offset"); + } pos_ = offset; } diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 12aa1fcac..28ee8d6d7 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -12,6 +12,15 @@ #import "FileContentsInput.hpp" #import "scope_exit.hpp" +SFB::FileContentsInput::FileContentsInput(CFURLRef url) +: InputSource(url) +{ + if(!url) { + os_log_error(sLog, "Cannot create FileContentsInput with null CFURL"); + throw std::invalid_argument("Null URL"); + } +} + void SFB::FileContentsInput::_Open() { CFURLRef url = GetURL(); @@ -48,8 +57,10 @@ void SFB::FileContentsInput::_Open() int64_t SFB::FileContentsInput::_Read(void *buffer, int64_t count) { - if(count > SIZE_T_MAX) - throw std::invalid_argument("Count too large"); + if(count > SIZE_T_MAX) { + os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); + throw std::invalid_argument("Count greater than maximum allowable value"); + } const auto remaining = len_ - pos_; count = std::min(count, remaining); memcpy(buffer, reinterpret_cast(reinterpret_cast(buf_) + pos_), count); @@ -69,11 +80,14 @@ void SFB::FileContentsInput::_SeekToOffset(int64_t offset, int whence) offset += len_; break; default: + os_log_error(sLog, "_SeekToOffset() called on with unknown whence %d", this, whence); throw std::invalid_argument("Unknown whence"); } - if(offset < 0 || offset > len_) + if(offset < 0 || offset > len_) { + os_log_error(sLog, "_SeekToOffset() called on with invalid seek offset %lld", this, offset); throw std::out_of_range("Invalid seek offset"); + } pos_ = offset; } diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 98df1ef9d..b018f81aa 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -15,9 +15,7 @@ namespace SFB { class FileContentsInput: public InputSource { public: - explicit FileContentsInput(CFURLRef _Nonnull url) - : InputSource(url) - { if(!url) throw std::invalid_argument("Null URL"); } + explicit FileContentsInput(CFURLRef _Nonnull url); ~FileContentsInput() noexcept { std::free(buf_); } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 0a3c8ad49..da8cb8d83 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -10,6 +10,15 @@ #import "FileInput.hpp" +SFB::FileInput::FileInput(CFURLRef url) +: InputSource(url) +{ + if(!url) { + os_log_error(sLog, "Cannot create FileInput with null CFURL"); + throw std::invalid_argument("Null URL"); + } +} + void SFB::FileInput::_Open() { UInt8 path [PATH_MAX]; @@ -33,8 +42,10 @@ void SFB::FileInput::_Open() int64_t SFB::FileInput::_Read(void *buffer, int64_t count) { - if(count > SIZE_T_MAX) - throw std::invalid_argument("Count too large"); + if(count > SIZE_T_MAX) { + os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); + throw std::invalid_argument("Count greater than maximum allowable value"); + } const auto nitems = std::fread(buffer, 1, count, file_); if(nitems != count && std::ferror(file_)) throw std::system_error{errno, std::generic_category()}; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index a2a77356d..643d5a788 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -17,9 +17,7 @@ namespace SFB { class FileInput: public InputSource { public: - explicit FileInput(CFURLRef _Nonnull url) - : InputSource(url) - { if(!url) throw std::invalid_argument("Null URL"); } + explicit FileInput(CFURLRef _Nonnull url); ~FileInput() noexcept { if(file_) std::fclose(file_); } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index b416ec74b..2ae873fe3 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -12,6 +12,15 @@ #import "MemoryMappedFileInput.hpp" +SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) +: InputSource(url) +{ + if(!url) { + os_log_error(sLog, "Cannot create MemoryMappedFileInput with null CFURL"); + throw std::invalid_argument("Null URL"); + } +} + void SFB::MemoryMappedFileInput::_Open() { CFURLRef url = GetURL(); @@ -50,8 +59,10 @@ void SFB::MemoryMappedFileInput::_Open() int64_t SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) { - if(count > SIZE_T_MAX) - throw std::invalid_argument("Count too large"); + if(count > SIZE_T_MAX) { + os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); + throw std::invalid_argument("Count greater than maximum allowable value"); + } const auto remaining = len_ - pos_; count = std::min(count, remaining); memcpy(buffer, reinterpret_cast(reinterpret_cast(region_) + pos_), count); @@ -71,11 +82,14 @@ void SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, int whence) offset += len_; break; default: + os_log_error(sLog, "_SeekToOffset() called on with unknown whence %d", this, whence); throw std::invalid_argument("Unknown whence"); } - if(offset < 0 || offset > len_) + if(offset < 0 || offset > len_) { + os_log_error(sLog, "_SeekToOffset() called on with invalid seek offset %lld", this, offset); throw std::out_of_range("Invalid seek offset"); + } pos_ = offset; } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 638128afc..79fdbd1fc 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -16,9 +16,7 @@ namespace SFB { class MemoryMappedFileInput: public InputSource { public: - explicit MemoryMappedFileInput(CFURLRef _Nonnull url) - : InputSource(url) - { if(!url) throw std::invalid_argument("Null URL"); } + explicit MemoryMappedFileInput(CFURLRef _Nonnull url); ~MemoryMappedFileInput() noexcept { if(region_) munmap(region_, len_); } From 031a3d9d8f261ae2b6b775e45be0333a2cf6af1d Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Tue, 25 Nov 2025 23:08:57 -0600 Subject: [PATCH 33/75] Remove unused parameter names --- Sources/CSFBAudioEngine/Input/DataInput.hpp | 8 ++++---- Sources/CSFBAudioEngine/Input/FileContentsInput.hpp | 8 ++++---- Sources/CSFBAudioEngine/Input/FileInput.hpp | 8 ++++---- Sources/CSFBAudioEngine/Input/InputSource.hpp | 8 ++++---- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp | 9 +++++---- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index da563e733..b5cc2d55d 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -19,12 +19,12 @@ class DataInput: public InputSource { CFRelease(data_); } // This class is non-copyable. - DataInput(const DataInput& rhs) = delete; - DataInput(DataInput&& rhs) = delete; + DataInput(const DataInput&) = delete; + DataInput(DataInput&&) = delete; // This class is non-assignable. - DataInput& operator=(const DataInput& rhs) = delete; - DataInput& operator=(DataInput&& rhs) = delete; + DataInput& operator=(const DataInput&) = delete; + DataInput& operator=(DataInput&&) = delete; private: void _Open() noexcept override diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index b018f81aa..9f903dfee 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -21,12 +21,12 @@ class FileContentsInput: public InputSource { std::free(buf_); } // This class is non-copyable. - FileContentsInput(const FileContentsInput& rhs) = delete; - FileContentsInput(FileContentsInput&& rhs) = delete; + FileContentsInput(const FileContentsInput&) = delete; + FileContentsInput(FileContentsInput&&) = delete; // This class is non-assignable. - FileContentsInput& operator=(const FileContentsInput& rhs) = delete; - FileContentsInput& operator=(FileContentsInput&& rhs) = delete; + FileContentsInput& operator=(const FileContentsInput&) = delete; + FileContentsInput& operator=(FileContentsInput&&) = delete; private: void _Open() override; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 643d5a788..1ec8a0fe1 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -23,12 +23,12 @@ class FileInput: public InputSource { if(file_) std::fclose(file_); } // This class is non-copyable. - FileInput(const FileInput& rhs) = delete; - FileInput(FileInput&& rhs) = delete; + FileInput(const FileInput&) = delete; + FileInput(FileInput&&) = delete; // This class is non-assignable. - FileInput& operator=(const FileInput& rhs) = delete; - FileInput& operator=(FileInput&& rhs) = delete; + FileInput& operator=(const FileInput&) = delete; + FileInput& operator=(FileInput&&) = delete; private: void _Open() override; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 029d90457..aa29d0a23 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -27,12 +27,12 @@ class InputSource { if(url_) CFRelease(url_); } // This class is non-copyable. - InputSource(const InputSource& rhs) = delete; - InputSource(InputSource&& rhs) = delete; + InputSource(const InputSource&) = delete; + InputSource(InputSource&&) = delete; // This class is non-assignable. - InputSource& operator=(const InputSource& rhs) = delete; - InputSource& operator=(InputSource&& rhs) = delete; + InputSource& operator=(const InputSource&) = delete; + InputSource& operator=(InputSource&&) = delete; CFURLRef _Nullable GetURL() const noexcept { return url_; } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 79fdbd1fc..8f310f7ea 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -22,15 +22,16 @@ class MemoryMappedFileInput: public InputSource { if(region_) munmap(region_, len_); } // This class is non-copyable. - MemoryMappedFileInput(const MemoryMappedFileInput& rhs) = delete; - MemoryMappedFileInput(MemoryMappedFileInput&& rhs) = delete; + MemoryMappedFileInput(const MemoryMappedFileInput&) = delete; + MemoryMappedFileInput(MemoryMappedFileInput&&) = delete; // This class is non-assignable. - MemoryMappedFileInput& operator=(const MemoryMappedFileInput& rhs) = delete; - MemoryMappedFileInput& operator=(MemoryMappedFileInput&& rhs) = delete; + MemoryMappedFileInput& operator=(const MemoryMappedFileInput&) = delete; + MemoryMappedFileInput& operator=(MemoryMappedFileInput&&) = delete; private: void _Open() override; + void _Close() override { const auto defer = scope_exit{[this]() noexcept { region_ = nullptr; }}; From 83a6fe2bc9e32fd1f99dc8d31e6a08e36db04651 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 06:36:45 -0600 Subject: [PATCH 34/75] Improve log messages --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index 86e4db3b5..b6902981e 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -9,7 +9,7 @@ SFB::DataInput::DataInput(CFDataRef data) { if(!data) { - os_log_error(sLog, "Cannot create DataInput with null CFData"); + os_log_error(sLog, "Cannot create DataInput with null data"); throw std::invalid_argument("Null data"); } data_ = static_cast(CFRetain(data)); diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 28ee8d6d7..abab97de2 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -16,7 +16,7 @@ SFB::FileContentsInput::FileContentsInput(CFURLRef url) : InputSource(url) { if(!url) { - os_log_error(sLog, "Cannot create FileContentsInput with null CFURL"); + os_log_error(sLog, "Cannot create FileContentsInput with null URL"); throw std::invalid_argument("Null URL"); } } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index da8cb8d83..412acb75a 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -14,7 +14,7 @@ SFB::FileInput::FileInput(CFURLRef url) : InputSource(url) { if(!url) { - os_log_error(sLog, "Cannot create FileInput with null CFURL"); + os_log_error(sLog, "Cannot create FileInput with null URL"); throw std::invalid_argument("Null URL"); } } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 2ae873fe3..1d25cb6f4 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -16,7 +16,7 @@ SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) : InputSource(url) { if(!url) { - os_log_error(sLog, "Cannot create MemoryMappedFileInput with null CFURL"); + os_log_error(sLog, "Cannot create MemoryMappedFileInput with null URL"); throw std::invalid_argument("Null URL"); } } From 67ca581b08bd50f480e78d487a52bc4227a93c9f Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 06:48:11 -0600 Subject: [PATCH 35/75] Move some function implementations to .cpp file --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 5 +++++ Sources/CSFBAudioEngine/Input/DataInput.hpp | 4 +--- .../CSFBAudioEngine/Input/FileContentsInput.cpp | 11 +++++++++++ .../CSFBAudioEngine/Input/FileContentsInput.hpp | 11 ++--------- Sources/CSFBAudioEngine/Input/FileInput.cpp | 14 ++++++++++++++ Sources/CSFBAudioEngine/Input/FileInput.hpp | 13 ++----------- .../Input/MemoryMappedFileInput.cpp | 14 ++++++++++++++ .../Input/MemoryMappedFileInput.hpp | 13 ++----------- 8 files changed, 51 insertions(+), 34 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index b6902981e..dc52284a7 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -15,6 +15,11 @@ SFB::DataInput::DataInput(CFDataRef data) data_ = static_cast(CFRetain(data)); } +SFB::DataInput::~DataInput() noexcept +{ + CFRelease(data_); +} + int64_t SFB::DataInput::_Read(void *buffer, int64_t count) { if(count > LONG_MAX) { diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index b5cc2d55d..4f2365973 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -14,9 +14,7 @@ class DataInput: public InputSource { public: explicit DataInput(CFDataRef _Nonnull data); - - ~DataInput() noexcept - { CFRelease(data_); } + ~DataInput() noexcept; // This class is non-copyable. DataInput(const DataInput&) = delete; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index abab97de2..e8d560853 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -21,6 +21,11 @@ SFB::FileContentsInput::FileContentsInput(CFURLRef url) } } +SFB::FileContentsInput::~FileContentsInput() noexcept +{ + std::free(buf_); +} + void SFB::FileContentsInput::_Open() { CFURLRef url = GetURL(); @@ -55,6 +60,12 @@ void SFB::FileContentsInput::_Open() pos_ = 0; } +void SFB::FileContentsInput::_Close() noexcept +{ + std::free(buf_); + buf_ = nullptr; +} + int64_t SFB::FileContentsInput::_Read(void *buffer, int64_t count) { if(count > SIZE_T_MAX) { diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 9f903dfee..60c2e88e4 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -16,9 +16,7 @@ class FileContentsInput: public InputSource { public: explicit FileContentsInput(CFURLRef _Nonnull url); - - ~FileContentsInput() noexcept - { std::free(buf_); } + ~FileContentsInput() noexcept; // This class is non-copyable. FileContentsInput(const FileContentsInput&) = delete; @@ -30,12 +28,7 @@ class FileContentsInput: public InputSource private: void _Open() override; - - void _Close() noexcept override - { - std::free(buf_); - buf_ = nullptr; - } + void _Close() noexcept override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 412acb75a..583bff87b 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -9,6 +9,7 @@ #import #import "FileInput.hpp" +#import "scope_exit.hpp" SFB::FileInput::FileInput(CFURLRef url) : InputSource(url) @@ -19,6 +20,12 @@ SFB::FileInput::FileInput(CFURLRef url) } } +SFB::FileInput::~FileInput() noexcept +{ + if(file_) + std::fclose(file_); +} + void SFB::FileInput::_Open() { UInt8 path [PATH_MAX]; @@ -40,6 +47,13 @@ void SFB::FileInput::_Open() len_ = s.st_size; } +void SFB::FileInput::_Close() +{ + const auto defer = scope_exit{[this]() noexcept { file_ = nullptr; }}; + if(std::fclose(file_)) + throw std::system_error{errno, std::generic_category()}; +} + int64_t SFB::FileInput::_Read(void *buffer, int64_t count) { if(count > SIZE_T_MAX) { diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 1ec8a0fe1..0fd1a9974 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -10,7 +10,6 @@ #import #import "InputSource.hpp" -#import "scope_exit.hpp" namespace SFB { @@ -18,9 +17,7 @@ class FileInput: public InputSource { public: explicit FileInput(CFURLRef _Nonnull url); - - ~FileInput() noexcept - { if(file_) std::fclose(file_); } + ~FileInput() noexcept; // This class is non-copyable. FileInput(const FileInput&) = delete; @@ -32,13 +29,7 @@ class FileInput: public InputSource private: void _Open() override; - - void _Close() override - { - const auto defer = scope_exit{[this]() noexcept { file_ = nullptr; }}; - if(std::fclose(file_)) - throw std::system_error{errno, std::generic_category()}; - } + void _Close() override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 1d25cb6f4..2665817c4 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -11,6 +11,7 @@ #import #import "MemoryMappedFileInput.hpp" +#import "scope_exit.hpp" SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) : InputSource(url) @@ -21,6 +22,12 @@ SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) } } +SFB::MemoryMappedFileInput::~MemoryMappedFileInput() noexcept +{ + if(region_) + munmap(region_, len_); +} + void SFB::MemoryMappedFileInput::_Open() { CFURLRef url = GetURL(); @@ -57,6 +64,13 @@ void SFB::MemoryMappedFileInput::_Open() pos_ = 0; } +void SFB::MemoryMappedFileInput::_Close() +{ + const auto defer = scope_exit{[this]() noexcept { region_ = nullptr; }}; + if(munmap(region_, len_)) + throw std::system_error{errno, std::generic_category()}; +} + int64_t SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) { if(count > SIZE_T_MAX) { diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 8f310f7ea..bc103e58b 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -9,7 +9,6 @@ #import #import "InputSource.hpp" -#import "scope_exit.hpp" namespace SFB { @@ -17,9 +16,7 @@ class MemoryMappedFileInput: public InputSource { public: explicit MemoryMappedFileInput(CFURLRef _Nonnull url); - - ~MemoryMappedFileInput() noexcept - { if(region_) munmap(region_, len_); } + ~MemoryMappedFileInput() noexcept; // This class is non-copyable. MemoryMappedFileInput(const MemoryMappedFileInput&) = delete; @@ -31,13 +28,7 @@ class MemoryMappedFileInput: public InputSource private: void _Open() override; - - void _Close() override - { - const auto defer = scope_exit{[this]() noexcept { region_ = nullptr; }}; - if(munmap(region_, len_)) - throw std::system_error{errno, std::generic_category()}; - } + void _Close() override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; From 41c25ddaba60c9c6ed5d61797098ec4106d9159d Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 07:03:35 -0600 Subject: [PATCH 36/75] Reorganize --- Sources/CSFBAudioEngine/Input/DataInput.hpp | 25 ++++----------- .../Input/FileContentsInput.hpp | 20 +++--------- Sources/CSFBAudioEngine/Input/FileInput.cpp | 14 +++++++++ Sources/CSFBAudioEngine/Input/FileInput.hpp | 31 ++++--------------- .../Input/MemoryMappedFileInput.hpp | 20 +++--------- 5 files changed, 36 insertions(+), 74 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index 4f2365973..1a000b4ae 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -25,29 +25,16 @@ class DataInput: public InputSource DataInput& operator=(DataInput&&) = delete; private: - void _Open() noexcept override - { pos_ = 0; } - - void _Close() noexcept override - {} + void _Open() noexcept override { pos_ = 0; } + void _Close() noexcept override {} + bool _AtEOF() const noexcept override { return CFDataGetLength(data_) == pos_; } + int64_t _Offset() const noexcept override { return pos_; } + int64_t _Length() const noexcept override { return CFDataGetLength(data_); } + bool _SupportsSeeking() const noexcept override { return true; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; - - bool _AtEOF() const noexcept override - { return CFDataGetLength(data_) == pos_; } - - int64_t _Offset() const noexcept override - { return pos_; } - - int64_t _Length() const noexcept override - { return CFDataGetLength(data_); } - - bool _SupportsSeeking() const noexcept override - { return true; } - void _SeekToOffset(int64_t offset, int whence) override; - // Data members CFDataRef _Nonnull data_ {nullptr}; CFIndex pos_ {0}; }; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 60c2e88e4..c2f363e8c 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -27,26 +27,16 @@ class FileContentsInput: public InputSource FileContentsInput& operator=(FileContentsInput&&) = delete; private: + bool _AtEOF() const noexcept override { return len_ == pos_; } + int64_t _Offset() const noexcept override { return pos_; } + int64_t _Length() const noexcept override { return len_; } + bool _SupportsSeeking() const noexcept override { return true; } + void _Open() override; void _Close() noexcept override; - int64_t _Read(void * _Nonnull buffer, int64_t count) override; - - bool _AtEOF() const noexcept override - { return len_ == pos_; } - - int64_t _Offset() const noexcept override - { return pos_; } - - int64_t _Length() const noexcept override - { return len_; } - - bool _SupportsSeeking() const noexcept override - { return true; } - void _SeekToOffset(int64_t offset, int whence) override; - // Data members void * _Nullable buf_ {nullptr}; int64_t len_ {0}; int64_t pos_ {0}; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 583bff87b..110fcfab6 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -65,3 +65,17 @@ int64_t SFB::FileInput::_Read(void *buffer, int64_t count) throw std::system_error{errno, std::generic_category()}; return nitems; } + +int64_t SFB::FileInput::_Offset() const +{ + const auto offset = ::ftello(file_); + if(offset == -1) + throw std::system_error{errno, std::generic_category()}; + return offset; +} + +void SFB::FileInput::_SeekToOffset(int64_t offset, int whence) +{ + if(::fseeko(file_, static_cast(offset), whence)) + throw std::system_error{errno, std::generic_category()}; +} diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 0fd1a9974..8848d39b6 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -28,35 +28,16 @@ class FileInput: public InputSource FileInput& operator=(FileInput&&) = delete; private: + bool _AtEOF() const noexcept override { return std::feof(file_) != 0; } + int64_t _Length() const noexcept override { return len_; } + bool _SupportsSeeking() const noexcept override { return true; } + void _Open() override; void _Close() override; - int64_t _Read(void * _Nonnull buffer, int64_t count) override; + int64_t _Offset() const override; + void _SeekToOffset(int64_t offset, int whence) override; - bool _AtEOF() const noexcept override - { return std::feof(file_) != 0; } - - int64_t _Offset() const override - { - const auto offset = ::ftello(file_); - if(offset == -1) - throw std::system_error{errno, std::generic_category()}; - return offset; - } - - int64_t _Length() const noexcept override - { return len_; } - - bool _SupportsSeeking() const noexcept override - { return true; } - - void _SeekToOffset(int64_t offset, int whence) override - { - if(::fseeko(file_, static_cast(offset), whence)) - throw std::system_error{errno, std::generic_category()}; - } - - // Data members FILE * _Nullable file_ {nullptr}; int64_t len_ {0}; }; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index bc103e58b..3fe11a152 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -27,26 +27,16 @@ class MemoryMappedFileInput: public InputSource MemoryMappedFileInput& operator=(MemoryMappedFileInput&&) = delete; private: + bool _AtEOF() const noexcept override { return len_ == pos_; } + int64_t _Offset() const noexcept override { return pos_; } + int64_t _Length() const noexcept override { return len_; } + bool _SupportsSeeking() const noexcept override { return true; } + void _Open() override; void _Close() override; - int64_t _Read(void * _Nonnull buffer, int64_t count) override; - - bool _AtEOF() const noexcept override - { return len_ == pos_; } - - int64_t _Offset() const noexcept override - { return pos_; } - - int64_t _Length() const noexcept override - { return len_; } - - bool _SupportsSeeking() const noexcept override - { return true; } - void _SeekToOffset(int64_t offset, int whence) override; - // Data members void * _Nullable region_ {nullptr}; int64_t len_ {0}; int64_t pos_ {0}; From 4689a9c3d9e5003634abaa6f8af7645df11bde52 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 07:07:32 -0600 Subject: [PATCH 37/75] More cleanup --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 2 ++ Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 1 + Sources/CSFBAudioEngine/Input/FileContentsInput.hpp | 2 -- Sources/CSFBAudioEngine/Input/FileInput.cpp | 1 + Sources/CSFBAudioEngine/Input/FileInput.hpp | 1 - Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 1 + Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp | 2 -- 7 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index dc52284a7..c723517c8 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -4,6 +4,8 @@ // MIT license // +#import + #import "DataInput.hpp" SFB::DataInput::DataInput(CFDataRef data) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index e8d560853..9877041cf 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -4,6 +4,7 @@ // MIT license // +#import #import #import diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index c2f363e8c..2764a5c13 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -6,8 +6,6 @@ #pragma once -#import - #import "InputSource.hpp" namespace SFB { diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 110fcfab6..661faf1cd 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -4,6 +4,7 @@ // MIT license // +#import #import #import diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 8848d39b6..74972d32e 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -7,7 +7,6 @@ #pragma once #import -#import #import "InputSource.hpp" diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 2665817c4..d034d293a 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -4,6 +4,7 @@ // MIT license // +#import #import #import diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 3fe11a152..f99318800 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -6,8 +6,6 @@ #pragma once -#import - #import "InputSource.hpp" namespace SFB { From 969672af0613390a10282c5535c2c5eb09a4f420 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 07:27:25 -0600 Subject: [PATCH 38/75] Add `BufferInput` class --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 74 +++++++++++++++++++ Sources/CSFBAudioEngine/Input/BufferInput.hpp | 44 +++++++++++ 2 files changed, 118 insertions(+) create mode 100644 Sources/CSFBAudioEngine/Input/BufferInput.cpp create mode 100644 Sources/CSFBAudioEngine/Input/BufferInput.hpp diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp new file mode 100644 index 000000000..51a0053d4 --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -0,0 +1,74 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#import +#import +#import +#import + +#import + +#import "BufferInput.hpp" + +SFB::BufferInput::BufferInput(const void *buf, int64_t len, bool copy) +: buf_{const_cast(buf)}, len_{len}, free_{copy} +{ + if(!buf || len < 0) { + os_log_error(sLog, "Cannot create BufferInput with null buffer or negative length"); + throw std::invalid_argument("Null buffer or negative length"); + } + + if(copy) { + buf_ = std::malloc(len_); + if(!buf_) + throw std::bad_alloc(); + std::memcpy(buf_, buf, len_); + } +} + +SFB::BufferInput::~BufferInput() noexcept +{ + if(free_) + std::free(buf_); +} + +int64_t SFB::BufferInput::_Read(void *buffer, int64_t count) +{ + if(count > SIZE_T_MAX) { + os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); + throw std::invalid_argument("Count greater than maximum allowable value"); + } + const auto remaining = len_ - pos_; + count = std::min(count, remaining); + memcpy(buffer, reinterpret_cast(reinterpret_cast(buf_) + pos_), count); + pos_ += count; + return count; +} + +void SFB::BufferInput::_SeekToOffset(int64_t offset, int whence) +{ + switch(whence) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += pos_; + break; + case SEEK_END: + offset += len_; + break; + default: + os_log_error(sLog, "_SeekToOffset() called on with unknown whence %d", this, whence); + throw std::invalid_argument("Unknown whence"); + } + + if(offset < 0 || offset > len_) { + os_log_error(sLog, "_SeekToOffset() called on with invalid seek offset %lld", this, offset); + throw std::out_of_range("Invalid seek offset"); + } + + pos_ = offset; +} + diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp new file mode 100644 index 000000000..1b5b2b737 --- /dev/null +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2010-2025 Stephen F. Booth +// Part of https://github.com/sbooth/SFBAudioEngine +// MIT license +// + +#pragma once + +#import "InputSource.hpp" + +namespace SFB { + +class BufferInput: public InputSource +{ +public: + BufferInput(const void * _Nonnull buf, int64_t len, bool copy = true); + ~BufferInput() noexcept; + + // This class is non-copyable. + BufferInput(const BufferInput&) = delete; + BufferInput(BufferInput&&) = delete; + + // This class is non-assignable. + BufferInput& operator=(const BufferInput&) = delete; + BufferInput& operator=(BufferInput&&) = delete; + +private: + void _Open() noexcept override { pos_ = 0; } + void _Close() noexcept override {} + bool _AtEOF() const noexcept override { return len_ == pos_; } + int64_t _Offset() const noexcept override { return pos_; } + int64_t _Length() const noexcept override { return len_; } + bool _SupportsSeeking() const noexcept override { return true; } + + int64_t _Read(void * _Nonnull buffer, int64_t count) override; + void _SeekToOffset(int64_t offset, int whence) override; + + void * _Nonnull buf_ {nullptr}; + bool free_ {false}; + int64_t len_ {0}; + int64_t pos_ {0}; +}; + +} /* namespace SFB */ From 03bffdd4c2cb2f14747969590c52dbdee44bc009 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 08:44:01 -0600 Subject: [PATCH 39/75] Add `BufferAdoption` --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 6 +++--- Sources/CSFBAudioEngine/Input/BufferInput.hpp | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index 51a0053d4..0293f12a2 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -13,15 +13,15 @@ #import "BufferInput.hpp" -SFB::BufferInput::BufferInput(const void *buf, int64_t len, bool copy) -: buf_{const_cast(buf)}, len_{len}, free_{copy} +SFB::BufferInput::BufferInput(const void *buf, int64_t len, BufferAdoption owned) +: buf_{const_cast(buf)}, free_{owned == BufferAdoption::copy || owned == BufferAdoption::noCopyAndFree}, len_{len} { if(!buf || len < 0) { os_log_error(sLog, "Cannot create BufferInput with null buffer or negative length"); throw std::invalid_argument("Null buffer or negative length"); } - if(copy) { + if(owned == BufferAdoption::copy) { buf_ = std::malloc(len_); if(!buf_) throw std::bad_alloc(); diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp index 1b5b2b737..cd8125ad3 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.hpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -13,7 +13,8 @@ namespace SFB { class BufferInput: public InputSource { public: - BufferInput(const void * _Nonnull buf, int64_t len, bool copy = true); + enum class BufferAdoption { copy, noCopy, noCopyAndFree }; + BufferInput(const void * _Nonnull buf, int64_t len, BufferAdoption owned = BufferAdoption::copy); ~BufferInput() noexcept; // This class is non-copyable. From 7f3dee3aa7dc59c35373ef20191bb494bf8e0b56 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 09:04:46 -0600 Subject: [PATCH 40/75] Add `BufferInput` support to `SFBInputSource` --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 6 +- Sources/CSFBAudioEngine/Input/BufferInput.hpp | 2 +- .../CSFBAudioEngine/Input/SFBInputSource.mm | 93 ++++++++++++------- 3 files changed, 64 insertions(+), 37 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index 0293f12a2..a12bb9556 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -13,15 +13,15 @@ #import "BufferInput.hpp" -SFB::BufferInput::BufferInput(const void *buf, int64_t len, BufferAdoption owned) -: buf_{const_cast(buf)}, free_{owned == BufferAdoption::copy || owned == BufferAdoption::noCopyAndFree}, len_{len} +SFB::BufferInput::BufferInput(const void *buf, int64_t len, BufferAdoption behavior) +: buf_{const_cast(buf)}, free_{behavior == BufferAdoption::copy || behavior == BufferAdoption::noCopyAndFree}, len_{len} { if(!buf || len < 0) { os_log_error(sLog, "Cannot create BufferInput with null buffer or negative length"); throw std::invalid_argument("Null buffer or negative length"); } - if(owned == BufferAdoption::copy) { + if(behavior == BufferAdoption::copy) { buf_ = std::malloc(len_); if(!buf_) throw std::bad_alloc(); diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp index cd8125ad3..c01a0e206 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.hpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -14,7 +14,7 @@ class BufferInput: public InputSource { public: enum class BufferAdoption { copy, noCopy, noCopyAndFree }; - BufferInput(const void * _Nonnull buf, int64_t len, BufferAdoption owned = BufferAdoption::copy); + BufferInput(const void * _Nonnull buf, int64_t len, BufferAdoption behavior = BufferAdoption::copy); ~BufferInput() noexcept; // This class is non-copyable. diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index dff34dd9e..84d703a6f 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -6,6 +6,7 @@ #import "SFBInputSource+Internal.h" +#import "BufferInput.hpp" #import "DataInput.hpp" #import "FileContentsInput.hpp" #import "FileInput.hpp" @@ -45,46 +46,54 @@ + (instancetype)inputSourceForURL:(NSURL *)url flags:(SFBInputSourceFlags)flags NSParameterAssert(url != nil); NSParameterAssert(url.isFileURL); - SFB::InputSource::unique_ptr up; + try { + SFB::InputSource::unique_ptr up; + + if(flags & SFBInputSourceFlagsMemoryMapFiles) + up = std::make_unique((__bridge CFURLRef)url); + else if(flags & SFBInputSourceFlagsLoadFilesInMemory) + up = std::make_unique((__bridge CFURLRef)url); + else + up = std::make_unique((__bridge CFURLRef)url); + + if(!up) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; + return nil; + } - if(flags & SFBInputSourceFlagsMemoryMapFiles) - up = std::make_unique((__bridge CFURLRef)url); - else if(flags & SFBInputSourceFlagsLoadFilesInMemory) - up = std::make_unique((__bridge CFURLRef)url); - else - up = std::make_unique((__bridge CFURLRef)url); + SFBInputSource *inputSource = [[SFBInputSource alloc] init]; + if(!inputSource) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; + return nil; + } - if(!up) { - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; - return nil; + inputSource->_input = std::move(up); + return inputSource; } - - SFBInputSource *inputSource = [[SFBInputSource alloc] init]; - if(!inputSource) { - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; + catch(const std::exception& e) { return nil; } - - inputSource->_input = std::move(up); - return inputSource; } + (instancetype)inputSourceWithData:(NSData *)data { NSParameterAssert(data != nil); - auto up = std::make_unique((__bridge CFDataRef)data); - if(!up) - return nil; + try { + auto up = std::make_unique((__bridge CFDataRef)data); + if(!up) + return nil; - SFBInputSource *inputSource = [[SFBInputSource alloc] init]; - if(!inputSource) + SFBInputSource *inputSource = [[SFBInputSource alloc] init]; + if(inputSource) + inputSource->_input = std::move(up); + return inputSource; + } + catch(const std::exception& e) { return nil; - - inputSource->_input = std::move(up); - return inputSource; + } } + (instancetype)inputSourceWithBytes:(const void *)bytes length:(NSInteger)length @@ -92,10 +101,19 @@ + (instancetype)inputSourceWithBytes:(const void *)bytes length:(NSInteger)lengt NSParameterAssert(bytes != nullptr); NSParameterAssert(length >= 0); - NSData *data = [NSData dataWithBytes:bytes length:(NSUInteger)length]; - if(!data) + try { + auto up = std::make_unique(bytes, length, SFB::BufferInput::BufferAdoption::copy); + if(!up) + return nil; + + SFBInputSource *inputSource = [[SFBInputSource alloc] init]; + if(inputSource) + inputSource->_input = std::move(up); + return inputSource; + } + catch(const std::exception& e) { return nil; - return [SFBInputSource inputSourceWithData:data]; + } } + (instancetype)inputSourceWithBytesNoCopy:(void *)bytes length:(NSInteger)length freeWhenDone:(BOOL)freeWhenDone @@ -103,10 +121,19 @@ + (instancetype)inputSourceWithBytesNoCopy:(void *)bytes length:(NSInteger)lengt NSParameterAssert(bytes != nullptr); NSParameterAssert(length >= 0); - NSData *data = [NSData dataWithBytesNoCopy:bytes length:(NSUInteger)length freeWhenDone:freeWhenDone]; - if(!data) + try { + auto up = std::make_unique(bytes, length, freeWhenDone ? SFB::BufferInput::BufferAdoption::noCopyAndFree : SFB::BufferInput::BufferAdoption::noCopy); + if(!up) + return nil; + + SFBInputSource *inputSource = [[SFBInputSource alloc] init]; + if(inputSource) + inputSource->_input = std::move(up); + return inputSource; + } + catch(const std::exception& e) { return nil; - return [SFBInputSource inputSourceWithData:data]; + } } - (void)dealloc From 8a0d46c2617f1abd1241ba4f02682f0dc2aca2f3 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 09:07:34 -0600 Subject: [PATCH 41/75] Refactor --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 6 +++++ Sources/CSFBAudioEngine/Input/InputSource.hpp | 26 ++++++------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index b5767c632..4ef653bc5 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -15,6 +15,12 @@ const os_log_t InputSource::sLog = os_log_create("org.sbooth.AudioEngine", "Inpu } /* namespace SFB */ +SFB::InputSource::~InputSource() noexcept +{ + if(url_) + CFRelease(url_); +} + void SFB::InputSource::Open() { if(IsOpen()) { diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index aa29d0a23..39acc209b 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -18,13 +18,9 @@ namespace SFB { class InputSource { public: - /// The shared log for all `InputSource` instances - static const os_log_t _Nonnull sLog; - using unique_ptr = std::unique_ptr; - virtual ~InputSource() noexcept - { if(url_) CFRelease(url_); } + virtual ~InputSource() noexcept; // This class is non-copyable. InputSource(const InputSource&) = delete; @@ -34,21 +30,18 @@ class InputSource InputSource& operator=(const InputSource&) = delete; InputSource& operator=(InputSource&&) = delete; - CFURLRef _Nullable GetURL() const noexcept - { return url_; } + CFURLRef _Nullable GetURL() const noexcept { return url_; } // Opening and closing void Open(); void Close(); - - bool IsOpen() const noexcept - { return isOpen_; } + bool IsOpen() const noexcept { return isOpen_; } // Reading int64_t Read(void * _Nonnull buffer, int64_t count); + // Position bool AtEOF() const; - int64_t Offset() const; int64_t Length() const; @@ -57,6 +50,8 @@ class InputSource void SeekToOffset(int64_t offset, int whence); protected: + /// The shared log for all `InputSource` instances + static const os_log_t _Nonnull sLog; explicit InputSource() noexcept = default; @@ -64,7 +59,6 @@ class InputSource { if(url) url_ = static_cast(CFRetain(url)); } private: - // Subclasses must implement the following methods virtual void _Open() = 0; virtual void _Close() = 0; @@ -72,13 +66,9 @@ class InputSource virtual bool _AtEOF() const = 0; virtual int64_t _Offset() const = 0; virtual int64_t _Length() const = 0; - // Optional seeking support - virtual bool _SupportsSeeking() const noexcept - { return false; } - - virtual void _SeekToOffset(int64_t offset, int whence) - { throw std::logic_error("Seeking not supported"); } + virtual bool _SupportsSeeking() const noexcept { return false; } + virtual void _SeekToOffset(int64_t offset, int whence) { throw std::logic_error("Seeking not supported"); } /// The location of the bytes to be read CFURLRef _Nullable url_ {nullptr}; From 8635356562f8824a81c6964af34c18ed8eced9db Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 09:38:23 -0600 Subject: [PATCH 42/75] Add description --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 4 ++++ Sources/CSFBAudioEngine/Input/BufferInput.hpp | 1 + Sources/CSFBAudioEngine/Input/DataInput.cpp | 5 +++++ Sources/CSFBAudioEngine/Input/DataInput.hpp | 1 + Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 6 ++++++ Sources/CSFBAudioEngine/Input/FileContentsInput.hpp | 1 + Sources/CSFBAudioEngine/Input/FileInput.cpp | 7 +++++++ Sources/CSFBAudioEngine/Input/FileInput.hpp | 1 + Sources/CSFBAudioEngine/Input/InputSource.cpp | 5 +++++ Sources/CSFBAudioEngine/Input/InputSource.hpp | 4 ++++ Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 7 +++++++ Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp | 1 + 12 files changed, 43 insertions(+) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index a12bb9556..cf81a8051 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -72,3 +72,7 @@ void SFB::BufferInput::_SeekToOffset(int64_t offset, int whence) pos_ = offset; } +CFStringRef SFB::BufferInput::_CopyDescription() const noexcept +{ + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_); +} diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp index c01a0e206..b47d8a4e5 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.hpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -35,6 +35,7 @@ class BufferInput: public InputSource int64_t _Read(void * _Nonnull buffer, int64_t count) override; void _SeekToOffset(int64_t offset, int whence) override; + CFStringRef _CopyDescription() const noexcept override; void * _Nonnull buf_ {nullptr}; bool free_ {false}; diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index c723517c8..0b3dc708f 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -61,3 +61,8 @@ void SFB::DataInput::_SeekToOffset(int64_t offset, int whence) pos_ = offset; } + +CFStringRef SFB::DataInput::_CopyDescription() const noexcept +{ + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, data_); +} diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index 1a000b4ae..e5a6e055d 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -34,6 +34,7 @@ class DataInput: public InputSource int64_t _Read(void * _Nonnull buffer, int64_t count) override; void _SeekToOffset(int64_t offset, int whence) override; + CFStringRef _CopyDescription() const noexcept override; CFDataRef _Nonnull data_ {nullptr}; CFIndex pos_ {0}; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 9877041cf..faa5842f4 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -104,3 +104,9 @@ void SFB::FileContentsInput::_SeekToOffset(int64_t offset, int whence) pos_ = offset; } +CFStringRef SFB::FileContentsInput::_CopyDescription() const noexcept +{ + CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); + const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_, lastPathComponent); +} diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 2764a5c13..7b2dcaf74 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -34,6 +34,7 @@ class FileContentsInput: public InputSource void _Close() noexcept override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; void _SeekToOffset(int64_t offset, int whence) override; + CFStringRef _CopyDescription() const noexcept override; void * _Nullable buf_ {nullptr}; int64_t len_ {0}; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 661faf1cd..027741914 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -80,3 +80,10 @@ void SFB::FileInput::_SeekToOffset(int64_t offset, int whence) if(::fseeko(file_, static_cast(offset), whence)) throw std::system_error{errno, std::generic_category()}; } + +CFStringRef SFB::FileInput::_CopyDescription() const noexcept +{ + CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); + const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); } /// The location of the bytes to be read CFURLRef _Nullable url_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index d034d293a..058e7294a 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -108,3 +108,10 @@ void SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, int whence) pos_ = offset; } + +CFStringRef SFB::MemoryMappedFileInput::_CopyDescription() const noexcept +{ + CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); + const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, region_, lastPathComponent); +} diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index f99318800..4d57fe25e 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -34,6 +34,7 @@ class MemoryMappedFileInput: public InputSource void _Close() override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; void _SeekToOffset(int64_t offset, int whence) override; + CFStringRef _CopyDescription() const noexcept override; void * _Nullable region_ {nullptr}; int64_t len_ {0}; From da62256a6cebf246fb808effb62845977fff5eae Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 09:39:29 -0600 Subject: [PATCH 43/75] Use `CopyDescription` --- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 84d703a6f..6ac75b438 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -253,11 +253,7 @@ - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error - (NSString *)description { - NSURL *url = (__bridge NSURL *)_input->GetURL(); - if(url) - return [NSString stringWithFormat:@"<%@ %p: \"%@\">", [self class], self, [[NSFileManager defaultManager] displayNameAtPath:url.path]]; - else - return [NSString stringWithFormat:@"<%@ %p>", [self class], self]; + return (__bridge_transfer NSString *)_input->CopyDescription(); } @end From e01d806d5fdba7793fcd6456707211eae6f5c72d Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 09:42:11 -0600 Subject: [PATCH 44/75] Fix format strings --- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index faa5842f4..ccdccc749 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -108,5 +108,5 @@ CFStringRef SFB::FileContentsInput::_CopyDescription() const noexcept { CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; - return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_, lastPathComponent); + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_, lastPathComponent); } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 027741914..8ae168024 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -85,5 +85,5 @@ CFStringRef SFB::FileInput::_CopyDescription() const noexcept { CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; - return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, region_, lastPathComponent); + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, region_, lastPathComponent); } From c484210add0f284f571e9d0f882132be8705f672 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 09:45:52 -0600 Subject: [PATCH 45/75] Fix format strings again --- Sources/CSFBAudioEngine/Input/DataInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index 0b3dc708f..c4208b519 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -64,5 +64,5 @@ void SFB::DataInput::_SeekToOffset(int64_t offset, int whence) CFStringRef SFB::DataInput::_CopyDescription() const noexcept { - return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, data_); + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, data_); } diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index ccdccc749..46c004389 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -108,5 +108,5 @@ CFStringRef SFB::FileContentsInput::_CopyDescription() const noexcept { CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; - return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_, lastPathComponent); + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_, lastPathComponent); } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 8ae168024..3048ce4be 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -85,5 +85,5 @@ CFStringRef SFB::FileInput::_CopyDescription() const noexcept { CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; - return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, lastPathComponent); } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index aee37e8fc..80c8bf5dd 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -113,5 +113,5 @@ CFStringRef SFB::MemoryMappedFileInput::_CopyDescription() const noexcept { CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; - return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, region_, lastPathComponent); + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, region_, lastPathComponent); } From 69844f7d0de38387408e6375adbec9e2a3e9a5e6 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 10:12:24 -0600 Subject: [PATCH 46/75] Add `CopyDataWithLength` --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 32 +++++++++++++++++++ Sources/CSFBAudioEngine/Input/InputSource.hpp | 1 + .../CSFBAudioEngine/Input/SFBInputSource.mm | 17 +++------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index f26f72b3b..19c672ace 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -58,6 +58,38 @@ int64_t SFB::InputSource::Read(void *buffer, int64_t count) return _Read(buffer, count); } +CFDataRef SFB::InputSource::CopyDataWithLength(int64_t length) +{ + if(!IsOpen()) { + os_log_error(sLog, "CopyDataOfLength() called on that hasn't been opened", this); + throw std::logic_error("Input source not open"); + } + + if(length < 0 || length > LONG_MAX) { + os_log_error(sLog, "CopyDataOfLength() called on with invalid length", this); + throw std::invalid_argument("Invalid length"); + } + + if(length == 0) + return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, nullptr, 0, kCFAllocatorNull); + + void *buf = std::malloc(length); + if(!buf) + throw std::bad_alloc(); + + try { + const auto read = _Read(buf, length); + auto data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, static_cast(buf), read, kCFAllocatorMalloc); + if(!data) + std::free(buf); + return data; + } + catch(...) { + std::free(buf); + throw; + } +} + bool SFB::InputSource::AtEOF() const { if(!IsOpen()) { diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 53c326d6a..718d1c7de 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -39,6 +39,7 @@ class InputSource // Reading int64_t Read(void * _Nonnull buffer, int64_t count); + CFDataRef _Nullable CopyDataWithLength(int64_t length); // Position bool AtEOF() const; diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 6ac75b438..2b84c3588 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -358,23 +358,14 @@ @implementation SFBInputSource (SFBDataReading) - (NSData *)readDataOfLength:(NSUInteger)length error:(NSError **)error { - if(length == 0) - return [NSData data]; - - void *buf = malloc(length); - if(!buf) { + try { + return (__bridge_transfer NSData *)_input->CopyDataWithLength(length); + } + catch(const std::exception& e) { if(error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; return nil; } - - NSInteger bytesRead = 0; - if(![self readBytes:buf length:length bytesRead:&bytesRead error:error]) { - free(buf); - return nil; - } - - return [NSData dataWithBytesNoCopy:buf length:bytesRead freeWhenDone:YES]; } @end From f1c9fcfe0d3167964f9e347cf47224d8ae27e7d4 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 10:55:26 -0600 Subject: [PATCH 47/75] Translate exceptions to NSError --- .../CSFBAudioEngine/Input/SFBInputSource.mm | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 2b84c3588..f43799130 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -4,6 +4,8 @@ // MIT license // +#import + #import "SFBInputSource+Internal.h" #import "BufferInput.hpp" @@ -14,6 +16,26 @@ #import "NSData+SFBExtensions.h" +namespace { + +NSError * NSErrorFromInputSourceException(const std::exception *e) noexcept +{ + NSCParameterAssert(e != nullptr); + + if(const auto se = dynamic_cast(e); se) + return [NSError errorWithDomain:NSPOSIXErrorDomain code:se->code().value() userInfo:@{ NSDebugDescriptionErrorKey: @(se->code().message().c_str()) }]; + + if(const auto ia = dynamic_cast(e); ia) + return [NSError errorWithDomain:NSPOSIXErrorDomain code:EINVAL userInfo:@{ NSDebugDescriptionErrorKey: @(ia->what()) }]; + + if(const auto oor = dynamic_cast(e); oor) + return [NSError errorWithDomain:NSPOSIXErrorDomain code:EDOM userInfo:@{ NSDebugDescriptionErrorKey: @(oor->what()) }]; + + return [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:@{ NSDebugDescriptionErrorKey: @(e->what()) }]; +} + +} /* namespace */ + // NSError domain for InputSource and subclasses NSErrorDomain const SFBInputSourceErrorDomain = @"org.sbooth.AudioEngine.InputSource"; @@ -154,7 +176,7 @@ - (BOOL)openReturningError:(NSError **)error } catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; + *error = NSErrorFromInputSourceException(&e); return NO; } } @@ -167,7 +189,7 @@ - (BOOL)closeReturningError:(NSError **)error } catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; + *error = NSErrorFromInputSourceException(&e); return NO; } } @@ -187,7 +209,7 @@ - (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger * } catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; + *error = NSErrorFromInputSourceException(&e); return NO; } } @@ -213,7 +235,7 @@ - (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error } catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; + *error = NSErrorFromInputSourceException(&e); return NO; } } @@ -228,7 +250,7 @@ - (BOOL)getLength:(NSInteger *)length error:(NSError **)error } catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; + *error = NSErrorFromInputSourceException(&e); return NO; } } @@ -246,7 +268,7 @@ - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error } catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeInputOutput userInfo:nil]; + *error = NSErrorFromInputSourceException(&e); return NO; } } @@ -363,7 +385,7 @@ - (NSData *)readDataOfLength:(NSUInteger)length error:(NSError **)error } catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; + *error = NSErrorFromInputSourceException(&e); return nil; } } From 78544787acb891682858865b6cac11b93497722f Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 11:13:41 -0600 Subject: [PATCH 48/75] Improve/eliminate numeric limit checks --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 4 ---- Sources/CSFBAudioEngine/Input/DataInput.cpp | 3 ++- Sources/CSFBAudioEngine/Input/FileContentsInput.cpp | 4 ---- Sources/CSFBAudioEngine/Input/FileInput.cpp | 4 ---- Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp | 4 ---- 5 files changed, 2 insertions(+), 17 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index cf81a8051..02afce372 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -37,10 +37,6 @@ SFB::BufferInput::~BufferInput() noexcept int64_t SFB::BufferInput::_Read(void *buffer, int64_t count) { - if(count > SIZE_T_MAX) { - os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); - throw std::invalid_argument("Count greater than maximum allowable value"); - } const auto remaining = len_ - pos_; count = std::min(count, remaining); memcpy(buffer, reinterpret_cast(reinterpret_cast(buf_) + pos_), count); diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index c4208b519..8b48041c6 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -5,6 +5,7 @@ // #import +#import #import "DataInput.hpp" @@ -24,7 +25,7 @@ SFB::DataInput::~DataInput() noexcept int64_t SFB::DataInput::_Read(void *buffer, int64_t count) { - if(count > LONG_MAX) { + if(count > std::numeric_limits::max()) { os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); throw std::invalid_argument("Count greater than maximum allowable value"); } diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 46c004389..ea2029027 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -69,10 +69,6 @@ void SFB::FileContentsInput::_Close() noexcept int64_t SFB::FileContentsInput::_Read(void *buffer, int64_t count) { - if(count > SIZE_T_MAX) { - os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); - throw std::invalid_argument("Count greater than maximum allowable value"); - } const auto remaining = len_ - pos_; count = std::min(count, remaining); memcpy(buffer, reinterpret_cast(reinterpret_cast(buf_) + pos_), count); diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 3048ce4be..04b99d582 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -57,10 +57,6 @@ void SFB::FileInput::_Close() int64_t SFB::FileInput::_Read(void *buffer, int64_t count) { - if(count > SIZE_T_MAX) { - os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); - throw std::invalid_argument("Count greater than maximum allowable value"); - } const auto nitems = std::fread(buffer, 1, count, file_); if(nitems != count && std::ferror(file_)) throw std::system_error{errno, std::generic_category()}; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 80c8bf5dd..e12f3f471 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -74,10 +74,6 @@ void SFB::MemoryMappedFileInput::_Close() int64_t SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) { - if(count > SIZE_T_MAX) { - os_log_error(sLog, "_Read() called on with count greater than maximum allowable value", this); - throw std::invalid_argument("Count greater than maximum allowable value"); - } const auto remaining = len_ - pos_; count = std::min(count, remaining); memcpy(buffer, reinterpret_cast(reinterpret_cast(region_) + pos_), count); From 7a370697a9ccca75b368cb58a27afa13c2b7ee50 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 17:53:16 -0600 Subject: [PATCH 49/75] Add `SeekAnchor`, `ReadUnsigned`, and `ReadSigned` --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 16 +-- Sources/CSFBAudioEngine/Input/BufferInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/DataInput.cpp | 16 +-- Sources/CSFBAudioEngine/Input/DataInput.hpp | 2 +- .../Input/FileContentsInput.cpp | 16 +-- .../Input/FileContentsInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.cpp | 10 +- Sources/CSFBAudioEngine/Input/FileInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/InputSource.cpp | 2 +- Sources/CSFBAudioEngine/Input/InputSource.hpp | 83 ++++++++++++-- .../Input/MemoryMappedFileInput.cpp | 16 +-- .../Input/MemoryMappedFileInput.hpp | 2 +- .../CSFBAudioEngine/Input/SFBInputSource.mm | 104 +++++++++++++----- 13 files changed, 185 insertions(+), 88 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index 02afce372..2d5014f6e 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -44,20 +44,12 @@ int64_t SFB::BufferInput::_Read(void *buffer, int64_t count) return count; } -void SFB::BufferInput::_SeekToOffset(int64_t offset, int whence) +void SFB::BufferInput::_SeekToOffset(int64_t offset, SeekAnchor whence) { switch(whence) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += pos_; - break; - case SEEK_END: - offset += len_; - break; - default: - os_log_error(sLog, "_SeekToOffset() called on with unknown whence %d", this, whence); - throw std::invalid_argument("Unknown whence"); + case SeekAnchor::start: /* unchanged */ break; + case SeekAnchor::current: offset += pos_; break; + case SeekAnchor::end: offset += len_; break; } if(offset < 0 || offset > len_) { diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp index b47d8a4e5..bbd071186 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.hpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -34,7 +34,7 @@ class BufferInput: public InputSource bool _SupportsSeeking() const noexcept override { return true; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; - void _SeekToOffset(int64_t offset, int whence) override; + void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; void * _Nonnull buf_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index 8b48041c6..b26fcefee 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -37,22 +37,14 @@ int64_t SFB::DataInput::_Read(void *buffer, int64_t count) return count; } -void SFB::DataInput::_SeekToOffset(int64_t offset, int whence) +void SFB::DataInput::_SeekToOffset(int64_t offset, SeekAnchor whence) { const auto length = CFDataGetLength(data_); switch(whence) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += pos_; - break; - case SEEK_END: - offset += length; - break; - default: - os_log_error(sLog, "_SeekToOffset() called on with unknown whence %d", this, whence); - throw std::invalid_argument("Unknown whence"); + case SeekAnchor::start: /* unchanged */ break; + case SeekAnchor::current: offset += pos_; break; + case SeekAnchor::end: offset += length; break; } if(offset < 0 || offset > length) { diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index e5a6e055d..8dc4aa389 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -33,7 +33,7 @@ class DataInput: public InputSource bool _SupportsSeeking() const noexcept override { return true; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; - void _SeekToOffset(int64_t offset, int whence) override; + void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; CFDataRef _Nonnull data_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index ea2029027..6ff6ea19e 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -76,20 +76,12 @@ int64_t SFB::FileContentsInput::_Read(void *buffer, int64_t count) return count; } -void SFB::FileContentsInput::_SeekToOffset(int64_t offset, int whence) +void SFB::FileContentsInput::_SeekToOffset(int64_t offset, SeekAnchor whence) { switch(whence) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += pos_; - break; - case SEEK_END: - offset += len_; - break; - default: - os_log_error(sLog, "_SeekToOffset() called on with unknown whence %d", this, whence); - throw std::invalid_argument("Unknown whence"); + case SeekAnchor::start: /* unchanged */ break; + case SeekAnchor::current: offset += pos_; break; + case SeekAnchor::end: offset += len_; break; } if(offset < 0 || offset > len_) { diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 7b2dcaf74..ea71cb424 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -33,7 +33,7 @@ class FileContentsInput: public InputSource void _Open() override; void _Close() noexcept override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; - void _SeekToOffset(int64_t offset, int whence) override; + void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; void * _Nullable buf_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 04b99d582..fd80ae858 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -71,9 +71,15 @@ int64_t SFB::FileInput::_Offset() const return offset; } -void SFB::FileInput::_SeekToOffset(int64_t offset, int whence) +void SFB::FileInput::_SeekToOffset(int64_t offset, SeekAnchor whence) { - if(::fseeko(file_, static_cast(offset), whence)) + int _whence; + switch(whence) { + case SeekAnchor::start: _whence = SEEK_SET; break; + case SeekAnchor::current: _whence = SEEK_CUR; break; + case SeekAnchor::end: _whence = SEEK_END; break; + } + if(::fseeko(file_, static_cast(offset), _whence)) throw std::system_error{errno, std::generic_category()}; } diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 9bf332260..4ff4983a2 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -35,7 +35,7 @@ class FileInput: public InputSource void _Close() override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; int64_t _Offset() const override; - void _SeekToOffset(int64_t offset, int whence) override; + void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; FILE * _Nullable file_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 19c672ace..88f7cb93b 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -125,7 +125,7 @@ bool SFB::InputSource::SupportsSeeking() const noexcept return _SupportsSeeking(); } -void SFB::InputSource::SeekToOffset(int64_t offset, int whence) +void SFB::InputSource::SeekToOffset(int64_t offset, SeekAnchor whence) { if(!IsOpen()) { os_log_error(sLog, "SeekToOffset() called on that hasn't been opened", this); diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 718d1c7de..08fddacf0 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -8,13 +8,16 @@ #import #import +#import +#import #import #import namespace SFB { +/// An input source. class InputSource { public: @@ -30,30 +33,96 @@ class InputSource InputSource& operator=(const InputSource&) = delete; InputSource& operator=(InputSource&&) = delete; + /// Returns the URL, if any, of the input source. CFURLRef _Nullable GetURL() const noexcept { return url_; } // Opening and closing + /// Opens the input source. void Open(); + /// Closes the input source. void Close(); + /// Returns `true` if the input source is open. bool IsOpen() const noexcept { return isOpen_; } // Reading + /// Attempts to read `count` bytes from the input source into `buffer` and returns the number of bytes read. int64_t Read(void * _Nonnull buffer, int64_t count); + /// Attempts to read `length` bytes from the input source into a `CFData` object and returns it. CFDataRef _Nullable CopyDataWithLength(int64_t length); // Position + /// Returns `true` if the input source is at the end of input. bool AtEOF() const; + /// Returns the current read position of the input source in bytes. int64_t Offset() const; + /// Returns the number of bytes in the input source. int64_t Length() const; // Seeking + /// Returns `true` if the inputs source is seekable. bool SupportsSeeking() const noexcept; - void SeekToOffset(int64_t offset, int whence); + /// Possible seek anchor points. + enum class SeekAnchor { start, current, end, }; + /// Seeks to `offset` bytes relative to `whence`. + void SeekToOffset(int64_t offset, SeekAnchor whence); + /// Returns a description of the input source. CFStringRef CopyDescription() const noexcept; + // Helpers + /// Possible byte orders. + enum class ByteOrder { little, big, host, swapped, }; + + /// Reads and returns an unsigned integer value in the specified byte order. + template || std::is_same_v || std::is_same_v>> + U ReadUnsigned(ByteOrder order = ByteOrder::host) + { + if(!IsOpen()) { + os_log_error(sLog, "ReadUnsigned() called on that hasn't been opened", this); + throw std::logic_error("Input source not open"); + } + + U value; + if(_Read(&value, sizeof(U)) != sizeof(U)) + throw std::runtime_error("Insufficient data"); + + if constexpr (std::is_same_v) { + switch(order) { + case ByteOrder::little: return OSSwapLittleToHostInt16(value); + case ByteOrder::big: return OSSwapBigToHostInt16(value); + case ByteOrder::host: return value; + case ByteOrder::swapped: return OSSwapInt16(value); + } + } + else if constexpr (std::is_same_v) { + switch(order) { + case ByteOrder::little: return OSSwapLittleToHostInt32(value); + case ByteOrder::big: return OSSwapBigToHostInt32(value); + case ByteOrder::host: return value; + case ByteOrder::swapped: return OSSwapInt32(value); + } + } + else if constexpr (std::is_same_v) { + switch(order) { + case ByteOrder::little: return OSSwapLittleToHostInt64(value); + case ByteOrder::big: return OSSwapBigToHostInt64(value); + case ByteOrder::host: return value; + case ByteOrder::swapped: return OSSwapInt64(value); + } + } + else + static_assert(false, "Unsupported unsigned integer type"); + } + + /// Reads and returns a signed integer value in the specified byte order. + template || std::is_same_v || std::is_same_v>> + S ReadSigned(ByteOrder order = ByteOrder::host) + { + return std::make_signed(ReadUnsigned>(order)); + } + protected: - /// The shared log for all `InputSource` instances + /// The shared log for all `InputSource` instances. static const os_log_t _Nonnull sLog; explicit InputSource() noexcept = default; @@ -70,14 +139,14 @@ class InputSource virtual int64_t _Offset() const = 0; virtual int64_t _Length() const = 0; // Optional seeking support - virtual bool _SupportsSeeking() const noexcept { return false; } - virtual void _SeekToOffset(int64_t offset, int whence) { throw std::logic_error("Seeking not supported"); } + virtual bool _SupportsSeeking() const noexcept { return false; } + virtual void _SeekToOffset(int64_t offset, SeekAnchor whence) { throw std::logic_error("Seeking not supported"); } // Optional description - virtual CFStringRef _CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); } + virtual CFStringRef _CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); } - /// The location of the bytes to be read + /// The location of the bytes to be read. CFURLRef _Nullable url_ {nullptr}; - /// `true` if the input source is open + /// `true` if the input source is open. bool isOpen_ {false}; }; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index e12f3f471..6730696d5 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -81,20 +81,12 @@ int64_t SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) return count; } -void SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, int whence) +void SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, SeekAnchor whence) { switch(whence) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += pos_; - break; - case SEEK_END: - offset += len_; - break; - default: - os_log_error(sLog, "_SeekToOffset() called on with unknown whence %d", this, whence); - throw std::invalid_argument("Unknown whence"); + case SeekAnchor::start: /* unchanged */ break; + case SeekAnchor::current: offset += pos_; break; + case SeekAnchor::end: offset += len_; break; } if(offset < 0 || offset > len_) { diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 4d57fe25e..940b0969c 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -33,7 +33,7 @@ class MemoryMappedFileInput: public InputSource void _Open() override; void _Close() override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; - void _SeekToOffset(int64_t offset, int whence) override; + void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; void * _Nullable region_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index f43799130..3e72a277f 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -263,7 +263,7 @@ - (BOOL)supportsSeeking - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error { try { - _input->SeekToOffset(offset, SEEK_SET); + _input->SeekToOffset(offset, SFB::BufferInput::SeekAnchor::start); return YES; } catch(const std::exception& e) { @@ -296,20 +296,44 @@ - (BOOL)readUInt8:(uint8_t *)ui8 error:(NSError **)error - (BOOL)readUInt16:(uint16_t *)ui16 error:(NSError **)error { - NSInteger bytesRead; - return [self readBytes:ui16 length:sizeof(uint16_t) bytesRead:&bytesRead error:error] && bytesRead == sizeof(uint16_t); + NSParameterAssert(ui16 != nil); + try { + *ui16 = _input->ReadUnsigned(); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); + return NO; + } } - (BOOL)readUInt32:(uint32_t *)ui32 error:(NSError **)error { - NSInteger bytesRead; - return [self readBytes:ui32 length:sizeof(uint32_t) bytesRead:&bytesRead error:error] && bytesRead == sizeof(uint32_t); + NSParameterAssert(ui32 != nil); + try { + *ui32 = _input->ReadUnsigned(); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); + return NO; + } } - (BOOL)readUInt64:(uint64_t *)ui64 error:(NSError **)error { - NSInteger bytesRead; - return [self readBytes:ui64 length:sizeof(uint64_t) bytesRead:&bytesRead error:error] && bytesRead == sizeof(uint64_t); + NSParameterAssert(ui64 != nil); + try { + *ui64 = _input->ReadUnsigned(); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); + return NO; + } } @end @@ -319,28 +343,43 @@ @implementation SFBInputSource (SFBBigEndianReading) - (BOOL)readUInt16BigEndian:(uint16_t *)ui16 error:(NSError **)error { NSParameterAssert(ui16 != nil); - if(![self readUInt16:ui16 error:error]) + try { + *ui16 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::big); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); return NO; - *ui16 = OSSwapHostToBigInt16(*ui16); - return YES; + } } - (BOOL)readUInt32BigEndian:(uint32_t *)ui32 error:(NSError **)error { NSParameterAssert(ui32 != nil); - if(![self readUInt32:ui32 error:error]) + try { + *ui32 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::big); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); return NO; - *ui32 = OSSwapHostToBigInt32(*ui32); - return YES; + } } - (BOOL)readUInt64BigEndian:(uint64_t *)ui64 error:(NSError **)error { NSParameterAssert(ui64 != nil); - if(![self readUInt64:ui64 error:error]) + try { + *ui64 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::big); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); return NO; - *ui64 = OSSwapHostToBigInt64(*ui64); - return YES; + } } @end @@ -350,28 +389,43 @@ @implementation SFBInputSource (SFBLittleEndianReading) - (BOOL)readUInt16LittleEndian:(uint16_t *)ui16 error:(NSError **)error { NSParameterAssert(ui16 != nil); - if(![self readUInt16:ui16 error:error]) + try { + *ui16 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::little); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); return NO; - *ui16 = OSSwapHostToLittleInt16(*ui16); - return YES; + } } - (BOOL)readUInt32LittleEndian:(uint32_t *)ui32 error:(NSError **)error { NSParameterAssert(ui32 != nil); - if(![self readUInt32:ui32 error:error]) + try { + *ui32 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::little); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); return NO; - *ui32 = OSSwapHostToLittleInt32(*ui32); - return YES; + } } - (BOOL)readUInt64LittleEndian:(uint64_t *)ui64 error:(NSError **)error { NSParameterAssert(ui64 != nil); - if(![self readUInt64:ui64 error:error]) + try { + *ui64 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::little); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); return NO; - *ui64 = OSSwapHostToLittleInt64(*ui64); - return YES; + } } @end From f47f4604bbe57344792dded9565dce605eb2e92c Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 22:03:04 -0600 Subject: [PATCH 50/75] Add `ReadValue` --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 16 ++++++++-------- Sources/CSFBAudioEngine/Input/InputSource.hpp | 18 ++++++++++++++++-- .../CSFBAudioEngine/Input/SFBInputSource.mm | 2 +- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 88f7cb93b..f037e1c46 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -58,27 +58,27 @@ int64_t SFB::InputSource::Read(void *buffer, int64_t count) return _Read(buffer, count); } -CFDataRef SFB::InputSource::CopyDataWithLength(int64_t length) +CFDataRef SFB::InputSource::CopyData(int64_t count) { if(!IsOpen()) { - os_log_error(sLog, "CopyDataOfLength() called on that hasn't been opened", this); + os_log_error(sLog, "CopyData() called on that hasn't been opened", this); throw std::logic_error("Input source not open"); } - if(length < 0 || length > LONG_MAX) { - os_log_error(sLog, "CopyDataOfLength() called on with invalid length", this); - throw std::invalid_argument("Invalid length"); + if(count < 0 || count > LONG_MAX) { + os_log_error(sLog, "CopyData() called on with invalid count", this); + throw std::invalid_argument("Invalid count"); } - if(length == 0) + if(count == 0) return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, nullptr, 0, kCFAllocatorNull); - void *buf = std::malloc(length); + void *buf = std::malloc(count); if(!buf) throw std::bad_alloc(); try { - const auto read = _Read(buf, length); + const auto read = _Read(buf, count); auto data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, static_cast(buf), read, kCFAllocatorMalloc); if(!data) std::free(buf); diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 08fddacf0..ff944551f 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -47,8 +47,8 @@ class InputSource // Reading /// Attempts to read `count` bytes from the input source into `buffer` and returns the number of bytes read. int64_t Read(void * _Nonnull buffer, int64_t count); - /// Attempts to read `length` bytes from the input source into a `CFData` object and returns it. - CFDataRef _Nullable CopyDataWithLength(int64_t length); + /// Attempts to read `count` bytes from the input source into a `CFData` object and returns it. + CFDataRef _Nullable CopyData(int64_t count); // Position /// Returns `true` if the input source is at the end of input. @@ -70,6 +70,20 @@ class InputSource CFStringRef CopyDescription() const noexcept; // Helpers + template && std::is_trivially_default_constructible_v>> + V ReadValue() + { + if(!IsOpen()) { + os_log_error(sLog, "ReadValue() called on that hasn't been opened", this); + throw std::logic_error("Input source not open"); + } + + V value; + if(_Read(&value, sizeof(V)) != sizeof(V)) + throw std::runtime_error("Insufficient data"); + return value; + } + /// Possible byte orders. enum class ByteOrder { little, big, host, swapped, }; diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 3e72a277f..f4884966d 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -435,7 +435,7 @@ @implementation SFBInputSource (SFBDataReading) - (NSData *)readDataOfLength:(NSUInteger)length error:(NSError **)error { try { - return (__bridge_transfer NSData *)_input->CopyDataWithLength(length); + return (__bridge_transfer NSData *)_input->CopyData(length); } catch(const std::exception& e) { if(error) From 07775258b29ba785efceddf1d342bf68f60b885d Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 22:04:35 -0600 Subject: [PATCH 51/75] Add documentation --- Sources/CSFBAudioEngine/Input/InputSource.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index ff944551f..0fafc6421 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -70,6 +70,7 @@ class InputSource CFStringRef CopyDescription() const noexcept; // Helpers + /// Reads and returns a value from the input source. template && std::is_trivially_default_constructible_v>> V ReadValue() { From 7898c49d5b712808fd72e0fa35ad3a6b52185251 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 22:14:32 -0600 Subject: [PATCH 52/75] Add default parameter to `SeekToOffset` --- Sources/CSFBAudioEngine/Input/InputSource.hpp | 2 +- .../CSFBAudioEngine/Input/SFBInputSource.mm | 48 +++++++++---------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 0fafc6421..5969b2114 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -64,7 +64,7 @@ class InputSource /// Possible seek anchor points. enum class SeekAnchor { start, current, end, }; /// Seeks to `offset` bytes relative to `whence`. - void SeekToOffset(int64_t offset, SeekAnchor whence); + void SeekToOffset(int64_t offset, SeekAnchor whence = SeekAnchor::start); /// Returns a description of the input source. CFStringRef CopyDescription() const noexcept; diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index f4884966d..d0d67da1d 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -263,7 +263,7 @@ - (BOOL)supportsSeeking - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error { try { - _input->SeekToOffset(offset, SFB::BufferInput::SeekAnchor::start); + _input->SeekToOffset(offset); return YES; } catch(const std::exception& e) { @@ -452,45 +452,43 @@ - (NSData *)readHeaderOfLength:(NSUInteger)length skipID3v2Tag:(BOOL)skipID3v2Ta { NSParameterAssert(length > 0); - if(!self.supportsSeeking) { + if(!_input->SupportsSeeking()) { if(error) *error = [NSError errorWithDomain:SFBInputSourceErrorDomain code:SFBInputSourceErrorCodeNotSeekable userInfo:nil]; return nil; } - NSInteger originalOffset; - if(![self getOffset:&originalOffset error:error]) - return nil; + try { + const auto originalOffset = _input->Offset(); + _input->SeekToOffset(0); - if(![self seekToOffset:0 error:error]) - return nil; + if(skipID3v2Tag) { + int64_t offset = 0; - if(skipID3v2Tag) { - NSInteger offset = 0; + // Attempt to detect and minimally parse an ID3v2 tag header + NSData *data = (__bridge_transfer NSData *)_input->CopyData(SFBID3v2HeaderSize); + if([data isID3v2Header]) + offset = [data id3v2TagTotalSize]; - // Attempt to detect and minimally parse an ID3v2 tag header - NSData *data = [self readDataOfLength:SFBID3v2HeaderSize error:error]; - if([data isID3v2Header]) - offset = [data id3v2TagTotalSize]; + _input->SeekToOffset(offset); + } - if(![self seekToOffset:offset error:error]) + NSData *data = (__bridge_transfer NSData *)_input->CopyData(length); + if(data.length < length) { + if(error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:EINVAL userInfo:@{ NSURLErrorKey: self.url }]; return nil; - } + } - NSData *data = [self readDataOfLength:length error:error]; - if(!data) - return nil; + _input->SeekToOffset(originalOffset); - if(data.length < length) { + return data; + } + catch(const std::exception& e) { if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:EINVAL userInfo:@{ NSURLErrorKey: self.url }]; + *error = NSErrorFromInputSourceException(&e); return nil; } - - if(![self seekToOffset:originalOffset error:error]) - return nil; - - return data; } @end From 1b85a772a67e5b53280b7f8472ec0063807f5d3f Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 22:30:11 -0600 Subject: [PATCH 53/75] Remove unneeded parameter --- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index d0d67da1d..67f626b7f 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -124,7 +124,7 @@ + (instancetype)inputSourceWithBytes:(const void *)bytes length:(NSInteger)lengt NSParameterAssert(length >= 0); try { - auto up = std::make_unique(bytes, length, SFB::BufferInput::BufferAdoption::copy); + auto up = std::make_unique(bytes, length); if(!up) return nil; From a4360feeaa0cdd60bcd883a854b36bbe1fb8fc22 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 22:36:01 -0600 Subject: [PATCH 54/75] Simplify creation --- .../CSFBAudioEngine/Input/SFBInputSource.mm | 46 ++++--------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 67f626b7f..45347d351 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -69,29 +69,15 @@ + (instancetype)inputSourceForURL:(NSURL *)url flags:(SFBInputSourceFlags)flags NSParameterAssert(url.isFileURL); try { - SFB::InputSource::unique_ptr up; - - if(flags & SFBInputSourceFlagsMemoryMapFiles) - up = std::make_unique((__bridge CFURLRef)url); - else if(flags & SFBInputSourceFlagsLoadFilesInMemory) - up = std::make_unique((__bridge CFURLRef)url); - else - up = std::make_unique((__bridge CFURLRef)url); - - if(!up) { - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; - return nil; - } - SFBInputSource *inputSource = [[SFBInputSource alloc] init]; - if(!inputSource) { - if(error) - *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]; - return nil; + if(inputSource) { + if(flags & SFBInputSourceFlagsMemoryMapFiles) + inputSource->_input = std::make_unique((__bridge CFURLRef)url); + else if(flags & SFBInputSourceFlagsLoadFilesInMemory) + inputSource->_input = std::make_unique((__bridge CFURLRef)url); + else + inputSource->_input = std::make_unique((__bridge CFURLRef)url); } - - inputSource->_input = std::move(up); return inputSource; } catch(const std::exception& e) { @@ -104,13 +90,9 @@ + (instancetype)inputSourceWithData:(NSData *)data NSParameterAssert(data != nil); try { - auto up = std::make_unique((__bridge CFDataRef)data); - if(!up) - return nil; - SFBInputSource *inputSource = [[SFBInputSource alloc] init]; if(inputSource) - inputSource->_input = std::move(up); + inputSource->_input = std::make_unique((__bridge CFDataRef)data); return inputSource; } catch(const std::exception& e) { @@ -124,13 +106,9 @@ + (instancetype)inputSourceWithBytes:(const void *)bytes length:(NSInteger)lengt NSParameterAssert(length >= 0); try { - auto up = std::make_unique(bytes, length); - if(!up) - return nil; - SFBInputSource *inputSource = [[SFBInputSource alloc] init]; if(inputSource) - inputSource->_input = std::move(up); + inputSource->_input = std::make_unique(bytes, length); return inputSource; } catch(const std::exception& e) { @@ -144,13 +122,9 @@ + (instancetype)inputSourceWithBytesNoCopy:(void *)bytes length:(NSInteger)lengt NSParameterAssert(length >= 0); try { - auto up = std::make_unique(bytes, length, freeWhenDone ? SFB::BufferInput::BufferAdoption::noCopyAndFree : SFB::BufferInput::BufferAdoption::noCopy); - if(!up) - return nil; - SFBInputSource *inputSource = [[SFBInputSource alloc] init]; if(inputSource) - inputSource->_input = std::move(up); + inputSource->_input = std::make_unique(bytes, length, freeWhenDone ? SFB::BufferInput::BufferAdoption::noCopyAndFree : SFB::BufferInput::BufferAdoption::noCopy); return inputSource; } catch(const std::exception& e) { From 882dd88a73bd4633e29de0449439758b113f7591 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Wed, 26 Nov 2025 22:40:15 -0600 Subject: [PATCH 55/75] Improve limit testing --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index f037e1c46..bdaeced4d 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -4,6 +4,7 @@ // MIT license // +#import #import #import "InputSource.hpp" @@ -65,7 +66,7 @@ CFDataRef SFB::InputSource::CopyData(int64_t count) throw std::logic_error("Input source not open"); } - if(count < 0 || count > LONG_MAX) { + if(count < 0 || count > std::numeric_limits::max()) { os_log_error(sLog, "CopyData() called on with invalid count", this); throw std::invalid_argument("Invalid count"); } From 6772055ebf26c9d9784d4e4f9d57be5113d28a53 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 06:55:11 -0600 Subject: [PATCH 56/75] Add `ReadBlock` --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 21 +++++++++++++++++++ Sources/CSFBAudioEngine/Input/InputSource.hpp | 7 +++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index bdaeced4d..43dc5c31a 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -91,6 +91,27 @@ CFDataRef SFB::InputSource::CopyData(int64_t count) } } +std::vector SFB::InputSource::ReadBlock(std::vector::size_type count) +{ + if(!IsOpen()) { + os_log_error(sLog, "ReadBlock() called on that hasn't been opened", this); + throw std::logic_error("Input source not open"); + } + + if(count < 0) { + os_log_error(sLog, "ReadBlock() called on with invalid count", this); + throw std::invalid_argument("Invalid count"); + } + + if(count == 0) + return {}; + + std::vector vec; + vec.reserve(count); + vec.resize(_Read(vec.data(), vec.capacity())); + return vec; +} + bool SFB::InputSource::AtEOF() const { if(!IsOpen()) { diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 5969b2114..e436b7c93 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -9,6 +9,7 @@ #import #import #import +#import #import #import @@ -45,10 +46,12 @@ class InputSource bool IsOpen() const noexcept { return isOpen_; } // Reading - /// Attempts to read `count` bytes from the input source into `buffer` and returns the number of bytes read. + /// Attempts to read up to `count` bytes from the input source into `buffer` and returns the number of bytes read. int64_t Read(void * _Nonnull buffer, int64_t count); - /// Attempts to read `count` bytes from the input source into a `CFData` object and returns it. + /// Reads and returns up to `count` bytes from the input source in a `CFData` object. CFDataRef _Nullable CopyData(int64_t count); + /// Reads and returns up to `count` bytes from the input source in a `std::vector` object. + std::vector ReadBlock(std::vector::size_type count); // Position /// Returns `true` if the input source is at the end of input. From 17f550fa108e7413dfc9f2256a796091f60ab795 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 06:57:00 -0600 Subject: [PATCH 57/75] vector::size_type is unsigned --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 43dc5c31a..eb6fc7c10 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -98,11 +98,6 @@ std::vector SFB::InputSource::ReadBlock(std::vector::size_type throw std::logic_error("Input source not open"); } - if(count < 0) { - os_log_error(sLog, "ReadBlock() called on with invalid count", this); - throw std::invalid_argument("Invalid count"); - } - if(count == 0) return {}; From d9fefbd48870f185b3f51d86644ea5cb55631528 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 07:01:20 -0600 Subject: [PATCH 58/75] Fill out `error` when an exception is thrown --- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 45347d351..b8deebb9d 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -81,6 +81,8 @@ + (instancetype)inputSourceForURL:(NSURL *)url flags:(SFBInputSourceFlags)flags return inputSource; } catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); return nil; } } From 770ffe29217616bbbfa31fcf32d34cd48eea90b7 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 07:03:43 -0600 Subject: [PATCH 59/75] Add factory methods --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 30 +++++++++++++++++++ Sources/CSFBAudioEngine/Input/InputSource.hpp | 6 ++++ 2 files changed, 36 insertions(+) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index eb6fc7c10..3f26e8261 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -10,12 +10,42 @@ #import "InputSource.hpp" #import "scope_exit.hpp" +#import "BufferInput.hpp" +#import "DataInput.hpp" +#import "FileContentsInput.hpp" +#import "FileInput.hpp" +#import "MemoryMappedFileInput.hpp" + namespace SFB { const os_log_t InputSource::sLog = os_log_create("org.sbooth.AudioEngine", "InputSource"); } /* namespace SFB */ +SFB::InputSource::unique_ptr SFB::InputSource::CreateForURL(CFURLRef url, FileReadMode mode) +{ + switch(mode) { + case FileReadMode::normal: return std::make_unique(url); + case FileReadMode::memoryMap: return std::make_unique(url); + case FileReadMode::loadInMemory: return std::make_unique(url); + } +} + +SFB::InputSource::unique_ptr SFB::InputSource::CreateWithData(CFDataRef data) +{ + return std::make_unique(data); +} + +SFB::InputSource::unique_ptr SFB::InputSource::CreateWithBytes(const void *buf, int64_t len) +{ + return std::make_unique(buf, len, BufferInput::BufferAdoption::copy); +} + +SFB::InputSource::unique_ptr SFB::InputSource::CreateWithBytesNoCopy(const void *buf, int64_t len, bool free) +{ + return std::make_unique(buf, len, free ? BufferInput::BufferAdoption::noCopyAndFree : BufferInput::BufferAdoption::noCopy); +} + SFB::InputSource::~InputSource() noexcept { if(url_) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index e436b7c93..e8f8260fa 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -24,6 +24,12 @@ class InputSource public: using unique_ptr = std::unique_ptr; + enum class FileReadMode { normal, memoryMap, loadInMemory, }; + static unique_ptr CreateForURL(CFURLRef url, FileReadMode mode = FileReadMode::normal); + static unique_ptr CreateWithData(CFDataRef _Nonnull data); + static unique_ptr CreateWithBytes(const void * _Nonnull buf, int64_t len); + static unique_ptr CreateWithBytesNoCopy(const void * _Nonnull buf, int64_t len, bool free = true); + virtual ~InputSource() noexcept; // This class is non-copyable. From 0a6f9ba37d599770ad7bb9d41cbc966b6ef608e3 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 07:56:04 -0600 Subject: [PATCH 60/75] Refactor buffer-based inputs --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 2 + Sources/CSFBAudioEngine/Input/BufferInput.hpp | 22 ++++++--- .../Input/FileContentsInput.cpp | 39 ++-------------- .../Input/FileContentsInput.hpp | 17 ++----- Sources/CSFBAudioEngine/Input/FileInput.cpp | 6 +-- Sources/CSFBAudioEngine/Input/InputSource.hpp | 6 +-- .../Input/MemoryMappedFileInput.cpp | 46 ++++--------------- .../Input/MemoryMappedFileInput.hpp | 15 +----- 8 files changed, 41 insertions(+), 112 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index 2d5014f6e..fa08fe8fb 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -47,7 +47,9 @@ int64_t SFB::BufferInput::_Read(void *buffer, int64_t count) void SFB::BufferInput::_SeekToOffset(int64_t offset, SeekAnchor whence) { switch(whence) { +#if false case SeekAnchor::start: /* unchanged */ break; +#endif case SeekAnchor::current: offset += pos_; break; case SeekAnchor::end: offset += len_; break; } diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp index bbd071186..779b707ab 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.hpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -13,6 +13,7 @@ namespace SFB { class BufferInput: public InputSource { public: + /// Buffer adoption behaviors. enum class BufferAdoption { copy, noCopy, noCopyAndFree }; BufferInput(const void * _Nonnull buf, int64_t len, BufferAdoption behavior = BufferAdoption::copy); ~BufferInput() noexcept; @@ -25,9 +26,21 @@ class BufferInput: public InputSource BufferInput& operator=(const BufferInput&) = delete; BufferInput& operator=(BufferInput&&) = delete; +protected: + explicit BufferInput() noexcept = default; + + /// The data buffer. + void * _Nonnull buf_ {nullptr}; + /// Whether the buffer should be freed in the destructor. + bool free_ {false}; + /// The length of the buffer in bytes. + int64_t len_ {0}; + /// The current byte position in the buffer. + int64_t pos_ {0}; + private: - void _Open() noexcept override { pos_ = 0; } - void _Close() noexcept override {} + void _Open() override { pos_ = 0; } + void _Close() override {} bool _AtEOF() const noexcept override { return len_ == pos_; } int64_t _Offset() const noexcept override { return pos_; } int64_t _Length() const noexcept override { return len_; } @@ -36,11 +49,6 @@ class BufferInput: public InputSource int64_t _Read(void * _Nonnull buffer, int64_t count) override; void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; - - void * _Nonnull buf_ {nullptr}; - bool free_ {false}; - int64_t len_ {0}; - int64_t pos_ {0}; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp index 6ff6ea19e..ad6031808 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.cpp @@ -14,25 +14,19 @@ #import "scope_exit.hpp" SFB::FileContentsInput::FileContentsInput(CFURLRef url) -: InputSource(url) { if(!url) { os_log_error(sLog, "Cannot create FileContentsInput with null URL"); throw std::invalid_argument("Null URL"); } -} - -SFB::FileContentsInput::~FileContentsInput() noexcept -{ - std::free(buf_); + url_ = static_cast(CFRetain(url)); + free_ = true; } void SFB::FileContentsInput::_Open() { - CFURLRef url = GetURL(); - UInt8 path [PATH_MAX]; - auto success = CFURLGetFileSystemRepresentation(url, FALSE, path, PATH_MAX); + auto success = CFURLGetFileSystemRepresentation(url_, FALSE, path, PATH_MAX); if(!success) throw std::runtime_error("Unable to get URL file system representation"); @@ -67,34 +61,9 @@ void SFB::FileContentsInput::_Close() noexcept buf_ = nullptr; } -int64_t SFB::FileContentsInput::_Read(void *buffer, int64_t count) -{ - const auto remaining = len_ - pos_; - count = std::min(count, remaining); - memcpy(buffer, reinterpret_cast(reinterpret_cast(buf_) + pos_), count); - pos_ += count; - return count; -} - -void SFB::FileContentsInput::_SeekToOffset(int64_t offset, SeekAnchor whence) -{ - switch(whence) { - case SeekAnchor::start: /* unchanged */ break; - case SeekAnchor::current: offset += pos_; break; - case SeekAnchor::end: offset += len_; break; - } - - if(offset < 0 || offset > len_) { - os_log_error(sLog, "_SeekToOffset() called on with invalid seek offset %lld", this, offset); - throw std::out_of_range("Invalid seek offset"); - } - - pos_ = offset; -} - CFStringRef SFB::FileContentsInput::_CopyDescription() const noexcept { - CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); + CFStringRef lastPathComponent = CFURLCopyLastPathComponent(url_); const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_, lastPathComponent); } diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index ea71cb424..1d2ac70cc 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -6,15 +6,15 @@ #pragma once -#import "InputSource.hpp" +#import "BufferInput.hpp" namespace SFB { -class FileContentsInput: public InputSource +class FileContentsInput: public BufferInput { public: explicit FileContentsInput(CFURLRef _Nonnull url); - ~FileContentsInput() noexcept; + ~FileContentsInput() noexcept = default; // This class is non-copyable. FileContentsInput(const FileContentsInput&) = delete; @@ -25,20 +25,9 @@ class FileContentsInput: public InputSource FileContentsInput& operator=(FileContentsInput&&) = delete; private: - bool _AtEOF() const noexcept override { return len_ == pos_; } - int64_t _Offset() const noexcept override { return pos_; } - int64_t _Length() const noexcept override { return len_; } - bool _SupportsSeeking() const noexcept override { return true; } - void _Open() override; void _Close() noexcept override; - int64_t _Read(void * _Nonnull buffer, int64_t count) override; - void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; - - void * _Nullable buf_ {nullptr}; - int64_t len_ {0}; - int64_t pos_ {0}; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index fd80ae858..f88ae0740 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -13,12 +13,12 @@ #import "scope_exit.hpp" SFB::FileInput::FileInput(CFURLRef url) -: InputSource(url) { if(!url) { os_log_error(sLog, "Cannot create FileInput with null URL"); throw std::invalid_argument("Null URL"); } + url_ = static_cast(CFRetain(url)); } SFB::FileInput::~FileInput() noexcept @@ -30,7 +30,7 @@ SFB::FileInput::~FileInput() noexcept void SFB::FileInput::_Open() { UInt8 path [PATH_MAX]; - auto success = CFURLGetFileSystemRepresentation(GetURL(), FALSE, path, PATH_MAX); + auto success = CFURLGetFileSystemRepresentation(url_, FALSE, path, PATH_MAX); if(!success) throw std::runtime_error("Unable to get URL file system representation"); @@ -85,7 +85,7 @@ void SFB::FileInput::_SeekToOffset(int64_t offset, SeekAnchor whence) CFStringRef SFB::FileInput::_CopyDescription() const noexcept { - CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); + CFStringRef lastPathComponent = CFURLCopyLastPathComponent(url_); const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, lastPathComponent); } diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index e8f8260fa..99841e271 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -151,8 +151,8 @@ class InputSource explicit InputSource() noexcept = default; - explicit InputSource(CFURLRef _Nullable url) noexcept - { if(url) url_ = static_cast(CFRetain(url)); } + /// The location of the input. + CFURLRef _Nullable url_ {nullptr}; private: // Subclasses must implement the following methods @@ -168,8 +168,6 @@ class InputSource // Optional description virtual CFStringRef _CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); } - /// The location of the bytes to be read. - CFURLRef _Nullable url_ {nullptr}; /// `true` if the input source is open. bool isOpen_ {false}; }; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp index 6730696d5..4489bd046 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.cpp @@ -15,26 +15,25 @@ #import "scope_exit.hpp" SFB::MemoryMappedFileInput::MemoryMappedFileInput(CFURLRef url) -: InputSource(url) { if(!url) { os_log_error(sLog, "Cannot create MemoryMappedFileInput with null URL"); throw std::invalid_argument("Null URL"); } + url_ = static_cast(CFRetain(url)); + free_ = false; } SFB::MemoryMappedFileInput::~MemoryMappedFileInput() noexcept { - if(region_) - munmap(region_, len_); + if(buf_) + munmap(buf_, len_); } void SFB::MemoryMappedFileInput::_Open() { - CFURLRef url = GetURL(); - UInt8 path [PATH_MAX]; - auto success = CFURLGetFileSystemRepresentation(url, FALSE, path, PATH_MAX); + auto success = CFURLGetFileSystemRepresentation(url_, FALSE, path, PATH_MAX); if(!success) throw std::runtime_error("Unable to get URL file system representation"); @@ -60,46 +59,21 @@ void SFB::MemoryMappedFileInput::_Open() if(region == MAP_FAILED) throw std::system_error{errno, std::generic_category()}; - region_ = region; + buf_ = region; len_ = s.st_size; pos_ = 0; } void SFB::MemoryMappedFileInput::_Close() { - const auto defer = scope_exit{[this]() noexcept { region_ = nullptr; }}; - if(munmap(region_, len_)) + const auto defer = scope_exit{[this]() noexcept { buf_ = nullptr; }}; + if(munmap(buf_, len_)) throw std::system_error{errno, std::generic_category()}; } -int64_t SFB::MemoryMappedFileInput::_Read(void *buffer, int64_t count) -{ - const auto remaining = len_ - pos_; - count = std::min(count, remaining); - memcpy(buffer, reinterpret_cast(reinterpret_cast(region_) + pos_), count); - pos_ += count; - return count; -} - -void SFB::MemoryMappedFileInput::_SeekToOffset(int64_t offset, SeekAnchor whence) -{ - switch(whence) { - case SeekAnchor::start: /* unchanged */ break; - case SeekAnchor::current: offset += pos_; break; - case SeekAnchor::end: offset += len_; break; - } - - if(offset < 0 || offset > len_) { - os_log_error(sLog, "_SeekToOffset() called on with invalid seek offset %lld", this, offset); - throw std::out_of_range("Invalid seek offset"); - } - - pos_ = offset; -} - CFStringRef SFB::MemoryMappedFileInput::_CopyDescription() const noexcept { - CFStringRef lastPathComponent = CFURLCopyLastPathComponent(GetURL()); + CFStringRef lastPathComponent = CFURLCopyLastPathComponent(url_); const auto guard = scope_exit{[&lastPathComponent]() noexcept { CFRelease(lastPathComponent); }}; - return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, region_, lastPathComponent); + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_, lastPathComponent); } diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 940b0969c..32d63d1ba 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -6,11 +6,11 @@ #pragma once -#import "InputSource.hpp" +#import "BufferInput.hpp" namespace SFB { -class MemoryMappedFileInput: public InputSource +class MemoryMappedFileInput: public BufferInput { public: explicit MemoryMappedFileInput(CFURLRef _Nonnull url); @@ -25,20 +25,9 @@ class MemoryMappedFileInput: public InputSource MemoryMappedFileInput& operator=(MemoryMappedFileInput&&) = delete; private: - bool _AtEOF() const noexcept override { return len_ == pos_; } - int64_t _Offset() const noexcept override { return pos_; } - int64_t _Length() const noexcept override { return len_; } - bool _SupportsSeeking() const noexcept override { return true; } - void _Open() override; void _Close() override; - int64_t _Read(void * _Nonnull buffer, int64_t count) override; - void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; - - void * _Nullable region_ {nullptr}; - int64_t len_ {0}; - int64_t pos_ {0}; }; } /* namespace SFB */ From 3261a50a4048b8091935067966d430baab1d7c54 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 08:04:36 -0600 Subject: [PATCH 61/75] Refactor seek logic --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 18 ------------------ Sources/CSFBAudioEngine/Input/BufferInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/DataInput.cpp | 18 ------------------ Sources/CSFBAudioEngine/Input/DataInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.cpp | 15 --------------- Sources/CSFBAudioEngine/Input/FileInput.hpp | 3 ++- Sources/CSFBAudioEngine/Input/InputSource.cpp | 18 +++++++++++++++++- Sources/CSFBAudioEngine/Input/InputSource.hpp | 6 +++--- 8 files changed, 24 insertions(+), 58 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index fa08fe8fb..3ad34f6a5 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -44,24 +44,6 @@ int64_t SFB::BufferInput::_Read(void *buffer, int64_t count) return count; } -void SFB::BufferInput::_SeekToOffset(int64_t offset, SeekAnchor whence) -{ - switch(whence) { -#if false - case SeekAnchor::start: /* unchanged */ break; -#endif - case SeekAnchor::current: offset += pos_; break; - case SeekAnchor::end: offset += len_; break; - } - - if(offset < 0 || offset > len_) { - os_log_error(sLog, "_SeekToOffset() called on with invalid seek offset %lld", this, offset); - throw std::out_of_range("Invalid seek offset"); - } - - pos_ = offset; -} - CFStringRef SFB::BufferInput::_CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, len_, buf_); diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp index 779b707ab..6e5f52753 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.hpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -45,9 +45,9 @@ class BufferInput: public InputSource int64_t _Offset() const noexcept override { return pos_; } int64_t _Length() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override { return true; } + void _SeekToOffset(int64_t offset) override { pos_ = offset; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; - void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; }; diff --git a/Sources/CSFBAudioEngine/Input/DataInput.cpp b/Sources/CSFBAudioEngine/Input/DataInput.cpp index b26fcefee..68d4c53c4 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.cpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.cpp @@ -37,24 +37,6 @@ int64_t SFB::DataInput::_Read(void *buffer, int64_t count) return count; } -void SFB::DataInput::_SeekToOffset(int64_t offset, SeekAnchor whence) -{ - const auto length = CFDataGetLength(data_); - - switch(whence) { - case SeekAnchor::start: /* unchanged */ break; - case SeekAnchor::current: offset += pos_; break; - case SeekAnchor::end: offset += length; break; - } - - if(offset < 0 || offset > length) { - os_log_error(sLog, "_SeekToOffset() called on with invalid seek offset %lld", this, offset); - throw std::out_of_range("Invalid seek offset"); - } - - pos_ = offset; -} - CFStringRef SFB::DataInput::_CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this, data_); diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index 8dc4aa389..bf8a7de83 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -31,9 +31,9 @@ class DataInput: public InputSource int64_t _Offset() const noexcept override { return pos_; } int64_t _Length() const noexcept override { return CFDataGetLength(data_); } bool _SupportsSeeking() const noexcept override { return true; } + void _SeekToOffset(int64_t offset) override { pos_ = offset; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; - void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; CFDataRef _Nonnull data_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index f88ae0740..55ec2b2a3 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -4,9 +4,6 @@ // MIT license // -#import -#import - #import #import "FileInput.hpp" @@ -71,18 +68,6 @@ int64_t SFB::FileInput::_Offset() const return offset; } -void SFB::FileInput::_SeekToOffset(int64_t offset, SeekAnchor whence) -{ - int _whence; - switch(whence) { - case SeekAnchor::start: _whence = SEEK_SET; break; - case SeekAnchor::current: _whence = SEEK_CUR; break; - case SeekAnchor::end: _whence = SEEK_END; break; - } - if(::fseeko(file_, static_cast(offset), _whence)) - throw std::system_error{errno, std::generic_category()}; -} - CFStringRef SFB::FileInput::_CopyDescription() const noexcept { CFStringRef lastPathComponent = CFURLCopyLastPathComponent(url_); diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 4ff4983a2..5d83d7090 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -7,6 +7,7 @@ #pragma once #import +#import #import "InputSource.hpp" @@ -30,12 +31,12 @@ class FileInput: public InputSource bool _AtEOF() const noexcept override { return std::feof(file_) != 0; } int64_t _Length() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override { return true; } + void _SeekToOffset(int64_t offset) override { if(::fseeko(file_, static_cast(offset), SEEK_SET)) throw std::system_error{errno, std::generic_category()}; } void _Open() override; void _Close() override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; int64_t _Offset() const override; - void _SeekToOffset(int64_t offset, SeekAnchor whence) override; CFStringRef _CopyDescription() const noexcept override; FILE * _Nullable file_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 3f26e8261..89556c4f6 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -184,7 +184,23 @@ void SFB::InputSource::SeekToOffset(int64_t offset, SeekAnchor whence) throw std::logic_error("Seeking not supported"); } - return _SeekToOffset(offset, whence); + const auto pos = _Offset(); + const auto len = _Length(); + + switch(whence) { +#if false + case SeekAnchor::start: /* unchanged */ break; +#endif + case SeekAnchor::current: offset += pos; break; + case SeekAnchor::end: offset += len; break; + } + + if(offset < 0 || offset > len) { + os_log_error(sLog, "SeekToOffset() called on with invalid seek offset %lld", this, offset); + throw std::out_of_range("Invalid seek offset"); + } + + return _SeekToOffset(offset); } CFStringRef SFB::InputSource::CopyDescription() const noexcept diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 99841e271..ecb108adc 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -163,10 +163,10 @@ class InputSource virtual int64_t _Offset() const = 0; virtual int64_t _Length() const = 0; // Optional seeking support - virtual bool _SupportsSeeking() const noexcept { return false; } - virtual void _SeekToOffset(int64_t offset, SeekAnchor whence) { throw std::logic_error("Seeking not supported"); } + virtual bool _SupportsSeeking() const noexcept { return false; } + virtual void _SeekToOffset(int64_t offset) { throw std::logic_error("Seeking not supported"); } // Optional description - virtual CFStringRef _CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); } + virtual CFStringRef _CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); } /// `true` if the input source is open. bool isOpen_ {false}; From 2b927de06d88923a78a4e9064a12db292902397c Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 08:13:49 -0600 Subject: [PATCH 62/75] Use `ReadValue` --- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index b8deebb9d..5de7dc977 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -266,8 +266,16 @@ - (BOOL)readInt64:(int64_t *)i64 error:(NSError **)error { return [self readUIn @implementation SFBInputSource (SFBUnsignedIntegerReading) - (BOOL)readUInt8:(uint8_t *)ui8 error:(NSError **)error { - NSInteger bytesRead; - return [self readBytes:ui8 length:sizeof(uint8_t) bytesRead:&bytesRead error:error] && bytesRead == sizeof(uint8_t); + NSParameterAssert(ui8 != nil); + try { + *ui8 = _input->ReadValue(); + return YES; + } + catch(const std::exception& e) { + if(error) + *error = NSErrorFromInputSourceException(&e); + return NO; + } } - (BOOL)readUInt16:(uint16_t *)ui16 error:(NSError **)error From 4b8eae2b5d1df3702cacf11b3ba0da6287df476b Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 08:20:24 -0600 Subject: [PATCH 63/75] Remove include --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index 3ad34f6a5..829aba435 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -7,7 +7,6 @@ #import #import #import -#import #import From 4def4ff7842fc38b08f5a34673c6cef639f01061 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 08:20:40 -0600 Subject: [PATCH 64/75] Add TODO --- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 5de7dc977..b9491f706 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -22,6 +22,8 @@ { NSCParameterAssert(e != nullptr); + // TODO: Set NSURLErrorKey? + if(const auto se = dynamic_cast(e); se) return [NSError errorWithDomain:NSPOSIXErrorDomain code:se->code().value() userInfo:@{ NSDebugDescriptionErrorKey: @(se->code().message().c_str()) }]; From b8addf511b409fca6c68f06aa7f109548f0fcb32 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 08:28:35 -0600 Subject: [PATCH 65/75] Remove unneeded import --- Sources/CSFBAudioEngine/Input/BufferInput.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.cpp b/Sources/CSFBAudioEngine/Input/BufferInput.cpp index 829aba435..86164c194 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.cpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.cpp @@ -8,8 +8,6 @@ #import #import -#import - #import "BufferInput.hpp" SFB::BufferInput::BufferInput(const void *buf, int64_t len, BufferAdoption behavior) From 3b903a47814b5d9b39334e134ce3233b7123fb4f Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 08:33:22 -0600 Subject: [PATCH 66/75] Improve comment --- Sources/CSFBAudioEngine/Input/InputSource.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index ecb108adc..95bcf6155 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -52,7 +52,7 @@ class InputSource bool IsOpen() const noexcept { return isOpen_; } // Reading - /// Attempts to read up to `count` bytes from the input source into `buffer` and returns the number of bytes read. + /// Reads up to `count` bytes from the input source into `buffer` and returns the number of bytes read. int64_t Read(void * _Nonnull buffer, int64_t count); /// Reads and returns up to `count` bytes from the input source in a `CFData` object. CFDataRef _Nullable CopyData(int64_t count); From 05581cee7dc75adade6464adebc7666876c21297 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 11:49:19 -0600 Subject: [PATCH 67/75] Rename offset to position --- Sources/CSFBAudioEngine/Input/BufferInput.hpp | 4 ++-- Sources/CSFBAudioEngine/Input/DataInput.hpp | 4 ++-- Sources/CSFBAudioEngine/Input/FileInput.cpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.hpp | 4 ++-- Sources/CSFBAudioEngine/Input/InputSource.cpp | 16 ++++++++-------- Sources/CSFBAudioEngine/Input/InputSource.hpp | 6 +++--- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 4 ++-- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp index 6e5f52753..daedb1c2e 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.hpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -42,10 +42,10 @@ class BufferInput: public InputSource void _Open() override { pos_ = 0; } void _Close() override {} bool _AtEOF() const noexcept override { return len_ == pos_; } - int64_t _Offset() const noexcept override { return pos_; } + int64_t _Position() const noexcept override { return pos_; } int64_t _Length() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override { return true; } - void _SeekToOffset(int64_t offset) override { pos_ = offset; } + void _SeekToPosition(int64_t position) override { pos_ = position; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; CFStringRef _CopyDescription() const noexcept override; diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index bf8a7de83..d2c6c955c 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -28,10 +28,10 @@ class DataInput: public InputSource void _Open() noexcept override { pos_ = 0; } void _Close() noexcept override {} bool _AtEOF() const noexcept override { return CFDataGetLength(data_) == pos_; } - int64_t _Offset() const noexcept override { return pos_; } + int64_t _Position() const noexcept override { return pos_; } int64_t _Length() const noexcept override { return CFDataGetLength(data_); } bool _SupportsSeeking() const noexcept override { return true; } - void _SeekToOffset(int64_t offset) override { pos_ = offset; } + void _SeekToPosition(int64_t position) override { pos_ = position; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; CFStringRef _CopyDescription() const noexcept override; diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index 55ec2b2a3..a279de318 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -60,7 +60,7 @@ int64_t SFB::FileInput::_Read(void *buffer, int64_t count) return nitems; } -int64_t SFB::FileInput::_Offset() const +int64_t SFB::FileInput::_Position() const { const auto offset = ::ftello(file_); if(offset == -1) diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 5d83d7090..bb04c65b9 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -31,12 +31,12 @@ class FileInput: public InputSource bool _AtEOF() const noexcept override { return std::feof(file_) != 0; } int64_t _Length() const noexcept override { return len_; } bool _SupportsSeeking() const noexcept override { return true; } - void _SeekToOffset(int64_t offset) override { if(::fseeko(file_, static_cast(offset), SEEK_SET)) throw std::system_error{errno, std::generic_category()}; } + void _SeekToPosition(int64_t position) override { if(::fseeko(file_, static_cast(position), SEEK_SET)) throw std::system_error{errno, std::generic_category()}; } void _Open() override; void _Close() override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; - int64_t _Offset() const override; + int64_t _Position() const override; CFStringRef _CopyDescription() const noexcept override; FILE * _Nullable file_ {nullptr}; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 89556c4f6..f5d8f64b6 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -147,20 +147,20 @@ bool SFB::InputSource::AtEOF() const return _AtEOF(); } -int64_t SFB::InputSource::Offset() const +int64_t SFB::InputSource::Position() const { if(!IsOpen()) { - os_log_error(sLog, "GetOffset() called on that hasn't been opened", this); + os_log_error(sLog, "Position() called on that hasn't been opened", this); throw std::logic_error("Input source not open"); } - return _Offset(); + return _Position(); } int64_t SFB::InputSource::Length() const { if(!IsOpen()) { - os_log_error(sLog, "GetLength() called on that hasn't been opened", this); + os_log_error(sLog, "Length() called on that hasn't been opened", this); throw std::logic_error("Input source not open"); } @@ -184,7 +184,7 @@ void SFB::InputSource::SeekToOffset(int64_t offset, SeekAnchor whence) throw std::logic_error("Seeking not supported"); } - const auto pos = _Offset(); + const auto pos = _Position(); const auto len = _Length(); switch(whence) { @@ -196,11 +196,11 @@ void SFB::InputSource::SeekToOffset(int64_t offset, SeekAnchor whence) } if(offset < 0 || offset > len) { - os_log_error(sLog, "SeekToOffset() called on with invalid seek offset %lld", this, offset); - throw std::out_of_range("Invalid seek offset"); + os_log_error(sLog, "SeekToOffset() called on with invalid position %lld", this, offset); + throw std::out_of_range("Invalid seek position"); } - return _SeekToOffset(offset); + return _SeekToPosition(offset); } CFStringRef SFB::InputSource::CopyDescription() const noexcept diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 95bcf6155..57a7234f2 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -63,7 +63,7 @@ class InputSource /// Returns `true` if the input source is at the end of input. bool AtEOF() const; /// Returns the current read position of the input source in bytes. - int64_t Offset() const; + int64_t Position() const; /// Returns the number of bytes in the input source. int64_t Length() const; @@ -160,11 +160,11 @@ class InputSource virtual void _Close() = 0; virtual int64_t _Read(void * _Nonnull buffer, int64_t count) = 0; virtual bool _AtEOF() const = 0; - virtual int64_t _Offset() const = 0; + virtual int64_t _Position() const = 0; virtual int64_t _Length() const = 0; // Optional seeking support virtual bool _SupportsSeeking() const noexcept { return false; } - virtual void _SeekToOffset(int64_t offset) { throw std::logic_error("Seeking not supported"); } + virtual void _SeekToPosition(int64_t position) { throw std::logic_error("Seeking not supported"); } // Optional description virtual CFStringRef _CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); } diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index b9491f706..c655989cc 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -208,7 +208,7 @@ - (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error NSParameterAssert(offset != nullptr); try { - *offset = _input->Offset(); + *offset = _input->Position(); return YES; } catch(const std::exception& e) { @@ -445,7 +445,7 @@ - (NSData *)readHeaderOfLength:(NSUInteger)length skipID3v2Tag:(BOOL)skipID3v2Ta } try { - const auto originalOffset = _input->Offset(); + const auto originalOffset = _input->Position(); _input->SeekToOffset(0); if(skipID3v2Tag) { From 3aa0e434634c626d843f107760931d8d19c6e1af Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Thu, 27 Nov 2025 22:00:27 -0600 Subject: [PATCH 68/75] Check `IsOpen()` in `SupportsSeeking` --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 5 +++++ Sources/CSFBAudioEngine/Input/InputSource.hpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index f5d8f64b6..a4afa0315 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -169,6 +169,11 @@ int64_t SFB::InputSource::Length() const bool SFB::InputSource::SupportsSeeking() const noexcept { + if(!IsOpen()) { + os_log_error(sLog, "SupportsSeeking() called on that hasn't been opened", this); + throw std::logic_error("Input source not open"); + } + return _SupportsSeeking(); } diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 57a7234f2..1f66a0c22 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -68,7 +68,7 @@ class InputSource int64_t Length() const; // Seeking - /// Returns `true` if the inputs source is seekable. + /// Returns `true` if the input source is seekable. bool SupportsSeeking() const noexcept; /// Possible seek anchor points. enum class SeekAnchor { start, current, end, }; From 93245b3c39edf22f0c1e439a7af431ac1a5b418f Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 28 Nov 2025 07:18:03 -0600 Subject: [PATCH 69/75] Reformat --- Sources/CSFBAudioEngine/Input/InputSource.cpp | 3 +- Sources/CSFBAudioEngine/Input/InputSource.hpp | 14 ++-- .../CSFBAudioEngine/Input/SFBInputSource.mm | 69 +++++++------------ 3 files changed, 28 insertions(+), 58 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index a4afa0315..03db6892f 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -114,8 +114,7 @@ CFDataRef SFB::InputSource::CopyData(int64_t count) if(!data) std::free(buf); return data; - } - catch(...) { + } catch(...) { std::free(buf); throw; } diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 1f66a0c22..c67e8e5ea 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -117,33 +117,27 @@ class InputSource case ByteOrder::host: return value; case ByteOrder::swapped: return OSSwapInt16(value); } - } - else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { switch(order) { case ByteOrder::little: return OSSwapLittleToHostInt32(value); case ByteOrder::big: return OSSwapBigToHostInt32(value); case ByteOrder::host: return value; case ByteOrder::swapped: return OSSwapInt32(value); } - } - else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { switch(order) { case ByteOrder::little: return OSSwapLittleToHostInt64(value); case ByteOrder::big: return OSSwapBigToHostInt64(value); case ByteOrder::host: return value; case ByteOrder::swapped: return OSSwapInt64(value); } - } - else + } else static_assert(false, "Unsupported unsigned integer type"); } /// Reads and returns a signed integer value in the specified byte order. template || std::is_same_v || std::is_same_v>> - S ReadSigned(ByteOrder order = ByteOrder::host) - { - return std::make_signed(ReadUnsigned>(order)); - } + S ReadSigned(ByteOrder order = ByteOrder::host) { return std::make_signed(ReadUnsigned>(order)); } protected: /// The shared log for all `InputSource` instances. diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index c655989cc..ab481178c 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -81,8 +81,7 @@ + (instancetype)inputSourceForURL:(NSURL *)url flags:(SFBInputSourceFlags)flags inputSource->_input = std::make_unique((__bridge CFURLRef)url); } return inputSource; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return nil; @@ -98,8 +97,7 @@ + (instancetype)inputSourceWithData:(NSData *)data if(inputSource) inputSource->_input = std::make_unique((__bridge CFDataRef)data); return inputSource; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { return nil; } } @@ -114,8 +112,7 @@ + (instancetype)inputSourceWithBytes:(const void *)bytes length:(NSInteger)lengt if(inputSource) inputSource->_input = std::make_unique(bytes, length); return inputSource; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { return nil; } } @@ -130,8 +127,7 @@ + (instancetype)inputSourceWithBytesNoCopy:(void *)bytes length:(NSInteger)lengt if(inputSource) inputSource->_input = std::make_unique(bytes, length, freeWhenDone ? SFB::BufferInput::BufferAdoption::noCopyAndFree : SFB::BufferInput::BufferAdoption::noCopy); return inputSource; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { return nil; } } @@ -151,8 +147,7 @@ - (BOOL)openReturningError:(NSError **)error try { _input->Open(); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -164,8 +159,7 @@ - (BOOL)closeReturningError:(NSError **)error try { _input->Close(); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -184,8 +178,7 @@ - (BOOL)readBytes:(void *)buffer length:(NSInteger)length bytesRead:(NSInteger * try { *bytesRead = _input->Read(buffer, length); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -196,8 +189,7 @@ - (BOOL)atEOF { try { return _input->AtEOF(); - } - catch(const std::exception& e) { + } catch(const std::exception& e) { // FIXME: Is `NO` the best error return? return NO; } @@ -210,8 +202,7 @@ - (BOOL)getOffset:(NSInteger *)offset error:(NSError **)error try { *offset = _input->Position(); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -225,8 +216,7 @@ - (BOOL)getLength:(NSInteger *)length error:(NSError **)error try { *length = _input->Length(); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -243,8 +233,7 @@ - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error try { _input->SeekToOffset(offset); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -272,8 +261,7 @@ - (BOOL)readUInt8:(uint8_t *)ui8 error:(NSError **)error try { *ui8 = _input->ReadValue(); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -286,8 +274,7 @@ - (BOOL)readUInt16:(uint16_t *)ui16 error:(NSError **)error try { *ui16 = _input->ReadUnsigned(); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -300,8 +287,7 @@ - (BOOL)readUInt32:(uint32_t *)ui32 error:(NSError **)error try { *ui32 = _input->ReadUnsigned(); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -314,8 +300,7 @@ - (BOOL)readUInt64:(uint64_t *)ui64 error:(NSError **)error try { *ui64 = _input->ReadUnsigned(); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -332,8 +317,7 @@ - (BOOL)readUInt16BigEndian:(uint16_t *)ui16 error:(NSError **)error try { *ui16 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::big); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -346,8 +330,7 @@ - (BOOL)readUInt32BigEndian:(uint32_t *)ui32 error:(NSError **)error try { *ui32 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::big); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -360,8 +343,7 @@ - (BOOL)readUInt64BigEndian:(uint64_t *)ui64 error:(NSError **)error try { *ui64 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::big); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -378,8 +360,7 @@ - (BOOL)readUInt16LittleEndian:(uint16_t *)ui16 error:(NSError **)error try { *ui16 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::little); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -392,8 +373,7 @@ - (BOOL)readUInt32LittleEndian:(uint32_t *)ui32 error:(NSError **)error try { *ui32 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::little); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -406,8 +386,7 @@ - (BOOL)readUInt64LittleEndian:(uint64_t *)ui64 error:(NSError **)error try { *ui64 = _input->ReadUnsigned(SFB::InputSource::ByteOrder::little); return YES; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return NO; @@ -422,8 +401,7 @@ - (NSData *)readDataOfLength:(NSUInteger)length error:(NSError **)error { try { return (__bridge_transfer NSData *)_input->CopyData(length); - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return nil; @@ -469,8 +447,7 @@ - (NSData *)readHeaderOfLength:(NSUInteger)length skipID3v2Tag:(BOOL)skipID3v2Ta _input->SeekToOffset(originalOffset); return data; - } - catch(const std::exception& e) { + } catch(const std::exception& e) { if(error) *error = NSErrorFromInputSourceException(&e); return nil; From eacb98c297af9b4d08fa87f9446f259d266272e9 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sun, 30 Nov 2025 16:51:36 -0600 Subject: [PATCH 70/75] Add nullability specifier --- Sources/CSFBAudioEngine/Input/InputSource.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index c67e8e5ea..493d2bf1d 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -25,7 +25,7 @@ class InputSource using unique_ptr = std::unique_ptr; enum class FileReadMode { normal, memoryMap, loadInMemory, }; - static unique_ptr CreateForURL(CFURLRef url, FileReadMode mode = FileReadMode::normal); + static unique_ptr CreateForURL(CFURLRef _Nonnull url, FileReadMode mode = FileReadMode::normal); static unique_ptr CreateWithData(CFDataRef _Nonnull data); static unique_ptr CreateWithBytes(const void * _Nonnull buf, int64_t len); static unique_ptr CreateWithBytesNoCopy(const void * _Nonnull buf, int64_t len, bool free = true); From ff77cf1403ea1c5d8d98a402536f369c03486d31 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sun, 30 Nov 2025 22:05:06 -0600 Subject: [PATCH 71/75] Add `_Nonnull` and general cleanup --- Sources/CSFBAudioEngine/Input/BufferInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/DataInput.hpp | 2 +- .../Input/FileContentsInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/FileInput.hpp | 2 +- Sources/CSFBAudioEngine/Input/InputSource.cpp | 17 ++++--- Sources/CSFBAudioEngine/Input/InputSource.hpp | 47 +++++++++++++++---- .../Input/MemoryMappedFileInput.hpp | 2 +- 7 files changed, 53 insertions(+), 21 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/BufferInput.hpp b/Sources/CSFBAudioEngine/Input/BufferInput.hpp index daedb1c2e..dfa3c6733 100644 --- a/Sources/CSFBAudioEngine/Input/BufferInput.hpp +++ b/Sources/CSFBAudioEngine/Input/BufferInput.hpp @@ -48,7 +48,7 @@ class BufferInput: public InputSource void _SeekToPosition(int64_t position) override { pos_ = position; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; - CFStringRef _CopyDescription() const noexcept override; + CFStringRef _Nonnull _CopyDescription() const noexcept override; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/DataInput.hpp b/Sources/CSFBAudioEngine/Input/DataInput.hpp index d2c6c955c..c7ac764a5 100644 --- a/Sources/CSFBAudioEngine/Input/DataInput.hpp +++ b/Sources/CSFBAudioEngine/Input/DataInput.hpp @@ -34,7 +34,7 @@ class DataInput: public InputSource void _SeekToPosition(int64_t position) override { pos_ = position; } int64_t _Read(void * _Nonnull buffer, int64_t count) override; - CFStringRef _CopyDescription() const noexcept override; + CFStringRef _Nonnull _CopyDescription() const noexcept override; CFDataRef _Nonnull data_ {nullptr}; CFIndex pos_ {0}; diff --git a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp index 1d2ac70cc..e4ac9e50f 100644 --- a/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileContentsInput.hpp @@ -27,7 +27,7 @@ class FileContentsInput: public BufferInput private: void _Open() override; void _Close() noexcept override; - CFStringRef _CopyDescription() const noexcept override; + CFStringRef _Nonnull _CopyDescription() const noexcept override; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index bb04c65b9..13e425097 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -37,7 +37,7 @@ class FileInput: public InputSource void _Close() override; int64_t _Read(void * _Nonnull buffer, int64_t count) override; int64_t _Position() const override; - CFStringRef _CopyDescription() const noexcept override; + CFStringRef _Nonnull _CopyDescription() const noexcept override; FILE * _Nullable file_ {nullptr}; int64_t len_ {0}; diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index 03db6892f..b46ba64ee 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -188,15 +188,20 @@ void SFB::InputSource::SeekToOffset(int64_t offset, SeekAnchor whence) throw std::logic_error("Seeking not supported"); } - const auto pos = _Position(); const auto len = _Length(); switch(whence) { -#if false - case SeekAnchor::start: /* unchanged */ break; -#endif - case SeekAnchor::current: offset += pos; break; - case SeekAnchor::end: offset += len; break; + case SeekAnchor::start: + /* unchanged */ + break; + + case SeekAnchor::current: + offset += _Position(); + break; + + case SeekAnchor::end: + offset += len; + break; } if(offset < 0 || offset > len) { diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 493d2bf1d..9b27b40f4 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -43,42 +43,52 @@ class InputSource /// Returns the URL, if any, of the input source. CFURLRef _Nullable GetURL() const noexcept { return url_; } - // Opening and closing + // MARK: Opening and closing + /// Opens the input source. void Open(); + /// Closes the input source. void Close(); + /// Returns `true` if the input source is open. bool IsOpen() const noexcept { return isOpen_; } - // Reading + // MARK: Reading + /// Reads up to `count` bytes from the input source into `buffer` and returns the number of bytes read. int64_t Read(void * _Nonnull buffer, int64_t count); + /// Reads and returns up to `count` bytes from the input source in a `CFData` object. CFDataRef _Nullable CopyData(int64_t count); + /// Reads and returns up to `count` bytes from the input source in a `std::vector` object. std::vector ReadBlock(std::vector::size_type count); - // Position + // MARK: Position + /// Returns `true` if the input source is at the end of input. bool AtEOF() const; + /// Returns the current read position of the input source in bytes. int64_t Position() const; + /// Returns the number of bytes in the input source. int64_t Length() const; - // Seeking + // MARK: Seeking + /// Returns `true` if the input source is seekable. bool SupportsSeeking() const noexcept; + /// Possible seek anchor points. enum class SeekAnchor { start, current, end, }; + /// Seeks to `offset` bytes relative to `whence`. void SeekToOffset(int64_t offset, SeekAnchor whence = SeekAnchor::start); - /// Returns a description of the input source. - CFStringRef CopyDescription() const noexcept; + // MARK: Helpers - // Helpers /// Reads and returns a value from the input source. template && std::is_trivially_default_constructible_v>> V ReadValue() @@ -139,6 +149,11 @@ class InputSource template || std::is_same_v || std::is_same_v>> S ReadSigned(ByteOrder order = ByteOrder::host) { return std::make_signed(ReadUnsigned>(order)); } + // MARK: Debugging + + /// Returns a description of the input source. + CFStringRef _Nonnull CopyDescription() const noexcept; + protected: /// The shared log for all `InputSource` instances. static const os_log_t _Nonnull sLog; @@ -156,11 +171,23 @@ class InputSource virtual bool _AtEOF() const = 0; virtual int64_t _Position() const = 0; virtual int64_t _Length() const = 0; + // Optional seeking support - virtual bool _SupportsSeeking() const noexcept { return false; } - virtual void _SeekToPosition(int64_t position) { throw std::logic_error("Seeking not supported"); } + virtual bool _SupportsSeeking() const noexcept + { + return false; + } + + virtual void _SeekToPosition(int64_t position) + { + throw std::logic_error("Seeking not supported"); + } + // Optional description - virtual CFStringRef _CopyDescription() const noexcept { return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); } + virtual CFStringRef _Nonnull _CopyDescription() const noexcept + { + return CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR(""), this); + } /// `true` if the input source is open. bool isOpen_ {false}; diff --git a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp index 32d63d1ba..40b5608d7 100644 --- a/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/MemoryMappedFileInput.hpp @@ -27,7 +27,7 @@ class MemoryMappedFileInput: public BufferInput private: void _Open() override; void _Close() override; - CFStringRef _CopyDescription() const noexcept override; + CFStringRef _Nonnull _CopyDescription() const noexcept override; }; } /* namespace SFB */ From efa6368e2ab9cf15a10dafd0075ad8657d932b63 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sun, 30 Nov 2025 22:22:41 -0600 Subject: [PATCH 72/75] Add seekability test --- Sources/CSFBAudioEngine/Input/FileInput.cpp | 8 ++++++++ Sources/CSFBAudioEngine/Input/FileInput.hpp | 3 ++- Sources/CSFBAudioEngine/Input/InputSource.cpp | 2 +- Sources/CSFBAudioEngine/Input/InputSource.hpp | 4 ++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/FileInput.cpp b/Sources/CSFBAudioEngine/Input/FileInput.cpp index a279de318..ba077f9b0 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.cpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.cpp @@ -43,6 +43,14 @@ void SFB::FileInput::_Open() } len_ = s.st_size; + + // Regular files are always seekable + if(S_ISREG(s.st_mode)) + seekable_ = true; + else if(const auto offset = ::ftello(file_); offset != -1) { + if(::fseeko(file_, offset, SEEK_SET) == 0) + seekable_ = true; + } } void SFB::FileInput::_Close() diff --git a/Sources/CSFBAudioEngine/Input/FileInput.hpp b/Sources/CSFBAudioEngine/Input/FileInput.hpp index 13e425097..7e6da83d3 100644 --- a/Sources/CSFBAudioEngine/Input/FileInput.hpp +++ b/Sources/CSFBAudioEngine/Input/FileInput.hpp @@ -30,7 +30,7 @@ class FileInput: public InputSource private: bool _AtEOF() const noexcept override { return std::feof(file_) != 0; } int64_t _Length() const noexcept override { return len_; } - bool _SupportsSeeking() const noexcept override { return true; } + bool _SupportsSeeking() const noexcept override { return seekable_; } void _SeekToPosition(int64_t position) override { if(::fseeko(file_, static_cast(position), SEEK_SET)) throw std::system_error{errno, std::generic_category()}; } void _Open() override; @@ -41,6 +41,7 @@ class FileInput: public InputSource FILE * _Nullable file_ {nullptr}; int64_t len_ {0}; + bool seekable_{false}; }; } /* namespace SFB */ diff --git a/Sources/CSFBAudioEngine/Input/InputSource.cpp b/Sources/CSFBAudioEngine/Input/InputSource.cpp index b46ba64ee..82d8407db 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.cpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.cpp @@ -166,7 +166,7 @@ int64_t SFB::InputSource::Length() const return _Length(); } -bool SFB::InputSource::SupportsSeeking() const noexcept +bool SFB::InputSource::SupportsSeeking() const { if(!IsOpen()) { os_log_error(sLog, "SupportsSeeking() called on that hasn't been opened", this); diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 9b27b40f4..188e52d3a 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -79,7 +79,7 @@ class InputSource // MARK: Seeking /// Returns `true` if the input source is seekable. - bool SupportsSeeking() const noexcept; + bool SupportsSeeking() const; /// Possible seek anchor points. enum class SeekAnchor { start, current, end, }; @@ -173,7 +173,7 @@ class InputSource virtual int64_t _Length() const = 0; // Optional seeking support - virtual bool _SupportsSeeking() const noexcept + virtual bool _SupportsSeeking() const { return false; } From 550f3ca7398a709bd02c401736c757697bc350c5 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sun, 30 Nov 2025 22:43:57 -0600 Subject: [PATCH 73/75] Catch exceptions thrown by `SupportsSeeking()` --- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index ab481178c..164421040 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -225,7 +225,11 @@ - (BOOL)getLength:(NSInteger *)length error:(NSError **)error - (BOOL)supportsSeeking { - return _input->SupportsSeeking(); + try { + return _input->SupportsSeeking(); + } catch(...) { + return NO; + } } - (BOOL)seekToOffset:(NSInteger)offset error:(NSError **)error From 54f93495c3ce8b4d6e1191826e998b67bb8447c9 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 1 Dec 2025 12:20:10 -0600 Subject: [PATCH 74/75] Update formatting --- Sources/CSFBAudioEngine/Input/InputSource.hpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/InputSource.hpp b/Sources/CSFBAudioEngine/Input/InputSource.hpp index 188e52d3a..0b8d0a836 100644 --- a/Sources/CSFBAudioEngine/Input/InputSource.hpp +++ b/Sources/CSFBAudioEngine/Input/InputSource.hpp @@ -41,9 +41,12 @@ class InputSource InputSource& operator=(InputSource&&) = delete; /// Returns the URL, if any, of the input source. - CFURLRef _Nullable GetURL() const noexcept { return url_; } + CFURLRef _Nullable GetURL() const noexcept + { + return url_; + } - // MARK: Opening and closing + // MARK: Opening and Closing /// Opens the input source. void Open(); @@ -52,7 +55,10 @@ class InputSource void Close(); /// Returns `true` if the input source is open. - bool IsOpen() const noexcept { return isOpen_; } + bool IsOpen() const noexcept + { + return isOpen_; + } // MARK: Reading From ac13af115ef4ba0fda7e5d515a3f77aa8f619b7c Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Sun, 28 Dec 2025 23:49:19 -0600 Subject: [PATCH 75/75] Remove `-dealloc` --- Sources/CSFBAudioEngine/Input/SFBInputSource.mm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm index 164421040..3bdbb8863 100644 --- a/Sources/CSFBAudioEngine/Input/SFBInputSource.mm +++ b/Sources/CSFBAudioEngine/Input/SFBInputSource.mm @@ -132,11 +132,6 @@ + (instancetype)inputSourceWithBytesNoCopy:(void *)bytes length:(NSInteger)lengt } } -- (void)dealloc -{ - _input.reset(); -} - - (NSURL *)url { return (__bridge NSURL *)_input->GetURL();