Skip to content

Commit 24c4e22

Browse files
committed
MIDI1File: Refactored redundant decode methods as inits, added workaround for Swift 6.1 compiler bug
1 parent 7d8a340 commit 24c4e22

3 files changed

Lines changed: 32 additions & 67 deletions

File tree

Sources/SwiftMIDIFile/MIDI1File/MIDI1File init.swift

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,56 +10,7 @@ internal import SwiftMIDIInternals
1010

1111
// MARK: - Init: Raw Data
1212

13-
extension MIDI1File {
14-
/// Initialize by loading the contents of a MIDI file's raw data.
15-
///
16-
/// - Tip: Consider using the `async` overload of this initializer, as it is much more performant.
17-
public init(
18-
data: some DataProtocol & Sendable,
19-
options: MIDI1FileDecodeOptions = MIDI1FileDecodeOptions(),
20-
predicate: DecodePredicate? = nil
21-
) throws(MIDIFileDecodeError) {
22-
try decode(
23-
data: data,
24-
options: options,
25-
predicate: predicate
26-
)
27-
}
28-
29-
/// Initialize by loading the contents of a MIDI file's raw data, parsing chunks concurrently for improved performance.
30-
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
31-
public init(
32-
data: some DataProtocol & Sendable,
33-
options: MIDI1FileDecodeOptions = MIDI1FileDecodeOptions(),
34-
predicate: DecodePredicate? = nil
35-
) async throws(MIDIFileDecodeError) {
36-
try await decode(
37-
data: data,
38-
options: options,
39-
predicate: predicate
40-
)
41-
}
42-
43-
/// Initialize by loading the contents of a MIDI file's raw data, parsing chunks concurrently for improved performance.
44-
/// As each chunk completes parsing, a closure is called with the parsing results and the chunk's content.
45-
///
46-
/// If the file header cannot be parsed or overall file structure is malformed, this method throws an error.
47-
/// Errors encountered during individual chunk parsing are returned within the result closure and not thrown from this method.
48-
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
49-
public init(
50-
data: some DataProtocol & Sendable,
51-
options: MIDI1FileDecodeOptions = MIDI1FileDecodeOptions(),
52-
predicate: DecodePredicate? = nil,
53-
parsedChunk: @escaping ChunkDecodeBlock
54-
) async throws(MIDIFileDecodeError) {
55-
try await decode(
56-
data: data,
57-
options: options,
58-
predicate: predicate,
59-
parsedChunk: parsedChunk
60-
)
61-
}
62-
}
13+
// see MIDI1File+Decoding.swift
6314

6415
// MARK: - Init: File Path
6516

@@ -116,7 +67,7 @@ extension MIDI1File {
11667
predicate: DecodePredicate? = nil
11768
) throws(MIDIFileDecodeError) {
11869
let data = try AnyMIDI1File.data(forFileURL: url)
119-
try decode(
70+
try self.init(
12071
data: data,
12172
options: options,
12273
predicate: predicate
@@ -131,7 +82,7 @@ extension MIDI1File {
13182
predicate: DecodePredicate? = nil
13283
) async throws(MIDIFileDecodeError) {
13384
let data = try AnyMIDI1File.data(forFileURL: url)
134-
try await decode(
85+
try await self.init(
13586
data: data,
13687
options: options,
13788
predicate: predicate
@@ -151,7 +102,7 @@ extension MIDI1File {
151102
parsedChunk: @escaping ChunkDecodeBlock
152103
) async throws(MIDIFileDecodeError) {
153104
let data = try AnyMIDI1File.data(forFileURL: url)
154-
try await decode(
105+
try await self.init(
155106
data: data,
156107
options: options,
157108
predicate: predicate,

Sources/SwiftMIDIFile/MIDI1File/MIDI1File+Decoding.swift

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import SwiftMIDICore
1010
// MARK: - Internal Parsing Entry-point Methods
1111

1212
extension MIDI1File {
13-
/// Decode chunks sequentially, without concurrency.
14-
mutating func decode(
13+
/// Initialize by loading the contents of a MIDI file's raw data.
14+
///
15+
/// - Tip: Consider using the `async` overload of this initializer, as it is much more performant.
16+
public init(
1517
data: some DataProtocol & Sendable,
1618
options: MIDI1FileDecodeOptions,
1719
predicate: DecodePredicate?
@@ -26,10 +28,10 @@ extension MIDI1File {
2628
)
2729
chunks = parsedChunks
2830
}
29-
30-
/// Decode chunks concurrently for improved performance.
31+
32+
/// Initialize by loading the contents of a MIDI file's raw data, parsing chunks concurrently for improved performance.
3133
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
32-
mutating func decode(
34+
public init(
3335
data: some DataProtocol & Sendable,
3436
options: MIDI1FileDecodeOptions,
3537
predicate: DecodePredicate?
@@ -38,19 +40,30 @@ extension MIDI1File {
3840

3941
header = parser.fileDescriptor.header
4042

41-
let parsedChunks = try await parser.chunks(
42-
options: options,
43-
predicate: predicate
44-
)
45-
chunks = parsedChunks
43+
// TODO: Workaround for Swift 6.1 compiler bug which prevents us from using throws(MIDIFileDecodeError) on chunks(), but would work in 6.2+
44+
do {
45+
let parsedChunks = try await parser.chunks(
46+
options: options,
47+
predicate: predicate
48+
)
49+
chunks = parsedChunks
50+
} catch let error as MIDIFileDecodeError { // TODO: only necessary as a workaround
51+
throw error
52+
} catch { // TODO: only necessary as a workaround
53+
// should never happen, as all errors thrown are MIDIFileDecodeError.
54+
// we only have a spillover catch because of the Swift 6.1 compiler bug not allowing
55+
// typed throws on the chunks() method.
56+
throw .malformed(error.localizedDescription)
57+
}
4658
}
47-
48-
/// Decode tracks concurrently for improved performance and call a closure every time a track finishes parsing.
59+
60+
/// Initialize by loading the contents of a MIDI file's raw data, parsing chunks concurrently for improved performance.
61+
/// As each chunk completes parsing, a closure is called with the parsing results and the chunk's content.
4962
///
5063
/// If the file header cannot be parsed or overall file structure is malformed, this method throws an error.
5164
/// Errors encountered during individual chunk parsing are returned within the result closure and not thrown from this method.
5265
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
53-
mutating func decode(
66+
public init(
5467
data: some DataProtocol & Sendable,
5568
options: MIDI1FileDecodeOptions,
5669
predicate: DecodePredicate?,

Sources/SwiftMIDIFile/MIDI1File/Parser/Parser.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,13 @@ extension MIDI1File.Parser {
6161
return newChunks
6262
}
6363

64+
// TODO: Swift 6.1 compiler bug prevents us from using throws(MIDIFileDecodeError) here, but would work in 6.2+
6465
/// Parses all chunks concurrently and returns all chunks in order once all tracks have been parsed.
6566
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
6667
func chunks(
6768
options: MIDI1FileDecodeOptions,
6869
predicate: MIDI1File.DecodePredicate?
69-
) async throws(MIDIFileDecodeError) -> [MIDI1File.AnyChunk] {
70+
) async throws /* (MIDIFileDecodeError) */ -> [MIDI1File.AnyChunk] {
7071
let result: Result<[MIDI1File.AnyChunk], MIDIFileDecodeError> = await withTaskGroup(
7172
of: Result<(index: Int, chunk: MIDI1File.AnyChunk?), MIDIFileDecodeError>.self,
7273
returning: Result<[MIDI1File.AnyChunk], MIDIFileDecodeError>.self

0 commit comments

Comments
 (0)