#if canImport(Darwin)
import func Darwin.memcmp
#elseif canImport(Glibc)
import func Glibc.memcmp
#elseif canImport(WinSDK)
import func WinSDK.memcmp
#elseif canImport(Android)
import func Android.memcmp
#endif
/// A type similar to KeyPath, but that can be constructed at run time given
/// an instance of a struct, and the value of the desired property.
///
/// Construction fails if the property's in-memory representation is not unique within the
/// struct. SwiftCrossUI only uses `DynamicKeyPath` in situations where it is
/// highly likely for properties to have unique in-memory representations, such
/// as when properties have internal storage pointers.
struct DynamicKeyPath {
/// The property's offset within instances of `Base`.
var offset: Int
/// Constructs a key path given an instance of the base type, and the
/// value of the desired property.
///
/// The initializer will search through the base instance's in-memory
/// representation to find the unique offset that matches the representation
/// of the given property value. If such an offset can't be found or isn't
/// unique, then the initialiser returns `nil`.
///
/// - Parameters:
/// - value: The value to construct a key path to.
/// - base: The value to construct a key path from.
/// - label: The property's label, if any. Only used for debugging.
init?(
forProperty value: Value,
of base: Base,
label: String? = nil
) {
let propertyAlignment = MemoryLayout.alignment
let propertySize = MemoryLayout.size
let baseStructSize = MemoryLayout.size
var index = 0
var matches: [Int] = []
while index + propertySize <= baseStructSize {
let isMatch =
withUnsafeBytes(of: base) { viewPointer in
withUnsafeBytes(of: value) { valuePointer in
let viewBytes = UnsafeRawBufferPointer(start: viewPointer.baseAddress!.advanced(by: index), count: propertySize)
return viewBytes.elementsEqual(valuePointer)
}
}
if isMatch {
matches.append(index)
}
index += propertyAlignment
}
guard let offset = matches.first else {
logger.warning(
"no offset found for dynamic property",
metadata: ["property": "\(label ?? "")"]
)
return nil
}
guard matches.count == 1 else {
logger.warning(
"multiple offsets found for dynamic property",
metadata: ["property": "\(label ?? "")"]
)
return nil
}
self.offset = offset
}
/// Gets the property's value on the given instance.
///
/// - Parameter base: The instance to get the property on.
/// - Returns: This property's value on `base`.
func get(_ base: Base) -> Value {
withUnsafeBytes(of: base) { buffer in
buffer.baseAddress!.advanced(by: offset)
.assumingMemoryBound(to: Value.self)
.pointee
}
}
/// Sets the property's value to a new value on the given instance.
///
/// - Parameters:
/// - base: The instance to set the property on.
/// - newValue: The new value of the property.
func set(_ base: inout Base, _ newValue: Value) {
withUnsafeMutableBytes(of: &base) { buffer in
buffer.baseAddress!.advanced(by: offset)
.assumingMemoryBound(to: Value.self)
.pointee = newValue
}
}
}