Skip to content

Commit 89e4e73

Browse files
committed
feat: DecodeResult.terminate
1 parent 59fee81 commit 89e4e73

File tree

3 files changed

+45
-16
lines changed

3 files changed

+45
-16
lines changed

Binary/Basic.lean

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ instance : ToString DecodeError where
2424
inductive DecodeResult (α) where
2525
| success (x : α) (k : Decoder)
2626
| error (err : DecodeError) (cur : Decoder)
27-
| pending (fn : ByteArray → DecodeResult α)
27+
| pending (fn : Option ByteArray → DecodeResult α)
2828
deriving Inhabited
2929

3030
@[always_inline]
@@ -51,6 +51,7 @@ def DecodeResult.toExceptString : DecodeResult α → Except String α
5151
@[expose]
5252
def Get (α : Type) : Type := Decoder → (DecodeResult α)
5353

54+
/-- The bytes in the current inner buffer, **not** considering pending input. -/
5455
@[always_inline]
5556
def remaining : Get Nat := fun d => DecodeResult.success (d.data.size - d.offset) d
5657

@@ -60,6 +61,15 @@ def DecodeResult.feed {α} (bytes : ByteArray) : DecodeResult α → DecodeResul
6061
| .error err k => .error err (k.append bytes)
6162
| .pending fn => fn bytes
6263

64+
/--
65+
Immediately terminate the pending state.
66+
This is especially useful for terminating something like `optional (pending x)`. -/
67+
@[always_inline]
68+
def DecodeResult.terminate {α} : DecodeResult α → DecodeResult α
69+
| .success x k => .success x k
70+
| .error err k => .error err k
71+
| .pending fn => fn none
72+
6373
@[always_inline]
6474
instance : Monad Get where
6575
pure x := fun d => DecodeResult.success x d
@@ -191,12 +201,15 @@ Catch any `DecodeError.eoi` and recover to a pending state rather than exit with
191201
* ensure that `x` terminates when enough bytes are fed,
192202
* or define your own pending function to cache intermediate result as much as possible.
193203
-/
194-
@[specialize, always_inline]
204+
@[specialize]
195205
partial def pending (x : Get α) : Get α := do
196206
try x
197207
catch err =>
198208
match err with
199-
| .eoi => fun d => .pending fun bytes => pending x (d.append bytes)
209+
| .eoi => fun d => .pending fun bytes? =>
210+
match bytes? with
211+
| none => DecodeResult.mkEOI d
212+
| some bytes => pending x (d.append bytes)
200213
| e => throw e
201214

202215
namespace Primitive

Binary/Get.lean

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ public section
1111
def fail (msg : String) : Get α :=
1212
throw (.userError msg)
1313

14-
@[always_inline, specialize]
14+
@[specialize]
1515
def many (p : Get α) : Get (Array α) := do
1616
let mut data := #[]
1717
repeat
1818
let some x ← optional p | break
1919
data := data.push x
2020
return data
2121

22-
@[always_inline, specialize]
22+
@[inline, specialize]
2323
def many1 (p : Get α) : Get (Array α) := do
2424
let first ← p
2525
let rest ← many p
@@ -35,6 +35,13 @@ def shouldBeEOI (includeUnExpected : Bool := false) : Get Unit := do
3535
else
3636
fail "expected EOI"
3737

38+
@[always_inline]
39+
def notFollowedBy (p : Get α) : Get Unit := fun d =>
40+
match p d with
41+
| .success _ _ => DecodeResult.error (.userError "unexpected lookahead") d
42+
| .error _ _ => DecodeResult.success () d
43+
| .pending _ => DecodeResult.error (.userError "unexpected pending lookahead") d
44+
3845
-- TODO: refactor following definitions for performance
3946

4047
@[inline, specialize]
@@ -153,6 +160,26 @@ instance : Decode Int8 where
153160

154161
end
155162

163+
public section
164+
165+
/--
166+
This function **exhaustively** reads in all bytes starting from the current offset.
167+
The outermost caller **must** call `DecodeResult.terminate` to break from this function. -/
168+
def exhaust : Get ByteArray := do
169+
let r ← remaining
170+
let data ← get_bytes r
171+
let mut rs := #[]
172+
repeat
173+
shrink
174+
let some x ← (optional <| pending <| getThe UInt8) | break
175+
let r ← remaining
176+
let xs ← get_bytes r
177+
rs := rs.push ⟨#[x]⟩
178+
rs := rs.push xs
179+
return rs.foldl (init := data) (· ++ ·)
180+
181+
end
182+
156183
namespace Primitive
157184

158185
variable {ω m} [Monad m] [STWorld ω m] [MonadLiftT (ST ω) m]

Binary/UTF8/Get.lean

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ namespace Binary.UTF8
1111
private def byteToChar (b : UInt8) : Char :=
1212
Char.ofNat b.toNat
1313

14-
-- @[always_inline]
15-
-- private def charToString (c : Char) : String :=
16-
-- String.ofList [c] -- TODO: how about `Char.toString`?
17-
1814
@[always_inline]
1915
private def chars_to_string (xs : Array Char) : String :=
2016
String.ofList xs.toList
@@ -50,10 +46,3 @@ def manyChars (p : Get Char) : Get String :=
5046
@[always_inline, specialize]
5147
def many1Chars (p : Get Char) : Get String :=
5248
chars_to_string <$> many1 p
53-
54-
@[always_inline]
55-
def notFollowedBy (p : Get α) : Get Unit := fun d =>
56-
match p d with
57-
| .success _ _ => DecodeResult.error (.userError "unexpected lookahead") d
58-
| .error _ _ => DecodeResult.success () d
59-
| .pending _ => DecodeResult.error (.userError "unexpected pending lookahead") d

0 commit comments

Comments
 (0)