-
Notifications
You must be signed in to change notification settings - Fork 98
Experimental C++ version of InputSource and company
#599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
sbooth
wants to merge
90
commits into
main
Choose a base branch
from
c++-input
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 18 commits
Commits
Show all changes
90 commits
Select commit
Hold shift + click to select a range
08e00a1
Experimental C++ version of `InputSource` and company
sbooth 3588137
Return a value
sbooth 9985e95
Fix destructors
sbooth 34636bb
Correct test behavior for new input sources
sbooth d98d523
Use `override` instead of `virtual`
sbooth 405684c
Change error code
sbooth be16b5c
Use `scope_exit` for `Close()`
sbooth a77b5b3
Tweak lambda capture lists
sbooth 3d93af7
Add missing newline
sbooth 10044c4
Add `noexcept` to dtors
sbooth c2f64e6
Add noexcept to dtor
sbooth dcb419b
Make `final`
sbooth e443d17
Simplify NULL checks
sbooth 200fdd4
Replace concept with requires
sbooth 7a32aab
Use `std::malloc` and `std::free`
sbooth 87c7a76
Merge branch 'main' into c++-input
sbooth f96f065
Fix includes
sbooth 0545919
Use uniform initialization
sbooth 7ae8066
Merge branch 'main' into c++-input
sbooth 4694e9b
Fix use of `CFRangeMake`
sbooth 6b8144f
Actually read the file contents into memory
sbooth 83062a8
Use `ftello` and `fseeko` for 64-bit support
sbooth e912e74
Verify input counts are within limits
sbooth cab5e38
Set `buf_` to null in _Close()
sbooth 4186d30
Use const for scope_exit declarations
sbooth 77b62f4
Minor cleanup
sbooth 27f52ca
Fill out error on failure
sbooth 5319297
Move member function definition to .cpp file
sbooth 36d3a02
Refactor to make some functions inline
sbooth 3bb6e33
Merge branch 'main' into c++-input
sbooth e700c88
Replace `std::expected` with exceptions
sbooth 9170058
Add empty parameter clause to lambdas
sbooth b409d10
Rename functions
sbooth 5d26f7d
Improve log messages
sbooth 8b66472
Additional log messages
sbooth 3a956e4
Merge branch 'main' into c++-input
sbooth 031a3d9
Remove unused parameter names
sbooth 83a6fe2
Improve log messages
sbooth 67ca581
Move some function implementations to .cpp file
sbooth 41c25dd
Reorganize
sbooth 4689a9c
More cleanup
sbooth 969672a
Add `BufferInput` class
sbooth 03bffdd
Add `BufferAdoption`
sbooth 7f3dee3
Add `BufferInput` support to `SFBInputSource`
sbooth 8a0d46c
Refactor
sbooth 8635356
Add description
sbooth da62256
Use `CopyDescription`
sbooth e01d806
Fix format strings
sbooth c484210
Fix format strings again
sbooth 69844f7
Add `CopyDataWithLength`
sbooth f1c9fcf
Translate exceptions to NSError
sbooth 7854478
Improve/eliminate numeric limit checks
sbooth 7a37069
Add `SeekAnchor`, `ReadUnsigned<T>`, and `ReadSigned<T>`
sbooth f47f460
Add `ReadValue`
sbooth 0777525
Add documentation
sbooth 7898c49
Add default parameter to `SeekToOffset`
sbooth 1b85a77
Remove unneeded parameter
sbooth a4360fe
Simplify creation
sbooth 882dd88
Improve limit testing
sbooth 6772055
Add `ReadBlock`
sbooth 17f550f
vector::size_type is unsigned
sbooth d9fefbd
Fill out `error` when an exception is thrown
sbooth 770ffe2
Add factory methods
sbooth 0a6f9ba
Refactor buffer-based inputs
sbooth 3261a50
Refactor seek logic
sbooth 2b927de
Use `ReadValue`
sbooth 4b8eae2
Remove include
sbooth 4def4ff
Add TODO
sbooth b8addf5
Remove unneeded import
sbooth 3b903a4
Improve comment
sbooth 05581ce
Rename offset to position
sbooth 3aa0e43
Check `IsOpen()` in `SupportsSeeking`
sbooth 93245b3
Reformat
sbooth 19e29b5
Merge branch 'main' into c++-input
sbooth eacb98c
Add nullability specifier
sbooth ff77cf1
Add `_Nonnull` and general cleanup
sbooth efa6368
Add seekability test
sbooth d0bc562
Merge branch 'main' into c++-input
sbooth 550f3ca
Catch exceptions thrown by `SupportsSeeking()`
sbooth 54f9349
Update formatting
sbooth 43bc118
Merge branch 'main' into c++-input
sbooth a2f602e
Merge branch 'main' into c++-input
sbooth 1b27347
Merge branch 'main' into c++-input
sbooth f8c03c5
Merge branch 'main' into c++-input
sbooth ac13af1
Remove `-dealloc`
sbooth 2cfe12c
Merge branch 'main' into c++-input
sbooth 8b56c15
Delete unneeded merged files
sbooth 4bf892e
Update naming and formatting
sbooth b47316d
Reformat
sbooth 3e60e7d
Rename namespace
sbooth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| // | ||
| // Copyright (c) 2010-2025 Stephen F. Booth <me@sbooth.org> | ||
| // 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() noexcept | ||
| { | ||
| if(data_) | ||
| CFRelease(data_); | ||
| } | ||
|
|
||
| std::expected<void, int> SFB::DataInput::_Open() noexcept | ||
| { | ||
| if(!data_) | ||
| return std::unexpected{ENOENT}; | ||
| pos_ = 0; | ||
| return {}; | ||
| } | ||
|
|
||
| std::expected<void, int> SFB::DataInput::_Close() noexcept | ||
| { | ||
| return {}; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> 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<UInt8 *>(buffer)); | ||
|
|
||
| pos_ += count; | ||
|
|
||
| return count; | ||
| } | ||
|
|
||
| std::expected<bool, int> SFB::DataInput::_AtEOF() const noexcept | ||
| { | ||
| return CFDataGetLength(data_) == pos_; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> SFB::DataInput::_GetOffset() const noexcept | ||
| { | ||
| return pos_; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> SFB::DataInput::_GetLength() const noexcept | ||
| { | ||
| return CFDataGetLength(data_); | ||
| } | ||
|
|
||
| bool SFB::DataInput::_SupportsSeeking() const noexcept | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| std::expected<void, int> 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 {}; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| // | ||
| // Copyright (c) 2010-2025 Stephen F. Booth <me@sbooth.org> | ||
| // 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; | ||
| ~DataInput() noexcept; | ||
|
|
||
| // 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: | ||
| std::expected<void, int> _Open() noexcept override; | ||
| std::expected<void, int> _Close() noexcept override; | ||
| std::expected<int64_t, int> _Read(void * _Nonnull buffer, int64_t count) noexcept override; | ||
| std::expected<bool, int> _AtEOF() const noexcept override; | ||
| std::expected<int64_t, int> _GetOffset() const noexcept override; | ||
| std::expected<int64_t, int> _GetLength() const noexcept override; | ||
| bool _SupportsSeeking() const noexcept override; | ||
| std::expected<void, int> _SeekToOffset(int64_t offset, int whence) noexcept override; | ||
|
|
||
| CFDataRef _Nullable data_ {nullptr}; | ||
| CFIndex pos_ {0}; | ||
| }; | ||
|
|
||
| } /* namespace SFB */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| // | ||
| // Copyright (c) 2010-2025 Stephen F. Booth <me@sbooth.org> | ||
| // Part of https://github.com/sbooth/SFBAudioEngine | ||
| // MIT license | ||
| // | ||
|
|
||
| #import <cstdio> | ||
| #import <cstdlib> | ||
|
|
||
| #import <sys/stat.h> | ||
|
|
||
| #import "FileContentsInput.hpp" | ||
| #import "scope_exit.hpp" | ||
|
|
||
| SFB::FileContentsInput::FileContentsInput(CFURLRef url) noexcept | ||
| : InputSource(url) | ||
| {} | ||
|
|
||
| SFB::FileContentsInput::~FileContentsInput() noexcept | ||
| { | ||
| std::free(buf_); | ||
| } | ||
|
|
||
| std::expected<void, int> 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<const char *>(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_ = std::malloc(s.st_size); | ||
| if(!buf_) | ||
| return std::unexpected{ENOMEM}; | ||
|
|
||
| len_ = s.st_size; | ||
| pos_ = 0; | ||
|
|
||
| return {}; | ||
|
sbooth marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| std::expected<void, int> SFB::FileContentsInput::_Close() noexcept | ||
| { | ||
| free(buf_); | ||
|
sbooth marked this conversation as resolved.
Outdated
|
||
| buf_ = nullptr; | ||
| len_ = 0; | ||
|
|
||
| return {}; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> SFB::FileContentsInput::_Read(void *buffer, int64_t count) noexcept | ||
| { | ||
| auto remaining = len_ - pos_; | ||
| count = std::min(count, remaining); | ||
|
|
||
| memcpy(buffer, reinterpret_cast<const void *>(reinterpret_cast<uintptr_t>(buf_) + pos_), count); | ||
| pos_ += count; | ||
|
|
||
| return count; | ||
| } | ||
|
|
||
| std::expected<bool, int> SFB::FileContentsInput::_AtEOF() const noexcept | ||
| { | ||
| return len_ == pos_; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> SFB::FileContentsInput::_GetOffset() const noexcept | ||
| { | ||
| return pos_; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> SFB::FileContentsInput::_GetLength() const noexcept | ||
| { | ||
| return len_; | ||
| } | ||
|
|
||
| bool SFB::FileContentsInput::_SupportsSeeking() const noexcept | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| std::expected<void, int> 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 {}; | ||
| } | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // | ||
| // Copyright (c) 2010-2025 Stephen F. Booth <me@sbooth.org> | ||
| // 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; | ||
| ~FileContentsInput() noexcept; | ||
|
|
||
| // 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: | ||
| std::expected<void, int> _Open() noexcept override; | ||
| std::expected<void, int> _Close() noexcept override; | ||
| std::expected<int64_t, int> _Read(void * _Nonnull buffer, int64_t count) noexcept override; | ||
| std::expected<bool, int> _AtEOF() const noexcept override; | ||
| std::expected<int64_t, int> _GetOffset() const noexcept override; | ||
| std::expected<int64_t, int> _GetLength() const noexcept override; | ||
| bool _SupportsSeeking() const noexcept override; | ||
| std::expected<void, int> _SeekToOffset(int64_t offset, int whence) noexcept override; | ||
|
|
||
| void * _Nullable buf_ {nullptr}; | ||
| int64_t len_ {0}; | ||
| int64_t pos_ {0}; | ||
| }; | ||
|
|
||
| } /* namespace SFB */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| // | ||
| // Copyright (c) 2010-2025 Stephen F. Booth <me@sbooth.org> | ||
| // Part of https://github.com/sbooth/SFBAudioEngine | ||
| // MIT license | ||
| // | ||
|
|
||
| #import <sys/stat.h> | ||
|
|
||
| #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<void, int> 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<const char *>(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<void, int> SFB::FileInput::_Close() noexcept | ||
| { | ||
| auto defer = scope_exit{[this] noexcept { | ||
| file_ = nullptr; | ||
| len_ = 0; | ||
| }}; | ||
|
|
||
| if(std::fclose(file_)) | ||
| return std::unexpected{errno}; | ||
| return {}; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> 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<bool, int> SFB::FileInput::_AtEOF() const noexcept | ||
| { | ||
| return std::feof(file_) != 0; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> SFB::FileInput::_GetOffset() const noexcept | ||
| { | ||
| auto offset = std::ftell(file_); | ||
|
sbooth marked this conversation as resolved.
Outdated
|
||
| if(offset == -1) | ||
| return std::unexpected{errno}; | ||
| return offset; | ||
| } | ||
|
|
||
| std::expected<int64_t, int> SFB::FileInput::_GetLength() const noexcept | ||
| { | ||
| return len_; | ||
| } | ||
|
|
||
| bool SFB::FileInput::_SupportsSeeking() const noexcept | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| std::expected<void, int> SFB::FileInput::_SeekToOffset(int64_t offset, int whence) noexcept | ||
| { | ||
| if(std::fseek(file_, offset, whence)) | ||
|
sbooth marked this conversation as resolved.
Outdated
|
||
| return std::unexpected{errno}; | ||
| return {}; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.