@@ -1439,6 +1439,50 @@ private final class DataTests {
1439
1439
#expect( data. count == 0 )
1440
1440
}
1441
1441
}
1442
+
1443
+ @Test func validateMutation_cow_mutableBytes( ) {
1444
+ var data = Data ( count: 32 )
1445
+ holdReference ( data) {
1446
+ var bytes = data. mutableBytes
1447
+ bytes. storeBytes ( of: 1 , toByteOffset: 0 , as: UInt8 . self)
1448
+
1449
+ #expect( data [ 0 ] == 1 )
1450
+ #expect( heldData ? [ 0 ] == 0 )
1451
+ }
1452
+
1453
+ var data2 = Data ( count: 32 )
1454
+ // Escape the pointer to compare after a mutation without dereferencing the pointer
1455
+ let originalPointer = data2. withUnsafeBytes { $0. baseAddress }
1456
+
1457
+ var bytes = data2. mutableBytes
1458
+ bytes. storeBytes ( of: 1 , toByteOffset: 0 , as: UInt8 . self)
1459
+ #expect( data2 [ 0 ] == 1 )
1460
+ data2. withUnsafeBytes {
1461
+ #expect( $0. baseAddress == originalPointer)
1462
+ }
1463
+ }
1464
+
1465
+ @Test func validateMutation_cow_mutableSpan( ) {
1466
+ var data = Data ( count: 32 )
1467
+ holdReference ( data) {
1468
+ var bytes = data. mutableSpan
1469
+ bytes [ 0 ] = 1
1470
+
1471
+ #expect( data [ 0 ] == 1 )
1472
+ #expect( heldData ? [ 0 ] == 0 )
1473
+ }
1474
+
1475
+ var data2 = Data ( count: 32 )
1476
+ // Escape the pointer to compare after a mutation without dereferencing the pointer
1477
+ let originalPointer = data2. withUnsafeBytes { $0. baseAddress }
1478
+
1479
+ var bytes = data2. mutableSpan
1480
+ bytes [ 0 ] = 1
1481
+ #expect( data2 [ 0 ] == 1 )
1482
+ data2. withUnsafeBytes {
1483
+ #expect( $0. baseAddress == originalPointer)
1484
+ }
1485
+ }
1442
1486
1443
1487
@Test func sliceHash( ) {
1444
1488
let base1 = Data ( [ 0 , 0xFF , 0xFF , 0 ] )
@@ -2385,17 +2429,16 @@ extension DataTests {
2385
2429
// These tests require allocating an extremely large amount of data and are serialized to prevent the test runner from using all available memory at once
2386
2430
@Suite( " Large Data Tests " , . serialized)
2387
2431
struct LargeDataTests {
2388
- @Test
2389
- func largeSliceDataSpan( ) throws {
2390
2432
#if _pointerBitWidth(_64)
2391
- let count = Int ( Int32 . max)
2433
+ let largeCount = Int ( Int32 . max)
2392
2434
#elseif _pointerBitWidth(_32)
2393
- let count = Int ( Int16 . max)
2435
+ let largeCount = Int ( Int16 . max)
2394
2436
#else
2395
2437
#error("This test needs updating")
2396
2438
#endif
2397
-
2398
- let source = Data ( repeating: 0 , count: count) . dropFirst ( )
2439
+ @Test
2440
+ func largeSliceDataSpan( ) throws {
2441
+ let source = Data ( repeating: 0 , count: largeCount) . dropFirst ( )
2399
2442
#expect( source. startIndex != 0 )
2400
2443
let span = source. span
2401
2444
let isEmpty = span. isEmpty
@@ -2404,20 +2447,11 @@ struct LargeDataTests {
2404
2447
2405
2448
@Test
2406
2449
func largeSliceDataMutableSpan( ) throws {
2407
- #if _pointerBitWidth(_64)
2408
- var count = Int ( Int32 . max)
2409
- #elseif _pointerBitWidth(_32)
2410
- var count = Int ( Int16 . max)
2411
- #else
2412
- #error("This test needs updating")
2413
- #endif
2414
-
2415
2450
#if !canImport(Darwin) || FOUNDATION_FRAMEWORK
2416
- var source = Data ( repeating: 0 , count: count ) . dropFirst ( )
2451
+ var source = Data ( repeating: 0 , count: largeCount ) . dropFirst ( )
2417
2452
#expect( source. startIndex != 0 )
2418
- count = source. count
2419
2453
var span = source. mutableSpan
2420
- #expect( span. count == count )
2454
+ #expect( span. count == largeCount - 1 )
2421
2455
let i = try #require( span. indices. dropFirst ( ) . randomElement ( ) )
2422
2456
span [ i] = . max
2423
2457
#expect( source [ i] == 0 )
@@ -2427,23 +2461,62 @@ struct LargeDataTests {
2427
2461
2428
2462
@Test
2429
2463
func largeSliceDataMutableRawSpan( ) throws {
2430
- #if _pointerBitWidth(_64)
2431
- var count = Int ( Int32 . max)
2432
- #elseif _pointerBitWidth(_32)
2433
- var count = Int ( Int16 . max)
2434
- #else
2435
- #error("This test needs updating")
2436
- #endif
2437
-
2438
- var source = Data ( repeating: 0 , count: count) . dropFirst ( )
2464
+ var source = Data ( repeating: 0 , count: largeCount) . dropFirst ( )
2439
2465
#expect( source. startIndex != 0 )
2440
- count = source. count
2441
2466
var span = source. mutableBytes
2442
2467
let byteCount = span. byteCount
2443
- #expect( byteCount == count )
2468
+ #expect( byteCount == largeCount - 1 )
2444
2469
let i = try #require( span. byteOffsets. dropFirst ( ) . randomElement ( ) )
2445
2470
span. storeBytes ( of: - 1 , toByteOffset: i, as: Int8 . self)
2446
2471
#expect( source [ i] == 0 )
2447
2472
#expect( source [ i+ 1 ] == . max)
2448
2473
}
2474
+
2475
+ @Test func validateMutation_cow_largeMutableBytes( ) {
2476
+ // Avoid copying a large data on platforms with constrained memory limits
2477
+ #if !canImport(Darwin) || os(macOS)
2478
+ var data = Data ( count: largeCount)
2479
+ let heldData = data
2480
+ var bytes = data. mutableBytes
2481
+ bytes. storeBytes ( of: 1 , toByteOffset: 0 , as: UInt8 . self)
2482
+
2483
+ #expect( data [ 0 ] == 1 )
2484
+ #expect( heldData [ 0 ] == 0 )
2485
+ #endif
2486
+
2487
+ var data2 = Data ( count: largeCount)
2488
+ // Escape the pointer to compare after a mutation without dereferencing the pointer
2489
+ let originalPointer = data2. withUnsafeBytes { $0. baseAddress }
2490
+
2491
+ var bytes2 = data2. mutableBytes
2492
+ bytes2. storeBytes ( of: 1 , toByteOffset: 0 , as: UInt8 . self)
2493
+ #expect( data2 [ 0 ] == 1 )
2494
+ data2. withUnsafeBytes {
2495
+ #expect( $0. baseAddress == originalPointer)
2496
+ }
2497
+ }
2498
+
2499
+ @Test func validateMutation_cow_largeMutableSpan( ) {
2500
+ // Avoid copying a large data on platforms with constrained memory limits
2501
+ #if !canImport(Darwin) || os(macOS)
2502
+ var data = Data ( count: largeCount)
2503
+ let heldData = data
2504
+ var bytes = data. mutableSpan
2505
+ bytes [ 0 ] = 1
2506
+
2507
+ #expect( data [ 0 ] == 1 )
2508
+ #expect( heldData [ 0 ] == 0 )
2509
+ #endif
2510
+
2511
+ var data2 = Data ( count: largeCount)
2512
+ // Escape the pointer to compare after a mutation without dereferencing the pointer
2513
+ let originalPointer = data2. withUnsafeBytes { $0. baseAddress }
2514
+
2515
+ var bytes2 = data2. mutableSpan
2516
+ bytes2 [ 0 ] = 1
2517
+ #expect( data2 [ 0 ] == 1 )
2518
+ data2. withUnsafeBytes {
2519
+ #expect( $0. baseAddress == originalPointer)
2520
+ }
2521
+ }
2449
2522
}
0 commit comments