diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,8 +1,15 @@
+0.3.0.0:
+  - Fix #19: Improve performance by using binary
+  - Fix #4: Suspicious amount of time and allocation on decode path
+
+0.2.1.0:
+  - Fix #17: Repeated n (Enumeration a) difficulty
+
 0.2.0.4:
-  - Fix #13 "Getting the error "Always is not a Monoid"
+  - Fix #13: Getting the error "Always is not a Monoid
 
 0.2.0.3:
-  - Fix #11 "Missing optional enum in incoming message causes decodeMessage to fail"
+  - Fix #11: Missing optional enum in incoming message causes decodeMessage to fail
 
 0.2.0.2:
   - Export {get,put}Varint* from Data.ProtocolBuffers.Internal
diff --git a/bench/Bench.hs b/bench/Bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/Bench.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Main where
+
+import Criterion.Main
+import Data
+import Nested
+
+main :: IO ()
+main = defaultMain [
+  bgroup "burst" [
+     bgroup "encoding" [
+        bench "1" $ nf enc (burst 1),
+        bench "10" $ nf enc (burst 10),
+        bench "100" $ nf enc (burst 100),
+        bench "1000" $ nf enc (burst 1000)
+        ],
+     bgroup "decoding" [
+        bench "1" $ nf decBurst (encodeBurst 1),
+        bench "10" $ nf decBurst (encodeBurst 10),
+        bench "100" $ nf decBurst (encodeBurst 100),
+        bench "1000" $ nf decBurst (encodeBurst 1000)
+        ]
+  ],
+  bgroup "nested" [
+     bgroup "encoding" [
+        bench "a1" $ nf enc (nested 1 1),
+        bench "a10" $ nf enc (nested 1 10),
+        bench "a100" $ nf enc (nested 1 100),
+        bench "a1000" $ nf enc (nested 1 1000),
+        bench "b10" $ nf enc (nested 10 1),
+        bench "b100" $ nf enc (nested 100 1),
+        bench "b1000" $ nf enc (nested 1000 1)
+     ],
+     bgroup "decoding" [
+        bench "a1" $ nf decNested (enc $ nested 1 1),
+        bench "a10" $ nf decNested (enc $ nested 1 10),
+        bench "a100" $ nf decNested (enc $ nested 1 100),
+        bench "a1000" $ nf decNested (enc $ nested 1 1000),
+        bench "b10" $ nf decNested (enc $ nested 10 1),
+        bench "b100" $ nf decNested (enc $ nested 100 1),
+        bench "b1000" $ nf decNested (enc $ nested 1000 1)
+        ]
+     ]
+  ]
diff --git a/protobuf.cabal b/protobuf.cabal
--- a/protobuf.cabal
+++ b/protobuf.cabal
@@ -1,5 +1,5 @@
 name:                protobuf
-version:             0.2.0.4
+version:             0.2.1.4
 synopsis:            Google Protocol Buffers via GHC.Generics
 description:
   Google Protocol Buffers via GHC.Generics.
@@ -37,6 +37,7 @@
     Data.ProtocolBuffers
     Data.ProtocolBuffers.Internal
     Data.ProtocolBuffers.Orphans
+    Data.Binary.Builder.Sized
   other-modules:
     Data.ProtocolBuffers.Decode
     Data.ProtocolBuffers.Encode
@@ -46,14 +47,16 @@
     Data.ProtocolBuffers.Wire
   build-depends:
     base                       >= 4.7 && < 5,
+    base-orphans               >= 0.5,
     bytestring                 >= 0.9,
-    cereal                     >= 0.3,
+    binary                     >= 0.7,
     data-binary-ieee754        >= 0.4,
     deepseq                    >= 1.1,
     mtl                        == 2.*,
     -- pretty,
     text                       >= 0.10,
-    unordered-containers       >= 0.2
+    unordered-containers       >= 0.2,
+    semigroups                 >= 0.18
   ghc-options:
     -Wall
 
@@ -89,7 +92,7 @@
   build-depends:
     base                       >= 4.7 && < 5,
     bytestring,
-    cereal,
+    binary,
     containers,
     hex,
     mtl,
@@ -102,6 +105,30 @@
     tasty-quickcheck,
     HUnit                      >= 1.2,
     QuickCheck                 >= 2.4
+
+
+benchmark bench
+  default-language:
+    Haskell2010
+  type:
+    exitcode-stdio-1.0
+  hs-source-dirs:
+    bench
+  main-is:
+    Bench.hs
+  build-depends:
+    base,
+    criterion,
+    deepseq,
+    bytestring,
+    binary,
+    containers,
+    hex,
+    mtl,
+    protobuf,
+    tagged,
+    text,
+    unordered-containers
 
 source-repository head
   type:     git
