An open API service indexing awesome lists of open source software.

https://github.com/michaeleisel/fastcast

Fast protocol casting and conformance checking for Swift
https://github.com/michaeleisel/fastcast

Last synced: 8 months ago
JSON representation

Fast protocol casting and conformance checking for Swift

Awesome Lists containing this project

README

          

# FastCast

### Fast protocol conformance checking for Swift

## Introduction

This is an experimental library for faster casting by caching protocol conformance checks, so that unsuccessful `as?` casts are faster. The first time a conformance check for a [type, protocol] pair is done, e.g. `foo as? BarProtocol`, it can be costly if the lookup is not stored in Apple's protocol conformance cache already. If `foo` does indeed conform to `BarProtocol`, then the cost is unavoidable. There's no way to not go through Apple's machinery and be able to call `BarProtocol` functions on `foo`, at least not without relying on internal ABI details (as far as I know). However, if `foo` _does not_ conform to `BarProtocol`, then `foo as? BarProtocol` just returns nil and we won't be calling any `BarProtocol` functions on it. In this case, if we know that it doesn't conform via a cache (e.g., from a previous run where this check was done) then we can just return nil.

## Usage

```
FastCaster sharedFastCaster = FastCaster(cacheURL: ...)

func foo(object: T) {
if let decodable = sharedFastCaster.cast(object, as: Decodable.self) { // Replaces `if let decodable = object as? Decodable`
...
}
}
```

## How the cache works

### Determining if the app has changed in a way that may add/remove conformances, thus invalidating the cache

We want a cache that is stable between runs and is invalidated when the OS version or app binaries (the main binary and all dylibs) change at all. Roughly, the cache consists of a mapping of `(source type, target type)` to `bool`. To determine if the app binaries have changed, we can look at some hash of the binary. E.g., this can be the LC\_UUID for each binary (as long as the developer is using different UUIDs for each build, e.g. with `-Wl,-random_uuid`). Or, it can be the hashes computed for code signing.

One interesting topic is, can system binaries change even if the OS version doesn't, thus adding/removing conformances? If this is possible, then maybe we can either look at the UUIDs of all loaded Apple binaries, or just version metadata of them. Or, we could restric this cache to only work pairs of types from within the app.

### Doing a fast lookup at runtime using the cache file

We want a stable key for each type, be it the type we want to cast or the protocol type we want to cast it to, that won't change between runs and can be serialized. There are a number of keys that may possibly work:
- `String(reflecting: type)` (however, this is slow because it does protocol conformance checks internally)
- `_getMangledTypeName(type)` which is what it currently uses
- The pointer to the type metadata, as long as it's a pointer to an address in the binary, and with the base subtracted to account for ASLR (can type metadata exist outside the binary?)

It could also be desirable to use, say, both the mangled name and the pointer together if we want to be extra cautious.