Skip to content

Instantly share code, notes, and snippets.

@mikeash
Created June 13, 2015 04:18
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save mikeash/0ac60ce46f40e9c825a3 to your computer and use it in GitHub Desktop.
Save mikeash/0ac60ce46f40e9c825a3 to your computer and use it in GitHub Desktop.
import Foundation
protocol Serializable {
static func deserializeInto(bytePtr: UnsafeMutablePointer<UInt8>, bytes: ArraySlice<UInt8>) -> ArraySlice<UInt8>
}
extension Serializable {
typealias WorkaroundSelf = Self
static func size() -> Int {
return sizeof(Self.self)
}
static func align() -> Int {
return alignof(Self.self)
}
static func deserializeInto(bytePtr: UnsafeMutablePointer<UInt8>, bytes: ArraySlice<UInt8>) -> ArraySlice<UInt8> {
let typedPtr = UnsafeMutablePointer<WorkaroundSelf>(bytePtr)
let mirror = reflect(typedPtr.memory)
if mirror.count > 0 {
var remainingBytes = bytes
var cursor = 0
for i in 0..<mirror.count {
let child = mirror[i].1
guard let serializableType = child.valueType as? Serializable.Type else { preconditionFailure("All members must be serializable") }
let size = serializableType.size()
let align = serializableType.align()
let misalignment = cursor % align
if misalignment != 0 {
cursor += align - misalignment
}
let ptr = bytePtr + cursor
remainingBytes = serializableType.deserializeInto(ptr, bytes: remainingBytes)
cursor += size
}
return remainingBytes
} else {
for i in 0..<size() {
bytePtr[i] = bytes[i]
}
return bytes[size()..<bytes.endIndex]
}
}
static func r() {}
static func deserialize(bytes: [UInt8]) -> Self {
return deserialize(ArraySlice(bytes)).0
}
static func deserialize(bytes: ArraySlice<UInt8>) -> (Self, ArraySlice<UInt8>) {
let size = sizeof(Self.self)
var backingData = [UInt8](count: size, repeatedValue: 0)
return backingData.withUnsafeMutableBufferPointer({ (inout buffer: UnsafeMutableBufferPointer<UInt8>) -> (WorkaroundSelf, ArraySlice<UInt8>) in
let bytePtr = buffer.baseAddress
let remainder = deserializeInto(bytePtr, bytes: ArraySlice(bytes))
let typedPtr = UnsafeMutablePointer<WorkaroundSelf>(bytePtr)
return (typedPtr.memory, remainder)
})
}
}
extension Int64: Serializable {}
extension Int32: Serializable {}
extension String: Serializable {
static func deserializeInto(bytePtr: UnsafeMutablePointer<UInt8>, bytes: ArraySlice<UInt8>) -> ArraySlice<UInt8> {
let (length, remainder) = Int64.deserialize(bytes)
let utf8Data = remainder[0..<Int(length)]
let string = String(bytes: utf8Data, encoding: NSUTF8StringEncoding)!
let finalRemainder = remainder[Int(length)..<remainder.endIndex]
let stringPointer = UnsafeMutablePointer<String>(bytePtr)
stringPointer.initialize(string)
return finalRemainder
}
}
struct TestStruct: Serializable {
let x: Int32
let y: Int64
let z: String
}
let bytes: [UInt8] = [1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100]
print(TestStruct.deserialize(bytes))
// prints: SerializationExperiment.TestStruct(x: 1, y: 2, z: "Hello, world")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment