Skip to content

Commit

Permalink
Add MonadRec constraint to put functions.
Browse files Browse the repository at this point in the history
Replace all internal use of traverse_ with foldRecM.

putRepeated and putPacked will be stack-safe.
  • Loading branch information
jamesdbrock committed Oct 15, 2024
1 parent dbe4d50 commit 07d6e84
Show file tree
Hide file tree
Showing 7 changed files with 401 additions and 77 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- Bugfix #38 Add `MonadRec` constraint to `put` functions.

# v4.3.0 2023-01-30

- Development: purs v0.15.7, node-streams-aff v5.0.0, assorted improvements.
Expand Down
4 changes: 2 additions & 2 deletions plugin/ProtocPlugin/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -1303,12 +1303,12 @@ genFile proto_file ( FileDescriptorProto
-- https://github.com/purescript/purescript/issues/2975#issuecomment-313650710
, Right $ "instance show" <> tname <> " :: Prelude.Show " <> tname <> " where show x = Prelude.genericShow x"
, Right ""
, Right $ "put" <> tname <> " :: forall m. Prelude.MonadEffect m => " <> tname <> " -> Prelude.PutM m Prelude.Unit"
, Right $ "put" <> tname <> " :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => " <> tname <> " -> Prelude.PutM m Prelude.Unit"
, Right $ "put" <> tname <> " (" <> tname <> " r) = do"
, map (String.joinWith "\n")
$ (traverse (genFieldPut nameSpace) fields_singular)
<> (sequence $ map (genOneofPut (nameSpace <> [ msgName ])) oneof_decl_fields)
, Right " Prelude.traverse_ Prelude.putFieldUnknown r.__unknown_fields"
, Right " Prelude.foldRecM (\\_ x -> Prelude.putFieldUnknown x) unit r.__unknown_fields"
, Right ""
, Right $ "parse" <> tname <> " :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => Prelude.ByteLength -> Prelude.ParserT Prelude.DataView m " <> tname
, Right $ "parse" <> tname <> " length = Prelude.label \"" <> msgName <> " / \" $"
Expand Down
427 changes: 373 additions & 54 deletions plugin/ProtocPlugin/descriptor.Google.Protobuf.purs

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions plugin/ProtocPlugin/plugin.Google.Protobuf.Compiler.purs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ derive instance newtypeVersion :: Prelude.Newtype Version _
derive instance eqVersion :: Prelude.Eq Version
instance showVersion :: Prelude.Show Version where show x = Prelude.genericShow x

putVersion :: forall m. Prelude.MonadEffect m => Version -> Prelude.PutM m Prelude.Unit
putVersion :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => Version -> Prelude.PutM m Prelude.Unit
putVersion (Version r) = do
Prelude.putOptional 1 r.major Prelude.isDefault Prelude.encodeInt32Field
Prelude.putOptional 2 r.minor Prelude.isDefault Prelude.encodeInt32Field
Prelude.putOptional 3 r.patch Prelude.isDefault Prelude.encodeInt32Field
Prelude.putOptional 4 r.suffix Prelude.isDefault Prelude.encodeStringField
Prelude.traverse_ Prelude.putFieldUnknown r.__unknown_fields
Prelude.foldRecM (\_ x -> Prelude.putFieldUnknown x) unit r.__unknown_fields

parseVersion :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => Prelude.ByteLength -> Prelude.ParserT Prelude.DataView m Version
parseVersion length = Prelude.label "Version / " $
Expand Down Expand Up @@ -100,13 +100,13 @@ derive instance newtypeCodeGeneratorRequest :: Prelude.Newtype CodeGeneratorRequ
derive instance eqCodeGeneratorRequest :: Prelude.Eq CodeGeneratorRequest
instance showCodeGeneratorRequest :: Prelude.Show CodeGeneratorRequest where show x = Prelude.genericShow x

putCodeGeneratorRequest :: forall m. Prelude.MonadEffect m => CodeGeneratorRequest -> Prelude.PutM m Prelude.Unit
putCodeGeneratorRequest :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => CodeGeneratorRequest -> Prelude.PutM m Prelude.Unit
putCodeGeneratorRequest (CodeGeneratorRequest r) = do
Prelude.putRepeated 1 r.file_to_generate Prelude.encodeStringField
Prelude.putOptional 2 r.parameter Prelude.isDefault Prelude.encodeStringField
Prelude.putRepeated 15 r.proto_file $ Prelude.putLenDel Google.Protobuf.putFileDescriptorProto
Prelude.putOptional 3 r.compiler_version (\_ -> false) $ Prelude.putLenDel putVersion
Prelude.traverse_ Prelude.putFieldUnknown r.__unknown_fields
Prelude.foldRecM (\_ x -> Prelude.putFieldUnknown x) unit r.__unknown_fields

parseCodeGeneratorRequest :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => Prelude.ByteLength -> Prelude.ParserT Prelude.DataView m CodeGeneratorRequest
parseCodeGeneratorRequest length = Prelude.label "CodeGeneratorRequest / " $
Expand Down Expand Up @@ -168,12 +168,12 @@ derive instance newtypeCodeGeneratorResponse :: Prelude.Newtype CodeGeneratorRes
derive instance eqCodeGeneratorResponse :: Prelude.Eq CodeGeneratorResponse
instance showCodeGeneratorResponse :: Prelude.Show CodeGeneratorResponse where show x = Prelude.genericShow x

putCodeGeneratorResponse :: forall m. Prelude.MonadEffect m => CodeGeneratorResponse -> Prelude.PutM m Prelude.Unit
putCodeGeneratorResponse :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => CodeGeneratorResponse -> Prelude.PutM m Prelude.Unit
putCodeGeneratorResponse (CodeGeneratorResponse r) = do
Prelude.putOptional 1 r.error Prelude.isDefault Prelude.encodeStringField
Prelude.putOptional 2 r.supported_features Prelude.isDefault Prelude.encodeUint64Field
Prelude.putRepeated 15 r.file $ Prelude.putLenDel putCodeGeneratorResponse_File
Prelude.traverse_ Prelude.putFieldUnknown r.__unknown_fields
Prelude.foldRecM (\_ x -> Prelude.putFieldUnknown x) unit r.__unknown_fields

parseCodeGeneratorResponse :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => Prelude.ByteLength -> Prelude.ParserT Prelude.DataView m CodeGeneratorResponse
parseCodeGeneratorResponse length = Prelude.label "CodeGeneratorResponse / " $
Expand Down Expand Up @@ -231,13 +231,13 @@ derive instance newtypeCodeGeneratorResponse_File :: Prelude.Newtype CodeGenerat
derive instance eqCodeGeneratorResponse_File :: Prelude.Eq CodeGeneratorResponse_File
instance showCodeGeneratorResponse_File :: Prelude.Show CodeGeneratorResponse_File where show x = Prelude.genericShow x

putCodeGeneratorResponse_File :: forall m. Prelude.MonadEffect m => CodeGeneratorResponse_File -> Prelude.PutM m Prelude.Unit
putCodeGeneratorResponse_File :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => CodeGeneratorResponse_File -> Prelude.PutM m Prelude.Unit
putCodeGeneratorResponse_File (CodeGeneratorResponse_File r) = do
Prelude.putOptional 1 r.name Prelude.isDefault Prelude.encodeStringField
Prelude.putOptional 2 r.insertion_point Prelude.isDefault Prelude.encodeStringField
Prelude.putOptional 15 r.content Prelude.isDefault Prelude.encodeStringField
Prelude.putOptional 16 r.generated_code_info (\_ -> false) $ Prelude.putLenDel Google.Protobuf.putGeneratedCodeInfo
Prelude.traverse_ Prelude.putFieldUnknown r.__unknown_fields
Prelude.foldRecM (\_ x -> Prelude.putFieldUnknown x) unit r.__unknown_fields

parseCodeGeneratorResponse_File :: forall m. Prelude.MonadEffect m => Prelude.MonadRec m => Prelude.ByteLength -> Prelude.ParserT Prelude.DataView m CodeGeneratorResponse_File
parseCodeGeneratorResponse_File length = Prelude.label "File / " $
Expand Down
4 changes: 1 addition & 3 deletions src/Protobuf/Internal/Prelude.purs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ module Protobuf.Internal.Prelude
, module Data.String
, module Type.Proxy
, module Record
, module Data.Traversable
, module Data.Tuple
, module Data.UInt
, module Prim.Row
Expand All @@ -42,7 +41,7 @@ module Protobuf.Internal.Prelude
import Type.Proxy (Proxy(..))
import Control.Alt (alt)
import Control.Monad.Rec.Class (class MonadRec) as MonadRec.Class
import Data.Array (snoc)
import Data.Array (snoc, foldRecM)
import Data.ArrayBuffer.Builder (PutM)
import Data.ArrayBuffer.Types (DataView, ByteLength)
import Data.Bounded (class Bounded)
Expand All @@ -63,7 +62,6 @@ import Data.Ord (class Ord)
import Data.Semigroup ((<>))
import Data.Show (class Show)
import Data.String (joinWith)
import Data.Traversable (traverse_)
import Data.Tuple (Tuple(..))
import Data.UInt (toInt, fromInt, UInt)
import Effect.Class (class MonadEffect) as Effect.Class
Expand Down
12 changes: 8 additions & 4 deletions src/Protobuf/Internal/Runtime.purs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import Prelude

import Control.Monad.Rec.Class (class MonadRec, tailRecM, Step(..))
import Control.Monad.Trans.Class (lift)
import Data.Array (snoc)
import Data.Array (snoc, foldRecM)
import Data.Array as Array
import Data.ArrayBuffer.Builder (DataBuff(..), PutM, subBuilder)
import Data.ArrayBuffer.Types (DataView, ByteLength)
import Data.Enum (class BoundedEnum, fromEnum, toEnum)
import Data.Foldable (foldl, traverse_)
import Data.Foldable (foldl)
import Data.Generic.Rep (class Generic)
import Data.Int64 as Int64
import Data.List (List, (:))
Expand Down Expand Up @@ -207,23 +207,27 @@ putOptional fieldNumber (Just x) isDefault encoder = do
putRepeated ::
forall m a.
MonadEffect m =>
MonadRec m =>
FieldNumberInt ->
Array a ->
(FieldNumber -> a -> PutM m Unit) ->
PutM m Unit
putRepeated fieldNumber xs encoder = flip traverse_ xs $ encoder $ UInt.fromInt fieldNumber
putRepeated fieldNumber xs encoder = foldRecM (\_ x -> encoder fn x) unit xs
where
fn = UInt.fromInt fieldNumber

putPacked ::
forall m a.
MonadEffect m =>
MonadRec m =>
FieldNumberInt ->
Array a ->
(a -> PutM m Unit) ->
PutM m Unit
putPacked _ [] _ = pure unit

putPacked fieldNumber xs encoder = do
b <- subBuilder $ traverse_ encoder xs
b <- subBuilder $ foldRecM (\_ x -> encoder x) unit xs
Encode.encodeBuilder (UInt.fromInt fieldNumber) b

putEnumField ::
Expand Down
13 changes: 7 additions & 6 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@ import Data.UInt64 (UInt64)
import Data.UInt64 as UInt64
import Data.Unfoldable (replicate)
import Effect (Effect)
import Protobuf.Internal.Decode as Decode
import Protobuf.Library (Bytes(..))
import Test.Assert (assert')
import Parsing (runParserT)
import Web.Encoding.TextEncoder as TextEncoder
-- import Effect.Console as Console
import Pack.Msg1 as Pack1
import Pack.Msg2 as Pack2
import Pack3.Msg3 as Pack3
import Pack4.Msg4 as Pack4
import Pack5.Msg5 as Pack5
import Parsing (runParserT)
import Protobuf.Internal.Decode as Decode
import Protobuf.Library (Bytes(..))
import Test.Assert (assert')
import Web.Encoding.TextEncoder as TextEncoder

billion' :: Int
billion' = -1000000000
Expand Down Expand Up @@ -88,7 +89,7 @@ main = do
Right msg1' -> assert' "msg1 roundtrip" $ msg1 == msg1'

let msg2 = (Pack2.mkMsg2
{ f1: replicate 3 1234.5
{ f1: replicate 10000 1234.5
, f2: catMaybes [Float32.fromNumber 345.6, Float32.fromNumber 345.6]
, f3: replicate 3 billion'
, f4: replicate 3 billion2'
Expand Down

0 comments on commit 07d6e84

Please sign in to comment.