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

View File

@@ -389,9 +389,6 @@ extension ${Self} {
extension ${Self} {
/// 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>(
body: (UnsafePointer<T>) -> R
) -> R {
@@ -401,7 +398,20 @@ extension ${Self} {
public mutating func withUnsafeMutablePointerToElements<R>(
body: (UnsafeMutablePointer<T>) -> 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
}
var position, end: UnsafeMutablePointer<T>
var position, end: UnsafePointer<T>
}
%for Mutable in ('Mutable', ''):
@@ -55,7 +55,7 @@ public struct Unsafe${Mutable}Array<T> : ${Mutable}CollectionType {
%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")
_position = start
_end = start + length
@@ -65,7 +65,11 @@ public struct Unsafe${Mutable}Array<T> : ${Mutable}CollectionType {
return UnsafeArrayGenerator(position: _position, end: _end)
}
var _position, _end: UnsafeMutablePointer<T>
public var elementStorage: Unsafe${Mutable}Pointer<T> {
return _position
}
var _position, _end: Unsafe${Mutable}Pointer<T>
}
extension Unsafe${Mutable}Array : DebugPrintable {

View File

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

View File

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