Skip to content

Commit d5ac0bc

Browse files
committed
manyLength fail early if not enough bytes
Improve generation speed for large inputs.
1 parent 2bdbfd1 commit d5ac0bc

File tree

1 file changed

+21
-14
lines changed

1 file changed

+21
-14
lines changed

library/src/Protobuf/Internal/Runtime.purs

+21-14
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Control.Monad.Trans.Class (lift)
2424
import Data.Array (snoc, foldRecM)
2525
import Data.Array as Array
2626
import Data.ArrayBuffer.Builder (DataBuff(..), PutM, subBuilder)
27+
import Data.ArrayBuffer.DataView (byteLength)
2728
import Data.ArrayBuffer.Types (DataView, ByteLength)
2829
import Data.Enum (class BoundedEnum, fromEnum, toEnum)
2930
import Data.Foldable (foldl)
@@ -39,7 +40,8 @@ import Data.UInt64 (UInt64)
3940
import Data.UInt64 as UInt64
4041
import Effect.Class (class MonadEffect)
4142
import Parsing (ParserT, Position(..), fail, position)
42-
import Parsing.DataView (takeN)
43+
import Parsing.Combinators (lookAhead)
44+
import Parsing.DataView (takeN, takeRest)
4345
import Protobuf.Internal.Common (Bytes(..), FieldNumber, WireType(..), label)
4446
import Protobuf.Internal.Decode as Decode
4547
import Protobuf.Internal.Encode as Encode
@@ -76,6 +78,7 @@ type FieldNumberInt
7678
= Int
7779

7880
-- | Call a parser repeatedly until exactly *N* bytes have been consumed.
81+
-- | Will fail if not enough bytes remain in the DataView.
7982
-- | Will fail if too many bytes are consumed.
8083
manyLength ::
8184
forall m a.
@@ -85,19 +88,23 @@ manyLength ::
8588
ByteLength ->
8689
ParserT DataView m (Array a)
8790
manyLength p len = do
88-
Position { index: posBegin } <- position
89-
let
90-
go :: List a -> ParserT DataView m (Step (List a) (List a))
91-
go accum = do
92-
Position { index: pos } <- position
93-
case compare (pos - posBegin) len of
94-
GT -> fail "manyLength consumed too many bytes."
95-
EQ -> lift $ pure (Done accum)
96-
LT -> do
97-
x <- p
98-
pure (Loop (x : accum))
99-
-- https://github.com/purescript-contrib/purescript-parsing/pull/199#issuecomment-1145956271
100-
Array.reverse <$> Array.fromFoldable <$> tailRecM go List.Nil
91+
remaining_bytes :: Int <- byteLength <$> lookAhead takeRest
92+
if remaining_bytes < len
93+
then fail $ "manyLength " <> show len <> " not enough bytes of input " <> show remaining_bytes <> " remaining."
94+
else do
95+
Position { index: posBegin } <- position
96+
let
97+
go :: List a -> ParserT DataView m (Step (List a) (List a))
98+
go accum = do
99+
Position { index: pos } <- position
100+
case compare (pos - posBegin) len of
101+
GT -> fail "manyLength consumed too many bytes."
102+
EQ -> lift $ pure (Done accum)
103+
LT -> do
104+
x <- p
105+
pure (Loop (x : accum))
106+
-- https://github.com/purescript-contrib/purescript-parsing/pull/199#issuecomment-1145956271
107+
Array.reverse <$> Array.fromFoldable <$> tailRecM go List.Nil
101108

102109
-- | A message field value from an unknown `.proto` definition.
103110
-- |

0 commit comments

Comments
 (0)