Skip to content

Instantly share code, notes, and snippets.

@macshome
Created April 3, 2023 19:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save macshome/6ee9164ee6c5f294237c1b62fd481325 to your computer and use it in GitHub Desktop.
Save macshome/6ee9164ee6c5f294237c1b62fd481325 to your computer and use it in GitHub Desktop.
Swift playground for exploring memory layout
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