Skip to content

Commit c2dfb94

Browse files
committed
[stdlib] update existing withMemoryRebound
- updated documentation and adjust implementations
1 parent 9438cf6 commit c2dfb94

File tree

2 files changed

+121
-62
lines changed

2 files changed

+121
-62
lines changed

stdlib/public/core/UnsafeBufferPointer.swift.gyb

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -654,8 +654,16 @@ extension Unsafe${Mutable}BufferPointer {
654654
/// the same memory as an unrelated type without first rebinding the memory
655655
/// is undefined.
656656
///
657-
/// The entire region of memory referenced by this buffer must be initialized.
657+
/// The number of instances of `T` referenced by the rebound buffer may be
658+
/// different than the number of instances of `Element` referenced by the
659+
/// original buffer. The number of instances of `T` will be calculated
660+
/// at runtime.
658661
///
662+
/// Any instance of `T` within the re-bound region may be initialized or
663+
/// uninitialized. If a given instance of `T` overlaps with memory previously
664+
/// bound to an uninitialized `Pointee`, it shall be considered uninitialized
665+
/// when executing `body`.
666+
///
659667
/// Because this buffer's memory is no longer bound to its `Element` type
660668
/// while the `body` closure executes, do not access memory using the
661669
/// original buffer from within `body`. Instead, use the `body` closure's
@@ -666,39 +674,57 @@ extension Unsafe${Mutable}BufferPointer {
666674
/// `Element` type.
667675
///
668676
/// - Note: Only use this method to rebind the buffer's memory to a type
669-
/// with the same size and stride as the currently bound `Element` type.
670-
/// To bind a region of memory to a type that is a different size, convert
671-
/// the buffer to a raw buffer and use the `bindMemory(to:)` method.
677+
/// that is layout compatible with the currently bound `Element` type.
678+
/// The stride of the temporary type (`T`) may be an integer multiple
679+
/// or a whole fraction of `Element`'s stride.
680+
/// To bind a region of memory to a type that does not match these
681+
/// requirements, convert the buffer to a raw buffer and use the
682+
/// `bindMemory(to:)` method.
683+
/// If `T` and `Element` have different alignments, this buffer's
684+
/// `baseAddress` must be aligned with the larger of the two alignments.
672685
///
673686
/// - Parameters:
674687
/// - type: The type to temporarily bind the memory referenced by this
675-
/// buffer. The type `T` must have the same size and be layout compatible
688+
/// buffer. The type `T` must be layout compatible
676689
/// with the pointer's `Element` type.
677690
/// - body: A closure that takes a ${Mutable.lower()} typed buffer to the
678-
/// same memory as this buffer, only bound to type `T`. The buffer argument
679-
/// contains the same number of complete instances of `T` as the original
680-
/// buffer’s `count`. The closure's buffer argument is valid only for the
681-
/// duration of the closure's execution. If `body` has a return value, that
682-
/// value is also used as the return value for the `withMemoryRebound(to:_:)`
691+
/// same memory as this buffer, only bound to type `T`. The buffer
692+
/// parameter contains a number of complete instances of `T` based
693+
/// on the capacity of the original buffer and the stride of `Element`.
694+
/// The closure's buffer argument is valid only for the duration of the
695+
/// closure's execution. If `body` has a return value, that value
696+
/// is also used as the return value for the `withMemoryRebound(to:_:)`
683697
/// method.
698+
/// - buffer: The buffer temporarily bound to `T`.
684699
/// - Returns: The return value, if any, of the `body` closure parameter.
685700
@inlinable // unsafe-performance
701+
@_alwaysEmitIntoClient
686702
public func withMemoryRebound<T, Result>(
687-
to type: T.Type, _ body: (${Self}<T>) throws -> Result
703+
to type: T.Type,
704+
_ body: (_ buffer: ${Self}<T>) throws -> Result
688705
) rethrows -> Result {
689-
if let base = _position {
690-
_debugPrecondition(MemoryLayout<Element>.stride == MemoryLayout<T>.stride)
691-
Builtin.bindMemory(base._rawValue, count._builtinWordValue, T.self)
692-
defer {
693-
Builtin.bindMemory(base._rawValue, count._builtinWordValue, Element.self)
694-
}
695-
696-
return try body(${Self}<T>(
697-
start: Unsafe${Mutable}Pointer<T>(base._rawValue), count: count))
706+
guard let base = _position?._rawValue else {
707+
return try body(.init(start: nil, count: 0))
698708
}
699-
else {
700-
return try body(${Self}<T>(start: nil, count: 0))
709+
710+
let newCount: Int
711+
if MemoryLayout<T>.stride == MemoryLayout<Element>.stride {
712+
newCount = count
713+
_debugPrecondition(
714+
MemoryLayout<T>.alignment == MemoryLayout<Element>.alignment
715+
)
716+
} else {
717+
newCount = count * MemoryLayout<Element>.stride / MemoryLayout<T>.stride
718+
_debugPrecondition(
719+
Int(bitPattern: .init(base)) & (MemoryLayout<T>.alignment-1) == 0 &&
720+
MemoryLayout<T>.stride > MemoryLayout<Element>.stride
721+
? MemoryLayout<T>.stride % MemoryLayout<Element>.stride == 0
722+
: MemoryLayout<Element>.stride % MemoryLayout<T>.stride == 0
723+
)
701724
}
725+
let binding = Builtin.bindMemory(base, newCount._builtinWordValue, T.self)
726+
defer { Builtin.rebindMemory(base, binding) }
727+
return try body(.init(start: .init(base), count: newCount))
702728
}
703729

704730
/// A pointer to the first element of the buffer.

stdlib/public/core/UnsafePointer.swift

Lines changed: 73 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@ public struct UnsafePointer<Pointee>: _Pointer {
243243
}
244244
}
245245

246-
/// Executes the given closure while temporarily binding the specified number
247-
/// of instances to the given type.
246+
/// Executes the given closure while temporarily binding memory to
247+
/// the specified number of instances of type `T`.
248248
///
249249
/// Use this method when you have a pointer to memory bound to one type and
250250
/// you need to access that memory as instances of another type. Accessing
@@ -253,15 +253,20 @@ public struct UnsafePointer<Pointee>: _Pointer {
253253
/// the same memory as an unrelated type without first rebinding the memory
254254
/// is undefined.
255255
///
256-
/// The region of memory starting at this pointer and covering `count`
257-
/// instances of the pointer's `Pointee` type must be initialized.
256+
/// The region of memory that starts at this pointer and covers `count`
257+
/// strides of `T` instances must be bound to `Pointee`.
258+
/// Any instance of `T` within the re-bound region may be initialized or
259+
/// uninitialized. If a given instance of `T` overlaps with memory previously
260+
/// bound to an uninitialized `Pointee`, it shall be considered uninitialized
261+
/// when executing `body`.
258262
///
259263
/// The following example temporarily rebinds the memory of a `UInt64`
260264
/// pointer to `Int64`, then accesses a property on the signed integer.
261265
///
262266
/// let uint64Pointer: UnsafePointer<UInt64> = fetchValue()
263-
/// let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in
264-
/// return ptr.pointee < 0
267+
/// let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self,
268+
/// capacity: 1) {
269+
/// return $0.pointee < 0
265270
/// }
266271
///
267272
/// Because this pointer's memory is no longer bound to its `Pointee` type
@@ -274,31 +279,43 @@ public struct UnsafePointer<Pointee>: _Pointer {
274279
/// `Pointee` type.
275280
///
276281
/// - Note: Only use this method to rebind the pointer's memory to a type
277-
/// with the same size and stride as the currently bound `Pointee` type.
278-
/// To bind a region of memory to a type that is a different size, convert
279-
/// the pointer to a raw pointer and use the `bindMemory(to:capacity:)`
280-
/// method.
282+
/// that is layout compatible with the `Pointee` type. The stride of the
283+
/// temporary type (`T`) may be an integer multiple or a whole fraction
284+
/// of `Pointee`'s stride, for example to point to one element of
285+
/// an aggregate.
286+
/// To bind a region of memory to a type that does not match these
287+
/// requirements, convert the pointer to a raw pointer and use the
288+
/// `bindMemory(to:)` method.
289+
/// If `T` and `Pointee` have different alignments, this pointer
290+
/// must be aligned with the larger of the two alignments.
281291
///
282292
/// - Parameters:
283293
/// - type: The type to temporarily bind the memory referenced by this
284-
/// pointer. The type `T` must be the same size and be layout compatible
294+
/// pointer. The type `T` must be layout compatible
285295
/// with the pointer's `Pointee` type.
286-
/// - count: The number of instances of `Pointee` to bind to `type`.
287-
/// - body: A closure that takes a typed pointer to the
296+
/// - count: The number of instances of `T` in the re-bound region.
297+
/// - body: A closure that takes a typed pointer to the
288298
/// same memory as this pointer, only bound to type `T`. The closure's
289299
/// pointer argument is valid only for the duration of the closure's
290300
/// execution. If `body` has a return value, that value is also used as
291301
/// the return value for the `withMemoryRebound(to:capacity:_:)` method.
302+
/// - pointer: The pointer temporarily bound to `T`.
292303
/// - Returns: The return value, if any, of the `body` closure parameter.
293304
@inlinable
294-
public func withMemoryRebound<T, Result>(to type: T.Type, capacity count: Int,
295-
_ body: (UnsafePointer<T>) throws -> Result
305+
@_alwaysEmitIntoClient
306+
public func withMemoryRebound<T, Result>(
307+
to type: T.Type, capacity count: Int,
308+
_ body: (_ pointer: UnsafePointer<T>) throws -> Result
296309
) rethrows -> Result {
297-
Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self)
298-
defer {
299-
Builtin.bindMemory(_rawValue, count._builtinWordValue, Pointee.self)
300-
}
301-
return try body(UnsafePointer<T>(_rawValue))
310+
_debugPrecondition(
311+
Int(bitPattern: .init(_rawValue)) & (MemoryLayout<T>.alignment-1) == 0 &&
312+
MemoryLayout<Pointee>.stride > MemoryLayout<T>.stride
313+
? MemoryLayout<Pointee>.stride % MemoryLayout<T>.stride == 0
314+
: MemoryLayout<T>.stride % MemoryLayout<Pointee>.stride == 0
315+
)
316+
let binding = Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self)
317+
defer { Builtin.rebindMemory(_rawValue, binding) }
318+
return try body(.init(_rawValue))
302319
}
303320

304321
/// Accesses the pointee at the specified offset from this pointer.
@@ -894,8 +911,8 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
894911
return UnsafeMutableRawPointer(self)
895912
}
896913

897-
/// Executes the given closure while temporarily binding the specified number
898-
/// of instances to the given type.
914+
/// Executes the given closure while temporarily binding memory to
915+
/// the specified number of instances of the given type.
899916
///
900917
/// Use this method when you have a pointer to memory bound to one type and
901918
/// you need to access that memory as instances of another type. Accessing
@@ -904,15 +921,19 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
904921
/// the same memory as an unrelated type without first rebinding the memory
905922
/// is undefined.
906923
///
907-
/// The region of memory starting at this pointer and covering `count`
908-
/// instances of the pointer's `Pointee` type must be initialized.
924+
/// The region of memory that starts at this pointer and covers `count`
925+
/// strides of `T` instances must be bound to `Pointee`.
926+
/// Any instance of `T` within the re-bound region may be initialized or
927+
/// uninitialized. If a given instance of `T` overlaps with memory previously
928+
/// bound to an uninitialized `Pointee`, it shall be considered uninitialized
929+
/// when executing `body`.
909930
///
910931
/// The following example temporarily rebinds the memory of a `UInt64`
911-
/// pointer to `Int64`, then accesses a property on the signed integer.
932+
/// pointer to `Int64`, then modifies the signed integer.
912933
///
913934
/// let uint64Pointer: UnsafeMutablePointer<UInt64> = fetchValue()
914-
/// let isNegative = uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in
915-
/// return ptr.pointee < 0
935+
/// uint64Pointer.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in
936+
/// ptr.pointee.negate()
916937
/// }
917938
///
918939
/// Because this pointer's memory is no longer bound to its `Pointee` type
@@ -925,31 +946,43 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
925946
/// `Pointee` type.
926947
///
927948
/// - Note: Only use this method to rebind the pointer's memory to a type
928-
/// with the same size and stride as the currently bound `Pointee` type.
929-
/// To bind a region of memory to a type that is a different size, convert
930-
/// the pointer to a raw pointer and use the `bindMemory(to:capacity:)`
931-
/// method.
949+
/// that is layout compatible with the `Pointee` type. The stride of the
950+
/// temporary type (`T`) may be an integer multiple or a whole fraction
951+
/// of `Pointee`'s stride, for example to point to one element of
952+
/// an aggregate.
953+
/// To bind a region of memory to a type that does not match these
954+
/// requirements, convert the pointer to a raw pointer and use the
955+
/// `bindMemory(to:)` method.
956+
/// If `T` and `Pointee` have different alignments, this pointer
957+
/// must be aligned with the larger of the two alignments.
932958
///
933959
/// - Parameters:
934960
/// - type: The type to temporarily bind the memory referenced by this
935-
/// pointer. The type `T` must be the same size and be layout compatible
961+
/// pointer. The type `T` must be layout compatible
936962
/// with the pointer's `Pointee` type.
937-
/// - count: The number of instances of `Pointee` to bind to `type`.
963+
/// - count: The number of instances of `T` in the re-bound region.
938964
/// - body: A closure that takes a mutable typed pointer to the
939965
/// same memory as this pointer, only bound to type `T`. The closure's
940966
/// pointer argument is valid only for the duration of the closure's
941967
/// execution. If `body` has a return value, that value is also used as
942968
/// the return value for the `withMemoryRebound(to:capacity:_:)` method.
969+
/// - pointer: The pointer temporarily bound to `T`.
943970
/// - Returns: The return value, if any, of the `body` closure parameter.
944971
@inlinable
945-
public func withMemoryRebound<T, Result>(to type: T.Type, capacity count: Int,
946-
_ body: (UnsafeMutablePointer<T>) throws -> Result
972+
@_alwaysEmitIntoClient
973+
public func withMemoryRebound<T, Result>(
974+
to type: T.Type, capacity count: Int,
975+
_ body: (_ pointer: UnsafeMutablePointer<T>) throws -> Result
947976
) rethrows -> Result {
948-
Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self)
949-
defer {
950-
Builtin.bindMemory(_rawValue, count._builtinWordValue, Pointee.self)
951-
}
952-
return try body(UnsafeMutablePointer<T>(_rawValue))
977+
_debugPrecondition(
978+
Int(bitPattern: .init(_rawValue)) & (MemoryLayout<T>.alignment-1) == 0 &&
979+
MemoryLayout<Pointee>.stride > MemoryLayout<T>.stride
980+
? MemoryLayout<Pointee>.stride % MemoryLayout<T>.stride == 0
981+
: MemoryLayout<T>.stride % MemoryLayout<Pointee>.stride == 0
982+
)
983+
let binding = Builtin.bindMemory(_rawValue, count._builtinWordValue, T.self)
984+
defer { Builtin.rebindMemory(_rawValue, binding) }
985+
return try body(.init(_rawValue))
953986
}
954987

955988
/// Accesses the pointee at the specified offset from this pointer.

0 commit comments

Comments
 (0)