Skip to content
REFERENCE / ZLIB × LEAN

public-gz-offset.lean

Source snapshot · b8e7281919064e5b · Source at the matching commit ↗

Lean source with line numbers. Follow a line number to locate a statement, or an import to read its dependency.

1import ZlibF.Spec.Public.GzControl
2import ZlibF.Proof.«gzlib.c.simple»
3
4namespace ZlibF.Spec.Public.OffsetProof
5open C Zlib.GzFs Zlib.Gzguts Zlib.Gzlib Zlib.Gzlib.Spec Zlib.Gzlib.Proofs
6
7def lseekRes (fd : Int32) (off : Int64) (wh : Int32) (w : Fs) : Int64 × Fs :=
8  match fsLseek fd off wh w Heap.empty with
9  | .ok (r, _) => r
10  | .error _ => (-1, w)
11
12
13theorem fsLseek_pure (fd : Int32) (off : Int64) (wh : Int32) (w : Fs) :
14    ∃ x, fsLseek fd off wh w = (pure x : M (Int64 × Fs)) ∧ (x.1 = -1 ∨ 0 ≤ x.1.toInt) := by
15  unfold fsLseek
16  repeat' first
17    | exact ⟨_, rfl, Or.inl rfl⟩
18    | (refine ⟨_, rfl, Or.inr ?_⟩
19       simp only []
20       rw [i64_toInt_ofInt (by omega) (by omega)]
21       omega)
22    | split
23    | dsimp only
24
25theorem fsLseek_eq (fd : Int32) (off : Int64) (wh : Int32) (w : Fs) (h : Heap) :
26    fsLseek fd off wh w h = .ok (lseekRes fd off wh w, h) := by
27  obtain ⟨x, hx, -⟩ := fsLseek_pure fd off wh w
28  unfold lseekRes
29  rw [hx]
30  rfl
31
32/-- `lseek` returns `-1` or a position that is not negative. -/
33theorem lseekRes_ret (fd : Int32) (off : Int64) (wh : Int32) (w : Fs) :
34    (lseekRes fd off wh w).1 = -1 ∨ 0 ≤ (lseekRes fd off wh w).1.toInt := by
35  obtain ⟨x, hx, hp⟩ := fsLseek_pure fd off wh w
36  unfold lseekRes
37  rw [hx]
38  exact hp
39
40theorem G_bind_app {α β : Type} (m : G α) (f : α → G β) (fs : Fs) (h : Heap) :
41    (m >>= f) fs h = match m fs h with | .ok ((a, fs1), h1) => f a fs1 h1 | .error e => .error e := by
42  simp only [bind, StateT.bind]
43  cases hm : m fs h with
44  | error e => rfl
45  | ok v => obtain ⟨⟨a, fs1⟩, h1⟩ := v; rfl
46
47theorem G_pure_app {α : Type} (a : α) (fs : Fs) (h : Heap) : (pure a : G α) fs h = .ok ((a, fs), h) := rfl
48
49theorem G_lift_app {α : Type} (m : M α) (fs : Fs) (h : Heap) :
50    (StateT.lift m : G α) fs h = match m h with | .ok (a, h1) => .ok ((a, fs), h1) | .error e => .error e := rfl
51
52/-- Evaluate a straight-line `G` computation on a `gz_state` record: the lifts, the binds, the field accesses and the
53file calls. -/
54macro "offsetsimp" "[" ts:Lean.Parser.Tactic.simpLemma,* "]" : tactic =>
55  `(tactic| simp [liftM, monadLift, MonadLift.monadLift, MonadLiftT.monadLift, G_bind_app, G_pure_app, G_lift_app,
56    bind_app, pure_app, ldS_eq, updS_eq, updX_eq, ldZ_eq, updZ_eq, castState_hnd, fsLseek_eq, $ts,*])
57
58theorem gzoffset64_null (fs : Fs) (h : Heap) : gzoffset64 none fs h = .ok ((-1, fs), h) := rfl
59
60theorem gzoffset_eq (file : GzFilePtr) (fs : Fs) (h : Heap) : gzoffset file fs h = gzoffset64 file fs h := by
61  unfold gzoffset
62  simp only [G_bind_app]
63  cases hh : gzoffset64 file fs h with
64  | error e => rfl
65  | ok v => obtain ⟨⟨a, fs1⟩, h1⟩ := v; simp [G_pure_app]
66
67
68theorem lseek_current_public (fd : Int32) (fs : Fs)
69    (hpos : ∀ (i : Nat) (f : OFile), fs.fds[i]? = some (some f) → f.pos ≤ 2 ^ 63 - 1) :
70    { (lseekRes fd 0 SEEK_CUR fs).2 with errno := fs.errno } = fs ∧
71    (if fd.toInt < 0 then (lseekRes fd 0 SEEK_CUR fs).1 = -1 else
72      match fs.fds[fd.toInt.toNat]? with
73      | some (some file) => (lseekRes fd 0 SEEK_CUR fs).1.toInt = (file.pos : Int)
74      | _ => (lseekRes fd 0 SEEK_CUR fs).1 = -1) := by
75  by_cases hn : fd.toInt < 0
76  · have hf : fd < 0 := Int32.lt_iff_toInt_lt.mpr (by simpa using hn)
77    simp [lseekRes, fsLseek, hn, hf, pure_app]
78  · have hfd : ¬ fd < 0 := fun hf => hn (by simpa using Int32.lt_iff_toInt_lt.mp hf)
79    by_cases hi : fd.toInt.toNat < fs.fds.size
80    · have hbound : ¬ fd.toInt ≥ (fs.fds.size : Int) := by omega
81      have hget := Array.getElem?_eq_getElem hi
82      cases hf : fs.fds[fd.toInt.toNat] with
83      | none =>
84        have hget' : fs.fds[fd.toInt.toNat]! = none := by rw [getElem!_pos fs.fds _ hi, hf]
85        simp only [lseekRes, fsLseek, hfd, hbound, Bool.false_eq_true, Bool.false_or, ite_false,
86          hget', pure_app, hget, hf, hn, ite_eq_right]
87        simp [pure_app]
88      | some f =>
89        have hvalid : fs.fds[fd.toInt.toNat]? = some (some f) := by rw [hget, hf]
90        have hp := hpos _ _ hvalid
91        have hget' : fs.fds[fd.toInt.toNat]! = some f := by rw [getElem!_pos fs.fds _ hi, hf]
92        have hself : fs.fds.set! fd.toInt.toNat (some f) = fs.fds := by
93          rw [← hf, Array.set!_eq_setIfInBounds]
94          simp [Array.setIfInBounds, hi]
95        simp only [lseekRes, fsLseek, hfd, hbound, Bool.false_eq_true, Bool.false_or, ite_false,
96          hget', pure_app, hvalid, hn, ite_eq_right, SEEK_CUR, SEEK_SET, beq_iff_eq, Int.reduceEq,
97          Int64.toInt_ofNat_of_lt, ite_true, Int.reduceAdd,
98          Int.toNat_natCast, hself, Prod.fst, Prod.snd]
99        have hne : ¬ (f.pos : Int) < 0 := by omega
100        have hmax : ¬ (9223372036854775807 : Int) < f.pos := by omega
101        have hnat : f.pos < 2 ^ 63 := by omega
102        simp only [Array.set!_eq_setIfInBounds] at hself
103        change fs.fds.setIfInBounds fd.toNatClampNeg (some f) = fs.fds at hself
104        have hto : (Int64.ofInt (f.pos : Int)).toInt = (f.pos : Int) :=
105          i64_toInt_ofInt (by omega) (by omega)
106        simp [pure_app, hne, hmax, hself, hto, SEEK_CUR, SEEK_SET]
107    · have hbound : fd.toInt ≥ (fs.fds.size : Int) := by omega
108      have hnone : fs.fds[fd.toInt.toNat]? = none := Array.getElem?_eq_none (by omega)
109      change fs.fds[fd.toNatClampNeg]? = none at hnone
110      simp [lseekRes, fsLseek, hn, hfd, hbound, hnone, pure_app]
111
112/-- `z_off64_t gzoffset64(gzFile file)` has the exact result of the descriptor
113query on any existing state record with a supported mode. -/
114theorem gzoffset64_record {h : Heap} {i : Nat} {s : GzState}
115    (hs : h.gzstates i = some s) (hm : s.mode = GZ_READ ∨ s.mode = GZ_WRITE) (fs : Fs) :
116    gzoffset64 (hnd i) fs h =
117      if (lseekRes s.fd 0 SEEK_CUR fs).1 = -1 then .ok ((-1, (lseekRes s.fd 0 SEEK_CUR fs).2), h)
118      else if s.mode = GZ_READ then
119        .ok ((Int64.ofInt ((lseekRes s.fd 0 SEEK_CUR fs).1.toInt - s.strm.avail_in.toNat),
120          (lseekRes s.fd 0 SEEK_CUR fs).2), h)
121      else .ok (((lseekRes s.fd 0 SEEK_CUR fs).1, (lseekRes s.fd 0 SEEK_CUR fs).2), h) := by
122  have hr := lseekRes_ret s.fd 0 SEEK_CUR fs
123  have hav := s.strm.avail_in.toNat_lt
124  unfold gzoffset64
125  by_cases hl : (lseekRes s.fd 0 SEEK_CUR fs).1 = -1
126  · rcases hm with hm | hm <;> offsetsimp [hs, hm, hl, GZ_READ, GZ_WRITE]
127  · have hr' : 0 ≤ (lseekRes s.fd 0 SEEK_CUR fs).1.toInt := hr.resolve_left hl
128    have hlt := (lseekRes s.fd 0 SEEK_CUR fs).1.toInt_lt
129    have e := i64sub_run (lseekRes s.fd 0 SEEK_CUR fs).1 (u32ToI64 s.strm.avail_in)
130      (by rw [u32ToI64_toInt]; omega) (by rw [u32ToI64_toInt]; omega) h
131    rw [u32ToI64_toInt] at e
132    rcases hm with hm | hm
133    · offsetsimp [hs, hm, hl, GZ_READ, GZ_WRITE, e]
134    · offsetsimp [hs, hm, hl, GZ_READ, GZ_WRITE]
135
136/-- `z_off64_t gzoffset64(gzFile file)` returns the descriptor position less
137buffered input. The heap and all filesystem fields except errno are unchanged. -/
138theorem gzoffset64_public {h : Heap} {i : Nat} {s : GzState}
139    (hs : h.gzstates i = some s) (hm : s.mode = GZ_READ ∨ s.mode = GZ_WRITE) (fs : Fs)
140    (hpos : ∀ (j : Nat) (f : OFile), fs.fds[j]? = some (some f) → f.pos ≤ 2 ^ 63 - 1) :
141    ∃ value fs', gzoffset64 (hnd i) fs h = .ok ((value, fs'), h) ∧
142      { fs' with errno := fs.errno } = fs ∧
143      (if s.fd.toInt < 0 then value = -1 else match fs.fds[s.fd.toInt.toNat]? with
144       | some (some file) => value.toInt = (file.pos : Int) -
145           (if s.mode = GZ_READ then s.strm.avail_in.toNat else 0)
146       | _ => value = -1) := by
147  obtain ⟨hfs, hres⟩ := lseek_current_public s.fd fs hpos
148  have hex := gzoffset64_record hs hm fs
149  by_cases hfd : s.fd.toInt < 0
150  · have he : (lseekRes s.fd 0 SEEK_CUR fs).1 = -1 := by simpa [hfd] using hres
151    exact ⟨-1, _, by simpa [he] using hex, hfs, by simp [hfd]⟩
152  · cases hf : fs.fds[s.fd.toInt.toNat]? with
153    | none =>
154      have he : (lseekRes s.fd 0 SEEK_CUR fs).1 = -1 := by
155        rw [ite_eq_right hfd, hf] at hres
156        exact hres
157      exact ⟨-1, _, by simpa [he] using hex, hfs, by simp [hfd, hf]⟩
158    | some slot =>
159      cases slot with
160      | none =>
161        have he : (lseekRes s.fd 0 SEEK_CUR fs).1 = -1 := by
162          rw [ite_eq_right hfd, hf] at hres
163          exact hres
164        exact ⟨-1, _, by simpa [he] using hex, hfs, by simp [hfd, hf]⟩
165      | some file =>
166        have hr : (lseekRes s.fd 0 SEEK_CUR fs).1.toInt = (file.pos : Int) := by
167          rw [ite_eq_right hfd, hf] at hres
168          exact hres
169        have he : (lseekRes s.fd 0 SEEK_CUR fs).1 ≠ -1 := by
170          intro he
171          rw [he] at hr
172          have : (0 : Int) ≤ file.pos := by omega
173          have : (-1 : Int) = file.pos := by simpa using hr
174          omega
175        by_cases hread : s.mode = GZ_READ
176        · refine ⟨Int64.ofInt ((lseekRes s.fd 0 SEEK_CUR fs).1.toInt - s.strm.avail_in.toNat), _,
177            by simpa [he, hread] using hex, hfs, ?_⟩
178          simp only [hfd, ite_false, hf, hread, ite_true]
179          rw [i64_toInt_ofInt (by have := s.strm.avail_in.toNat_lt; omega)
180            (by have := (lseekRes s.fd 0 SEEK_CUR fs).1.toInt_lt; omega), hr]
181        · exact ⟨_, _, by simpa [he, hread] using hex, hfs, by simpa [hfd, hf, hread] using hr⟩
182
183/-- `z_off_t gzoffset(gzFile file)` has the same LP64 result as `gzoffset64`. -/
184theorem gzoffset_public {h : Heap} {i : Nat} {s : GzState}
185    (hs : h.gzstates i = some s) (hm : s.mode = GZ_READ ∨ s.mode = GZ_WRITE) (fs : Fs)
186    (hpos : ∀ (j : Nat) (f : OFile), fs.fds[j]? = some (some f) → f.pos ≤ 2 ^ 63 - 1) :
187    ∃ value fs', gzoffset (hnd i) fs h = .ok ((value, fs'), h) ∧
188      { fs' with errno := fs.errno } = fs ∧
189      (if s.fd.toInt < 0 then value = -1 else match fs.fds[s.fd.toInt.toNat]? with
190       | some (some file) => value.toInt = (file.pos : Int) -
191           (if s.mode = GZ_READ then s.strm.avail_in.toNat else 0)
192       | _ => value = -1) := by
193  obtain ⟨value, fs', hr, hf, hv⟩ := gzoffset64_public hs hm fs hpos
194  exact ⟨value, fs', by rw [gzoffset_eq]; exact hr, hf, hv⟩
195
196
197/-- `z_off64_t gzoffset64(gzFile file)`
198The exact public result, state, frame and ownership contract. -/
199theorem gzoffset64_public_goal : Goal.gzoffset64 := by
200  constructor
201  · intro h fs
202    exact gzoffset64_null fs h
203  · intro h fs i s _ hv hfs
204    have hpos : ∀ (j : Nat) (f : OFile), fs.fds[j]? = some (some f) → f.pos ≤ 2 ^ 63 - 1 := by
205      intro j f hf
206      exact (hfs.2.2.1 j f hf).2.1
207    exact gzoffset64_public hv.1 hv.2.1 fs hpos
208
209/-- `z_off_t gzoffset(gzFile file)`; the LP64 wrapper has the same physical-position contract.
210The exact public result, state, frame and ownership contract. -/
211theorem gzoffset_public_goal : Goal.gzoffset := by
212  constructor
213  · intro h fs
214    rw [gzoffset_eq]
215    exact gzoffset64_null fs h
216  · intro h fs i s _ hv hfs
217    have hpos : ∀ (j : Nat) (f : OFile), fs.fds[j]? = some (some f) → f.pos ≤ 2 ^ 63 - 1 := by
218      intro j f hf
219      exact (hfs.2.2.1 j f hf).2.1
220    exact gzoffset_public hv.1 hv.2.1 fs hpos
221
222
223#print axioms gzoffset64_public_goal
224#print axioms gzoffset_public_goal
225end ZlibF.Spec.Public.OffsetProof
ZLIB × LEAN

Global contents

READING NOWpublic-gz-offset.lean