Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.10.0
* [#306](https://github.com/awakesecurity/proto3-suite/pull/306) 0.10.0: Use proto3-wire 1.5.0
* Support proto3-wire 1.5 instead of proto3-wire 1.4.

# 0.9.5
* [#305](https://github.com/awakesecurity/proto3-suite/pull/305) Avoid unpacked packed fields
* The new system of typed builders exploited parser behavior that is required
Expand Down
4 changes: 2 additions & 2 deletions nix/overlays/haskell-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ in {
source = pkgsNew.fetchFromGitHub {
owner = "awakesecurity";
repo = "proto3-wire";
rev = "71a534488f01338726a5eeac81abb08b244daac4"; # 1.4.6
sha256 = "6H5Ql867gp83gOFgs7s92wa2VeaBYSYI6cmcbiXZvbM=";
rev = "3c689a9de13abbfb5666a0d25152c8d2c4a00159"; # 1.5.0
sha256 = "XIMGw3AgNA1AvdA7dIKhTfHGkuqwqIQDsMxKsDQVF1w=";
};
in
pkgsNew.haskell.lib.doJailbreak
Expand Down
46 changes: 23 additions & 23 deletions proto3-suite.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ common common

build-depends:
base >=4.15 && <5.0
, proto3-wire >= 1.4.6 && < 1.5
, proto3-wire >=1.5 && <1.6

ghc-options:
-O2
Expand All @@ -84,7 +84,7 @@ library

if flag(dhall)
exposed-modules: Proto3.Suite.DhallPB
build-depends: dhall >=1.13 && < 1.43
build-depends: dhall >=1.13 && <1.43
cpp-options: -DDHALL

if flag(swagger)
Expand Down Expand Up @@ -132,14 +132,14 @@ library
Turtle.Compat

build-depends:
aeson >= 1.1.1.0 && < 2.3
aeson >=1.1.1.0 && <2.3
, aeson-pretty
, attoparsec >= 0.13.0.1
, base64-bytestring >= 1.0.0.1 && < 1.3
, attoparsec >=0.13.0.1
, base64-bytestring >=1.0.0.1 && <1.3
, binary >=0.8.3
, bytestring >=0.10.6.0 && <0.13
, deepseq >=1.4 && <1.6
, cereal >= 0.5.1 && <0.6
, cereal >=0.5.1 && <0.6
, containers >=0.5 && <0.8
, contravariant >=1.4 && <1.6
, filepath
Expand All @@ -150,26 +150,26 @@ library
, lens
, mtl >=2.2 && <2.4
, neat-interpolation
, parsec >= 3.1.9 && <3.2
, parsers >= 0.12 && <0.13
, parsec >=3.1.9 && <3.2
, parsers >=0.12 && <0.13
, pretty ==1.1.*
, pretty-show >= 1.6.12 && < 2
, pretty-show >=1.6.12 && <2
, QuickCheck >=2.10 && <2.17
, quickcheck-instances >=0.3.26 && < 0.4
, quickcheck-instances >=0.3.26 && <0.4
, safe ==0.3.*
, split
, system-filepath
, template-haskell >=2.17 && <2.24
, text >= 0.2 && <2.2
, text >=0.2 && <2.2
, text-short >=0.1.3 && <0.2
, time
, transformers >=0.4 && <0.7
, turtle < 1.6.0 || >= 1.6.1 && < 1.7
, turtle <1.6.0 || >=1.6.1 && <1.7
, vector >=0.11 && <0.14

if flag(attoparsec-aeson)
build-depends:
attoparsec-aeson >= 2.2.0.0
attoparsec-aeson >=2.2.0.0

test-suite tests
import: common
Expand All @@ -184,7 +184,7 @@ test-suite tests

if flag(dhall)
other-modules: TestDhall
build-depends: dhall >=1.13 && < 1.43
build-depends: dhall >=1.13 && <1.43
cpp-options: -DDHALL

if flag(swagger)
Expand Down Expand Up @@ -227,28 +227,28 @@ test-suite tests

build-depends:
aeson
, attoparsec >= 0.13.0.1
, base64-bytestring >= 1.0.0.1 && < 1.3
, attoparsec >=0.13.0.1
, base64-bytestring >=1.0.0.1 && <1.3
, bytestring >=0.10.6.0 && <0.13
, cereal >= 0.5.1 && <0.6
, cereal >=0.5.1 && <0.6
, containers >=0.5 && <0.8
, deepseq >=1.4 && <1.6
, doctest
, generic-arbitrary
, ghc-lib-parser
, hedgehog
, mtl >=2.2 && <2.4
, parsec >= 3.1.9 && <3.2.0
, parsec >=3.1.9 && <3.2.0
, pretty ==1.1.*
, pretty-show >= 1.6.12 && < 2.0
, pretty-show >=1.6.12 && <2.0
, proto3-suite
, QuickCheck >=2.10 && <2.17
, record-hasfield
, tasty >= 0.11 && <1.6
, tasty >=0.11 && <1.6
, tasty-hedgehog
, tasty-hunit >= 0.9 && <0.11
, tasty-quickcheck >= 0.8.4 && <0.12
, text >= 0.2 && <2.2
, tasty-hunit >=0.9 && <0.11
, tasty-quickcheck >=0.8.4 && <0.12
, text >=0.2 && <2.2
, text-short >=0.1.3 && <0.2
, transformers >=0.4 && <0.7
, turtle
Expand Down
37 changes: 24 additions & 13 deletions src/Proto3/Suite/Form/Encode.hs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ import Proto3.Wire.Encode qualified as Encode
import Proto3.Wire.Encode.Repeated (ToRepeated, mapRepeated)
import Proto3.Wire.Reverse qualified as RB
import Proto3.Wire.Types (FieldNumber, fieldNumber)
import Proto3.Wire.Types qualified as Wire (WireType(..))

-- | The unsafe but fast inverse of 'messageEncoderToByteString'.
unsafeByteStringToMessageEncoder :: B.ByteString -> MessageEncoder message
Expand Down Expand Up @@ -246,20 +247,21 @@ cachedFieldsEncoding = UnsafeFieldsEncoder . Encode.unsafeFromByteString . untyp
{-# INLINE cachedFieldsEncoding #-}

$(instantiatePackableField
[t| 'UInt32 |] [t| Word32 |] [| Encode.uint32 |] [| Encode.packedUInt32R |]
[t| 'UInt32 |] [t| Word32 |] [| Encode.uint32 |] [| Encode.packedField @'Wire.Varint |]
[ ([t| Word16 |], [| fromIntegral @Word16 @Word32 |], [t| 'UInt32 |])
, ([t| Word8 |], [| fromIntegral @Word8 @Word32 |], [t| 'UInt32 |])
] True)

$(instantiatePackableField
[t| 'UInt64 |] [t| Word64 |] [| Encode.uint64 |] [| Encode.packedUInt64R |]
[t| 'UInt64 |] [t| Word64 |] [| Encode.uint64 |] [| Encode.packedField @'Wire.Varint |]
[ ([t| Word32 |], [| id |], [t| 'UInt32 |])
, ([t| Word16 |], [| fromIntegral @Word16 @Word32 |], [t| 'UInt32 |])
, ([t| Word8 |], [| fromIntegral @Word8 @Word32 |], [t| 'UInt32 |])
] True)

$(instantiatePackableField
[t| 'Int32 |] [t| Int32 |] [| Encode.int32 |] [| Encode.packedInt32R |]
[t| 'Int32 |] [t| Int32 |] [| Encode.int32 |]
[| \(!fn) -> Encode.packedField @'Wire.Varint fn . fmap (fromIntegral @Int32 @Word64) |]
[ ([t| Int16 |], [| fromIntegral @Int16 @Int32 |], [t| 'Int32 |])
, ([t| Int8 |], [| fromIntegral @Int8 @Int32 |], [t| 'Int32 |])
-- Because the encoding for @int32@ is just a conversion to the 64-bit unsigned
Expand All @@ -271,7 +273,8 @@ $(instantiatePackableField
] True)

$(instantiatePackableField
[t| 'Int64 |] [t| Int64 |] [| Encode.int64 |] [| Encode.packedInt64R |]
[t| 'Int64 |] [t| Int64 |] [| Encode.int64 |]
[| \(!fn) -> Encode.packedField @'Wire.Varint fn . fmap (fromIntegral @Int64 @Word64) |]
[ ([t| Int32 |], [| fromIntegral @Int32 @Int64 |], [t| 'Int64 |])
, ([t| Int16 |], [| fromIntegral @Int16 @Int64 |], [t| 'Int64 |])
, ([t| Int8 |], [| fromIntegral @Int8 @Int64 |], [t| 'Int64 |])
Expand All @@ -285,15 +288,21 @@ $(instantiatePackableField
] True)

$(instantiatePackableField
[t| 'SInt32 |] [t| Int32 |] [| Encode.sint32 |] [| Encode.packedSInt32R |]
[t| 'SInt32 |] [t| Int32 |] [| Encode.sint32 |]
[| \(!fn) ->
Encode.packedField @'Wire.Varint fn .
fmap (fromIntegral @Int32 @Word32 . Encode.zigZagEncode) |]
[ ([t| Int16 |], [| fromIntegral @Int16 @Int32 |], [t| 'SInt32 |])
, ([t| Int8 |], [| fromIntegral @Int8 @Int32 |], [t| 'SInt32 |])
, ([t| Word16 |], [| fromIntegral @Word16 @Int32 |], [t| 'SInt32 |])
, ([t| Word8 |], [| fromIntegral @Word8 @Int32 |], [t| 'SInt32 |])
] False)

$(instantiatePackableField
[t| 'SInt64 |] [t| Int64 |] [| Encode.sint64 |] [| Encode.packedSInt64R |]
[t| 'SInt64 |] [t| Int64 |] [| Encode.sint64 |]
[| \(!fn) ->
Encode.packedField @'Wire.Varint fn .
fmap (fromIntegral @Int64 @Word64 . Encode.zigZagEncode) |]
[ ([t| Int32 |], [| id |], [t| 'SInt32 |])
, ([t| Int16 |], [| id |], [t| 'SInt32 |])
, ([t| Int8 |], [| id |], [t| 'SInt32 |])
Expand All @@ -303,28 +312,30 @@ $(instantiatePackableField
] False)

$(instantiatePackableField
[t| 'Fixed32 |] [t| Word32 |] [| Encode.fixed32 |] [| Encode.packedFixed32R |]
[t| 'Fixed32 |] [t| Word32 |] [| Encode.fixed32 |] [| Encode.packedField @'Wire.Fixed32 |]
[ ([t| Word16 |], [| fromIntegral @Word16 @Word32 |], [t| 'Fixed32 |])
, ([t| Word8 |], [| fromIntegral @Word8 @Word32 |], [t| 'Fixed32 |])
] False)

$(instantiatePackableField
[t| 'Fixed64 |] [t| Word64 |] [| Encode.fixed64 |] [| Encode.packedFixed64R |]
[t| 'Fixed64 |] [t| Word64 |] [| Encode.fixed64 |] [| Encode.packedField @'Wire.Fixed64 |]
[ ([t| Word32 |], [| fromIntegral @Word32 @Word64 |], [t| 'Fixed64 |])
, ([t| Word16 |], [| fromIntegral @Word16 @Word64 |], [t| 'Fixed64 |])
, ([t| Word8 |], [| fromIntegral @Word8 @Word64 |], [t| 'Fixed64 |])
] False)

$(instantiatePackableField
[t| 'SFixed32 |] [t| Int32 |] [| Encode.sfixed32 |] [| Encode.packedSFixed32R |]
[t| 'SFixed32 |] [t| Int32 |] [| Encode.sfixed32 |]
[| \(!fn) -> Encode.packedField @'Wire.Fixed32 fn . fmap (fromIntegral @Int32 @Word32) |]
[ ([t| Int16 |], [| fromIntegral @Int16 @Int32 |], [t| 'SFixed32 |])
, ([t| Int8 |], [| fromIntegral @Int8 @Int32 |], [t| 'SFixed32 |])
, ([t| Word16 |], [| fromIntegral @Word16 @Int32 |], [t| 'SFixed32 |])
, ([t| Word8 |], [| fromIntegral @Word8 @Int32 |], [t| 'SFixed32 |])
] False)

$(instantiatePackableField
[t| 'SFixed64 |] [t| Int64 |] [| Encode.sfixed64 |] [| Encode.packedSFixed64R |]
[t| 'SFixed64 |] [t| Int64 |] [| Encode.sfixed64 |]
[| \(!fn) -> Encode.packedField @'Wire.Fixed64 fn . fmap (fromIntegral @Int64 @Word64) |]
[ ([t| Int32 |], [| fromIntegral @Int32 @Int64 |], [t| 'SFixed64 |])
, ([t| Int16 |], [| fromIntegral @Int16 @Int64 |], [t| 'SFixed64 |])
, ([t| Int8 |], [| fromIntegral @Int8 @Int64 |], [t| 'SFixed64 |])
Expand All @@ -334,15 +345,15 @@ $(instantiatePackableField
] False)

$(instantiatePackableField
[t| 'Bool |] [t| Bool |] [| Encode.bool |] [| Encode.packedBoolsR |]
[t| 'Bool |] [t| Bool |] [| Encode.bool |] [| Encode.packedField @'Wire.Varint |]
[] True)

$(instantiatePackableField
[t| 'Float |] [t| Float |] [| Encode.float |] [| Encode.packedFloatsR |]
[t| 'Float |] [t| Float |] [| Encode.float |] [| Encode.packedField @'Wire.Fixed32 |]
[] True)

$(instantiatePackableField
[t| 'Double |] [t| Double |] [| Encode.double |] [| Encode.packedDoublesR |]
[t| 'Double |] [t| Double |] [| Encode.double |] [| Encode.packedField @'Wire.Fixed64 |]
[ ([t| Float |], [| realToFrac @Float @Double |], [t| 'Double |])
] True)

Expand Down
54 changes: 18 additions & 36 deletions src/Proto3/Suite/Form/Encode/Core.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveTraversable #-}
Expand Down Expand Up @@ -77,7 +76,7 @@ import Proto3.Suite.Form
(Association, NumberOf, OneOfOf, Packing(..), Cardinality(..),
CardinalityOf, ProtoType(..), ProtoTypeOf, Wrapper)
import Proto3.Wire.Encode qualified as Encode
import Proto3.Wire.Encode.Repeated (Repeated(..), ToRepeated(..), mapRepeated)
import Proto3.Wire.Encode.Repeated (Repeated, ToRepeated(..), mapRepeated, toRepeated)
import Proto3.Wire.Types (FieldNumber, fieldNumber)

-- | Annotates 'Encode.MessageBuilder' with the type of protobuf message it encodes.
Expand Down Expand Up @@ -378,16 +377,7 @@ class Field name a message
-- See also 'fieldForm'.
field :: forall names . a -> FieldsEncoder message names (Occupy message name names)

instance forall (name :: Symbol)
#if defined(__GLASGOW_HASKELL__) && 904 <= __GLASGOW_HASKELL__
r (a :: TYPE r)
#else
(a :: Type)
-- Regarding the call to @coerce@, GHC 9.2.8 would say:
-- "Cannot use function with levity-polymorphic arguments".
-- So we just drop support for unlifted arguments until GHC 9.4.
#endif
(message :: Type) .
instance forall (name :: Symbol) (a :: Type) (message :: Type) .
( KnownFieldNumber message name
, FieldForm (CardinalityOf message name) (ProtoTypeOf message name) a
) =>
Expand Down Expand Up @@ -456,7 +446,7 @@ instance forall (name :: Symbol)
-- defined in "Proto3.Suite.Form", which declare message format without
-- specifying any policy regarding how to efficiently encode or which
-- Haskell types may be encoded.
type FieldForm :: Cardinality -> ProtoType -> forall {r} . TYPE r -> Constraint
type FieldForm :: Cardinality -> ProtoType -> Type -> Constraint
class FieldForm cardinality protoType a
where
-- | Encodes a message field with the
Expand Down Expand Up @@ -493,31 +483,24 @@ instance ( ToRepeated c e
-- accept both packed and unpacked primitives regardless of packing preference.)
instance ( ToRepeated c e
, PackedFieldForm protoType e
, FieldForm 'Optional protoType (Identity e)
) =>
FieldForm ('Repeated 'Packed) protoType c
where
fieldForm _ ty !fn (toRepeated -> !xs@(ReverseRepeated prediction reversed))
| isEmpty = mempty -- 0 elements can be expressed implicitly
| otherwise = packedFieldForm ty fn xs -- at least one packed element
-- From <https://protobuf.dev/programming-guides/encoding/>, "Repeated Elements":
--
-- "Protocol buffer parsers must be able to parse repeated fields
-- that were compiled as packed as if they were not packed, and
-- vice versa. This permits adding [packed=true] to existing
-- fields in a forward- and backward-compatible way."
--
-- Therefore in principle we could save one octet by using unpacked
-- format for a repeated field containing exactly one element.
--
-- But at present the @proto3-suite@ decoder rejects unpacked format
-- when parsing a repeated field it expects to be packed. And even
-- after we improve compatibility, saving just one octet might not
-- justify a larger and perhaps slower generated encoder.
where
isEmpty = case prediction of
Just count -> count <= 0
Nothing -> null reversed
fieldForm _ ty !fn = packedFieldForm ty fn . toRepeated
-- From <https://protobuf.dev/programming-guides/encoding/>, "Repeated Elements":
--
-- "Protocol buffer parsers must be able to parse repeated fields
-- that were compiled as packed as if they were not packed, and
-- vice versa. This permits adding [packed=true] to existing
-- fields in a forward- and backward-compatible way."
--
-- Therefore in principle we could save one octet by using unpacked
-- format for a repeated field containing exactly one element.
--
-- But at present the @proto3-suite@ decoder rejects unpacked format
-- when parsing a repeated field it expects to be packed. And even
-- after we improve compatibility, saving just one octet might not
-- justify a larger and perhaps slower generated encoder.
{-# INLINE fieldForm #-}

instance FieldForm 'Optional ('Message inner) (Identity (MessageEncoder inner))
Expand All @@ -538,7 +521,6 @@ instance FieldForm 'Optional ('Map key value) (Identity (MessageEncoder (Associa

-- | 'FieldForm' delegates to this type class when encoding
-- packed repeated fields containing two or more elements.
type PackedFieldForm :: ProtoType -> forall {r} . TYPE r -> Constraint
class PackedFieldForm protoType a
where
-- | 'fieldForm' delegates to this method when encoding
Expand Down
Loading