支持将 ContiguousBytes 转换为 Array 和 "not-Array" 的现代规范方法是什么?
What is the modern canonical way to support converting ContiguousBytes into both Array and "not-Array"?
例如,如果没有第二次重载,加载数组将产生“UnsafeRawBufferPointer.load 越界”。有没有办法在不重载的情况下处理这两种情况?
let bytes: [UInt8] = [1, 0, 1, 0]
bytes.load() as Int32 // 0x1_00_01
bytes.load() as [Int16] // [1, 1]
public extension ContiguousBytes {
func load<T>(_: T.Type = T.self) -> T {
withUnsafeBytes { [=12=].load(as: T.self) }
}
func load<Element>(_: [Element].Type = [Element].self) -> [Element] {
withUnsafeBytes { .init([=12=].bindMemory(to: Element.self)) }
}
}
关于有两个重载:很遗憾,您无法避免它们。 ContiguousBytes.withUnsafeBytes
使您可以访问实现 ContiguousBytes
的类型的底层 存储 ,因此 [UInt8].withUnsafeBytes
给出的缓冲区将是实际的数组实例用于 store 数据的缓冲区,而不是内存中指向 自身 的指针(例如它的长度、容量、存储指针等) .
您的第一个重载无法工作的原因是您将调用等效于:
withUnsafeBytes { rawBuffer in
rawBuffer.load(as: [Int16].self)
}
其中 load(as:)
试图读取 [1, 0, 1, 0]
就好像这些内容是单个 [Int16]
实例的长度、容量和缓冲区指针一样,但事实并非如此 — 它是只有 bytes
.
的 内容
您需要第二次重载才能创建一个包含 UnsafeRawBufferPointer
.
的 内容 的新数组
至于数组变体的实际实现,从 Swift 5.7 中的 SE-0333 Expand usability of withMemoryRebound 开始,正确的方法是使用 withMemoryRebound(to:_:)
,因为无条件如果缓冲区已经绑定到不同的类型,bindMemory(to:)
是不安全的。 withMemoryRebound(to:_:)
现在总是安全的,即使缓冲区已绑定,只要您满足对齐要求:
/// A raw buffer may represent memory that has been bound to a type.
/// If that is the case, then T
must be layout compatible with the
/// type to which the memory has been bound. This requirement does not
/// apply if the raw buffer represents memory that has not been bound
/// to any type.
这看起来像
func load<Element>(_: [Element].Type = [Element].self) -> [Element] {
withUnsafeBytes { rawBuffer in
rawBuffer.withMemoryRebound(to: Element.self) { typedBuffer in
Array(typedBuffer)
}
}
}
UnsafeRawBufferPointer.withMemoryRebound(to:_:)
将计算将原始字节绑定到 Element
的右步长,但请记住,这 仅对 有效如果原始缓冲区的对齐方式符合 Element
的对齐要求,则执行此操作 - 否则您将崩溃或触发未定义的行为。
例如,如果没有第二次重载,加载数组将产生“UnsafeRawBufferPointer.load 越界”。有没有办法在不重载的情况下处理这两种情况?
let bytes: [UInt8] = [1, 0, 1, 0]
bytes.load() as Int32 // 0x1_00_01
bytes.load() as [Int16] // [1, 1]
public extension ContiguousBytes {
func load<T>(_: T.Type = T.self) -> T {
withUnsafeBytes { [=12=].load(as: T.self) }
}
func load<Element>(_: [Element].Type = [Element].self) -> [Element] {
withUnsafeBytes { .init([=12=].bindMemory(to: Element.self)) }
}
}
关于有两个重载:很遗憾,您无法避免它们。 ContiguousBytes.withUnsafeBytes
使您可以访问实现 ContiguousBytes
的类型的底层 存储 ,因此 [UInt8].withUnsafeBytes
给出的缓冲区将是实际的数组实例用于 store 数据的缓冲区,而不是内存中指向 自身 的指针(例如它的长度、容量、存储指针等) .
您的第一个重载无法工作的原因是您将调用等效于:
withUnsafeBytes { rawBuffer in
rawBuffer.load(as: [Int16].self)
}
其中 load(as:)
试图读取 [1, 0, 1, 0]
就好像这些内容是单个 [Int16]
实例的长度、容量和缓冲区指针一样,但事实并非如此 — 它是只有 bytes
.
您需要第二次重载才能创建一个包含 UnsafeRawBufferPointer
.
至于数组变体的实际实现,从 Swift 5.7 中的 SE-0333 Expand usability of withMemoryRebound 开始,正确的方法是使用 withMemoryRebound(to:_:)
,因为无条件如果缓冲区已经绑定到不同的类型,bindMemory(to:)
是不安全的。 withMemoryRebound(to:_:)
现在总是安全的,即使缓冲区已绑定,只要您满足对齐要求:
/// A raw buffer may represent memory that has been bound to a type.
/// If that is the case, thenT
must be layout compatible with the
/// type to which the memory has been bound. This requirement does not
/// apply if the raw buffer represents memory that has not been bound
/// to any type.
这看起来像
func load<Element>(_: [Element].Type = [Element].self) -> [Element] {
withUnsafeBytes { rawBuffer in
rawBuffer.withMemoryRebound(to: Element.self) { typedBuffer in
Array(typedBuffer)
}
}
}
UnsafeRawBufferPointer.withMemoryRebound(to:_:)
将计算将原始字节绑定到 Element
的右步长,但请记住,这 仅对 有效如果原始缓冲区的对齐方式符合 Element
的对齐要求,则执行此操作 - 否则您将崩溃或触发未定义的行为。