Created
April 3, 2023 19:20
-
-
Save macshome/6ee9164ee6c5f294237c1b62fd481325 to your computer and use it in GitHub Desktop.
Swift playground for exploring memory layout
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
import Foundation | |
// Playground for exploring memory layout in Swift. | |
// A simple struct | |
struct Foo { | |
let a: Int? | |
let b: Int? | |
var isTrue = true | |
} | |
// Two instances | |
var bar = Foo(a: nil, b: 123) | |
var baz = Foo(a: 100, b: Int.max, isTrue: false) | |
// Always use the static values when asking about a type. | |
MemoryLayout<Foo>.size //26 | |
MemoryLayout<Foo>.stride //32 | |
MemoryLayout<Foo>.alignment //8 | |
// The layout of the structs are the same, regardless if the values are nil or not. | |
// The size of the struct depends on the data inside it. | |
MemoryLayout.size(ofValue: bar) //26 | |
MemoryLayout.stride(ofValue: bar) //32 | |
MemoryLayout.alignment(ofValue: bar) //8 | |
MemoryLayout.size(ofValue: baz) //26 | |
MemoryLayout.stride(ofValue: baz) //32 | |
MemoryLayout.alignment(ofValue: baz) //8 | |
// Use the offset to find where inside the bytes a value is located. | |
// This shows the byte at which each value is stored. | |
MemoryLayout<Foo>.offset(of: \Foo.a) //0 | |
MemoryLayout<Foo>.offset(of: \Foo.b) //16 | |
MemoryLayout<Foo>.offset(of: \Foo.isTrue) //25 | |
// Always use the stride when getting the byte count of a type. | |
let barBytes = Data(bytes: &bar, | |
count: MemoryLayout<Foo>.stride) //32 bytes | |
// You can use subscript to read bytes from the data. Each byte is a UInt8 value. | |
barBytes[0] //0 | |
barBytes[16] //123 | |
barBytes[25] //1 | |
// Don't use size for the count when manually looking inside bytes. | |
// You might end up starting to read the next object too early. | |
let bazBytes = Data(bytes: &baz, | |
count: MemoryLayout<Foo>.stride) //32 bytes | |
bazBytes[0] //100 | |
bazBytes[16] //255 | |
bazBytes[25] //0 | |
// Int on Swift is 64-bits so it is larger than one byte can hold. | |
bazBytes[16] //255 | |
baz.b //9,223,372,036,854,775,807 | |
// Because of that you need to read all 8 bytes of the storage. | |
// Slice out just the bytes for `baz.b` as a Data chunk and convert to an int. | |
let bBytes = bazBytes[16...23] //8 bytes | |
// Swift Int is also little endian. To convert bytes back to an Int we need to specify that. | |
Int(littleEndian: bBytes.withUnsafeBytes { $0.pointee } ) //9,223,372,036,854,775,807 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment