Skip to content

Commit

Permalink
Add mapped forward builders.
Browse files Browse the repository at this point in the history
  • Loading branch information
j6carey committed Aug 8, 2024
1 parent 0ae1c74 commit bea7a82
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 65 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
1.4.4:
- Add encoders for packed repeated fields that reverse the order of the elements.
- Add encoders for packed repeated fields that reverse
the order of the elements and/or apply a function to each
element of a Foldable container to produce the desired value.

1.4.3
- Support GHC 9.8
Expand Down
2 changes: 1 addition & 1 deletion proto3-wire.cabal
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cabal-version: >=1.10

name: proto3-wire
version: 1.4.3
version: 1.4.4
synopsis: A low-level implementation of the Protocol Buffers (version 3) wire format
license: Apache-2.0
license-file: LICENSE
Expand Down
201 changes: 138 additions & 63 deletions src/Proto3/Wire/Encode.hs
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,27 @@ module Proto3.Wire.Encode
, embedded
-- * Packed repeated fields
, packedVarints
, packedVarintsF
, packedVarintsR
, packedVarintsV
, packedBools
, packedBoolsF
, packedBoolsR
, packedBoolsV
, packedFixed32
, packedFixed32F
, packedFixed32R
, packedFixed32V
, packedFixed64
, packedFixed64F
, packedFixed64R
, packedFixed64V
, packedFloats
, packedFloatsF
, packedFloatsR
, packedFloatsV
, packedDoubles
, packedDoublesF
, packedDoublesR
, packedDoublesV
-- * ZigZag codec
Expand Down Expand Up @@ -537,29 +543,42 @@ shortByteString num = embedded num . MessageBuilder . RB.shortByteString
-- | Encode varints in the space-efficient packed format.
-- But consider 'packedVarintsV' or 'packedVarintsR', either of which may be faster.
--
-- The values to be encoded are specified by mapping the elements of a vector.
--
-- >>> packedVarints 1 [1, 2, 3]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\ETX\SOH\STX\ETX"
packedVarints :: Foldable f => FieldNumber -> f Word64 -> MessageBuilder
packedVarints num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (liftBoundedPrim . base128Varint64)
packedVarints = packedVarintsF id
{-# INLINE packedVarints #-}

-- | Encode varints in the space-efficient packed format.
-- But consider 'packedVarintsV' or 'packedVarintsR', either of which may be faster.
--
-- The values to be encoded are specified by mapping foldable elements.
--
-- Note that 'packedVarints'@ = @'packedVarintsF' 'id', but
-- implementing 'packedVarintsF' in terms of 'packedVarints'
-- would require 'Functor' (in addition to 'Foldable').
--
-- >>> packedVarintsF (subtract 10) 1 [11, 12, 13]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\ETX\SOH\STX\ETX"
packedVarintsF :: Foldable f => (a -> Word64) -> FieldNumber -> f a -> MessageBuilder
packedVarintsF f num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (liftBoundedPrim . base128Varint64 . f)
{-# INLINE packedVarintsF #-}

-- | In reverse order, encode varints in the space-efficient packed format.
--
-- >>> packedVarintsR 1 [3, 2, 1]
-- The values to be encoded are specified by mapping foldable elements.
--
-- >>> packedVarintsR (subtract 10) 1 [13, 12, 11]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\ETX\SOH\STX\ETX"
packedVarintsR :: Foldable f => FieldNumber -> f Word64 -> MessageBuilder
packedVarintsR num = etaMessageBuilder (embedded num . payload)
packedVarintsR :: Foldable f => (a -> Word64) -> FieldNumber -> f a -> MessageBuilder
packedVarintsR f num = etaMessageBuilder (embedded num . payload)
where
payload = foldr (flip (<>) . liftBoundedPrim . base128Varint64) mempty
payload = foldr (flip (<>) . liftBoundedPrim . base128Varint64 . f) mempty
{-# INLINE packedVarintsR #-}

-- | A faster but more specialized variant of:
--
-- > \f num -> packedVarints num . fmap f
-- | A faster but more specialized variant of 'packedVarintsF'.
--
-- >>> packedVarintsV (subtract 10) 1 ([11, 12, 13] :: Data.Vector.Vector Word64)
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\ETX\SOH\STX\ETX"
Expand All @@ -572,31 +591,45 @@ packedVarintsV f num = embedded num . payload

-- | A faster but more specialized variant of:
--
-- > packedVarints num . fmap (fromIntegral . fromEnum)
-- > packedVarintsF (fromIntegral . fromEnum)
--
-- >>> packedBools 1 [True, False]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\STX\SOH\NUL"
packedBools :: forall f . Foldable f => FieldNumber -> f Bool -> MessageBuilder
packedBools num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.word8 . fromIntegral . fromEnum)
packedBools :: Foldable f => FieldNumber -> f Bool -> MessageBuilder
packedBools = packedBoolsF id
{-# INLINE packedBools #-}

-- | A faster but more specialized variant of:
--
-- > packedVarintsR num . fmap (fromIntegral . fromEnum)
-- The values to be encoded are specified by mapping foldable elements.
--
-- > \f -> packedVarintsF (fromIntegral . fromEnum . f)
--
-- >>> packedBoolsR 1 [False, True]
-- >>> packedBoolsF not 1 [False, True]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\STX\SOH\NUL"
packedBoolsR :: Foldable f => FieldNumber -> f Bool -> MessageBuilder
packedBoolsR num = etaMessageBuilder (embedded num . payload)
packedBoolsF :: Foldable f => (a -> Bool) -> FieldNumber -> f a -> MessageBuilder
packedBoolsF f num = etaMessageBuilder (embedded num . payload)
where
payload = foldr (flip (<>) . MessageBuilder . RB.word8 . fromIntegral . fromEnum) mempty
payload = foldMap (MessageBuilder . RB.word8 . fromIntegral . fromEnum . f)
{-# INLINE packedBoolsF #-}

-- | A faster but more specialized variant of:
--
-- The values to be encoded are specified by mapping foldable elements.
--
-- > \f -> packedVarintsR (fromIntegral . fromEnum . f)
--
-- >>> packedBoolsR not 1 [True, False]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\STX\SOH\NUL"
packedBoolsR :: Foldable f => (a -> Bool) -> FieldNumber -> f a -> MessageBuilder
packedBoolsR f num = etaMessageBuilder (embedded num . payload)
where
payload = foldr (flip (<>) . MessageBuilder . RB.word8 . fromIntegral . fromEnum . f) mempty
{-# INLINE packedBoolsR #-}

-- | A faster but more specialized variant of:
--
-- > \f -> packedVarintsV (fromIntegral . fromEnum . f) num
-- > \f -> packedVarintsV (fromIntegral . fromEnum . f)
--
-- >>> packedBoolsV not 1 ([False, True] :: Data.Vector.Vector Bool)
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\STX\SOH\NUL"
Expand All @@ -613,24 +646,35 @@ packedBoolsV f num = embedded num . MessageBuilder . payload
-- >>> packedFixed32 1 [1, 2, 3]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\f\SOH\NUL\NUL\NUL\STX\NUL\NUL\NUL\ETX\NUL\NUL\NUL"
packedFixed32 :: Foldable f => FieldNumber -> f Word32 -> MessageBuilder
packedFixed32 num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.word32LE)
packedFixed32 = packedFixed32F id
{-# INLINE packedFixed32 #-}

-- | Encode fixed-width Word32s in the space-efficient packed format.
-- But consider 'packedFixed32V' or 'packedFixed32R', either of which may be faster.
--
-- The values to be encoded are specified by mapping foldable elements.
--
-- >>> packedFixed32F (subtract 10) 1 [11, 12, 13]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\f\SOH\NUL\NUL\NUL\STX\NUL\NUL\NUL\ETX\NUL\NUL\NUL"
packedFixed32F :: Foldable f => (a -> Word32) -> FieldNumber -> f a -> MessageBuilder
packedFixed32F f num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.word32LE . f)
{-# INLINE packedFixed32F #-}

-- | In reverse order, encode fixed-width Word32s in the space-efficient packed format.
--
-- >>> packedFixed32R 1 [3, 2, 1]
-- The values to be encoded are specified by mapping foldable elements.
--
-- >>> packedFixed32R (subtract 10) 1 [13, 12, 11]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\f\SOH\NUL\NUL\NUL\STX\NUL\NUL\NUL\ETX\NUL\NUL\NUL"
packedFixed32R :: Foldable f => FieldNumber -> f Word32 -> MessageBuilder
packedFixed32R num = etaMessageBuilder (embedded num . payload)
packedFixed32R :: Foldable f => (a -> Word32) -> FieldNumber -> f a -> MessageBuilder
packedFixed32R f num = etaMessageBuilder (embedded num . payload)
where
payload = foldr (flip (<>) . MessageBuilder . RB.word32LE) mempty
payload = foldr (flip (<>) . MessageBuilder . RB.word32LE . f) mempty
{-# INLINE packedFixed32R #-}

-- | A faster but more specialized variant of:
--
-- > \f num -> packedFixed32 num . fmap f
-- | A faster but more specialized variant of 'packedFixed32F'.
--
-- >>> packedFixed32V (subtract 10) 1 ([11, 12, 13] :: Data.Vector.Vector Word32)
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\f\SOH\NUL\NUL\NUL\STX\NUL\NUL\NUL\ETX\NUL\NUL\NUL"
Expand All @@ -647,24 +691,35 @@ packedFixed32V f num = etaMessageBuilder (embedded num . payload)
-- >>> packedFixed64 1 [1, 2, 3]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\CAN\SOH\NUL\NUL\NUL\NUL\NUL\NUL\NUL\STX\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETX\NUL\NUL\NUL\NUL\NUL\NUL\NUL"
packedFixed64 :: Foldable f => FieldNumber -> f Word64 -> MessageBuilder
packedFixed64 num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.word64LE)
packedFixed64 = packedFixed64F id
{-# INLINE packedFixed64 #-}

-- | Encode fixed-width Word64s in the space-efficient packed format.
-- But consider 'packedFixed64V' or 'packedFixed64R', either of which may be faster.
--
-- The values to be encoded are specified by mapping foldable elements.
--
-- >>> packedFixed64F (subtract 10) 1 [11, 12, 13]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\CAN\SOH\NUL\NUL\NUL\NUL\NUL\NUL\NUL\STX\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETX\NUL\NUL\NUL\NUL\NUL\NUL\NUL"
packedFixed64F :: Foldable f => (a -> Word64) -> FieldNumber -> f a -> MessageBuilder
packedFixed64F f num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.word64LE . f)
{-# INLINE packedFixed64F #-}

-- | In reverse order, encode fixed-width Word64s in the space-efficient packed format.
--
-- >>> packedFixed64R 1 [3, 2, 1]
-- The values to be encoded are specified by mapping foldable elements.
--
-- >>> packedFixed64R (subtract 10) 1 [13, 12, 11]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\CAN\SOH\NUL\NUL\NUL\NUL\NUL\NUL\NUL\STX\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETX\NUL\NUL\NUL\NUL\NUL\NUL\NUL"
packedFixed64R :: Foldable f => FieldNumber -> f Word64 -> MessageBuilder
packedFixed64R num = etaMessageBuilder (embedded num . payload)
packedFixed64R :: Foldable f => (a -> Word64) -> FieldNumber -> f a -> MessageBuilder
packedFixed64R f num = etaMessageBuilder (embedded num . payload)
where
payload = foldr (flip (<>) . MessageBuilder . RB.word64LE) mempty
payload = foldr (flip (<>) . MessageBuilder . RB.word64LE . f) mempty
{-# INLINE packedFixed64R #-}

-- | A faster but more specialized variant of:
--
-- > \f num -> packedFixed64 num . fmap f
-- | A faster but more specialized variant of 'packedFixed64F'.
--
-- >>> packedFixed64V (subtract 10) 1 ([11, 12, 13] :: Data.Vector.Vector Word64)
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\CAN\SOH\NUL\NUL\NUL\NUL\NUL\NUL\NUL\STX\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETX\NUL\NUL\NUL\NUL\NUL\NUL\NUL"
Expand All @@ -681,24 +736,35 @@ packedFixed64V f num = etaMessageBuilder (embedded num . payload)
-- >>> 1 `packedFloats` [1, 2, 3]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\f\NUL\NUL\128?\NUL\NUL\NUL@\NUL\NUL@@"
packedFloats :: Foldable f => FieldNumber -> f Float -> MessageBuilder
packedFloats num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.floatLE)
packedFloats = packedFloatsF id
{-# INLINE packedFloats #-}

-- | Encode floats in the space-efficient packed format.
-- But consider 'packedFloatsV' or 'packedFloatsR', either of which may be faster.
--
-- The values to be encoded are specified by mapping foldable elements.
--
-- >>> packedFloatsF (subtract 10) 1 [11, 12, 13]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\f\NUL\NUL\128?\NUL\NUL\NUL@\NUL\NUL@@"
packedFloatsF :: Foldable f => (a -> Float) -> FieldNumber -> f a -> MessageBuilder
packedFloatsF f num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.floatLE . f)
{-# INLINE packedFloatsF #-}

-- | In reverse order, encode floats in the space-efficient packed format.
--
-- >>> 1 `packedFloatsR` [3, 2, 1]
-- The values to be encoded are specified by mapping foldable elements.
--
-- >>> packedFloatsR (subtract 10) 1 [13, 12, 11]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\f\NUL\NUL\128?\NUL\NUL\NUL@\NUL\NUL@@"
packedFloatsR :: Foldable f => FieldNumber -> f Float -> MessageBuilder
packedFloatsR num = etaMessageBuilder (embedded num . payload)
packedFloatsR :: Foldable f => (a -> Float) -> FieldNumber -> f a -> MessageBuilder
packedFloatsR f num = etaMessageBuilder (embedded num . payload)
where
payload = foldr (flip (<>) . MessageBuilder . RB.floatLE) mempty
payload = foldr (flip (<>) . MessageBuilder . RB.floatLE . f) mempty
{-# INLINE packedFloatsR #-}

-- | A faster but more specialized variant of:
--
-- > \f num -> packedFloats num . fmap f
-- | A faster but more specialized variant of 'packedFloatsF'.
--
-- >>> packedFloatsV (subtract 10) 1 ([11, 12, 13] :: Data.Vector.Vector Float)
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\f\NUL\NUL\128?\NUL\NUL\NUL@\NUL\NUL@@"
Expand All @@ -715,24 +781,33 @@ packedFloatsV f num = etaMessageBuilder (embedded num . payload)
-- >>> 1 `packedDoubles` [1, 2, 3]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\CAN\NUL\NUL\NUL\NUL\NUL\NUL\240?\NUL\NUL\NUL\NUL\NUL\NUL\NUL@\NUL\NUL\NUL\NUL\NUL\NUL\b@"
packedDoubles :: Foldable f => FieldNumber -> f Double -> MessageBuilder
packedDoubles num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.doubleLE)
packedDoubles = packedDoublesF id
{-# INLINE packedDoubles #-}

-- | Encode doubles in the space-efficient packed format.
-- But consider 'packedDoublesV' or 'packedDoublesR', either of which may be faster.
--
-- >>> packedDoublesF (subtract 10) 1 [11, 12, 13]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\CAN\NUL\NUL\NUL\NUL\NUL\NUL\240?\NUL\NUL\NUL\NUL\NUL\NUL\NUL@\NUL\NUL\NUL\NUL\NUL\NUL\b@"
packedDoublesF :: Foldable f => (a -> Double) -> FieldNumber -> f a -> MessageBuilder
packedDoublesF f num = etaMessageBuilder (embedded num . payload)
where
payload = foldMap (MessageBuilder . RB.doubleLE . f)
{-# INLINE packedDoublesF #-}

-- | In reverse order, encode doubles in the space-efficient packed format.
--
-- >>> 1 `packedDoublesR` [3, 2, 1]
-- The values to be encoded are specified by mapping foldable elements.
--
-- >>> packedDoublesR (subtract 10) 1 [13, 12, 11]
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\CAN\NUL\NUL\NUL\NUL\NUL\NUL\240?\NUL\NUL\NUL\NUL\NUL\NUL\NUL@\NUL\NUL\NUL\NUL\NUL\NUL\b@"
packedDoublesR :: Foldable f => FieldNumber -> f Double -> MessageBuilder
packedDoublesR num = etaMessageBuilder (embedded num . payload)
packedDoublesR :: Foldable f => (a -> Double) -> FieldNumber -> f a -> MessageBuilder
packedDoublesR f num = etaMessageBuilder (embedded num . payload)
where
payload = foldr (flip (<>) . MessageBuilder . RB.doubleLE) mempty
payload = foldr (flip (<>) . MessageBuilder . RB.doubleLE . f) mempty
{-# INLINE packedDoublesR #-}

-- | A faster but more specialized variant of:
--
-- > \f num -> packedDoubles num . fmap f
-- | A faster but more specialized variant of 'packedDoublesF'.
--
-- >>> packedDoublesV (subtract 10) 1 ([11, 12, 13] :: Data.Vector.Vector Double)
-- Proto3.Wire.Encode.unsafeFromLazyByteString "\n\CAN\NUL\NUL\NUL\NUL\NUL\NUL\240?\NUL\NUL\NUL\NUL\NUL\NUL\NUL@\NUL\NUL\NUL\NUL\NUL\NUL\b@"
Expand Down

0 comments on commit bea7a82

Please sign in to comment.