[stdlib] On-demand unique contiguous Array storage

Lift the precondition that contiguous storage already exist on the use
of Array.withUnsafe[Mutable]PointerToElements.  Automatically guarantee
that storage is unique when using the mutable variants of these methods.

Also, fix some---essentially---inout aliasing violations in
test/stdlib/Unicode.swift that were revealed by the new careful
implementation of these methods.

Swift SVN r20339
This commit is contained in:
Dave Abrahams
2014-07-22 21:34:57 +00:00
parent 41dec5b58c
commit e570866bf6
5 changed files with 53 additions and 24 deletions

View File

@@ -354,26 +354,30 @@ extension _ArrayBuffer {
} }
} }
/// Call body(p), where p is a pointer to the underlying contiguous storage /// Call `body(p)`, where `p` is a pointer to the underlying
/// Requires: such contiguous storage exists or the buffer is empty /// contiguous storage. If no contiguous storage exists, it is
/// created on-demand.
public public
func withUnsafePointerToElements<R>( func withUnsafePointerToElements<R>(
body: (UnsafePointer<T>)->R body: (UnsafePointer<T>)->R
) -> R { ) -> R {
_precondition( if _isClassOrObjCExistential(T.self) {
elementStorage != nil || count == 0, if _nonNative {
"Array is bridging an opaque NSArray; can't get a pointer to the elements" indirect.replaceStorage(_copyCollectionToNativeArrayBuffer(self))
) }
let ret = body(elementStorage) }
let ret = body(self.elementStorage)
_fixLifetime(self) _fixLifetime(self)
return ret return ret
} }
/// Call `body(p)`, where `p` is a pointer to the underlying contiguous
/// storage. Requires: Contiguous storage already exists
public public
mutating func withUnsafeMutablePointerToElements<R>( mutating func withUnsafeMutablePointerToElements<R>(
body: (UnsafeMutablePointer<T>)->R body: (UnsafeMutablePointer<T>)->R
) -> R { ) -> R {
_precondition( _sanityCheck(
elementStorage != nil || count == 0, elementStorage != nil || count == 0,
"Array is bridging an opaque NSArray; can't get a pointer to the elements" "Array is bridging an opaque NSArray; can't get a pointer to the elements"
) )

View File

@@ -389,9 +389,6 @@ extension ${Self} {
extension ${Self} { extension ${Self} {
/// Call body(p), where p is a pointer to the ${Self}'s contiguous storage /// Call body(p), where p is a pointer to the ${Self}'s contiguous storage
%if Self != 'Array':
/// Requires: the Array's storage is not provided by an opaque NSArray
%end
public func withUnsafePointerToElements<R>( public func withUnsafePointerToElements<R>(
body: (UnsafePointer<T>) -> R body: (UnsafePointer<T>) -> R
) -> R { ) -> R {
@@ -401,7 +398,20 @@ extension ${Self} {
public mutating func withUnsafeMutablePointerToElements<R>( public mutating func withUnsafeMutablePointerToElements<R>(
body: (UnsafeMutablePointer<T>) -> R body: (UnsafeMutablePointer<T>) -> R
) -> R { ) -> R {
return _buffer.withUnsafeMutablePointerToElements(body) // Ensure unique storage
_arrayReserve(&_buffer, 0)
// Ensure that body can't invalidate the storage or its bounds by
// moving self into a temporary working array.
var work = ${Self}()
swap(&work, &self)
// Invoke the body
let ret = work._buffer.withUnsafeMutablePointerToElements(body)
// Put the working array back before returning.
swap(&work, &self)
return ret
} }
} }

View File

@@ -26,7 +26,7 @@ public struct UnsafeArrayGenerator<T>: GeneratorType, SequenceType {
return self return self
} }
var position, end: UnsafeMutablePointer<T> var position, end: UnsafePointer<T>
} }
%for Mutable in ('Mutable', ''): %for Mutable in ('Mutable', ''):
@@ -55,7 +55,7 @@ public struct Unsafe${Mutable}Array<T> : ${Mutable}CollectionType {
%end %end
} }
public init(start: UnsafeMutablePointer<T>, length: Int) { public init(start: Unsafe${Mutable}Pointer<T>, length: Int) {
_precondition(length >= 0, "Unsafe${Mutable}Array with negative length") _precondition(length >= 0, "Unsafe${Mutable}Array with negative length")
_position = start _position = start
_end = start + length _end = start + length
@@ -64,8 +64,12 @@ public struct Unsafe${Mutable}Array<T> : ${Mutable}CollectionType {
public func generate() -> UnsafeArrayGenerator<T> { public func generate() -> UnsafeArrayGenerator<T> {
return UnsafeArrayGenerator(position: _position, end: _end) return UnsafeArrayGenerator(position: _position, end: _end)
} }
public var elementStorage: Unsafe${Mutable}Pointer<T> {
return _position
}
var _position, _end: UnsafeMutablePointer<T> var _position, _end: Unsafe${Mutable}Pointer<T>
} }
extension Unsafe${Mutable}Array : DebugPrintable { extension Unsafe${Mutable}Array : DebugPrintable {

View File

@@ -252,6 +252,13 @@ func testCocoa() {
println(a.capacity >= 30) println(a.capacity >= 30)
// CHECK-NEXT: true // CHECK-NEXT: true
// Prove that we create contiguous storage for an opaque NSArray
a.withUnsafePointerToElements {
(p)->() in
println(p.memory)
// CHECK-NEXT: foo
}
} }
testCocoa() testCocoa()

View File

@@ -259,25 +259,29 @@ func additionalUtf16Tests() {
var u8: [UTF8.CodeUnit] = [ 0, 1, 2, 3, 4, 5 ] var u8: [UTF8.CodeUnit] = [ 0, 1, 2, 3, 4, 5 ]
var u16: [UTF16.CodeUnit] = [ 6, 7, 8, 9, 10, 11 ] var u16: [UTF16.CodeUnit] = [ 6, 7, 8, 9, 10, 11 ]
u16.withUnsafeMutablePointerToElements { u16.withUnsafeMutableStorage {
(p16)->() in (u16)->() in
u8.withUnsafeMutablePointerToElements { let p16 = u16.elementStorage
(p8)->() in
u8.withUnsafeMutableStorage {
(u8)->() in
let p8 = u8.elementStorage
// CHECK-NEXT: [ 0, 1, 2, 9, 10, 11 ] // CHECK-NEXT: [ 0, 1, 2, 9, 10, 11 ]
UTF16.copy(p8, destination: p16, count: 3) UTF16.copy(p8, destination: p16, count: 3)
println(u16) println(Array(u16))
// CHECK-NEXT: [ 9, 10, 11, 3, 4, 5 ] // CHECK-NEXT: [ 9, 10, 11, 3, 4, 5 ]
UTF16.copy(p16 + 3, destination: p8, count: 3) UTF16.copy(p16 + 3, destination: p8, count: 3)
println(u8) println(Array(u8))
// CHECK-NEXT: [ 0, 1, 2, 0, 1, 2 ] // CHECK-NEXT: [ 0, 1, 2, 0, 1, 2 ]
UTF16.copy(p16, destination: p16 + 3, count: 3) UTF16.copy(p16, destination: p16 + 3, count: 3)
println(u16) println(Array(u16))
// CHECK-NEXT: [ 9, 10, 11, 9, 10, 11 ] // CHECK-NEXT: [ 9, 10, 11, 9, 10, 11 ]
UTF16.copy(p8, destination: p8 + 3, count: 3) UTF16.copy(p8, destination: p8 + 3, count: 3)
println(u8) println(Array(u8))
} }
} }
@@ -2341,7 +2345,7 @@ StringCookedViews.test("UTF8ForContiguousUTF16") {
}, },
stopOnError: false) stopOnError: false)
backingStorage.withUnsafeMutablePointerToElements { backingStorage.withUnsafePointerToElements {
(ptr) -> () in (ptr) -> () in
let cfstring = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, let cfstring = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
ptr, backingStorage.count, kCFAllocatorNull) ptr, backingStorage.count, kCFAllocatorNull)