Skip to content

Commit 8529fe3

Browse files
committed
AsyncBufferedPrefixSequence
1 parent fe9437d commit 8529fe3

File tree

2 files changed

+180
-0
lines changed

2 files changed

+180
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// AsyncBufferedPrefixSequence.swift
3+
// FlyingFox
4+
//
5+
// Created by Simon Whitty on 04/02/2025.
6+
// Copyright © 2025 Simon Whitty. All rights reserved.
7+
//
8+
// Distributed under the permissive MIT license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/FlyingFox
12+
//
13+
// Permission is hereby granted, free of charge, to any person obtaining a copy
14+
// of this software and associated documentation files (the "Software"), to deal
15+
// in the Software without restriction, including without limitation the rights
16+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
// copies of the Software, and to permit persons to whom the Software is
18+
// furnished to do so, subject to the following conditions:
19+
//
20+
// The above copyright notice and this permission notice shall be included in all
21+
// copies or substantial portions of the Software.
22+
//
23+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
// SOFTWARE.
30+
//
31+
32+
package struct AsyncBufferedPrefixSequence<Base: AsyncBufferedSequence>: AsyncBufferedSequence {
33+
private let base: Base
34+
private let count: Int
35+
36+
package init(base: Base, count: Int) {
37+
self.base = base
38+
self.count = count
39+
}
40+
41+
package func makeAsyncIterator() -> Iterator {
42+
Iterator(iterator: base.makeAsyncIterator(), remaining: count)
43+
}
44+
45+
package struct Iterator: AsyncBufferedIteratorProtocol {
46+
private var iterator: Base.AsyncIterator
47+
private var remaining: Int
48+
49+
init (iterator: Base.AsyncIterator, remaining: Int) {
50+
self.iterator = iterator
51+
self.remaining = remaining
52+
}
53+
54+
package mutating func next() async throws -> Base.Element? {
55+
guard remaining > 0 else { return nil }
56+
57+
if let element = try await iterator.next() {
58+
remaining -= 1
59+
return element
60+
} else {
61+
remaining = 0
62+
return nil
63+
}
64+
}
65+
66+
package mutating func nextBuffer(suggested count: Int) async throws -> Base.AsyncIterator.Buffer? {
67+
guard remaining > 0 else { return nil }
68+
69+
let count = Swift.min(remaining, count)
70+
if let buffer = try await iterator.nextBuffer(suggested: count) {
71+
remaining -= buffer.count
72+
return buffer
73+
} else {
74+
remaining = 0
75+
return nil
76+
}
77+
}
78+
}
79+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//
2+
// AsyncBufferedPrefixSequenceTests.swift
3+
// FlyingFox
4+
//
5+
// Created by Simon Whitty on 04/02/2025.
6+
// Copyright © 2025 Simon Whitty. All rights reserved.
7+
//
8+
// Distributed under the permissive MIT license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/FlyingFox
12+
//
13+
// Permission is hereby granted, free of charge, to any person obtaining a copy
14+
// of this software and associated documentation files (the "Software"), to deal
15+
// in the Software without restriction, including without limitation the rights
16+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
// copies of the Software, and to permit persons to whom the Software is
18+
// furnished to do so, subject to the following conditions:
19+
//
20+
// The above copyright notice and this permission notice shall be included in all
21+
// copies or substantial portions of the Software.
22+
//
23+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
// SOFTWARE.
30+
//
31+
32+
@testable import FlyingSocks
33+
import Foundation
34+
import Testing
35+
36+
struct AsyncBufferedPrefixSequenceTests {
37+
38+
@Test
39+
func next_terminates_after_count() async throws {
40+
let buffer = AsyncBufferedCollection(["a", "b", "c", "d", "e", "f"])
41+
var prefix = AsyncBufferedPrefixSequence(base: buffer, count: 4).makeAsyncIterator()
42+
43+
#expect(
44+
try await prefix.next() == "a"
45+
)
46+
#expect(
47+
try await prefix.next() == "b"
48+
)
49+
#expect(
50+
try await prefix.next() == "c"
51+
)
52+
#expect(
53+
try await prefix.next() == "d"
54+
)
55+
#expect(
56+
try await prefix.next() == nil
57+
)
58+
}
59+
60+
@Test
61+
func nextBuffer_terminates_after_count() async throws {
62+
let buffer = AsyncBufferedCollection(["a", "b", "c", "d", "e", "f"])
63+
var prefix = AsyncBufferedPrefixSequence(base: buffer, count: 4).makeAsyncIterator()
64+
65+
#expect(
66+
try await prefix.nextBuffer(suggested: 3) == ["a", "b", "c"]
67+
)
68+
#expect(
69+
try await prefix.nextBuffer(suggested: 3) == ["d"]
70+
)
71+
#expect(
72+
try await prefix.nextBuffer(suggested: 3) == nil
73+
)
74+
}
75+
76+
@Test
77+
func next_terminates_when_base_terminates() async throws {
78+
let buffer = AsyncBufferedCollection(["a"])
79+
var prefix = AsyncBufferedPrefixSequence(base: buffer, count: 2).makeAsyncIterator()
80+
81+
#expect(
82+
try await prefix.next() == "a"
83+
)
84+
#expect(
85+
try await prefix.next() == nil
86+
)
87+
}
88+
89+
@Test
90+
func nextBuffer_terminates_when_base_terminates() async throws {
91+
let buffer = AsyncBufferedCollection(["a"])
92+
var prefix = AsyncBufferedPrefixSequence(base: buffer, count: 10).makeAsyncIterator()
93+
94+
#expect(
95+
try await prefix.nextBuffer(suggested: 3) == ["a"]
96+
)
97+
#expect(
98+
try await prefix.nextBuffer(suggested: 3) == nil
99+
)
100+
}
101+
}

0 commit comments

Comments
 (0)