SafeCircularBuffer

main

bobh/SafeCircularBuffer

SafeCircularBuffer

Swift Platform

  1. What is a Circular Buffer? A circular buffer (also called a ring buffer) is a fixed-size data structure that stores elements in a way that “wraps around” when it reaches its capacity. Imagine a fixed-length array where, after adding elements to the end, you start overwriting the oldest elements at the beginning. It’s like a conveyor belt: new items are added at one end, and old items are removed or overwritten at the other. Key Features of a Circular Buffer • Fixed Size: The buffer has a set capacity that doesn’t change. • Efficient: Uses a single array and reuses space, avoiding the need to resize. • FIFO (First-In, First-Out): Elements are read in the order they were added. • Overwrites Old Data: When full, adding a new element overwrites the oldest one. The SafeCircularBuffer in this manual is an actor-based implementation, meaning it’s safe to use in concurrent applications (e.g., with multiple threads or tasks) without risking data corruption.

  2. Benefits of Using a Circular Buffer Circular buffers are useful in many scenarios, especially in performance-sensitive or resource-constrained applications. Here are the key benefits:

  3. Memory Efficiency: • A circular buffer uses a fixed amount of memory, unlike arrays that grow dynamically. This is ideal for applications where memory is limited, such as embedded systems or real-time apps.

  4. Constant-Time Operations: • Adding (pushing) and removing (popping) elements are fast (O(1) time complexity), making it suitable for high-performance tasks like streaming data or buffering audio.

  5. Automatic Overwrite: • When the buffer is full, new elements automatically overwrite the oldest ones. This is perfect for scenarios where you only need the most recent data, like logging recent user actions or storing sensor readings.

  6. Thread Safety (with SafeCircularBuffer): • The SafeCircularBuffer uses Swift’s actor model to ensure that multiple tasks can access the buffer simultaneously without causing data races or crashes. This is crucial in modern apps with concurrent operations, like networking or UI updates.

  7. Simplicity: • The API is straightforward, with methods like push, pop, and peek, making it easy to integrate into your app, even for beginners. Common Use Cases • Real-Time Data Processing: Buffering audio or video streams. • Logging: Storing the most recent log messages or events. • Producer-Consumer Systems: Handling data produced by one task and consumed by another (e.g., network packets). • Undo/Redo Features: Storing a fixed number of recent user actions.

  8. Getting Started with SafeCircularBuffer Prerequisites • Swift Version: Swift 6 or later (this code uses strict concurrency features). • Environment: Xcode 17 or later, or another Swift-compatible IDE. • Knowledge: Basic Swift syntax (structs, actors, async/await) and familiarity with generics. Installation The SafeCircularBuffer code is provided as a standalone Swift module. To use it:

  9. Create a new Swift file (e.g., SafeCircularBuffer.swift) in your project.

  10. Copy and paste the code from the revised implementation below (or the one provided earlier).

  11. Ensure your project targets Swift 6 with strict concurrency checking enabled.

Simple usage of SafeCircularBuffer

Example : Concurrent Usage (Sensor Data) This example simulates a sensor sending data to the buffer from one task while another task reads it, demonstrating thread safety.

import Foundation

// Create a SafeCircularBuffer to store sensor readings (Double) let sensorBuffer = SafeCircularBuffer(capacity: 3)

// Simulate sensor sending data func simulateSensor() async { let readings = [23.5, 24.0, 22.8, 25.1, 23.9] for reading in readings { if let overwritten = await sensorBuffer.push(reading) { print("Overwrote: (overwritten)") } else { print("Added: (reading)") } try? await Task.sleep(nanoseconds: 1_000_000_000) // Wait 1 second } }

// Simulate reading data func readSensorData() async { for _ in 0..<3 { if let value = await sensorBuffer.pop() { print("Read: (value)") } else { print("Buffer empty") } try? await Task.sleep(nanoseconds: 1_500_000_000) // Wait 1.5 seconds } }

// Run both tasks concurrently Task { async let sensorTask = simulateSensor() async let readerTask = readSensorData() _ = await (sensorTask, readerTask) }

Sample Output (order may vary due to concurrency):

Added: 23.5 Added: 24.0 Added: 22.8 Read: 23.5 Overwrote: 23.5 Read: 24.0 Overwrote: 24.0 Read: 22.8


