Last active
April 20, 2021 08:07
-
-
Save chosa91/5d3a13c4448972a199d4893f34832ec7 to your computer and use it in GitHub Desktop.
Destructure dictionaries into tuple
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Source: https://twitter.com/NSExceptional/status/1383665312684335105 | |
// MARK: - Helpers | |
extension UnsafePointer { | |
var raw: UnsafeRawPointer { | |
return UnsafeRawPointer(self) | |
} | |
var mutable: UnsafeMutablePointer<Pointee> { | |
return UnsafeMutablePointer<Pointee>(mutating: self) | |
} | |
func buffer(n: Int) -> UnsafeBufferPointer<Pointee> { | |
return UnsafeBufferPointer(start: self, count: n) | |
} | |
} | |
struct Vector<Element> { | |
var element: Element | |
mutating func vector(n: Int) -> UnsafeBufferPointer<Element> { | |
return withUnsafePointer(to: &self) { | |
$0.withMemoryRebound(to: Element.self, capacity: 1) { start in | |
return start.buffer(n: n) | |
} | |
} | |
} | |
mutating func element(at i: Int) -> UnsafeMutablePointer<Element> { | |
return withUnsafePointer(to: &self) { | |
return $0.raw.assumingMemoryBound(to: Element.self).advanced(by: i).mutable | |
} | |
} | |
} | |
struct TupleMetadataLayout { | |
var _kind: Int | |
var numberOfElements: Int | |
var labelsString: UnsafeMutablePointer<CChar> | |
var elementVector: Vector<TupleElementLayout> | |
} | |
struct TupleElementLayout { | |
var type: Any.Type | |
var offset: Int | |
} | |
struct TupleMetadata { | |
let pointer: UnsafeMutablePointer<TupleMetadataLayout> | |
init(type: Any.Type) { | |
pointer = unsafeBitCast(type, to: UnsafeMutablePointer<TupleMetadataLayout>.self) | |
} | |
var type: Any.Type { | |
return unsafeBitCast(pointer, to: Any.Type.self) | |
} | |
func numberOfElements() -> Int { | |
return pointer.pointee.numberOfElements | |
} | |
func labels() -> [String] { | |
guard Int(bitPattern: pointer.pointee.labelsString) != 0 else { | |
return (0..<numberOfElements()).map { _ in "" } | |
} | |
var labels = String(cString: pointer.pointee.labelsString).components(separatedBy: " ") | |
labels.removeLast() | |
return labels | |
} | |
func elements() -> UnsafeBufferPointer<TupleElementLayout> { | |
return pointer.pointee.elementVector.vector(n: numberOfElements()) | |
} | |
func properties() -> [PropertyInfo] { | |
let names = labels() | |
let el = elements() | |
let num = numberOfElements() | |
var properties = [PropertyInfo]() | |
for i in 0..<num { | |
properties.append( | |
PropertyInfo( | |
name: names[i], | |
type: el[i].type, | |
isVar: true, | |
offset: el[i].offset, | |
ownerType: type | |
) | |
) | |
} | |
return properties | |
} | |
// mutating func toTypeInfo() -> TypeInfo { | |
// var info = TypeInfo(metadata: self) | |
// info.properties = properies() | |
// return info | |
// } | |
} | |
public struct PropertyInfo { | |
public let name: String | |
public let type: Any.Type | |
public let isVar: Bool | |
public let offset: Int | |
public let ownerType: Any.Type | |
} | |
// MARK: - Example | |
do { | |
let person: (name: String, age: Int) = try JSUM.decode( | |
from: [ | |
"name": "Bob", | |
"age": 30, | |
] | |
) | |
dump(person) | |
// ▿ (2 elements) | |
// - name: "Bob" | |
// - age: 30 | |
} catch { | |
print(error) | |
} | |
// MARK: - JSUM | |
enum JSUM { | |
static func decode<T>(from: [String: Any]) throws -> T { | |
let meta = TupleMetadata(type: T.self) | |
let props = meta.properties() | |
let mutableRawPtr = UnsafeMutableRawPointer.allocate( | |
byteCount: MemoryLayout<T>.size, | |
alignment: MemoryLayout<T>.alignment | |
) | |
defer { mutableRawPtr.deallocate() } | |
props.forEach { p in | |
let value = from[p.name] | |
withUnsafePointer(to: value) { | |
mutableRawPtr.advanced(by: p.offset).copyMemory(from: $0, byteCount: MemoryLayout.size(ofValue: value)) | |
} | |
} | |
return mutableRawPtr.load(as: T.self) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment