REFERENCE / ZLIB × LEAN
deflate.c.life.getdict.lean
Source snapshot ·
69e270c696ce2c52 · 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.Proof.«deflate.c.base»
2import ZlibF.Proof.«deflate.c.engine.macros»
3
4/-!
5# deflate.c: `deflateGetDictionary`
6
7`deflateGetDictionary` copies the last `len = min(strstart + lookahead, w_size)` bytes of the history that the window
8holds into the caller's buffer, and reports `len`. It never changes the stream or the state. The stream check refuses a
9stream that is not initialised with `Z_STREAM_ERROR`.
10-/
11
12namespace Zlib.Deflate.Spec
13open C
14
15/-- The number of bytes `deflateGetDictionary` returns: the history in the window, `strstart + lookahead`, at most
16`w_size` (the sum is in `unsigned`; for a state with `strstart + lookahead <= window_size <= 2^16` it does not wrap). -/
17def dictLen (d : DeflateState) : UInt32 :=
18 if d.strstart + d.lookahead > d.w_size then d.w_size else d.strstart + d.lookahead
19
20/-- The caller's buffer can take the `n` bytes: it is a writable byte block with room for them at its offset, and the
21range does not overlap the source range `[src, src + n)` of the window (the two may share a block only if the ranges are
22disjoint, as `memcpy` requires). -/
23def DictDest (h : Heap) (d : DeflateState) (src n : Nat) (dictionary : BPtr) : Prop :=
24 ∃ x off da, dictionary = .ref (.blk x) off ∧ h.bytes x = some da ∧ h.ro x = false ∧ off + n ≤ da.size ∧
25 ∀ w, d.window = .ref (.blk w) 0 → (x ≠ w ∨ off + n ≤ src ∨ src + n ≤ off)
26
27end Zlib.Deflate.Spec
28
29namespace Zlib.Deflate.Proofs
30open C Zlib.Deflate Zlib.Deflate.Spec Zlib.Deflate.Engine.Proofs
31
32theorem dictLen_toNat {d : DeflateState} (hw : d.window_size.toNat = 2 * d.w_size.toNat)
33 (hws : d.w_size.toNat ≤ 32768) (hfill : d.strstart.toNat + d.lookahead.toNat ≤ d.window_size.toNat) :
34 (dictLen d).toNat = min (d.strstart.toNat + d.lookahead.toNat) d.w_size.toNat := by
35 have hs : (d.strstart + d.lookahead).toNat = d.strstart.toNat + d.lookahead.toNat := by
36 rw [UInt32.toNat_add]; exact Nat.mod_eq_of_lt (by omega)
37 unfold dictLen
38 by_cases hc : d.strstart + d.lookahead > d.w_size
39 · simp only [hc, ite_true]
40 have := UInt32.lt_iff_toNat_lt.mp hc
41 omega
42 · simp only [hc, ite_false]
43 have : ¬ d.w_size.toNat < (d.strstart + d.lookahead).toNat := fun e => hc (UInt32.lt_iff_toNat_lt.mpr e)
44 omega
45
46theorem getdict_unfold {h : Heap} {r : ZStreamRef} {ds : Nat} {z : ZStream} {d : DeflateState}
47 (hL : DLive h r ds z d) (dictionary : BPtr) (hasDictLength : Bool) :
48 deflateGetDictionary (some r) dictionary hasDictLength h =
49 ((if dictionary != .null && dictLen d != 0 then
50 (do let from_ ← padd (← padd d.window d.strstart.toNat) d.lookahead.toNat
51 memcpyB dictionary (← psub from_ (dictLen d).toNat) (dictLen d).toNat
52 pure (Z_OK, if hasDictLength then some (dictLen d) else none) : M (Int32 × Option UInt32))
53 else pure (Z_OK, if hasDictLength then some (dictLen d) else none)) h) := by
54 unfold deflateGetDictionary
55 dsimp' [stateCheck_live hL, hL.zst, hL.hz, hL.hs, dictLen]
56 split <;> split <;> simp_all
57
58/-- No copy: the buffer is `NULL`, or the history is empty. -/
59theorem deflateGetDictionary_run_nocopy {h : Heap} {r : ZStreamRef} {ds : Nat} {z : ZStream} {d : DeflateState}
60 (hL : DLive h r ds z d) (dictionary : BPtr) (hasDictLength : Bool)
61 (hn : dictionary = .null ∨ dictLen d = 0) :
62 deflateGetDictionary (some r) dictionary hasDictLength h =
63 .ok ((Z_OK, if hasDictLength then some (dictLen d) else none), h) := by
64 rw [getdict_unfold hL]
65 have : ¬ (dictionary != .null && dictLen d != 0) = true := by
66 rcases hn with e | e <;> simp [e]
67 simp only [this]
68 rfl
69
70/-- A copy of `n = dictLen d` bytes into a good buffer. -/
71theorem deflateGetDictionary_run_copy {h : Heap} {r : ZStreamRef} {ds : Nat} {z : ZStream} {d : DeflateState}
72 {w p q b : Nat} (hL : DLive h r ds z d) (hB : DBlocks h d w p q b)
73 (hfill : d.strstart.toNat + d.lookahead.toNat ≤ d.window_size.toNat)
74 (dictionary : BPtr) (hasDictLength : Bool) {x off : Nat} {da wa : Array UInt8}
75 (hd : dictionary = .ref (.blk x) off) (hda : h.bytes x = some da) (hro : h.ro x = false)
76 (hroom : off + (dictLen d).toNat ≤ da.size) (hwa : h.bytes w = some wa)
77 (hlen : (dictLen d).toNat = min (d.strstart.toNat + d.lookahead.toNat) d.w_size.toNat)
78 (hne : (dictLen d) ≠ 0)
79 (hdis : x ≠ w ∨ off + (dictLen d).toNat ≤ d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat ∨
80 d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat + (dictLen d).toNat ≤ off)
81 (hwsz : wa.size = d.window_size.toNat) :
82 deflateGetDictionary (some r) dictionary hasDictLength h =
83 .ok ((Z_OK, if hasDictLength then some (dictLen d) else none),
84 { h with bytes := upd h.bytes x (some (copyRange da wa off
85 (d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat) (dictLen d).toNat)) }) := by
86 rw [getdict_unfold hL]
87 have hc : (dictionary != .null && dictLen d != 0) = true := by simp [hd, hne]
88 simp only [hc, ite_true]
89 have hwa' : Space.get h (BRef.blk w) = some wa := hwa
90 have e1 : padd d.window d.strstart.toNat h = .ok (.ref (.blk w) d.strstart.toNat, h) := by
91 have := padd_ok' (r := BRef.blk w) (T := UInt8) (off := 0) (n := d.strstart.toNat) hwa' (by omega)
92 rw [hB.win]; simpa using this
93 have e2 : padd (.ref (.blk w) d.strstart.toNat : BPtr) d.lookahead.toNat h =
94 .ok (.ref (.blk w) (d.strstart.toNat + d.lookahead.toNat), h) :=
95 padd_ok' (r := BRef.blk w) (T := UInt8) (off := d.strstart.toNat) (n := d.lookahead.toNat) hwa' (by omega)
96 have e3 : psub (.ref (.blk w) (d.strstart.toNat + d.lookahead.toNat) : BPtr) (dictLen d).toNat h =
97 .ok (.ref (.blk w) (d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat), h) :=
98 psub_ok' (r := BRef.blk w) (T := UInt8) (off := d.strstart.toNat + d.lookahead.toNat) (n := (dictLen d).toNat)
99 hwa' (by omega) (by omega)
100 have e4 : memcpyB dictionary (.ref (.blk w) (d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat))
101 (dictLen d).toNat h = .ok ((), { h with bytes := upd h.bytes x (some (copyRange da wa off
102 (d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat) (dictLen d).toNat)) }) := by
103 subst hd
104 have := Ptr.memcpy_ok (rd := BRef.blk x) (rs := BRef.blk w) (od := off)
105 (os := d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat) (n := (dictLen d).toNat) (da := da) (sa := wa)
106 (by simpa using hda) (by simpa using hwa) (by simpa using hro) hroom (by omega)
107 (by rcases hdis with e | e | e
108 · exact Or.inl (fun hh => e (BRef.blk.inj hh))
109 · exact Or.inr (Or.inl e)
110 · exact Or.inr (Or.inr e))
111 simpa using this
112 simp only [bind_app, e1, e2, e3, e4, pure_app]
113
114
115theorem getD_copyRange (da wa : Array UInt8) (off s n : Nat) (hfit : off + n ≤ da.size) (i : Nat) :
116 (copyRange da wa off s n).getD i 0 = if off ≤ i ∧ i < off + n then wa.getD (s + (i - off)) 0 else da.getD i 0 := by
117 rw [Array.getD_eq_getD_getElem?, getElem?_copyRange _ _ _ _ _ _ hfit]
118 by_cases hi : off ≤ i ∧ i < off + n
119 · simp [hi, Array.getD_eq_getD_getElem?, getElem!_def]
120 split <;> simp_all <;> rfl
121 · simp [hi, Array.getD_eq_getD_getElem?]
122
123/-- **deflateGetDictionary, safety and specification.** On an initialised stream with the invariant (any stream state
124reachable by the engine: `strstart + lookahead <= window_size`, the precondition `hfill`, which `deflateInit2_`,
125`lm_init` and `fill_window` maintain) the call never faults and returns `Z_OK`. The length it reports (when the pointer
126`dictLength` is not `NULL`, `hasDictLength`) is `min(strstart + lookahead, w_size)`; it copies that many bytes, the
127last ones of the history `window[0 .. strstart + lookahead)`, to the caller's buffer `dictionary`; a `NULL` buffer
128receives nothing. The buffer must be a writable byte block with room for them that overlaps the source range at most
129if it lies in the window block outside it (`DictDest`); `zlib.h` says the buffer holds at least `w_size` bytes. The
130stream and the state are unchanged; only the bytes of the buffer's block in `[off, off + len)` change, and no block is
131allocated or freed. -/
132theorem deflateGetDictionary_safe {h : Heap} {r : ZStreamRef} {ds : Nat} {z : ZStream} {d : DeflateState}
133 (hG : DGood h r ds z d) (hfill : d.strstart.toNat + d.lookahead.toNat ≤ d.window_size.toNat)
134 (dictionary : BPtr) (hasDictLength : Bool)
135 (hdict : dictionary ≠ .null → dictLen d ≠ 0 →
136 DictDest h d (d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat) (dictLen d).toNat dictionary) :
137 ∃ h', C.run (deflateGetDictionary (some r) dictionary hasDictLength) h =
138 .ok ((Z_OK, if hasDictLength then some (dictLen d) else none), h') ∧
139 DGood h' r ds z d ∧
140 (dictLen d).toNat = min (d.strstart.toNat + d.lookahead.toNat) d.w_size.toNat ∧
141 h'.dstates = h.dstates ∧ h'.streams = h.streams ∧ h'.gzstates = h.gzstates ∧ h'.words = h.words ∧
142 h'.ro = h.ro ∧ h'.gzheaders = h.gzheaders ∧ h'.istates = h.istates ∧ h'.nextBlk = h.nextBlk ∧
143 h'.nalloc = h.nalloc ∧
144 (∀ x, (∀ off, dictionary ≠ .ref (.blk x) off) → h'.bytes x = h.bytes x) ∧
145 (∀ w wa, d.window = .ref (.blk w) 0 → h.bytes w = some wa → ∀ x off da, dictionary = .ref (.blk x) off →
146 h.bytes x = some da → ∃ da', h'.bytes x = some da' ∧ da'.size = da.size ∧
147 (∀ i, i < (dictLen d).toNat →
148 da'.getD (off + i) 0 = wa.getD (d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat + i) 0) ∧
149 (∀ i, (i < off ∨ off + (dictLen d).toNat ≤ i) → da'.getD i 0 = da.getD i 0)) := by
150 obtain ⟨w, p, q, b, hB⟩ := hG.inv.blocks
151 have hI := hG.inv
152 obtain ⟨wa, hwa, hwsz⟩ := hB.wa
153 have hws : d.w_size.toNat ≤ 32768 := by
154 have := hI.wsize; have := hI.wbits_hi
155 have : 2 ^ d.w_bits.toNat ≤ 2 ^ 15 := Nat.pow_le_pow_right (by omega) hI.wbits_hi
156 omega
157 have hlen := dictLen_toNat hI.wsz hws hfill
158 have hwsz' : wa.size = d.window_size.toNat := by have := hI.wsz; omega
159 by_cases hn : dictionary = .null ∨ dictLen d = 0
160 · refine ⟨h, ?_, hG, hlen, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, fun _ _ => rfl, ?_⟩
161 · rw [C.run]; exact deflateGetDictionary_run_nocopy hG.live dictionary hasDictLength hn
162 · intro w' wa' hw' hwa' x off da hd hda
163 refine ⟨da, hda, rfl, ?_, fun _ _ => rfl⟩
164 intro i hi
165 rcases hn with e | e
166 · rw [e] at hd; cases hd
167 · rw [e] at hi; simp at hi
168 · have hnn : dictionary ≠ .null := fun e => hn (Or.inl e)
169 have hne : dictLen d ≠ 0 := fun e => hn (Or.inr e)
170 obtain ⟨x, off, da, hd, hda, hro, hroom, hdis⟩ := hdict hnn hne
171 have hdis' := hdis w hB.win
172 have hrun := deflateGetDictionary_run_copy hG.live hB hfill dictionary hasDictLength hd hda hro hroom hwa hlen hne
173 hdis' hwsz'
174 have hsz : (copyRange da wa off (d.strstart.toNat + d.lookahead.toNat - (dictLen d).toNat)
175 (dictLen d).toNat).size = da.size := size_copyRange _ _ _ _ _
176 refine ⟨_, by rw [C.run]; exact hrun, hG.bytes_set x _ da hda hsz, hlen, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl,
177 ?_, ?_⟩
178 · intro y hy
179 have hyx : y ≠ x := fun e => hy off (by rw [hd, e])
180 simp [upd_apply, hyx]
181 · intro w' wa' hw' hwa' x' off' da' hd' hda'
182 rw [hd] at hd'
183 cases hd'
184 rw [hB.win] at hw'
185 cases hw'
186 rw [hwa] at hwa'
187 cases hwa'
188 rw [hda] at hda'
189 cases hda'
190 refine ⟨_, by simp, hsz, ?_, ?_⟩
191 · intro i hi
192 rw [getD_copyRange _ _ _ _ _ hroom]
193 simp only [show off ≤ off + i ∧ off + i < off + (dictLen d).toNat by omega, and_self, ite_true]
194 congr 2; omega
195 · intro i hi
196 rw [getD_copyRange _ _ _ _ _ hroom]
197 have : ¬ (off ≤ i ∧ i < off + (dictLen d).toNat) := by omega
198 simp only [this, ite_false]
199
200/-- **deflateGetDictionary, refusal.** On a stream the check refuses (including `NULL`): `Z_STREAM_ERROR`, no length
201stored, nothing changes. -/
202theorem deflateGetDictionary_reject {h : Heap} {strm : ZStreamPtr} (hD : StreamDom h strm) (hN : ¬ DInitialised h strm)
203 (dictionary : BPtr) (hasDictLength : Bool) :
204 C.run (deflateGetDictionary strm dictionary hasDictLength) h = .ok ((Z_STREAM_ERROR, none), h) := by
205 unfold deflateGetDictionary
206 exact check_refuse hD hN _ _
207
208end Zlib.Deflate.Proofs