@@ -567,6 +567,84 @@ extension _ArrayBuffer {
567567 }
568568 }
569569
570+ @inlinable @_alwaysEmitIntoClient
571+ static var associationKey : UnsafeRawPointer {
572+ //We never dereference this, we just need an address to use as a unique key
573+ UnsafeRawPointer ( Builtin . addressof ( & _swiftEmptyArrayStorage) )
574+ }
575+
576+ @inlinable @_alwaysEmitIntoClient
577+ internal func getAssociatedBuffer( ) -> _ContiguousArrayBuffer < Element > ? {
578+ let getter = unsafeBitCast (
579+ getGetAssociatedObjectPtr ( ) ,
580+ to: ( @convention( c) (
581+ AnyObject,
582+ UnsafeRawPointer
583+ ) - > UnsafeRawPointer? ) . self
584+ )
585+ if let assocPtr = getter (
586+ _storage. objCInstance,
587+ _ArrayBuffer. associationKey
588+ ) {
589+ let buffer = assocPtr. loadUnaligned (
590+ as: _ContiguousArrayStorage< Element> . self
591+ )
592+ return _ContiguousArrayBuffer ( buffer)
593+ }
594+ return nil
595+ }
596+
597+ @inlinable @_alwaysEmitIntoClient
598+ internal func setAssociatedBuffer( _ buffer: _ContiguousArrayBuffer < Element > ) {
599+ let setter = unsafeBitCast ( getSetAssociatedObjectPtr ( ) , to: ( @convention( c) (
600+ AnyObject,
601+ UnsafeRawPointer,
602+ AnyObject? ,
603+ UInt
604+ ) - > Void) . self)
605+ setter (
606+ _storage. objCInstance,
607+ _ArrayBuffer. associationKey,
608+ buffer. _storage,
609+ 1 //OBJC_ASSOCIATION_RETAIN_NONATOMIC
610+ )
611+ }
612+
613+ @_alwaysEmitIntoClient @inline ( never)
614+ internal func withUnsafeBufferPointer_nonNative< R, E> (
615+ _ body: ( UnsafeBufferPointer < Element > ) throws ( E ) -> R
616+ ) throws ( E) -> R {
617+ let unwrapped : _ContiguousArrayBuffer < Element >
618+ // libobjc already provides the necessary memory barriers for
619+ // double checked locking to be safe, per comments on
620+ // https://github.com/swiftlang/swift/pull/75148
621+ if let associatedBuffer = getAssociatedBuffer ( ) {
622+ unwrapped = associatedBuffer
623+ } else {
624+ let lock = _storage. objCInstance
625+ objc_sync_enter ( lock)
626+ var associatedBuffer = getAssociatedBuffer ( )
627+ if let associatedBuffer {
628+ unwrapped = associatedBuffer
629+ } else {
630+ associatedBuffer = ContiguousArray ( self ) . _buffer
631+ unwrapped = associatedBuffer. unsafelyUnwrapped
632+ setAssociatedBuffer ( unwrapped)
633+ }
634+ defer { _fixLifetime ( unwrapped) }
635+ objc_sync_exit ( lock)
636+ }
637+ return try body (
638+ UnsafeBufferPointer (
639+ start: unwrapped. firstElementAddress,
640+ count: unwrapped. count
641+ )
642+ )
643+ }
644+
645+ /// Call `body(p)`, where `p` is an `UnsafeBufferPointer` over the
646+ /// underlying contiguous storage. If no such storage exists, it is
647+ /// created on-demand.
570648 // Superseded by the typed-throws version of this function, but retained
571649 // for ABI reasons.
572650 @usableFromInline
@@ -594,7 +672,7 @@ extension _ArrayBuffer {
594672 return try body (
595673 UnsafeBufferPointer ( start: firstElementAddress, count: count) )
596674 }
597- return try ContiguousArray ( self ) . withUnsafeBufferPointer ( body)
675+ return try withUnsafeBufferPointer_nonNative ( body)
598676 }
599677
600678 // Superseded by the typed-throws version of this function, but retained
0 commit comments