Skip to content

Commit 35779a1

Browse files
committed
Add initializers to read from arbitrary sequences
1 parent f16de80 commit 35779a1

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed

Sources/EmbeddedIntegerCollection/EmbeddedIntegerCollection.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,99 @@ extension EmbeddedIntegerCollectionIndices: Equatable, Hashable, Codable,
345345
Sendable, BitwiseCopyable
346346
{}
347347

348+
// MARK: More Initializers
349+
350+
extension EmbeddedIntegerCollection {
351+
/// Creates a collection wrapping an instance of the given integer type,
352+
/// where the integer's initial value embeds elements taken from
353+
/// the given iterator's virtual sequence,
354+
/// notating which bit range cap stores the first element.
355+
///
356+
/// - Precondition: The bit length of `Wrapped` needs to be a multiple of
357+
/// the bit length of `Element`.
358+
///
359+
/// - Parameters:
360+
/// - iterator: The source for the embedded elements' values.
361+
/// - type: A metatype specifier for the `Wrapped` type.
362+
/// It does not need to be specified if the surrounding context already
363+
/// locks it in.
364+
/// - bitRange: Whether the collection's first element should be embedded at
365+
/// the most- or least-significant bit range of the wrapping integer.
366+
/// - Postcondition: This initializer fails if the `iterator` cannot
367+
/// supply enough elements.
368+
/// The count of elements extracted from the `iterator` will be the
369+
/// minimum between its virtual sequence's length and the number of
370+
/// elements supported by this collection type.
371+
@inlinable
372+
public init?<T: IteratorProtocol<Element>>(
373+
extractingFrom iterator: inout T,
374+
embeddingInto type: Wrapped.Type = Wrapped.self,
375+
fillingFrom bitRange: EmbeddedIteratorDirection
376+
) {
377+
self.init(iteratingFrom: bitRange)
378+
379+
// Fill up the wrapping word from the most-significant element down.
380+
var remainingElements = count
381+
while remainingElements > 0, let nextEmbeddedElement = iterator.next() {
382+
word <<= Element.bitWidth
383+
word |= Wrapped(nextEmbeddedElement)
384+
remainingElements -= 1
385+
}
386+
guard remainingElements == 0 else { return nil }
387+
388+
// Flip the elements if the starting bit range is wrong.
389+
if bitRange == .leastSignificantFirst, !isEmpty {
390+
var first = startIndex
391+
var last = index(before: endIndex)
392+
while first < last {
393+
swapAt(first, last)
394+
formIndex(after: &first)
395+
formIndex(before: &last)
396+
}
397+
}
398+
}
399+
400+
/// Creates a collection wrapping an instance of the given integer type,
401+
/// where the integer's initial value embeds elements taken from
402+
/// the prefix of the given sequence,
403+
/// notating which bit range cap stores the first element.
404+
///
405+
/// - Precondition: The bit length of `Wrapped` needs to be a multiple of
406+
/// the bit length of `Element`.
407+
///
408+
/// If access to the elements of the `sequence` after what's needed to
409+
/// fill this collection is required,
410+
/// use the iterator-based `init(extractingFrom:embeddingInto:fillingFrom:)`
411+
/// instead.
412+
///
413+
/// - Parameters:
414+
/// - sequence: The source for the embedded elements' values.
415+
/// - type: A metatype specifier for the `Wrapped` type.
416+
/// It does not need to be specified if the surrounding context already
417+
/// locks it in.
418+
/// - readAll: Whether every element of the `sequence` needs to
419+
/// be copied into this collection (i.e. not a strict prefix).
420+
/// - bitRange: Whether the collection's first element should be embedded at
421+
/// the most- or least-significant bit range of the wrapping integer.
422+
/// - Postcondition: This initializer fails if the `sequence` cannot
423+
/// supply enough elements.
424+
/// It also fails if `readAll` is `true` while the `sequence` has extra
425+
/// elements after filling this collection.
426+
@inlinable
427+
public init?<T: Sequence<Element>>(
428+
readingFrom sequence: T,
429+
embeddingInto type: Wrapped.Type = Wrapped.self,
430+
requireEverythingRead readAll: Bool,
431+
fillingFrom bitRange: EmbeddedIteratorDirection
432+
) {
433+
var iterator = sequence.makeIterator()
434+
self.init(
435+
extractingFrom: &iterator, embeddingInto: type, fillingFrom: bitRange
436+
)
437+
guard !readAll || iterator.next() == nil else { return nil }
438+
}
439+
}
440+
348441
// MARK: - Bit Manipulation Helpers
349442

350443
extension EmbeddedIntegerCollection {

Tests/EmbeddedIntegerCollectionTests/EmbeddedIntegerCollectionTests.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,58 @@ func elementSwap(_ startingBitRange: EmbeddedIteratorDirection) async throws {
223223
collection.swapAt(fourthIndex, secondIndex)
224224
#expect(collection.elementsEqual([0x40, 0x43, 0x42, 0x41]))
225225
}
226+
227+
@Test(
228+
"Arbitrary sequence reading",
229+
arguments: EmbeddedIteratorDirection.allCases
230+
)
231+
func sequenceInitialization(
232+
_ startingBitRange: EmbeddedIteratorDirection
233+
) async throws {
234+
typealias Collection = EmbeddedIntegerCollection<UInt32, UInt8>
235+
let normalFullCollection = try #require(
236+
Collection(
237+
readingFrom: [0x40, 0x41, 0x42, 0x43],
238+
requireEverythingRead: true,
239+
fillingFrom: startingBitRange
240+
)
241+
)
242+
#expect(normalFullCollection.elementsEqual(0x40...0x43))
243+
244+
let normalPrefixCollection = try #require(
245+
Collection(
246+
readingFrom: [0x40, 0x41, 0x42, 0x43],
247+
requireEverythingRead: false,
248+
fillingFrom: startingBitRange
249+
)
250+
)
251+
#expect(normalPrefixCollection.elementsEqual(0x40...0x43))
252+
253+
let excessPrefixCollection = try #require(
254+
Collection(
255+
readingFrom: [0x40, 0x41, 0x42, 0x43, 0x44],
256+
requireEverythingRead: false,
257+
fillingFrom: startingBitRange
258+
)
259+
)
260+
let overfullCollection = Collection(
261+
readingFrom: [0x40, 0x41, 0x42, 0x43, 0x44],
262+
requireEverythingRead: true,
263+
fillingFrom: startingBitRange
264+
)
265+
#expect(excessPrefixCollection.elementsEqual(0x40...0x43))
266+
#expect(overfullCollection == nil)
267+
268+
let shortPrefixCollection = Collection(
269+
readingFrom: [0x40],
270+
requireEverythingRead: false,
271+
fillingFrom: startingBitRange
272+
)
273+
let shortFullCollection = Collection(
274+
readingFrom: [0x40],
275+
requireEverythingRead: true,
276+
fillingFrom: startingBitRange
277+
)
278+
#expect(shortPrefixCollection == nil)
279+
#expect(shortFullCollection == nil)
280+
}

0 commit comments

Comments
 (0)