Skip to content
Draft
Show file tree
Hide file tree
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 Nov 3, 2025
3588137
Return a value
sbooth Nov 3, 2025
9985e95
Fix destructors
sbooth Nov 3, 2025
34636bb
Correct test behavior for new input sources
sbooth Nov 3, 2025
d98d523
Use `override` instead of `virtual`
sbooth Nov 3, 2025
405684c
Change error code
sbooth Nov 3, 2025
be16b5c
Use `scope_exit` for `Close()`
sbooth Nov 3, 2025
a77b5b3
Tweak lambda capture lists
sbooth Nov 3, 2025
3d93af7
Add missing newline
sbooth Nov 3, 2025
10044c4
Add `noexcept` to dtors
sbooth Nov 4, 2025
c2f64e6
Add noexcept to dtor
sbooth Nov 4, 2025
dcb419b
Make `final`
sbooth Nov 4, 2025
e443d17
Simplify NULL checks
sbooth Nov 4, 2025
200fdd4
Replace concept with requires
sbooth Nov 4, 2025
7a32aab
Use `std::malloc` and `std::free`
sbooth Nov 4, 2025
87c7a76
Merge branch 'main' into c++-input
sbooth Nov 6, 2025
f96f065
Fix includes
sbooth Nov 6, 2025
0545919
Use uniform initialization
sbooth Nov 6, 2025
7ae8066
Merge branch 'main' into c++-input
sbooth Nov 11, 2025
4694e9b
Fix use of `CFRangeMake`
sbooth Nov 12, 2025
6b8144f
Actually read the file contents into memory
sbooth Nov 12, 2025
83062a8
Use `ftello` and `fseeko` for 64-bit support
sbooth Nov 12, 2025
e912e74
Verify input counts are within limits
sbooth Nov 12, 2025
cab5e38
Set `buf_` to null in _Close()
sbooth Nov 12, 2025
4186d30
Use const for scope_exit declarations
sbooth Nov 12, 2025
77b62f4
Minor cleanup
sbooth Nov 12, 2025
27f52ca
Fill out error on failure
sbooth Nov 12, 2025
5319297
Move member function definition to .cpp file
sbooth Nov 12, 2025
36d3a02
Refactor to make some functions inline
sbooth Nov 12, 2025
3bb6e33
Merge branch 'main' into c++-input
sbooth Nov 24, 2025
e700c88
Replace `std::expected` with exceptions
sbooth Nov 24, 2025
9170058
Add empty parameter clause to lambdas
sbooth Nov 24, 2025
b409d10
Rename functions
sbooth Nov 24, 2025
5d26f7d
Improve log messages
sbooth Nov 25, 2025
8b66472
Additional log messages
sbooth Nov 25, 2025
3a956e4
Merge branch 'main' into c++-input
sbooth Nov 26, 2025
031a3d9
Remove unused parameter names
sbooth Nov 26, 2025
83a6fe2
Improve log messages
sbooth Nov 26, 2025
67ca581
Move some function implementations to .cpp file
sbooth Nov 26, 2025
41c25dd
Reorganize
sbooth Nov 26, 2025
4689a9c
More cleanup
sbooth Nov 26, 2025
969672a
Add `BufferInput` class
sbooth Nov 26, 2025
03bffdd
Add `BufferAdoption`
sbooth Nov 26, 2025
7f3dee3
Add `BufferInput` support to `SFBInputSource`
sbooth Nov 26, 2025
8a0d46c
Refactor
sbooth Nov 26, 2025
8635356
Add description
sbooth Nov 26, 2025
da62256
Use `CopyDescription`
sbooth Nov 26, 2025
e01d806
Fix format strings
sbooth Nov 26, 2025
c484210
Fix format strings again
sbooth Nov 26, 2025
69844f7
Add `CopyDataWithLength`
sbooth Nov 26, 2025
f1c9fcf
Translate exceptions to NSError
sbooth Nov 26, 2025
7854478
Improve/eliminate numeric limit checks
sbooth Nov 26, 2025
7a37069
Add `SeekAnchor`, `ReadUnsigned<T>`, and `ReadSigned<T>`
sbooth Nov 26, 2025
f47f460
Add `ReadValue`
sbooth Nov 27, 2025
0777525
Add documentation
sbooth Nov 27, 2025
7898c49
Add default parameter to `SeekToOffset`
sbooth Nov 27, 2025
1b85a77
Remove unneeded parameter
sbooth Nov 27, 2025
a4360fe
Simplify creation
sbooth Nov 27, 2025
882dd88
Improve limit testing
sbooth Nov 27, 2025
6772055
Add `ReadBlock`
sbooth Nov 27, 2025
17f550f
vector::size_type is unsigned
sbooth Nov 27, 2025
d9fefbd
Fill out `error` when an exception is thrown
sbooth Nov 27, 2025
770ffe2
Add factory methods
sbooth Nov 27, 2025
0a6f9ba
Refactor buffer-based inputs
sbooth Nov 27, 2025
3261a50
Refactor seek logic
sbooth Nov 27, 2025
2b927de
Use `ReadValue`
sbooth Nov 27, 2025
4b8eae2
Remove include
sbooth Nov 27, 2025
4def4ff
Add TODO
sbooth Nov 27, 2025
b8addf5
Remove unneeded import
sbooth Nov 27, 2025
3b903a4
Improve comment
sbooth Nov 27, 2025
05581ce
Rename offset to position
sbooth Nov 27, 2025
3aa0e43
Check `IsOpen()` in `SupportsSeeking`
sbooth Nov 28, 2025
93245b3
Reformat
sbooth Nov 28, 2025
19e29b5
Merge branch 'main' into c++-input
sbooth Nov 30, 2025
eacb98c
Add nullability specifier
sbooth Nov 30, 2025
ff77cf1
Add `_Nonnull` and general cleanup
sbooth Dec 1, 2025
efa6368
Add seekability test
sbooth Dec 1, 2025
d0bc562
Merge branch 'main' into c++-input
sbooth Dec 1, 2025
550f3ca
Catch exceptions thrown by `SupportsSeeking()`
sbooth Dec 1, 2025
54f9349
Update formatting
sbooth Dec 1, 2025
43bc118
Merge branch 'main' into c++-input
sbooth Dec 10, 2025
a2f602e
Merge branch 'main' into c++-input
sbooth Dec 17, 2025
1b27347
Merge branch 'main' into c++-input
sbooth Dec 21, 2025
f8c03c5
Merge branch 'main' into c++-input
sbooth Dec 29, 2025
ac13af1
Remove `-dealloc`
sbooth Dec 29, 2025
2cfe12c
Merge branch 'main' into c++-input
sbooth Jun 9, 2026
8b56c15
Delete unneeded merged files
sbooth Jun 9, 2026
4bf892e
Update naming and formatting
sbooth Jun 9, 2026
b47316d
Reformat
sbooth Jun 9, 2026
3e60e7d
Rename namespace
sbooth Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,5 @@ let package = Package(
])
],
cLanguageStandard: .c11,
cxxLanguageStandard: .cxx20
cxxLanguageStandard: .cxx2b
)
89 changes: 89 additions & 0 deletions Sources/CSFBAudioEngine/Input/DataInput.cpp
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);
Comment thread
sbooth marked this conversation as resolved.
Outdated
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 {};
}
41 changes: 41 additions & 0 deletions Sources/CSFBAudioEngine/Input/DataInput.hpp
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 */
119 changes: 119 additions & 0 deletions Sources/CSFBAudioEngine/Input/FileContentsInput.cpp
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 {};
Comment thread
sbooth marked this conversation as resolved.
Outdated
}

std::expected<void, int> SFB::FileContentsInput::_Close() noexcept
{
free(buf_);
Comment thread
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 {};
}

42 changes: 42 additions & 0 deletions Sources/CSFBAudioEngine/Input/FileContentsInput.hpp
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 */
93 changes: 93 additions & 0 deletions Sources/CSFBAudioEngine/Input/FileInput.cpp
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_);
Comment thread
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))
Comment thread
sbooth marked this conversation as resolved.
Outdated
return std::unexpected{errno};
return {};
}
Loading