Cairn · module

Cairn.UI.Images

lean/Cairn/UI/Images.lean

Images: what an app knows of the images it shows

An app keeps one Images in its state. load asks the host for a source (Request.loadImage) and remembers which request id is the source's latest; receive takes the reply to that id and records the size or the failure. A reply to an earlier load of the same source, or to an id this table never issued, changes nothing (receive_stale, load_supersedes), so a slow first load can't overwrite a reload that answered first.

The view reads state for each source and builds Node.image with it: the node's size is the app's choice and the same in every state (Layout.measure_image).

Imports Cairn.UI.Node, Cairn.Input

Order

Structures 3

structure

Cairn.Images

lean/Cairn/UI/Images.lean:21
Images : Type
Fields
known : List (String × ImageState)

What is known of each source; a source not listed is loading.

pending : List (Nat × String)

Each source's latest load: request id and source.

Instances

Repr Inhabited

structure

Cairn.Images.Cache.Sys

lean/Cairn/UI/Images.lean:396
Sys : Type
Fields
t : Images
has : Bool

Whether the host holds pixels for each source.

q : List Note

Messages to the app, the next one first.

found : List (Nat × String)

Loads the host has held pixels for, which it may answer ready.

asked : List (Nat × String)

Every load the app made: id and source.

next : Nat

The id the app's next load gets.

structure

Cairn.Images.Cache.Inv

lean/Cairn/UI/Images.lean:451
Inv (s : Sys) : Prop

What every reachable state keeps.

Fields
fresh : p.fst < s.next
unique : a = b
pendingAsked : p ∈ s.asked
foundAsked : p ∈ s.asked
readyFound : ∃ src, (id, src) ∈ s.found
held : s.has src = true ∨ Note.evicted src ∈ s.q

An image the table takes for ready is held, or its eviction is on its way.

heldPending : s.has src = true ∨ Note.evicted src ∈ s.q

So is one whose pending load the host answered or may answer ready.

Inductive types 3

inductive

Cairn.Images.Cache.Note

lean/Cairn/UI/Images.lean:392
Note : Type

A message to the app.

Constructors
reply (id : Nat) (r : Reply) : Note
evicted (source : String) : Note
inductive

Cairn.Images.Cache.Step

lean/Cairn/UI/Images.lean:416
Step : Sys → Sys → Prop
Constructors
load (s : Sys) (src : String) (reload : Bool) :
  Step s
    { t := (s.t.load s.next src reload).fst, has := s.has, q := s.q, found := s.found,
      asked := (s.next, src) :: s.asked, next := s.next + 1 }

The app loads src under its next id.

cancel (s : Sys) (t : Images) (hk : t.known = s.t.known)
  (hp : ∀ (p : Nat × String), p ∈ t.pending → p ∈ s.t.pending) :
  Step s
    { t := t, has := s.has, q := s.q, found := s.found, asked := s.asked, next := s.next }

The app stops waiting for some loads (cancel, cancelWhere, cancelAll).

find (s : Sys) (id : Nat) (src : String) (h : (id, src) ∈ s.asked) :
  Step s
    { t := s.t, has := setHas s.has src true, q := s.q, found := (id, src) :: s.found,
      asked := s.asked, next := s.next }

The host holds pixels for a load the app made (read and decoded, or already kept).

ready (s : Sys) (id : Nat) (src : String) (w h : Nat)
  (hf : (id, src) ∈ s.found) :
  Step s
    { t := s.t, has := s.has, q := s.q ++ [Note.reply id (Reply.image w h)],
      found := s.found, asked := s.asked, next := s.next }

The host answers ready a load it held the pixels for.

other (s : Sys) (id : Nat) (r : Reply)
  (hr : ∀ (w h : Nat), r ≠ Reply.image w h) :
  Step s
    { t := s.t, has := s.has, q := s.q ++ [Note.reply id r], found := s.found,
      asked := s.asked, next := s.next }

Any other answer, to any id.

evict (s : Sys) (src : String) :
  Step s
    { t := s.t, has := setHas s.has src false, q := s.q ++ [Note.evicted src],
      found := s.found, asked := s.asked, next := s.next }

The host drops src's pixels, then says so.

hearReply (s : Sys) (id : Nat) (r : Reply) (rest : List Note)
  (hq : s.q = Note.reply id r :: rest) :
  Step s
    { t := s.t.receive id r, has := s.has, q := rest, found := s.found, asked := s.asked,
      next := s.next }

The app hears a reply.