diff --git a/src/Data/Binary/Builder/Sized.hs b/src/Data/Binary/Builder/Sized.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Binary/Builder/Sized.hs
@@ -0,0 +1,56 @@
+module Data.Binary.Builder.Sized where
+
+import qualified Data.ByteString.Builder as B
+import Data.Monoid (mempty, mappend, Monoid)
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString as BS
+import Data.Semigroup (Semigroup, (<>))
+import qualified Data.Word as W
+
+data Builder = Builder {-# UNPACK #-} !Int B.Builder
+
+instance Semigroup Builder where
+  Builder i b <> Builder i' b' = Builder (i + i') (mappend b b')
+
+instance Monoid Builder where
+  mempty = Builder 0 mempty
+  mappend = (<>)
+
+toLazyByteString :: Builder -> LBS.ByteString
+toLazyByteString (Builder _ b) = B.toLazyByteString b
+
+size :: Builder -> Int
+size (Builder i _) = i
+
+empty :: Builder
+empty = mempty
+
+singleton :: W.Word8 -> Builder
+singleton = Builder 1 . B.word8
+
+append :: Builder -> Builder -> Builder
+append = mappend
+
+fromByteString :: BS.ByteString -> Builder
+fromByteString s = Builder (BS.length s) $ B.byteString s
+
+fromLazyByteString :: LBS.ByteString -> Builder
+fromLazyByteString s = Builder (fromIntegral $ LBS.length s) $ B.lazyByteString s
+
+putWord16be :: W.Word16 -> Builder
+putWord16be = Builder 2 . B.word16BE
+
+putWord32be :: W.Word32 -> Builder
+putWord32be = Builder 4 . B.word32BE
+
+putWord64be :: W.Word64 -> Builder
+putWord64be = Builder 8 . B.word64BE
+
+putWord16le :: W.Word16 -> Builder
+putWord16le = Builder 2 . B.word16LE
+
+putWord32le :: W.Word32 -> Builder
+putWord32le = Builder 4 . B.word32LE
+
+putWord64le :: W.Word64 -> Builder
+putWord64le = Builder 8 . B.word64LE
diff --git a/src/Data/ProtocolBuffers.hs b/src/Data/ProtocolBuffers.hs
--- a/src/Data/ProtocolBuffers.hs
+++ b/src/Data/ProtocolBuffers.hs
@@ -6,26 +6,30 @@
 -- and Google's reference implementation can be found at <http://code.google.com/p/protobuf/>.
 --
 -- It is intended to be used via "GHC.Generics" and does not require @ .proto @ files to function.
--- Tools are being developed that will convert a Haskell Protobuf definition into a @ .proto @ and vise versa.
+-- Tools are being developed that will convert a Haskell Protobuf definition into a @ .proto @ and vice versa.
 --
 -- Given a message definition:
 --
 -- @
 --{-\# LANGUAGE DeriveGeneric \#-}
+--{-\# LANGUAGE DataKinds     \#-}
 --
--- import "Data.Int"
+--import "Data.Int"
 --import "Data.ProtocolBuffers"
 --import "Data.Text"
 --import "GHC.Generics" ('GHC.Generics.Generic')
 --import "GHC.TypeLits"
+--import "Data.Monoid"
+--import "Data.Binary"
+--import "Data.Hex"  -- cabal install hex (for testing)
 --
 -- data Foo = Foo
---   { field1 :: 'Required' '1' ('Value' 'Data.Int.Int64') -- ^ The last field with tag = 1
---   , field2 :: 'Optional' '2' ('Value' 'Data.Text.Text') -- ^ The last field with tag = 2
---   , field3 :: 'Repeated' '3' ('Value' 'Prelude.Bool') -- ^ All fields with tag = 3, ordering is preserved
+--   { field1 :: 'Required' 1 ('Value' 'Data.Int.Int64') -- ^ The last field with tag = 1
+--   , field2 :: 'Optional' 2 ('Value' 'Data.Text.Text') -- ^ The last field with tag = 2
+--   , field3 :: 'Repeated' 3 ('Value' 'Prelude.Bool')   -- ^ All fields with tag = 3, ordering is preserved
 --   } deriving ('GHC.Generics.Generic', 'Prelude.Show')
 --
--- instance 'Encode' Foo
+--instance 'Encode' Foo
 --instance 'Decode' Foo
 -- @
 --
@@ -37,15 +41,15 @@
 --
 -- >>> let msg = Foo{field1 = putField 42, field2 = mempty, field3 = putField [True, False]}
 --
--- To serialize a message first convert it into a 'Data.Serialize.Put' by way of 'encodeMessage'
--- and then to a 'Data.ByteString.ByteString' by using 'Data.Serialize.runPut'. Lazy
--- 'Data.ByteString.Lazy.ByteString' serialization is done with 'Data.Serialize.runPutLazy'.
+-- To serialize a message first convert it into a 'Data.Binary.Put' by way of 'encodeMessage'
+-- and then to a 'Data.ByteString.ByteString' by using 'Data.Binary.runPut'. Lazy
+-- 'Data.ByteString.Lazy.ByteString' serialization is done with 'Data.Binary.runPutLazy'.
 --
 -- >>> fmap hex runPut $ encodeMessage msg
 -- "082A18011800"
 --
 -- Decoding is done with the inverse functions: 'decodeMessage'
--- and 'Data.Serialize.runGet', or 'Data.Serialize.runGetLazy'.
+-- and 'Data.Binary.runGet'.
 --
 -- >>> runGet decodeMessage =<< unhex "082A18011800" :: Either String Foo
 -- Right
diff --git a/src/Data/ProtocolBuffers/Decode.hs b/src/Data/ProtocolBuffers/Decode.hs
--- a/src/Data/ProtocolBuffers/Decode.hs
+++ b/src/Data/ProtocolBuffers/Decode.hs
@@ -16,7 +16,6 @@
 
 import Control.Applicative
 import Control.Monad
-import qualified Data.ByteString as B
 import Data.Foldable
 import Data.HashMap.Strict (HashMap)
 import qualified Data.HashMap.Strict as HashMap
@@ -24,7 +23,7 @@
 import Data.Maybe (fromMaybe)
 import Data.Monoid
 import Data.Proxy
-import Data.Serialize.Get
+import Data.Binary.Get
 import Data.Traversable (traverse)
 
 import GHC.Generics
@@ -32,6 +31,7 @@
 
 import Data.ProtocolBuffers.Types
 import Data.ProtocolBuffers.Wire
+import qualified Data.ByteString.Lazy as LBS
 
 -- |
 -- Decode a Protocol Buffers message.
@@ -46,17 +46,17 @@
       Nothing -> return msg
 
 -- |
--- Decode a Protocol Buffers message prefixed with a varint encoded 32-bit integer describing it's length.
+-- Decode a Protocol Buffers message prefixed with a varint encoded 32-bit integer describing its length.
 decodeLengthPrefixedMessage :: Decode a => Get a
 {-# INLINE decodeLengthPrefixedMessage #-}
 decodeLengthPrefixedMessage = do
   len :: Int64 <- getVarInt
-  bs <- getBytes $ fromIntegral len
-  case runGetState decodeMessage bs 0 of
-    Right (val, bs')
-      | B.null bs' -> return val
-      | otherwise  -> fail $ "Unparsed bytes leftover in decodeLengthPrefixedMessage: " ++ show (B.length bs')
-    Left err  -> fail err
+  bs <- getByteString $ fromIntegral len
+  case runGetOrFail decodeMessage (LBS.fromStrict bs) of
+   Right (bs', _, val)
+      | LBS.null bs' -> return val
+      | otherwise  -> fail $ "Unparsed bytes leftover in decodeLengthPrefixedMessage: " ++ show (LBS.length bs')
+   Left (_, _, err) -> fail err
 
 class Decode (a :: *) where
   decode :: HashMap Tag [WireField] -> Get a
@@ -120,7 +120,7 @@
   gdecode msg = fieldDecode Required msg
 
 instance (DecodeWire (PackedList a), KnownNat n) => GDecode (K1 i (Packed n a)) where
-  gdecode msg = fieldDecode PackedField msg
+  gdecode msg = fieldDecode PackedField msg <|> pure (K1 mempty)
 
 instance GDecode U1 where
   gdecode _ = return U1
diff --git a/src/Data/ProtocolBuffers/Encode.hs b/src/Data/ProtocolBuffers/Encode.hs
--- a/src/Data/ProtocolBuffers/Encode.hs
+++ b/src/Data/ProtocolBuffers/Encode.hs
@@ -12,12 +12,12 @@
   , GEncode
   ) where
 
-import qualified Data.ByteString as B
 import Data.Foldable
 import Data.HashMap.Strict (HashMap)
 import qualified Data.HashMap.Strict as HashMap
 import Data.Proxy
-import Data.Serialize.Put
+import Data.Binary.Builder.Sized
+import Data.Monoid
 
 import GHC.Generics
 import GHC.TypeLits
@@ -27,44 +27,43 @@
 
 -- |
 -- Encode a Protocol Buffers message.
-encodeMessage :: Encode a => a -> Put
+encodeMessage :: Encode a => a -> Builder
 encodeMessage = encode
 
 -- |
--- Encode a Protocol Buffers message prefixed with a varint encoded 32-bit integer describing it's length.
-encodeLengthPrefixedMessage :: Encode a => a -> Put
+-- Encode a Protocol Buffers message prefixed with a varint encoded 32-bit integer describing its length.
+encodeLengthPrefixedMessage :: Encode a => a -> Builder
 {-# INLINE encodeLengthPrefixedMessage #-}
-encodeLengthPrefixedMessage msg = do
-  let msg' = runPut $ encodeMessage msg
-  putVarUInt $ B.length msg'
-  putByteString msg'
+encodeLengthPrefixedMessage msg = (putVarUInt $ size msg') <> msg'
+  where
+    msg' = encodeMessage msg
 
 class Encode (a :: *) where
-  encode :: a -> Put
-  default encode :: (Generic a, GEncode (Rep a)) => a -> Put
+  encode :: a -> Builder
+  default encode :: (Generic a, GEncode (Rep a)) => a -> Builder
   encode = gencode . from
 
 -- | Untyped message encoding
 instance Encode (HashMap Tag [WireField]) where
-  encode = traverse_ step . HashMap.toList where
-    step = uncurry (traverse_ . encodeWire)
+  encode = foldMap step . HashMap.toList where
+    step = uncurry (foldMap . encodeWire)
 
 class GEncode (f :: * -> *) where
-  gencode :: f a -> Put
+  gencode :: f a -> Builder
 
 instance GEncode a => GEncode (M1 i c a) where
   gencode = gencode . unM1
 
 instance (GEncode a, GEncode b) => GEncode (a :*: b) where
-  gencode (x :*: y) = gencode x >> gencode y
+  gencode (x :*: y) = gencode x <> gencode y
 
 instance (GEncode a, GEncode b) => GEncode (a :+: b) where
   gencode (L1 x) = gencode x
   gencode (R1 y) = gencode y
 
 instance (EncodeWire a, KnownNat n, Foldable f) => GEncode (K1 i (Field n (f a))) where
-  gencode = traverse_ (encodeWire tag) . runField . unK1 where
+  gencode = foldMap (encodeWire tag) . runField . unK1 where
     tag = fromIntegral $ natVal (Proxy :: Proxy n)
 
 instance GEncode U1 where
-  gencode _ = return ()
+  gencode _ = empty
diff --git a/src/Data/ProtocolBuffers/Message.hs b/src/Data/ProtocolBuffers/Message.hs
--- a/src/Data/ProtocolBuffers/Message.hs
+++ b/src/Data/ProtocolBuffers/Message.hs
@@ -17,10 +17,10 @@
 import Control.Applicative
 import Control.DeepSeq (NFData(..))
 import Data.Foldable
-import Data.Monoid
-import Data.Serialize.Get
-import Data.Serialize.Put
+import Data.Monoid hiding ((<>))
+import Data.Binary.Get
 import Data.Traversable
+import Data.Semigroup (Semigroup(..))
 
 import GHC.Generics
 import GHC.TypeLits
@@ -29,6 +29,7 @@
 import Data.ProtocolBuffers.Encode
 import Data.ProtocolBuffers.Types
 import Data.ProtocolBuffers.Wire
+import qualified Data.ByteString.Lazy as LBS
 
 -- |
 -- The way to embed a message within another message.
@@ -96,9 +97,12 @@
 newtype Message m = Message {runMessage :: m}
   deriving (Eq, Foldable, Functor, Ord, Show, Traversable)
 
+instance (Generic m, GMessageMonoid (Rep m)) => Semigroup (Message m) where
+  Message x <> Message y = Message . to $ gmappend (from x) (from y)
+
 instance (Generic m, GMessageMonoid (Rep m)) => Monoid (Message m) where
   mempty = Message . to $ gmempty
-  Message x `mappend` Message y = Message . to $ gmappend (from x) (from y)
+  mappend = (<>)
 
 instance (Decode a, Monoid (Message a), KnownNat n) => GDecode (K1 i (Field n (RequiredField (Always (Message a))))) where
   gdecode = fieldDecode (Required . Always)
@@ -156,14 +160,10 @@
 type instance Required n (Message a) = Field n (RequiredField (Always (Message a)))
 
 instance (Foldable f, Encode m) => EncodeWire (f (Message m)) where
-  encodeWire t =
-    traverse_ (encodeWire t . runPut . encode . runMessage)
+  encodeWire t = foldMap (encodeWire t . encode . runMessage)
 
 instance Decode m => DecodeWire (Message m) where
-  decodeWire (DelimitedField _ bs) =
-    case runGet decodeMessage bs of
-      Right val -> pure $ Message val
-      Left err  -> fail $ "Embedded message decoding failed: " ++ show err
+  decodeWire (DelimitedField _ bs) = pure $ Message $ runGet decodeMessage $ LBS.fromStrict bs
   decodeWire _ = empty
 
 -- | Iso: @ 'FieldType' ('Required' n ('Message' a)) = a @
diff --git a/src/Data/ProtocolBuffers/Orphans.hs b/src/Data/ProtocolBuffers/Orphans.hs
--- a/src/Data/ProtocolBuffers/Orphans.hs
+++ b/src/Data/ProtocolBuffers/Orphans.hs
@@ -1,7 +1,3 @@
-{-# LANGUAGE DeriveFoldable #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
 -- |
 -- Messages containing 'Optional' 'Enumeration' fields fail to encode.
 -- This module contains orphan instances required to make these functional.
@@ -9,9 +5,6 @@
 -- For more information reference the associated ticket:
 -- <https://github.com/alphaHeavy/protobuf/issues/3>
 
-module Data.ProtocolBuffers.Orphans (Foldable) where
-
-import Data.Foldable (Foldable)
-import Data.Monoid (Last(..))
+module Data.ProtocolBuffers.Orphans () where
 
-deriving instance Foldable Last
+import Data.Orphans ()
diff --git a/src/Data/ProtocolBuffers/Types.hs b/src/Data/ProtocolBuffers/Types.hs
--- a/src/Data/ProtocolBuffers/Types.hs
+++ b/src/Data/ProtocolBuffers/Types.hs
@@ -29,7 +29,8 @@
 import Control.DeepSeq (NFData)
 import Data.Bits
 import Data.Foldable as Fold
-import Data.Monoid
+import Data.Monoid hiding ((<>))
+import Data.Semigroup (Semigroup(..))
 import Data.Traversable
 import Data.Typeable
 
@@ -38,25 +39,25 @@
 -- |
 -- 'Value' selects the normal/typical way for encoding scalar (primitive) values.
 newtype Value a       = Value       {runValue       :: a}
-  deriving (Bounded, Eq, Enum, Foldable, Functor, Monoid, Ord, NFData, Show, Traversable, Typeable)
+  deriving (Bounded, Eq, Enum, Foldable, Functor, Semigroup, Monoid, Ord, NFData, Show, Traversable, Typeable)
 
 -- |
 -- 'RequiredField' is a newtype wrapped used to break overlapping instances
 -- for encoding and decoding values
 newtype RequiredField a    = Required    {runRequired    :: a}
-  deriving (Bounded, Eq, Enum, Foldable, Functor, Monoid, Ord, NFData, Show, Traversable, Typeable)
+  deriving (Bounded, Eq, Enum, Foldable, Functor, Semigroup, Monoid, Ord, NFData, Show, Traversable, Typeable)
 
 -- |
 -- 'OptionalField' is a newtype wrapped used to break overlapping instances
 -- for encoding and decoding values
 newtype OptionalField a    = Optional    {runOptional    :: a}
-  deriving (Bounded, Eq, Enum, Foldable, Functor, Monoid, Ord, NFData, Show, Traversable, Typeable)
+  deriving (Bounded, Eq, Enum, Foldable, Functor, Semigroup, Monoid, Ord, NFData, Show, Traversable, Typeable)
 
 -- |
 -- 'RepeatedField' is a newtype wrapped used to break overlapping instances
 -- for encoding and decoding values
 newtype RepeatedField a    = Repeated    {runRepeated    :: a}
-  deriving (Bounded, Eq, Enum, Foldable, Functor, Monoid, Ord, NFData, Show, Traversable, Typeable)
+  deriving (Bounded, Eq, Enum, Foldable, Functor, Semigroup, Monoid, Ord, NFData, Show, Traversable, Typeable)
 
 -- |
 -- Fields are merely a way to hold a field tag along with its type, this shouldn't normally be referenced directly.
@@ -64,7 +65,7 @@
 -- This provides better error messages than older versions which used 'Data.Tagged.Tagged'
 --
 newtype Field (n :: Nat) a = Field {runField :: a}
-  deriving (Bounded, Eq, Enum, Foldable, Functor, Monoid, Ord, NFData, Show, Traversable, Typeable)
+  deriving (Bounded, Eq, Enum, Foldable, Functor, Semigroup, Monoid, Ord, NFData, Show, Traversable, Typeable)
 
 -- |
 -- To provide consistent instances for serialization a 'Traversable' 'Functor' is needed to
@@ -74,9 +75,12 @@
 newtype Always a = Always {runAlways :: a}
   deriving (Bounded, Eq, Enum, Foldable, Functor, Ord, NFData, Show, Traversable, Typeable)
 
+instance Semigroup (Always a) where
+  _ <> y = y
+
 instance Monoid (Always a) where
   mempty = error "Always is not a Monoid"
-  mappend _ y = y
+  mappend = (<>)
 
 -- |
 -- Functions for wrapping and unwrapping record fields.
@@ -175,24 +179,24 @@
 -- |
 -- 'Enumeration' fields use 'Prelude.fromEnum' and 'Prelude.toEnum' when encoding and decoding messages.
 newtype Enumeration a = Enumeration {runEnumeration :: a}
-  deriving (Bounded, Eq, Enum, Foldable, Functor, Ord, Monoid, NFData, Show, Traversable, Typeable)
+  deriving (Bounded, Eq, Enum, Foldable, Functor, Ord, Semigroup, Monoid, NFData, Show, Traversable, Typeable)
 
 -- |
 -- A 'Traversable' 'Functor' used to select packed sequence encoding/decoding.
 newtype PackedField a = PackedField {runPackedField :: a}
-  deriving (Eq, Foldable, Functor, Monoid, NFData, Ord, Show, Traversable, Typeable)
+  deriving (Eq, Foldable, Functor, Semigroup, Monoid, NFData, Ord, Show, Traversable, Typeable)
 
 -- |
 -- A list that is stored in a packed format.
 newtype PackedList a = PackedList {unPackedList :: [a]}
-  deriving (Eq, Foldable, Functor, Monoid, NFData, Ord, Show, Traversable, Typeable)
+  deriving (Eq, Foldable, Functor, Semigroup, Monoid, NFData, Ord, Show, Traversable, Typeable)
 
 -- |
 -- Signed integers are stored in a zz-encoded form.
 newtype Signed a = Signed a
-  deriving (Bits, Bounded, Enum, Eq, Floating, Foldable, Fractional, Functor, Integral, Monoid, NFData, Num, Ord, Real, RealFloat, RealFrac, Show, Traversable, Typeable)
+  deriving (Bits, Bounded, Enum, Eq, Floating, Foldable, Fractional, Functor, Integral, Semigroup, Monoid, NFData, Num, Ord, Real, RealFloat, RealFrac, Show, Traversable, Typeable)
 
 -- |
 -- Fixed integers are stored in little-endian form without additional encoding.
 newtype Fixed a = Fixed a
-  deriving (Bits, Bounded, Enum, Eq, Floating, Foldable, Fractional, Functor, Integral, Monoid, NFData, Num, Ord, Real, RealFloat, RealFrac, Show, Traversable, Typeable)
+  deriving (Bits, Bounded, Enum, Eq, Floating, Foldable, Fractional, Functor, Integral, Semigroup, Monoid, NFData, Num, Ord, Real, RealFloat, RealFrac, Show, Traversable, Typeable)
diff --git a/src/Data/ProtocolBuffers/Wire.hs b/src/Data/ProtocolBuffers/Wire.hs
--- a/src/Data/ProtocolBuffers/Wire.hs
+++ b/src/Data/ProtocolBuffers/Wire.hs
@@ -29,17 +29,17 @@
 import Data.Bits
 import Data.ByteString (ByteString)
 import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as LBS
 import Data.Foldable
 import Data.Int
 import Data.Monoid
-import Data.Serialize.Get
-import Data.Serialize.IEEE754
-import Data.Serialize.Put
+import Data.Binary.Get
+import qualified Data.Binary.IEEE754 as F
+import Data.Binary.Builder.Sized hiding (empty)
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
 import Data.Typeable
 import Data.Word
-import Data.Binary.IEEE754 (wordToDouble, wordToFloat)
 
 import Data.ProtocolBuffers.Types
 
@@ -59,11 +59,17 @@
   | Fixed32Field   {-# UNPACK #-} !Tag {-# UNPACK #-} !Word32 -- ^ For: fixed32, sfixed32, float
     deriving (Eq, Ord, Show, Typeable)
 
+putFloat32le :: Float -> Builder
+putFloat32le = putWord32le . F.floatToWord
+
+putFloat64le :: Double -> Builder
+putFloat64le = putWord64le . F.doubleToWord
+
 getVarintPrefixedBS :: Get ByteString
-getVarintPrefixedBS = getBytes =<< getVarInt
+getVarintPrefixedBS = getByteString =<< getVarInt
 
-putVarintPrefixedBS :: ByteString -> Put
-putVarintPrefixedBS bs = putVarUInt (B.length bs) >> putByteString bs
+putVarintPrefixedBS :: ByteString -> Builder
+putVarintPrefixedBS bs = putVarUInt (B.length bs) <> fromByteString bs
 
 getWireField :: Get WireField
 getWireField = do
@@ -78,19 +84,19 @@
     5 -> Fixed32Field   tag <$> getWord32le
     x -> fail $ "Wire type out of range: " ++ show x
 
-putWireField :: WireField -> Put
-putWireField (VarintField    t val) = putWireTag t 0 >> putVarUInt val
-putWireField (Fixed64Field   t val) = putWireTag t 1 >> putWord64le val
-putWireField (DelimitedField t val) = putWireTag t 2 >> putVarintPrefixedBS val
+putWireField :: WireField -> Builder
+putWireField (VarintField    t val) = putWireTag t 0 <> putVarUInt val
+putWireField (Fixed64Field   t val) = putWireTag t 1 <> putWord64le val
+putWireField (DelimitedField t val) = putWireTag t 2 <> putVarintPrefixedBS val
 putWireField (StartField     t    ) = putWireTag t 3
 putWireField (EndField       t    ) = putWireTag t 4
-putWireField (Fixed32Field   t val) = putWireTag t 5 >> putWord32le val
+putWireField (Fixed32Field   t val) = putWireTag t 5 <> putWord32le val
 
-putWireTag :: Tag -> Word32 -> Put
+putWireTag :: Tag -> Word32 -> Builder
 putWireTag tag typ
   | tag <= 0x1FFFFFFF, typ <= 7 = putVarUInt $ tag `shiftL` 3 .|. (typ .&. 7)
-  | tag  > 0x1FFFFFFF = fail $ "Wire tag out of range: "  ++ show tag
-  | otherwise         = fail $ "Wire type out of range: " ++ show typ
+  | tag  > 0x1FFFFFFF = error $ "Wire tag out of range: "  ++ show tag
+  | otherwise         = error $ "Wire type out of range: " ++ show typ
 
 getVarInt :: (Integral a, Bits a) => Get a
 getVarInt = go 0 0 where
@@ -103,26 +109,26 @@
 -- | This can be used on any Integral type and is needed for signed types; unsigned can use putVarUInt below.
 -- This has been changed to handle only up to 64 bit integral values (to match documentation).
 {-# INLINE putVarSInt #-}
-putVarSInt :: (Integral a, Bits a) => a -> Put
+putVarSInt :: (Integral a, Bits a) => a -> Builder
 putVarSInt bIn =
   case compare bIn 0 of
     LT -> let -- upcast to 64 bit to match documentation of 10 bytes for all negative values
               b = fromIntegral bIn
               len = 10                                -- (pred 10)*7 < 64 <= 10*7
               last'Mask = 1                           -- pred (1 `shiftL` 1)
-              go :: Int64 -> Int -> Put
-              go !i 1 = putWord8 (fromIntegral (i .&. last'Mask))
-              go !i n = putWord8 (fromIntegral (i .&. 0x7F) .|. 0x80) >> go (i `shiftR` 7) (pred n)
+              go :: Int64 -> Int -> Builder
+              go !i 1 = singleton (fromIntegral (i .&. last'Mask))
+              go !i n = singleton (fromIntegral (i .&. 0x7F) .|. 0x80) <> go (i `shiftR` 7) (pred n)
           in go b len
-    EQ -> putWord8 0
+    EQ -> singleton 0
     GT -> putVarUInt bIn
 
 -- | This should be used on unsigned Integral types only (not checked)
 {-# INLINE putVarUInt #-}
-putVarUInt :: (Integral a, Bits a) => a -> Put
+putVarUInt :: (Integral a, Bits a) => a -> Builder
 putVarUInt i
-  | i < 0x80  = putWord8 (fromIntegral i)
-  | otherwise = putWord8 (fromIntegral (i .&. 0x7F) .|. 0x80) >> putVarUInt (i `shiftR` 7)
+  | i < 0x80  = singleton (fromIntegral i)
+  | otherwise = singleton (fromIntegral (i .&. 0x7F) .|. 0x80) <> putVarUInt (i `shiftR` 7)
 
 wireFieldTag :: WireField -> Tag
 wireFieldTag f = case f of
@@ -134,7 +140,7 @@
   Fixed32Field   t _ -> t
 
 class EncodeWire a where
-  encodeWire :: Tag -> a -> Put
+  encodeWire :: Tag -> a -> Builder
 
 class DecodeWire a where
   decodeWire :: WireField -> Get a
@@ -146,121 +152,127 @@
 deriving instance DecodeWire a => DecodeWire (Last (Value a))
 
 instance EncodeWire a => EncodeWire [Value a] where
-  encodeWire t = traverse_ (encodeWire t)
+  encodeWire t = foldMap (encodeWire t)
 
 instance EncodeWire WireField where
   encodeWire t f
     | t == wireFieldTag f = putWireField f
-    | otherwise           = fail "Specified tag and field tag do not match"
+    | otherwise           = error "Specified tag and field tag do not match"
 
 instance DecodeWire WireField where
   decodeWire = pure
 
 instance EncodeWire a => EncodeWire (Value a) where
-  encodeWire t = traverse_ (encodeWire t)
+  encodeWire t = foldMap (encodeWire t)
 
 instance DecodeWire a => DecodeWire (Value a) where
   decodeWire = fmap Value . decodeWire
 
 instance EncodeWire a => EncodeWire (Maybe (Value a)) where
-  encodeWire t = traverse_ (encodeWire t)
+  encodeWire t = foldMap (encodeWire t)
 
 instance DecodeWire a => DecodeWire (Maybe (Value a)) where
   decodeWire = fmap (Just . Value) . decodeWire
 
 instance EncodeWire Int32 where
-  encodeWire t val = putWireTag t 0 >> putVarSInt val
+  encodeWire t val = putWireTag t 0 <> putVarSInt val
 
 instance DecodeWire Int32 where
   decodeWire (VarintField  _ val) = pure $ fromIntegral val
   decodeWire _ = empty
 
 instance EncodeWire Int64 where
-  encodeWire t val = putWireTag t 0 >> putVarSInt val
+  encodeWire t val = putWireTag t 0 <> putVarSInt val
 
 instance DecodeWire Int64 where
   decodeWire (VarintField  _ val) = pure $ fromIntegral val
   decodeWire _ = empty
 
 instance EncodeWire Word32 where
-  encodeWire t val = putWireTag t 0 >> putVarUInt val
+  encodeWire t val = putWireTag t 0 <> putVarUInt val
 
 instance DecodeWire Word32 where
   decodeWire (VarintField  _ val) = pure $ fromIntegral val
   decodeWire _ = empty
 
 instance EncodeWire Word64 where
-  encodeWire t val = putWireTag t 0 >> putVarUInt val
+  encodeWire t val = putWireTag t 0 <> putVarUInt val
 
 instance DecodeWire Word64 where
   decodeWire (VarintField  _ val) = pure val
   decodeWire _ = empty
 
 instance EncodeWire (Signed Int32) where
-  encodeWire t (Signed val) = putWireTag t 0 >> putVarSInt (zzEncode32 val)
+  encodeWire t (Signed val) = putWireTag t 0 <> putVarSInt (zzEncode32 val)
 
 instance DecodeWire (Signed Int32) where
   decodeWire (VarintField  _ val) = pure . Signed . zzDecode32 $ fromIntegral val
   decodeWire _ = empty
 
 instance EncodeWire (Signed Int64) where
-  encodeWire t (Signed val) = putWireTag t 0 >> putVarSInt (zzEncode64 val)
+  encodeWire t (Signed val) = putWireTag t 0 <> putVarSInt (zzEncode64 val)
 
 instance DecodeWire (Signed Int64) where
   decodeWire (VarintField  _ val) = pure . Signed . zzDecode64 $ fromIntegral val
   decodeWire _ = empty
 
 instance EncodeWire (Fixed Int32) where
-  encodeWire t (Fixed val) = putWireTag t 5 >> putWord32le (fromIntegral val)
+  encodeWire t (Fixed val) = putWireTag t 5 <> putWord32le (fromIntegral val)
 
 instance DecodeWire (Fixed Int32) where
   decodeWire (Fixed32Field _ val) = pure . Fixed $ fromIntegral val
   decodeWire _ = empty
 
 instance EncodeWire (Fixed Int64) where
-  encodeWire t (Fixed val) = putWireTag t 1 >> putWord64le (fromIntegral val)
+  encodeWire t (Fixed val) = putWireTag t 1 <> putWord64le (fromIntegral val)
 
 instance DecodeWire (Fixed Int64) where
   decodeWire (Fixed64Field _ val) = pure . Fixed $ fromIntegral val
   decodeWire _ = empty
 
 instance EncodeWire (Fixed Word32) where
-  encodeWire t (Fixed val) = putWireTag t 5 >> putWord32le val
+  encodeWire t (Fixed val) = putWireTag t 5 <> putWord32le val
 
 instance DecodeWire (Fixed Word32) where
   decodeWire (Fixed32Field _ val) = pure $ Fixed val
   decodeWire _ = empty
 
 instance EncodeWire (Fixed Word64) where
-  encodeWire t (Fixed val) = putWireTag t 1 >> putWord64le val
+  encodeWire t (Fixed val) = putWireTag t 1 <> putWord64le val
 
 instance DecodeWire (Fixed Word64) where
   decodeWire (Fixed64Field _ val) = pure $ Fixed val
   decodeWire _ = empty
 
 instance EncodeWire Bool where
-  encodeWire t val = putWireTag t 0 >> putVarUInt (if val then 1 else (0 :: Int32))
+  encodeWire t val = putWireTag t 0 <> putVarUInt (if val then 1 else (0 :: Int32))
 
 instance DecodeWire Bool where
   decodeWire (VarintField _ val) = pure $ val /= 0
   decodeWire _ = empty
 
 instance EncodeWire Float where
-  encodeWire t val = putWireTag t 5 >> putFloat32le val
+  encodeWire t val = putWireTag t 5 <> putFloat32le val
 
 instance DecodeWire Float where
-  decodeWire (Fixed32Field _ val) = pure $ wordToFloat val
+  decodeWire (Fixed32Field _ val) = pure $ F.wordToFloat val
   decodeWire _ = empty
 
 instance EncodeWire Double where
-  encodeWire t val = putWireTag t 1 >> putFloat64le val
+  encodeWire t val = putWireTag t 1 <> putFloat64le val
 
 instance DecodeWire Double where
-  decodeWire (Fixed64Field _ val) = pure $ wordToDouble val
+  decodeWire (Fixed64Field _ val) = pure $ F.wordToDouble val
   decodeWire _ = empty
 
+instance EncodeWire Builder where
+  encodeWire t val = putWireTag t 2 <> putVarUInt (size val) <> val
+
+instance EncodeWire LBS.ByteString where
+  encodeWire t val = encodeWire t $ fromLazyByteString val
+
 instance EncodeWire ByteString where
-  encodeWire t val = putWireTag t 2 >> putVarUInt (B.length val) >> putByteString val
+  encodeWire t val = encodeWire t $ fromByteString val
 
 instance DecodeWire ByteString where
   decodeWire (DelimitedField _ bs) = pure bs
@@ -284,24 +296,20 @@
 
 decodePackedList :: Get a -> WireField -> Get [a]
 {-# INLINE decodePackedList #-}
-decodePackedList g (DelimitedField _ bs) =
-  case runGet (many g) bs of
-    Right val -> return val
-    Left err  -> fail err
+decodePackedList g (DelimitedField _ bs) = return $ runGet (many g) (LBS.fromStrict bs)
 decodePackedList _ _ = empty
 
 -- |
 -- Empty lists are not written out
-encodePackedList :: Tag -> Put -> Put
+encodePackedList :: Tag -> Builder -> Builder
 {-# INLINE encodePackedList #-}
-encodePackedList t p
-  | bs <- runPut p
-  , not (B.null bs) = encodeWire t bs
-  | otherwise = pure ()
+encodePackedList t p = case size p of
+  0 -> mempty
+  _ -> encodeWire t p
 
 instance EncodeWire (PackedList (Value Int32)) where
   encodeWire t (PackedList xs) =
-    encodePackedList t $ traverse_ (putVarSInt . runValue) xs
+    encodePackedList t $ foldMap (putVarSInt . runValue) xs
 
 instance DecodeWire (PackedList (Value Int32)) where
   decodeWire x = do
@@ -310,7 +318,7 @@
 
 instance EncodeWire (PackedList (Value Int64)) where
   encodeWire t (PackedList xs) =
-    encodePackedList t $ traverse_ (putVarSInt . runValue) xs
+    encodePackedList t $ foldMap (putVarSInt . runValue) xs
 
 instance DecodeWire (PackedList (Value Int64)) where
   decodeWire x = do
@@ -319,7 +327,7 @@
 
 instance EncodeWire (PackedList (Value Word32)) where
   encodeWire t (PackedList xs) =
-    encodePackedList t $ traverse_ (putVarUInt . runValue) xs
+    encodePackedList t $ foldMap (putVarUInt . runValue) xs
 
 instance DecodeWire (PackedList (Value Word32)) where
   decodeWire x = do
@@ -328,7 +336,7 @@
 
 instance EncodeWire (PackedList (Value Word64)) where
   encodeWire t (PackedList xs) =
-    encodePackedList t $ traverse_ (putVarUInt . runValue) xs
+    encodePackedList t $ foldMap (putVarUInt . runValue) xs
 
 instance DecodeWire (PackedList (Value Word64)) where
   decodeWire x = do
@@ -338,7 +346,7 @@
 instance EncodeWire (PackedList (Value (Signed Int32))) where
   encodeWire t (PackedList xs) = do
     let c (Signed x) = putVarSInt $ zzEncode32 x
-    encodePackedList t $ traverse_ (c . runValue) xs
+    encodePackedList t $ foldMap (c . runValue) xs
 
 instance DecodeWire (PackedList (Value (Signed Int32))) where
   decodeWire x = do
@@ -348,7 +356,7 @@
 instance EncodeWire (PackedList (Value (Signed Int64))) where
   encodeWire t (PackedList xs) = do
     let c (Signed x) = putVarSInt $ zzEncode64 x
-    encodePackedList t $ traverse_ (c . runValue) xs
+    encodePackedList t $ foldMap (c . runValue) xs
 
 instance DecodeWire (PackedList (Value (Signed Int64))) where
   decodeWire x = do
@@ -358,7 +366,7 @@
 instance EncodeWire (PackedList (Value (Fixed Word32))) where
   encodeWire t (PackedList xs) = do
     let c (Fixed x) = putWord32le x
-    encodePackedList t $ traverse_ (c . runValue) xs
+    encodePackedList t $ foldMap (c . runValue) xs
 
 instance DecodeWire (PackedList (Value (Fixed Word32))) where
   decodeWire x = do
@@ -368,7 +376,7 @@
 instance EncodeWire (PackedList (Value (Fixed Word64))) where
   encodeWire t (PackedList xs) = do
     let c (Fixed x) = putWord64le x
-    encodePackedList t $ traverse_ (c . runValue) xs
+    encodePackedList t $ foldMap (c . runValue) xs
 
 instance DecodeWire (PackedList (Value (Fixed Word64))) where
   decodeWire x = do
@@ -378,7 +386,7 @@
 instance EncodeWire (PackedList (Value (Fixed Int32))) where
   encodeWire t (PackedList xs) = do
     let c (Fixed x) = putWord32le $ fromIntegral x
-    encodePackedList t $ traverse_ (c . runValue) xs
+    encodePackedList t $ foldMap (c . runValue) xs
 
 instance DecodeWire (PackedList (Value (Fixed Int32))) where
   decodeWire x = do
@@ -388,7 +396,7 @@
 instance EncodeWire (PackedList (Value (Fixed Int64))) where
   encodeWire t (PackedList xs) = do
     let c (Fixed x) = putWord64le $ fromIntegral x
-    encodePackedList t $ traverse_ (c . runValue) xs
+    encodePackedList t $ foldMap (c . runValue) xs
 
 instance DecodeWire (PackedList (Value (Fixed Int64))) where
   decodeWire x = do
@@ -397,25 +405,25 @@
 
 instance EncodeWire (PackedList (Value Float)) where
   encodeWire t (PackedList xs) =
-    encodePackedList t $ traverse_ (putFloat32le . runValue) xs
+    encodePackedList t $ foldMap (putFloat32le . runValue) xs
 
 instance DecodeWire (PackedList (Value Float)) where
   decodeWire x = do
-    xs <- decodePackedList getFloat32le x
+    xs <- decodePackedList F.getFloat32le x
     return . PackedList $ Value <$> xs
 
 instance EncodeWire (PackedList (Value Double)) where
   encodeWire t (PackedList xs) =
-    encodePackedList t $ traverse_ (putFloat64le . runValue) xs
+    encodePackedList t $ foldMap (putFloat64le . runValue) xs
 
 instance DecodeWire (PackedList (Value Double)) where
   decodeWire x = do
-    xs <- decodePackedList getFloat64le x
+    xs <- decodePackedList F.getFloat64le x
     return . PackedList $ Value <$> xs
 
 instance EncodeWire (PackedList (Value Bool)) where
   encodeWire t (PackedList xs) =
-    encodePackedList t $ traverse_ (putVarUInt . fromEnum) xs
+    encodePackedList t $ foldMap (putVarUInt . fromEnum) xs
 
 instance DecodeWire (PackedList (Value Bool)) where
   decodeWire x = do
@@ -424,7 +432,7 @@
 
 instance Enum a => EncodeWire (PackedList (Enumeration a)) where
   encodeWire t (PackedList xs) =
-    encodePackedList t $ traverse_ (putVarUInt . fromEnum) xs
+    encodePackedList t $ foldMap (putVarUInt . fromEnum) xs
 
 instance Enum a => DecodeWire (PackedList (Enumeration a)) where
   decodeWire x = do
@@ -432,9 +440,14 @@
     return . PackedList $ toEnum <$> xs
 
 instance (Foldable f, Enum a) => EncodeWire (f (Enumeration a)) where
-  encodeWire t = traverse_ (encodeWire t . c . runEnumeration) where
+  encodeWire t = foldMap (encodeWire t . c . runEnumeration) where
     c :: a -> Int32
     c = fromIntegral . fromEnum
+
+instance Enum a => DecodeWire (Enumeration a) where
+  decodeWire f = c <$> decodeWire f where
+    c :: Int32 -> Enumeration a
+    c = Enumeration . toEnum . fromIntegral
 
 instance Enum a => DecodeWire (Maybe (Enumeration a)) where
   decodeWire f = c <$> decodeWire f where
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -13,7 +13,7 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
 import Test.QuickCheck
-import Test.QuickCheck.Property
+import Test.QuickCheck.Property hiding (testCase)
 import Test.Tasty
 import Test.Tasty.HUnit
 import Test.Tasty.QuickCheck
@@ -36,11 +36,14 @@
 import Data.IntSet (IntSet)
 import qualified Data.IntSet as IntSet
 import Data.Monoid
-import Data.Serialize (Get, Putter, runGet, runPut)
+import Data.Binary.Get (Get, runGet)
+import Data.Binary.Builder.Sized (Builder, toLazyByteString)
 import Data.Proxy
 import Data.Text (Text)
 import Data.Typeable
 import Data.Word
+import qualified Data.ByteString.Lazy as LBS
+import Data.Binary.Get (runGetOrFail)
 
 main :: IO ()
 main = defaultMain tests
@@ -68,9 +71,18 @@
   , testCase "Google Reference Test2" test2
   , testCase "Google Reference Test3" test3
   , testCase "Google Reference Test4" test4
+  , testCase "Packed empty fields" test4_empty
   , testCase "Optional Enum Test5: Nothing" test5
   , testCase "Optional Enum Test5: Just Test5A" test6
   , testCase "Optional Enum Test5: Just Test5B" test7
+  , testCase "Repeated Enum Test6: []" test8
+  , testCase "Repeated Enum Test6: [Test6A]" test9
+  , testCase "Repeated Enum Test6: [Test6A, Test6B]" test10
+  , testCase "Repeated Enum Test6: [Test6A, Test6A]" test11
+  , testCase "Repeated Enum Test6: [Test6A, Test6B, Test6A]" test12
+  , testCase "Repeated Enum Test7: []" test13
+  , testCase "Repeated Enum Test7: [Test7]" test14
+  , testCase "Repeated Enum Test7: [Test7, Test7]" test15
   , testCase "Google WireFormatTest ZigZag" wireFormatZZ
   ]
 
@@ -288,14 +300,14 @@
 prop_wire _ = label ("prop_wire :: " ++ show (typeOf (undefined :: a))) $ do
   tag <- choose (0, 536870912)
   val <- arbitrary
-  let bs = runPut (encodeWire tag (val :: a))
+  let bs = toLazyByteString (encodeWire tag (val :: a))
       dec = do
         field <- getWireField
         guard $ tag == wireFieldTag field
         decodeWire field
-  case runGet dec bs of
-    Right val' -> return $ val == val'
-    Left err   -> fail err
+  case runGetOrFail dec bs of
+    Right (_, _, val') -> return $ val == val'
+    Left (_, _, err)   -> fail err
 
 prop_generic :: Gen Property
 prop_generic = do
@@ -305,34 +317,32 @@
 prop_generic_length_prefixed :: Gen Property
 prop_generic_length_prefixed = do
   msg <- HashMap.fromListWith (++) . fmap (\ c -> (wireFieldTag c, [c])) <$> listOf1 arbitrary
-  let bs = runPut $ encodeLengthPrefixedMessage (msg :: HashMap Tag [WireField])
-  case runGet decodeLengthPrefixedMessage bs of
-    Right msg' -> return $ counterexample "foo" $ msg == msg'
-    Left err   -> fail err
+  let bs = toLazyByteString $ encodeLengthPrefixedMessage (msg :: HashMap Tag [WireField])
+  case runGetOrFail decodeLengthPrefixedMessage bs of
+    Right (_, _, msg') -> return $ counterexample "foo" $ msg == msg'
+    Left (_, _, err)   -> fail err
 
 prop_roundtrip_msg :: (Eq a, Encode a, Decode a) => a -> Gen Property
 prop_roundtrip_msg msg = do
-  let bs = runPut $ encodeMessage msg
+  let bs = toLazyByteString $ encodeMessage msg
   case runGet decodeMessage bs of
-    Right msg' -> return . property $ msg == msg'
-    Left err   -> fail err
+    msg' -> return . property $ msg == msg'
 
 prop_varint_prefixed_bytestring :: Gen Property
 prop_varint_prefixed_bytestring = do
   bs <- B.pack <$> arbitrary
   prop_roundtrip_value getVarintPrefixedBS putVarintPrefixedBS bs
 
-prop_roundtrip_value :: (Eq a, Show a) => Get a -> Putter a -> a -> Gen Property
+prop_roundtrip_value :: (Eq a, Show a) => Get a -> (a -> Builder) -> a -> Gen Property
 prop_roundtrip_value get put val = do
-  let bs = runPut (put val)
+  let bs = toLazyByteString (put val)
   case runGet get bs of
-    Right val' -> return $ val === val'
-    Left err   -> fail err
+    val' -> return $ val === val'
 
 prop_encode_fail :: Encode a => a -> Gen Prop
 prop_encode_fail msg = unProperty $ ioProperty $ do
-  res <- try . evaluate . runPut $ encodeMessage msg
-  return $ case res :: Either SomeException B.ByteString of
+  res <- try . evaluate . toLazyByteString $ encodeMessage msg
+  return $ case res :: Either SomeException LBS.ByteString of
     Left  _ -> True
     Right _ -> False
 
@@ -347,7 +357,7 @@
   -- is also recommended since these are encoded as varints which have
   -- fairly high overhead for negative tags
   n <- choose (536870912, toInteger $ (maxBound :: Int))
-  case someNatVal n of 
+  case someNatVal n of
     Just (SomeNat x) -> g x
 
 prop_reify_valid_tag :: forall r . (forall n . KnownNat n => Proxy n -> Gen r) -> Gen r
@@ -359,7 +369,7 @@
   -- is also recommended since these are encoded as varints which have
   -- fairly high overhead for negative tags
   n <- choose (0, 536870911)
-  case someNatVal n of 
+  case someNatVal n of
     Just (SomeNat x) -> f x
 
 prop_req_reify :: forall a r . a -> (forall n . KnownNat n => RequiredValue n a -> Gen r) -> Gen r
@@ -400,12 +410,12 @@
 -- implement the examples from https://developers.google.com/protocol-buffers/docs/encoding
 testSpecific :: (Eq a, Show a, Encode a, Decode a) => a -> B.ByteString -> IO ()
 testSpecific msg ref = do
-  let bs = runPut $ encodeMessage msg
-  assertEqual "Encoded message mismatch" bs ref
+  let bs = toLazyByteString $ encodeMessage msg
+  assertEqual "Encoded message mismatch" (LBS.toStrict bs) ref
 
-  case runGet decodeMessage bs of
-    Right msg' -> assertEqual "Decoded message mismatch" msg msg'
-    Left err   -> assertFailure err
+  case runGetOrFail decodeMessage bs of
+    Right (_, _, msg') -> assertEqual "Decoded message mismatch" msg msg'
+    Left (_, _, err)   -> assertFailure err
 
 data Test1 = Test1{test1_a :: Required 1 (Value Int32)} deriving (Generic)
 deriving instance Eq Test1
@@ -443,11 +453,25 @@
 test4 = testSpecific msg =<< unhex "2206038e029ea705" where
   msg = Test4{test4_d = putField [3,270,86942]}
 
+test4_empty :: Assertion
+test4_empty = testSpecific msg =<< unhex "" where
+  msg = Test4{test4_d = putField mempty}
+
 data Test5Enum = Test5A | Test5B deriving (Eq, Show, Enum)
 data Test5 = Test5{test5_e :: Optional 5 (Enumeration Test5Enum)} deriving (Generic, Eq, Show)
 instance Encode Test5
 instance Decode Test5
 
+data Test6Enum = Test6A | Test6B deriving (Eq, Show, Enum)
+data Test6 = Test6{test6_e :: Repeated 6 (Enumeration Test6Enum)} deriving (Generic, Eq, Show)
+instance Encode Test6
+instance Decode Test6
+
+data Test7Enum = Test7A deriving (Eq, Show, Enum)
+data Test7 = Test7{test7_e :: Repeated 7 (Enumeration Test7Enum)} deriving (Generic, Eq, Show)
+instance Encode Test7
+instance Decode Test7
+
 test5 :: Assertion
 test5 = testSpecific msg =<< unhex "" where
   msg = Test5{test5_e = putField Nothing}
@@ -459,6 +483,38 @@
 test7 :: Assertion
 test7 = testSpecific msg =<< unhex "2801" where
   msg = Test5{test5_e = putField $ Just Test5B }
+
+test8 :: Assertion
+test8 = testSpecific msg =<< unhex "" where
+  msg = Test6{test6_e = putField $ [] }
+
+test9 :: Assertion
+test9 = testSpecific msg =<< unhex "3000" where
+  msg = Test6{test6_e = putField $ [Test6A] }
+
+test10 :: Assertion
+test10 = testSpecific msg =<< unhex "30003001" where
+  msg = Test6{test6_e = putField $ [Test6A, Test6B]}
+
+test11 :: Assertion
+test11 = testSpecific msg =<< unhex "30003000" where
+  msg = Test6{test6_e = putField $ [Test6A, Test6A]}
+
+test12 :: Assertion
+test12 = testSpecific msg =<< unhex "300030013000" where
+  msg = Test6{test6_e = putField $ [Test6A, Test6B, Test6A]}
+
+test13 :: Assertion
+test13 = testSpecific msg =<< unhex "" where
+  msg = Test7{test7_e = putField $ [] }
+
+test14 :: Assertion
+test14 = testSpecific msg =<< unhex "3800" where
+  msg = Test7{test7_e = putField $ [Test7A] }
+
+test15 :: Assertion
+test15 = testSpecific msg =<< unhex "38003800" where
+  msg = Test7{test7_e = putField $ [Test7A, Test7A] }
 
 -- some from http://code.google.com/p/protobuf/source/browse/trunk/src/google/protobuf/wire_format_unittest.cc
 wireFormatZZ :: Assertion