Test Suite 'Selected tests' started at 2025-05-28 15:41:14.189. Test Suite 'SafeCircularBufferTestTests.xctest' started at 2025-05-28 15:41:14.190. Test Suite 'SafeCircularBufferTests' started at 2025-05-28 15:41:14.190. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testActorIsolation]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testActorIsolation]' passed (0.006 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testCapacityConstraints]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testCapacityConstraints]' passed (0.007 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testClear]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testClear]' passed (0.001 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testConcurrentForEach]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testConcurrentForEach]' passed (0.001 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testConcurrentMultipleOperations]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testConcurrentMultipleOperations]' passed (0.005 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testConcurrentPushPop]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testConcurrentPushPop]' passed (0.004 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testForEach]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testForEach]' passed (0.001 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testInitialization]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testInitialization]' passed (0.001 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testLargeCapacity]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testLargeCapacity]' passed (0.058 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testOverwriteBehavior]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testOverwriteBehavior]' passed (0.001 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testPeek]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testPeek]' passed (0.001 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testPushAndPop]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testPushAndPop]' passed (0.001 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testPushBatch]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testPushBatch]' passed (0.030 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testSendableCompliance]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testSendableCompliance]' passed (0.001 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testStructSendableCompliance]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testStructSendableCompliance]' passed (0.000 seconds). Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testToArray]' started. Test Case '-[SafeCircularBufferTestTests.SafeCircularBufferTests testToArray]' passed (0.001 seconds). Test Suite 'SafeCircularBufferTests' passed at 2025-05-28 15:41:14.308. Executed 16 tests, with 0 failures (0 unexpected) in 0.117 (0.119) seconds Test Suite 'SafeCircularBufferTestTests.xctest' passed at 2025-05-28 15:41:14.308. Executed 16 tests, with 0 failures (0 unexpected) in 0.117 (0.119) seconds Test Suite 'Selected tests' passed at 2025-05-28 15:41:14.309. Executed 16 tests, with 0 failures (0 unexpected) in 0.117 (0.119) seconds


Setup: Use Swift 6

  1. Go to Build Settings. • Search for Swift Language Version. • Set it to Swift 6.0 (ensure you’re using Xcode 16 or later, as Swift 6 was introduced with it).
  2. Enable Strict Concurrency Checking: • In Build Settings, search for Swift Compiler - Language. • Find the Strict Concurrency Checking setting (or SWIFT_STRICT_CONCURRENCY). • Set it to Complete. This enforces Swift 6’s full concurrency model, including actor isolation and Sendable type requirements. • Complete: Enforces all concurrency rules, assuming all dependencies are concurrency-safe.

Claude Generated This comprehensive test suite covers all aspects of your SafeCircularBuffer implementation with particular focus on Swift 6’s strict concurrency checking. Here are the key areas tested: Test Coverage: Basic Functionality: • Initialization and capacity constraints • Push/pop operations with FIFO behavior • Overwrite behavior when buffer is full • Peek operations (non-destructive reads) • Clear functionality • Array conversion and batch operations • ForEach iteration Concurrency & Actor Isolation: • Concurrent push/pop operations using TaskGroup • Multiple simultaneous operations across different tasks • Actor isolation verification (all operations properly await) • Concurrent forEach operations • Cross-actor boundary sharing tests Sendable Compliance: • Tests that SafeCircularBuffer can be passed between actors • Verification that the underlying CircularBuffer struct is Sendable • Cross-actor boundary data consistency Edge Cases: • Single-capacity buffer behavior • Large capacity stress testing • Empty and full buffer state transitions Swift 6 Concurrency Features Tested: 1. Actor Isolation: All SafeCircularBuffer methods require await, ensuring proper actor isolation 2. Sendable Types: Tests verify both the actor and underlying struct conform to Sendable 3. Structured Concurrency: Uses TaskGroup and async/await patterns properly 4. Data Race Prevention: Concurrent operations are tested to ensure no data races occur The tests use @MainActor on the test class to ensure proper isolation and include realistic concurrent scenarios that would expose any concurrency issues. All async operations are properly awaited, which is enforced by Swift 6’s strict concurrency checking. To run these tests, make sure your project has: • Swift 6.0 language version • Strict Concurrency Checking set to “Complete” • The SafeCircularBuffer.swift file in your main target • This test file in your test target

To run these tests, make sure your project has: • Swift 6.0 language version • Strict Concurrency Checking set to “Complete” • The SafeCircularBuffer.swift file in your main target • This test file in your test target

*/

Description

  • Swift Tools 6.1.0
View More Packages from this Author

Dependencies

  • None
Last updated: Mon Jun 16 2025 06:15:07 GMT-0900 (Hawaii-Aleutian Daylight Time)