drop (s : Sys) (id : Nat) (r : Reply) (rest : List Note)
  (hq : s.q = Note.reply id r :: rest) :
  Step s
    { t := s.t, has := s.has, q := rest, found := s.found, asked := s.asked,
      next := s.next }

The runtime swallows a reply (to a cancelled request).

hearEvicted (s : Sys) (src : String) (rest : List Note)
  (hq : s.q = Note.evicted src :: rest) :
  Step s
    { t := (s.t.evicted src).fst, has := s.has, q := rest, found := s.found,
      asked := s.asked, next := s.next }

The app hears that src was evicted.

inductive

Cairn.Images.Cache.Reach

lean/Cairn/UI/Images.lean:446
Reach : Sys → Prop
Constructors
init (has : String → Bool) : Reach (init has)
step {s s' : Sys} : Reach s → Step s s' → Reach s'

Definitions 14

def

Cairn.Images.setAt

lean/Cairn/UI/Images.lean:31
setAt {α : Type} : List (String × α) → String → α → List (String × α)

xs with k's entry replaced by v (or (k, v) appended).

def

Cairn.Images.state

lean/Cairn/UI/Images.lean:65
state (t : Images) (source : String) : ImageState

What is known of source.

def

Cairn.Images.load

lean/Cairn/UI/Images.lean:70
load (t : Images) (id : Nat) (source : String) (reload : Bool := false) :
  Images × Effect

Load source under request id id (fresh: the app's next request id). The source shows as loading until the reply; any earlier load of it is forgotten. reload reads it again rather than answering from the host's cache.

def

Cairn.Images.ofReply

lean/Cairn/UI/Images.lean:76
ofReply : Reply → ImageState

What a reply says about an image.

def

Cairn.Images.receive

lean/Cairn/UI/Images.lean:83
receive (t : Images) (id : Nat) (r : Reply) : Images

A reply: if id is a source's latest load, record what it says and stop waiting for it; otherwise nothing changes.

def

Cairn.Images.waiting

lean/Cairn/UI/Images.lean:89
waiting (t : Images) (id : Nat) : Bool

Whether the table is waiting for a reply to id.

def

Cairn.Images.loadAll

lean/Cairn/UI/Images.lean:145
loadAll (t : Images) (id : Nat) (reload : Bool) :
  List String → Images × List Effect

Load each of sources in order, under ids id, id + 1, …: the table and the requests.

def

Cairn.Images.cancel

lean/Cairn/UI/Images.lean:258
cancel (t : Images) (source : String) : Images × List Effect

Stop waiting for source's latest load and cancel it.

def

Cairn.Images.cancelAll

lean/Cairn/UI/Images.lean:263
cancelAll (t : Images) : Images × List Effect

Stop waiting for every load and cancel each one.

def

Cairn.Images.cancelWhere

lean/Cairn/UI/Images.lean:267
cancelWhere (t : Images) (p : String → Bool) : Images × List Effect

Stop waiting for the loads of every source p picks, and cancel each one.

def

Cairn.Images.isReady

lean/Cairn/UI/Images.lean:307
isReady (t : Images) (source : String) : Bool

Whether the table takes source for ready.

def

Cairn.Images.evicted

lean/Cairn/UI/Images.lean:314
evicted (t : Images) (source : String) : Images × List Effect

The host dropped source's pixels (SubEvent.evicted): forget that it is ready, stop waiting for its loads and cancel them.

def

Cairn.Images.Cache.setHas

lean/Cairn/UI/Images.lean:410
setHas (has : String → Bool) (src : String) (b : Bool) : String → Bool

has with src set to b.

def

Cairn.Images.Cache.init

lean/Cairn/UI/Images.lean:414
init (has : String → Bool) : Sys

The app's table before anything was asked.

Theorems 32

theorem

Cairn.Images.lookup_setAt

provedlean/Cairn/UI/Images.lean:35
lookup_setAt {α : Type} (xs : List (String × α)) (k : String) (v : α) :
  List.lookup k (setAt xs k v) = some v
theorem

Cairn.Images.lookup_setAt_ne

provedlean/Cairn/UI/Images.lean:48
lookup_setAt_ne {α : Type} (xs : List (String × α)) (k k2 : String) (v : α)
  (h : k ≠ k2) : List.lookup k2 (setAt xs k v) = List.lookup k2 xs
theorem

Cairn.Images.state_load

provedlean/Cairn/UI/Images.lean:91
state_load (t : Images) (id : Nat) (source : String) (reload : Bool) :
  (t.load id source reload).fst.state source = ImageState.loading
theorem

Cairn.Images.receive_stale

provedlean/Cairn/UI/Images.lean:95
receive_stale (t : Images) (id : Nat) (r : Reply)
  (h : List.lookup id t.pending = none) : t.receive id r = t
theorem

Cairn.Images.state_receive

provedlean/Cairn/UI/Images.lean:99
state_receive (t : Images) (id : Nat) (source : String) (r : Reply)
  (h : List.lookup id t.pending = some source) : (t.receive id r).state source = ofReply r
theorem

Cairn.Images.load_supersedes

provedlean/Cairn/UI/Images.lean:105
load_supersedes (t : Images) (id old : Nat) (source : String) (reload : Bool)
  (hne : old ≠ id) : List.lookup old (t.load id source reload).fst.pending ≠ some source

After a load of source, the table waits for no other id for it.

theorem

Cairn.Images.superseded_reply

provedlean/Cairn/UI/Images.lean:129
superseded_reply (t : Images) (id old : Nat) (source : String) (reload : Bool)
  (r : Reply) (hne : old ≠ id) :
  ((t.load id source reload).fst.receive old r).state source = ImageState.loading

A reply to a superseded load changes nothing: once source was loaded again under id, the reply to any earlier id for it leaves what the table knows of source alone.

theorem

Cairn.Images.state_load_ne

provedlean/Cairn/UI/Images.lean:152
state_load_ne (t : Images) (id : Nat) (src other : String) (reload : Bool)
  (h : src ≠ other) : (t.load id src reload).fst.state other = t.state other
theorem

Cairn.Images.state_loadAll_notin

provedlean/Cairn/UI/Images.lean:156
state_loadAll_notin (t : Images) (id : Nat) (reload : Bool) (src : String)
  (srcs : List String) :
  ¬src ∈ srcs → (t.loadAll id reload srcs).fst.state src = t.state src
theorem

Cairn.Images.state_loadAll

provedlean/Cairn/UI/Images.lean:169
state_loadAll (t : Images) (id : Nat) (reload : Bool) (src : String)
  (srcs : List String) :
  src ∈ srcs → (t.loadAll id reload srcs).fst.state src = ImageState.loading

After loading a list, each of its sources shows as loading until its reply.

theorem

Cairn.Images.pending_load

provedlean/Cairn/UI/Images.lean:189
pending_load (t : Images) (id : Nat) (src : String) (reload : Bool)
  (p : Nat × String) (h : p ∈ (t.load id src reload).fst.pending) :
  p.fst = id ∨ p ∈ t.pending
theorem

Cairn.Images.pending_loadAll

provedlean/Cairn/UI/Images.lean:196
pending_loadAll (t : Images) (reload : Bool) (srcs : List String) (id : Nat)
  (p : Nat × String) :
  p ∈ (t.loadAll id reload srcs).fst.pending →
    id ≤ p.fst ∧ p.fst < id + srcs.length ∨ p ∈ t.pending
theorem

Cairn.Images.pending_load_src

provedlean/Cairn/UI/Images.lean:211
pending_load_src (t : Images) (id : Nat) (src : String) (reload : Bool)
  (p : Nat × String) (h : p ∈ (t.load id src reload).fst.pending) :
  p.snd = src ∨ p ∈ t.pending
theorem

Cairn.Images.pending_loadAll_src

provedlean/Cairn/UI/Images.lean:218
pending_loadAll_src (t : Images) (reload : Bool) (srcs : List String)
  (id : Nat) (p : Nat × String) :
  p ∈ (t.loadAll id reload srcs).fst.pending → p.snd ∈ srcs ∨ p ∈ t.pending
theorem

Cairn.Images.pending_receive

provedlean/Cairn/UI/Images.lean:233
pending_receive (t : Images) (id : Nat) (r : Reply) (p : Nat × String)
  (h : p ∈ (t.receive id r).pending) : p ∈ t.pending
theorem

Cairn.Images.loadAll_length

provedlean/Cairn/UI/Images.lean:240
loadAll_length (t : Images) (id : Nat) (reload : Bool) (srcs : List String) :
  (t.loadAll id reload srcs).snd.length = srcs.length
theorem

Cairn.Images.pending_cancelWhere

provedlean/Cairn/UI/Images.lean:271
pending_cancelWhere (t : Images) (p : String → Bool) (q : Nat × String)
  (h : q ∈ (t.cancelWhere p).fst.pending) : q ∈ t.pending ∧ p q.snd = false
theorem

Cairn.Images.receive_cancelAll

provedlean/Cairn/UI/Images.lean:277
receive_cancelAll (t : Images) (id : Nat) (r : Reply) :
  t.cancelAll.fst.receive id r = t.cancelAll.fst

After cancelAll, no reply changes the table.

theorem

Cairn.Images.cancelAll_effects

provedlean/Cairn/UI/Images.lean:282
cancelAll_effects (t : Images) :
  t.cancelAll.snd = List.map (fun p => Effect.cancelRequest p.fst) t.pending

cancelAll cancels exactly the ids the table was waiting for.

theorem

Cairn.Images.receive_cancel

provedlean/Cairn/UI/Images.lean:286
receive_cancel (t : Images) (source : String) (id : Nat) (r : Reply) :
  ((t.cancel source).fst.receive id r).state source = (t.cancel source).fst.state source

After cancel source, a reply to any id leaves what is known of source alone.

theorem

Cairn.Images.state_evicted

provedlean/Cairn/UI/Images.lean:319
state_evicted (t : Images) (source : String) (w h : Nat) :
  (t.evicted source).fst.state source ≠ ImageState.ready w h

After evicted, the source is not ready.

theorem

Cairn.Images.state_evicted_ne

provedlean/Cairn/UI/Images.lean:330
state_evicted_ne (t : Images) (source other : String) (h : source ≠ other) :
  (t.evicted source).fst.state other = t.state other
theorem

Cairn.Images.pending_evicted

provedlean/Cairn/UI/Images.lean:337
pending_evicted (t : Images) (source : String) (p : Nat × String)
  (h : p ∈ (t.evicted source).fst.pending) : p ∈ t.pending ∧ p.snd ≠ source
theorem

Cairn.Images.receive_evicted

provedlean/Cairn/UI/Images.lean:343
receive_evicted (t : Images) (source : String) (id : Nat) (r : Reply) :
  ((t.evicted source).fst.receive id r).state source = (t.evicted source).fst.state source

After evicted, no reply makes the source ready: the table waits for no load of it.

theorem

Cairn.Images.state_receive_waiting

provedlean/Cairn/UI/Images.lean:367
state_receive_waiting (t : Images) (id : Nat) (r : Reply) (source : String)
  (h : ∀ (p : Nat × String), p ∈ t.pending → p.snd ≠ source) :
  (t.receive id r).state source = t.state source

A reply leaves alone the state of a source the table waits for no load of.

theorem

Cairn.Images.Cache.ofReply_ready

provedlean/Cairn/UI/Images.lean:464
ofReply_ready {r : Reply} {w h : Nat}
  (e : ofReply r = ImageState.ready w h) : r = Reply.image w h
theorem

Cairn.Images.Cache.inv_init

provedlean/Cairn/UI/Images.lean:467
inv_init (has : String → Bool) : Inv (init has)
theorem

Cairn.Images.Cache.evicted_tail

provedlean/Cairn/UI/Images.lean:478
evicted_tail {src : String} {n : Note} {rest : List Note}
  (hn : ∀ (s : String), n ≠ Note.evicted s) (h : Note.evicted src ∈ n :: rest) :
  Note.evicted src ∈ rest

An evicted notice behind a reply is still there once the reply is heard.

theorem

Cairn.Images.Cache.inv_step

provedlean/Cairn/UI/Images.lean:484
inv_step {s s' : Sys} (hi : Inv s) (hs : Step s s') : Inv s'
theorem

Cairn.Images.Cache.inv_reach

provedlean/Cairn/UI/Images.lean:704
inv_reach {s : Sys} (h : Reach s) : Inv s
theorem

Cairn.Images.Cache.ready_held

provedlean/Cairn/UI/Images.lean:711
ready_held {s : Sys} (h : Reach s) (src : String) (w hh : Nat)
  (e : s.t.state src = ImageState.ready w hh) : s.has src = true ∨ Note.evicted src ∈ s.q

Ready is never stale for long: in every state reached, an image the app's table takes for ready has its pixels with the host, or an evicted notice for it is on its way to the app.

theorem

Cairn.Images.Cache.ready_held_quiet

provedlean/Cairn/UI/Images.lean:716
ready_held_quiet {s : Sys} (h : Reach s) (hq : s.q = []) (src : String)
  (w hh : Nat) (e : s.t.state src = ImageState.ready w hh) : s.has src = true

Once the app has heard everything, ready means the host holds the pixels.

Instances 2

instance

Cairn.instReprImages

lean/Cairn/UI/Images.lean:26
instReprImages : Repr Images
instance

Cairn.instInhabitedImages

lean/Cairn/UI/Images.lean:26
instInhabitedImages : Inhabited Images