diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2014, National ICT Australia Limited (NICTA)
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Maxwell Swadling nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,225 @@
+protobuf-native
+===============
+
+`protobuf-native` uses the code generated from protobuf for C++ in Haskell
+to go between Haskell and protobuf data structures.
+
+It makes use of Template Haskell to assist in generating the interface
+between protobuf and your data structures.
+
+Objects have finalizers so you never need to worry about memory management.
+
+Usage
+-----
+
+```haskell
+protobuf :: FilePath -> Name -> Q [Dec]
+```
+
+`protobuf` is a Template Haskell splice that takes the file path to a
+compiled protobuf object file and the name of the data type you want to
+build bindings to.
+
+The data type must:
+
+- Have a name the same as the protobuf message name with a `T` appended
+- Be a record data structure
+- Have a single constructor with the name of the data type with the final `T` omitted
+- If a field's name is a reserved word, it may have an `_` appended
+
+For example, if we have a Person protobuf structure in the file `person.proto`:
+
+```
+message Name {
+  optional string firstname = 1;
+  optional string lastname = 2;
+}
+
+message Person {
+  required Name name = 1;
+  required int32 id = 2;
+  optional string email = 3;
+}
+```
+
+First we run `protoc --cpp_out=. person.proto` then compile the `person.pb.cc`
+file. **Unfortunately**, at this point, you need to mangle the C++ header file as
+per the **Protobuf Mangling Guide** below, ideally this would be automated.
+Do not re-run `protoc` unless you want to re-mangle the file.
+Always check these files in to source control.
+
+Then we can write two Haskell data structures to represent these types:
+
+```haskell
+{-# LANGUAGE TemplateHaskell, MultiParamTypeClasses #-}
+import Data.Protobuf
+
+data NameT = Name { firstname :: Maybe String, lastname :: Maybe String}
+  deriving (Show, Eq)
+protobuf "person.pb.o" ''NameT
+
+data PersonT = Person { name :: NameT, id :: Int, email :: String }
+  deriving (Show, Eq)
+protobuf "person.pb.o" ''PersonT
+```
+
+If you get any of this wrong, you will get a compiler error.
+
+Note that `NameT` is the type of the `name` field in `PersonT`. With *data
+kinds* you may get confusing error messages here.
+
+Now we can:
+
+1. Construct a new Haskell `PersonT` value
+2. Send the Haskell value to a protobuf struct with `assign`
+3. Write it to a file using `writeProtobuf` which uses `SerializeToOstream`
+4. Read that file using `loadProtobuf` which uses `ParseFromIstream`
+5. Load that protobuf struct with `load`
+6. Comapre the values
+
+```haskell
+main = do
+  let val = (Person (Name (Just "Max") Nothing) 1 "maxwell.swadling@nicta.com.au")
+  person <- newPb :: IO PersonPtr
+  assign person val
+  writeProtobuf "person.pb" person
+  
+  person2 <- new :: IO PersonPtr
+  readProtobuf "person.pb" person2
+  val2 <- derefPb person2
+  print $ val == val2
+```
+
+The C++ are `ForeignPtr`s with finalizers, so you do not need to free anything.
+
+Working with **Cabal** requires extra build steps. See this project's `Setup.hs`
+for an example on how to run `protoc` and `clang++` in the build phase.
+
+Testing
+-------
+
+The property above is used by QuickCheck to test the library works.
+The tests are located in `tests/Tests.hs`.
+Namely, for all types in `Protobuf` the following property holds:
+
+```haskell
+testProtobuf b = do
+  x <- run $ do
+    p <- newPb
+    assign p b
+    v <- derefPb p
+    return $ b == v
+  assert x
+```
+
+By profiling the tests you can verify the library does not leak memory.
+
+Protobuf Mangling Guide
+-----------------------
+
+This is a temporary measure until we make a post-processor for protobuf header
+files.
+
+- Inline functions need to be un-inlined so they are linkable.
+  If you see something like:
+
+```
+Exception when trying to run compile-time code:
+  symbol "Name::clear_firstname()" missing from object file
+Code: protobuf "tests/person.pb.o" ''NameT
+```
+
+This is an inline function that must be un-inlined. 
+
+Go to the header file and search for that function name (in this
+case `clear_firstname`). You will find two occurances:
+
+```
+inline void clear_firstname();
+...
+inline void Name::clear_firstname() {
+  if (firstname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+...
+```
+
+Remove the `inline` from both of these.
+
+- When you see the linker error that some function could not be found, it is
+  probably still marked inline. Such as:
+
+```
+Undefined symbols for architecture x86_64:
+  "Name::set_lastname(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", referenced from:
+      _c73i_info in Person.o
+```
+
+Go to the header file and find `set_lastname` and remove the `inline`. It may also
+look like an field name, such as:
+
+```
+"Name::lastname() const", referenced from:
+      _c71z_info in Person.o
+```
+
+Remove the inline from:
+```
+inline const ::std::string& lastname() const;
+```
+
+When dealing with a string setter, you only need to un-inline the `std::string`
+function. I.e.
+```
+  /*inline*/ void set_email(const ::std::string& value);
+  inline void set_email(const char* value);
+  inline void set_email(const char* value, size_t size);
+```
+
+- We currently exchange protobuf values via "set and delete". If you see the linker error that an `add_x(x *)` symbol is missing, then "set and delete" needs to
+  be implemented for that field `x`. For example, if the `Graph::add_nodes(Node*)`
+  function is missing we need to add the following function and declaration to the
+  header file:
+
+```
+// in the class
+void add_nodes(Node *);
+
+// in the impl
+void Graph::add_nodes(Node *x) {
+  ::Node *n = nodes_.Add();
+  *n = *x;
+  delete x;
+}
+```
+
+This is currently another work around that is fixable.
+
+Related Work
+------------
+
+- http://hackage.haskell.org/package/protobuf lets you write Protobuf structures
+  in plain Haskell.
+- http://hackage.haskell.org/package/protocol-buffers generates Haskell code from
+  the proto files.
+- `protobuf-native` requries the user to both write proto files and Haskell
+  structures. It does not require the user to write marshalling code.
+  The main advantage is the ability to use C++ with these data structures.
+
+If your problem has performance constraints you may want to consider using this
+library. When working with large protobuf files, you may want to write file
+iterators / network operations in C++ and process the data in Haskell.
+This library lets you only pay for converting the parts of the data
+structure you need. You can, for example, iterate over a large file 20 
+elements at a time and only pull out the components of the protobuf structure
+you need to pass to Haskell.
+
+Future work
+-----------
+
+- The C++ protobuf implementations should be mangled automatically. A sufficiently
+  complex awk program would suffice.
+- Currently it is a lot of manual work writing data structures to match the
+  protobuf files. We should parse the protobuf files and generate the
+  data type definitions.
+- Support unknown field data.
+- It should support `Text`.
+- It should use `Vector` instead of `[]` for `repeated` values.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,32 @@
+import Distribution.Simple
+import Distribution.Simple.Setup
+import Distribution.Simple.Program
+import Distribution.Simple.Program.Types
+import Distribution.Simple.LocalBuildInfo
+import Distribution.PackageDescription
+
+main :: IO ()
+main = defaultMainWithHooks simpleUserHooks {
+    buildHook = buildCPlusPlus
+  }
+
+buildCPlusPlus :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()
+buildCPlusPlus pkg buildInfo hooks flags = do
+  -- library
+  progc "clang++" flags
+    ["-stdlib=libc++", "-o", "cbits/hsprotobuf.o", "-c", "cbits/hsprotobuf.cc"]
+  case executables pkg of
+    [Executable {exeName = "protobuf-native-test"}] -> do
+      progc "clang++" flags
+        ["-stdlib=libc++", "-o", "tests/person.pb.o", "-c", "tests/person.pb.cc"]
+    [] -> return ()
+    e -> error $ "unknown build target: " ++ show e
+  buildHook simpleUserHooks pkg buildInfo hooks flags
+
+progc prog flags cc_flags = do
+  let verb = fromFlag (buildVerbosity flags)
+  clang <- findProgramLocation verb prog
+  let clang' = case clang of
+                Just x -> x
+                Nothing -> error $ prog ++ " not on path"
+  runProgram verb (simpleConfiguredProgram prog (FoundOnSystem clang')) cc_flags
diff --git a/benchmark/Benchmark.hs b/benchmark/Benchmark.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/Benchmark.hs
@@ -0,0 +1,26 @@
+module Main where
+
+-- This isn't a very good benchmark.
+
+import Criterion.Main
+import Test.QuickCheck
+
+import DataProtobuf
+import DataProtocolBuffers
+import TextProtocolBuffers
+
+main = defaultMain [
+    bgroup "Data.Protobuf" [
+      bench "some" $ whnfIO $ mapM cBenchmark [1..1000]
+    , bench "lots" $ whnfIO $ mapM cBenchmark [1..10000]
+    ]
+  , bgroup "Data.ProtocolBuffers" [
+      bench "some" $ whnfIO $ mapM protocolBuffers [1..1000]
+    , bench "lots" $ whnfIO $ mapM protocolBuffers [1..10000]
+    ]
+  , bgroup "Text.ProtocolBuffers" [
+      bench "some" $ whnfIO $ mapM pBench [1..1000]
+    , bench "lots" $ whnfIO $ mapM pBench [1..10000]
+    ]
+  -- TODO: , bgroup "c++"
+  ]
diff --git a/benchmark/DataProtobuf.hs b/benchmark/DataProtobuf.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/DataProtobuf.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE DeriveGeneric, DataKinds, OverloadedStrings #-}
+module DataProtobuf (cBenchmark) where
+
+import Person
+import Data.Protobuf
+
+cBenchmark n = do
+  let val = Person (Name (Just "Max") Nothing) n (Just "maxwell.swadling@nicta.com.au")
+  person <- newPb :: IO PersonPtr
+  assign person val
+  val2 <- derefPb person
+  if (val /= val2)
+    then error "assert fail"
+    else return ()
diff --git a/benchmark/DataProtocolBuffers.hs b/benchmark/DataProtocolBuffers.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/DataProtocolBuffers.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE DeriveGeneric, DataKinds, OverloadedStrings #-}
+
+module DataProtocolBuffers (protocolBuffers) where
+
+import Data.Int
+import Data.ProtocolBuffers
+import Data.Serialize
+import Data.Text
+import GHC.Generics (Generic)
+import GHC.TypeLits
+
+data Name = Name
+  { firstname :: Optional 1 (Value Text)
+  , lastname :: Optional 2 (Value Text)
+  } deriving (Generic, Show, Eq)
+
+instance Encode Name
+instance Decode Name
+
+data Person = Person
+  { name :: Required 1 (Message Name)
+  , id :: Required 2 (Value Int32)
+  , email :: Optional 3 (Value Text)
+  } deriving (Generic, Show, Eq)
+
+instance Encode Person
+instance Decode Person
+
+protocolBuffers n = do
+  let name = Name { firstname = putField (Just "Max"), lastname = putField Nothing }
+  let msg = Person
+             { name = putField name
+             , DataProtocolBuffers.id = putField n
+             , email = putField (Just "maxwell.swadling@nicta.com.au")}
+  if (Right msg /= runGet decodeMessage (runPut (encodeMessage msg)))
+    then error "assert fail"
+    else return ()
diff --git a/benchmark/TestsPerson/Name.hs b/benchmark/TestsPerson/Name.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/TestsPerson/Name.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE BangPatterns, DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses #-}
+module TestsPerson.Name (Name(..)) where
+import Prelude ((+), (/))
+import qualified Prelude as Prelude'
+import qualified Data.Typeable as Prelude'
+import qualified Data.Data as Prelude'
+import qualified Text.ProtocolBuffers.Header as P'
+ 
+data Name = Name{firstname :: !(P'.Maybe P'.Utf8), lastname :: !(P'.Maybe P'.Utf8)}
+          deriving (Prelude'.Show, Prelude'.Eq, Prelude'.Ord, Prelude'.Typeable, Prelude'.Data)
+ 
+instance P'.Mergeable Name where
+  mergeAppend (Name x'1 x'2) (Name y'1 y'2) = Name (P'.mergeAppend x'1 y'1) (P'.mergeAppend x'2 y'2)
+ 
+instance P'.Default Name where
+  defaultValue = Name P'.defaultValue P'.defaultValue
+ 
+instance P'.Wire Name where
+  wireSize ft' self'@(Name x'1 x'2)
+   = case ft' of
+       10 -> calc'Size
+       11 -> P'.prependMessageSize calc'Size
+       _ -> P'.wireSizeErr ft' self'
+    where
+        calc'Size = (P'.wireSizeOpt 1 9 x'1 + P'.wireSizeOpt 1 9 x'2)
+  wirePut ft' self'@(Name x'1 x'2)
+   = case ft' of
+       10 -> put'Fields
+       11 -> do
+               P'.putSize (P'.wireSize 10 self')
+               put'Fields
+       _ -> P'.wirePutErr ft' self'
+    where
+        put'Fields
+         = do
+             P'.wirePutOpt 10 9 x'1
+             P'.wirePutOpt 18 9 x'2
+  wireGet ft'
+   = case ft' of
+       10 -> P'.getBareMessageWith update'Self
+       11 -> P'.getMessageWith update'Self
+       _ -> P'.wireGetErr ft'
+    where
+        update'Self wire'Tag old'Self
+         = case wire'Tag of
+             10 -> Prelude'.fmap (\ !new'Field -> old'Self{firstname = Prelude'.Just new'Field}) (P'.wireGet 9)
+             18 -> Prelude'.fmap (\ !new'Field -> old'Self{lastname = Prelude'.Just new'Field}) (P'.wireGet 9)
+             _ -> let (field'Number, wire'Type) = P'.splitWireTag wire'Tag in P'.unknown field'Number wire'Type old'Self
+ 
+instance P'.MessageAPI msg' (msg' -> Name) Name where
+  getVal m' f' = f' m'
+ 
+instance P'.GPB Name
+ 
+instance P'.ReflectDescriptor Name where
+  getMessageInfo _ = P'.GetMessageInfo (P'.fromDistinctAscList []) (P'.fromDistinctAscList [10, 18])
+  reflectDescriptorInfo _
+   = Prelude'.read
+      "DescriptorInfo {descName = ProtoName {protobufName = FIName \".TestsPerson.Name\", haskellPrefix = [], parentModule = [MName \"TestsPerson\"], baseName = MName \"Name\"}, descFilePath = [\"TestsPerson\",\"Name.hs\"], isGroup = False, fields = fromList [FieldInfo {fieldName = ProtoFName {protobufName' = FIName \".TestsPerson.Name.firstname\", haskellPrefix' = [], parentModule' = [MName \"TestsPerson\",MName \"Name\"], baseName' = FName \"firstname\"}, fieldNumber = FieldId {getFieldId = 1}, wireTag = WireTag {getWireTag = 10}, packedTag = Nothing, wireTagLength = 1, isPacked = False, isRequired = False, canRepeat = False, mightPack = False, typeCode = FieldType {getFieldType = 9}, typeName = Nothing, hsRawDefault = Nothing, hsDefault = Nothing},FieldInfo {fieldName = ProtoFName {protobufName' = FIName \".TestsPerson.Name.lastname\", haskellPrefix' = [], parentModule' = [MName \"TestsPerson\",MName \"Name\"], baseName' = FName \"lastname\"}, fieldNumber = FieldId {getFieldId = 2}, wireTag = WireTag {getWireTag = 18}, packedTag = Nothing, wireTagLength = 1, isPacked = False, isRequired = False, canRepeat = False, mightPack = False, typeCode = FieldType {getFieldType = 9}, typeName = Nothing, hsRawDefault = Nothing, hsDefault = Nothing}], keys = fromList [], extRanges = [], knownKeys = fromList [], storeUnknown = False, lazyFields = False}"
diff --git a/benchmark/TestsPerson/Person.hs b/benchmark/TestsPerson/Person.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/TestsPerson/Person.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE BangPatterns, DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses #-}
+module TestsPerson.Person (Person(..)) where
+import Prelude ((+), (/))
+import qualified Prelude as Prelude'
+import qualified Data.Typeable as Prelude'
+import qualified Data.Data as Prelude'
+import qualified Text.ProtocolBuffers.Header as P'
+import qualified TestsPerson.Name as TestsPerson (Name)
+ 
+data Person = Person{name :: !TestsPerson.Name, id :: !P'.Int32, email :: !(P'.Maybe P'.Utf8)}
+            deriving (Prelude'.Show, Prelude'.Eq, Prelude'.Ord, Prelude'.Typeable, Prelude'.Data)
+ 
+instance P'.Mergeable Person where
+  mergeAppend (Person x'1 x'2 x'3) (Person y'1 y'2 y'3)
+   = Person (P'.mergeAppend x'1 y'1) (P'.mergeAppend x'2 y'2) (P'.mergeAppend x'3 y'3)
+ 
+instance P'.Default Person where
+  defaultValue = Person P'.defaultValue P'.defaultValue P'.defaultValue
+ 
+instance P'.Wire Person where
+  wireSize ft' self'@(Person x'1 x'2 x'3)
+   = case ft' of
+       10 -> calc'Size
+       11 -> P'.prependMessageSize calc'Size
+       _ -> P'.wireSizeErr ft' self'
+    where
+        calc'Size = (P'.wireSizeReq 1 11 x'1 + P'.wireSizeReq 1 5 x'2 + P'.wireSizeOpt 1 9 x'3)
+  wirePut ft' self'@(Person x'1 x'2 x'3)
+   = case ft' of
+       10 -> put'Fields
+       11 -> do
+               P'.putSize (P'.wireSize 10 self')
+               put'Fields
+       _ -> P'.wirePutErr ft' self'
+    where
+        put'Fields
+         = do
+             P'.wirePutReq 10 11 x'1
+             P'.wirePutReq 16 5 x'2
+             P'.wirePutOpt 26 9 x'3
+  wireGet ft'
+   = case ft' of
+       10 -> P'.getBareMessageWith update'Self
+       11 -> P'.getMessageWith update'Self
+       _ -> P'.wireGetErr ft'
+    where
+        update'Self wire'Tag old'Self
+         = case wire'Tag of
+             10 -> Prelude'.fmap (\ !new'Field -> old'Self{name = P'.mergeAppend (name old'Self) (new'Field)}) (P'.wireGet 11)
+             16 -> Prelude'.fmap (\ !new'Field -> old'Self{id = new'Field}) (P'.wireGet 5)
+             26 -> Prelude'.fmap (\ !new'Field -> old'Self{email = Prelude'.Just new'Field}) (P'.wireGet 9)
+             _ -> let (field'Number, wire'Type) = P'.splitWireTag wire'Tag in P'.unknown field'Number wire'Type old'Self
+ 
+instance P'.MessageAPI msg' (msg' -> Person) Person where
+  getVal m' f' = f' m'
+ 
+instance P'.GPB Person
+ 
+instance P'.ReflectDescriptor Person where
+  getMessageInfo _ = P'.GetMessageInfo (P'.fromDistinctAscList [10, 16]) (P'.fromDistinctAscList [10, 16, 26])
+  reflectDescriptorInfo _
+   = Prelude'.read
+      "DescriptorInfo {descName = ProtoName {protobufName = FIName \".TestsPerson.Person\", haskellPrefix = [], parentModule = [MName \"TestsPerson\"], baseName = MName \"Person\"}, descFilePath = [\"TestsPerson\",\"Person.hs\"], isGroup = False, fields = fromList [FieldInfo {fieldName = ProtoFName {protobufName' = FIName \".TestsPerson.Person.name\", haskellPrefix' = [], parentModule' = [MName \"TestsPerson\",MName \"Person\"], baseName' = FName \"name\"}, fieldNumber = FieldId {getFieldId = 1}, wireTag = WireTag {getWireTag = 10}, packedTag = Nothing, wireTagLength = 1, isPacked = False, isRequired = True, canRepeat = False, mightPack = False, typeCode = FieldType {getFieldType = 11}, typeName = Just (ProtoName {protobufName = FIName \".TestsPerson.Name\", haskellPrefix = [], parentModule = [MName \"TestsPerson\"], baseName = MName \"Name\"}), hsRawDefault = Nothing, hsDefault = Nothing},FieldInfo {fieldName = ProtoFName {protobufName' = FIName \".TestsPerson.Person.id\", haskellPrefix' = [], parentModule' = [MName \"TestsPerson\",MName \"Person\"], baseName' = FName \"id\"}, fieldNumber = FieldId {getFieldId = 2}, wireTag = WireTag {getWireTag = 16}, packedTag = Nothing, wireTagLength = 1, isPacked = False, isRequired = True, canRepeat = False, mightPack = False, typeCode = FieldType {getFieldType = 5}, typeName = Nothing, hsRawDefault = Nothing, hsDefault = Nothing},FieldInfo {fieldName = ProtoFName {protobufName' = FIName \".TestsPerson.Person.email\", haskellPrefix' = [], parentModule' = [MName \"TestsPerson\",MName \"Person\"], baseName' = FName \"email\"}, fieldNumber = FieldId {getFieldId = 3}, wireTag = WireTag {getWireTag = 26}, packedTag = Nothing, wireTagLength = 1, isPacked = False, isRequired = False, canRepeat = False, mightPack = False, typeCode = FieldType {getFieldType = 9}, typeName = Nothing, hsRawDefault = Nothing, hsDefault = Nothing}], keys = fromList [], extRanges = [], knownKeys = fromList [], storeUnknown = False, lazyFields = False}"
diff --git a/benchmark/TextProtocolBuffers.hs b/benchmark/TextProtocolBuffers.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/TextProtocolBuffers.hs
@@ -0,0 +1,12 @@
+{-# LANGUAGE OverloadedStrings #-}
+module TextProtocolBuffers (pBench) where
+
+import TestsPerson.Person
+import TestsPerson.Name
+import Text.ProtocolBuffers
+
+pBench n = do
+  let val = Person (Name (Just (fromString "Max")) Nothing) n (Just (fromString "maxwell.swadling@nicta.com.au"))
+  if (Right (val, "") /= messageGet (messagePut val))
+    then error "assert fail"
+    else return ()
diff --git a/cbits/hsprotobuf.cc b/cbits/hsprotobuf.cc
new file mode 100644
--- /dev/null
+++ b/cbits/hsprotobuf.cc
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+#include <string>
+#include <iostream>
+#include <fstream>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/message.h>
+
+using namespace std;
+
+namespace haskell {
+
+int readProtobuf(char const *file, ::google::protobuf::Message &message) {
+  fstream input(file, ios::in | ios::binary);
+  if (!input) {
+    return -2;
+  } else if (!message.ParseFromIstream(&input)) {
+    return -1;
+  }
+  return 0;
+}
+
+int writeProtobuf(char const *file, ::google::protobuf::Message &message) {
+  fstream output(file, ios::out | ios::trunc | ios::binary);
+  if (!message.SerializeToOstream(&output)) {
+    return -1;
+  }
+  return 0;
+}
+
+}
diff --git a/protobuf-native.cabal b/protobuf-native.cabal
new file mode 100644
--- /dev/null
+++ b/protobuf-native.cabal
@@ -0,0 +1,55 @@
+name:                protobuf-native
+version:             1.0.0.0
+synopsis:            Protocol Buffers via C++
+description:         
+  <<http://i.imgur.com/Ns5hntl.jpg>>
+  .
+  protobuf-native uses the code generated from protobuf for C++
+  in Haskell.
+homepage:            https://github.com/nicta/protobuf-native
+license:             BSD3
+license-file:        LICENSE
+author:              Maxwell Swadling
+maintainer:          maxwell.swadling@nicta.com.au
+copyright:           Copyright (c) 2014, National ICT Australia Limited (NICTA)
+category:            Data
+build-type:          Custom
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Data.Protobuf
+  other-extensions:    TemplateHaskell, MultiParamTypeClasses, FunctionalDependencies, UnicodeSyntax, KindSignatures, OverlappingInstances, FlexibleInstances, ScopedTypeVariables, FlexibleContexts, EmptyDataDecls
+  build-depends:       base >=4.6 && <4.8, bytestring >=0.10 && <0.11, text >= 1 && <= 2, template-haskell, cplusplus-th == 1.*
+  hs-source-dirs:      src
+  extra-libraries:     protobuf, c++
+  c-sources:           cbits/hsprotobuf.cc
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+
+executable protobuf-native-test
+  -- Remember to check out the Setup.hs for the build hook
+  main-is:             Main.hs
+  other-modules:       Person
+  build-depends:       protobuf-native, base, bytestring
+  c-sources:           tests/person.pb.cc
+  hs-source-dirs:      tests
+  default-language:    Haskell2010
+
+test-suite quickcheck
+  type:                exitcode-stdio-1.0
+  main-is:             Tests.hs
+  other-modules:       Person
+  c-sources:           tests/person.pb.cc
+  hs-source-dirs:      tests
+  build-depends:       protobuf-native, base, bytestring, QuickCheck == 2.7.6
+  default-language:    Haskell2010
+
+test-suite benchmark
+  type:                exitcode-stdio-1.0
+  main-is:             Benchmark.hs
+  hs-source-dirs:      benchmark, tests
+  other-modules:       DataProtobuf, TextProtocolBuffers, DataProtocolBuffers, Person, TestsPerson.Name, TestsPerson.Person
+  c-sources:           tests/person.pb.cc
+  build-depends:       protobuf-native, base, bytestring, text, QuickCheck == 2.7.6, criterion == 1.0.2.0, protobuf == 0.2.0.4, cereal == 0.4.1.0, cplusplus-th, protocol-buffers-fork == 2.0.16, hprotoc-fork == 2.0.16.1, utf8-string == 0.3.8
+  default-language:    Haskell2010
diff --git a/src/Data/Protobuf.hs b/src/Data/Protobuf.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Protobuf.hs
@@ -0,0 +1,409 @@
+{-# LANGUAGE TemplateHaskell, MultiParamTypeClasses, FunctionalDependencies, UnicodeSyntax, KindSignatures, OverlappingInstances, FlexibleInstances, ScopedTypeVariables, FlexibleContexts #-}
+module Data.Protobuf (
+    protobuf
+  , protobuf'
+  , readProtobuf
+  , writeProtobuf
+  , Protobuf
+  , ProtobufValue(..)
+  , module Foreign.C
+  , module Control.Applicative
+  
+  , Std__basic_string
+  , newPb
+  , derefPb
+  ) where
+
+import Data.Int
+import Data.Word
+import Data.Char
+import Data.ByteString (ByteString)
+import Control.Applicative
+import Foreign.C
+import Foreign.Ptr
+import Foreign.ForeignPtr hiding (newForeignPtr, addForeignPtrFinalizer)
+import Foreign.ForeignPtr.Unsafe
+import Foreign.Concurrent
+import Foreign.CPlusPlus
+import Foreign.CPlusPlusStdLib
+import Language.Haskell.TH
+
+data TypeKind = Assign | Pointer | Vector | MaybeP TypeKind
+  deriving (Show, Eq)
+
+newPb :: Protobuf a => IO (ForeignPtr a)
+newPb = new >>= \p -> addForeignPtrFinalizer p (delete (unsafeForeignPtrToPtr p)) >> return p
+
+derefPb :: ProtobufValue a b => ForeignPtr a -> IO b
+derefPb p = withForeignPtr p load
+
+class Protobuf a where
+  new :: IO (ForeignPtr a)
+  delete :: Ptr a -> IO ()
+
+class Protobuf a => ProtobufValue a b | a -> b, b -> a where
+  load :: Ptr a -> IO b
+  assign :: ForeignPtr a -> b -> IO ()
+
+-- "not really" an orphan
+instance (ProtobufValue b a) => CPlusPlusLand (Maybe a) (Ptr b) where
+  to Nothing = return nullPtr
+  to (Just x) = to x
+  from x = if x == nullPtr then return Nothing else (Just `fmap` from x)
+
+-- "not really" an orphan
+instance (ProtobufValue b a) => CPlusPlusLand a (Ptr b) where
+  to x = new >>= \(p :: ForeignPtr b) -> assign p x >> withForeignPtr p (\p' -> return (p' :: Ptr b))
+  from = load
+
+cplusplus "haskell::readProtobuf(char const*, google::protobuf::Message&)"  "cbits/hsprotobuf.o" [t|CString -> Ptr () -> IO CInt|]
+cplusplus "haskell::writeProtobuf(char const*, google::protobuf::Message&)" "cbits/hsprotobuf.o" [t|CString -> Ptr () -> IO CInt|]
+
+readProtobuf :: Protobuf a => FilePath -> ForeignPtr a -> IO ()
+readProtobuf f m = do
+  s <- newCString f
+  r <- withForeignPtr m $ haskell__readProtobuf s . castPtr
+  if r /= 0
+    then error "readProtobuf exception" -- TODO: error
+    else return ()
+
+writeProtobuf :: Protobuf a => FilePath -> ForeignPtr a -> IO ()
+writeProtobuf f m = do
+  s <- newCString f
+  r <- withForeignPtr m $ haskell__writeProtobuf s . castPtr
+  if r /= 0
+    then error "writeProtobuf exception" -- TODO: error
+    else return ()
+
+-- use ("name", [t|String|])
+protobuf' :: FilePath -> String -> [(String, Type)] -> Q [Dec]
+protobuf' objfile tname fields = do
+  let dataDefn = DataD [] (mkName tname) [] [] []
+  let typeDefn = TySynD (mkName (tname ++ "Ptr")) [] (AppT (ConT ''ForeignPtr) (ConT (mkName tname)))
+  let lowCaseTname = toLower (head tname):(tail tname)
+  con <- cplusplus (tname ++ "::New() const") objfile
+          (appT (conT ''IO) (appT (conT ''Ptr) (conT (mkName tname))))
+  del <- cplusplus (tname ++ "::~" ++ tname ++ "()") objfile
+          (appT (appT arrowT (appT (conT ''Ptr) (conT (mkName tname)))) (appT (conT ''IO) (conT ''())))
+  instanceDef <- instanceD (cxt []) (appT (conT ''Protobuf) (conT (mkName tname)))
+                   [valD (varP 'new) (normalB
+                     (infixE
+                       (Just (varE (mkName (lowCaseTname ++ "__New"))))
+                       (varE '(>>=))
+                       (Just (varE 'newForeignPtr_)))) []
+                   ,funD 'delete [clause [varP (mkName "p")]
+                     (normalB (appE
+                       (varE (mkName (lowCaseTname ++ "__~" ++ tname)))
+                       (varE (mkName "p")))) []]]
+  xs <- fmap concat $ mapM (deHaskell objfile tname) fields
+  return $ dataDefn:typeDefn:instanceDef:[] ++ con ++ del ++ xs
+
+protobuf :: FilePath -> Name -> Q [Dec]
+protobuf objfile name = reify name >>= \val -> case val of
+  (TyConI (DataD [] _tyName [] [cx@(RecC _conName fields)] [])) -> do
+    pb <- protobuf' objfile (init (nameBase name)) $ map (\(n, _, t) -> (nameBase n, t)) fields
+    xs <- instanceD (cxt [])
+            (appT (appT (conT ''ProtobufValue) (conT (mkName (init (nameBase name))))) (conT name))
+            [
+              loadDef (nameBase name) cx
+            , assignDef (nameBase name) cx
+            ]
+    return $ pb ++ [xs]
+  x -> error $ "can not handle decl of shape: " ++ show x
+
+loadDef :: String -> Con -> DecQ
+loadDef name (RecC conName fields) =
+  let
+    n' = let (x:xs) = init name in toLower x:xs
+    ((fstField, _, fstType):_) = fields
+    apply y (Vector, _, _) =
+      -- optional_features_size >>= mapM optional_features . \x -> [0..(x-1)]
+      (InfixE
+        (Just (AppE (VarE (mkName (n' ++ "__" ++ nameBase y ++ "_size"))) (VarE (mkName "x"))))
+        (VarE '(>>=))
+        (Just
+          (InfixE
+            (Just
+              (AppE
+                (VarE 'mapM)
+                (LamE [VarP (mkName "y")]
+                  (InfixE
+                    (Just
+                      (AppE
+                        (AppE
+                          (VarE (mkName (n' ++ "__" ++ nameBase y)))
+                          (VarE (mkName "x")))
+                        (VarE (mkName "y"))))
+                    (VarE '(>>=))
+                    (Just (VarE 'from))))))
+            (VarE '(.))
+            (Just
+              (LamE [VarP (mkName "y")]
+                (ArithSeqE
+                  (FromToR (LitE (IntegerL 0))
+                    (InfixE
+                      (Just (VarE (mkName "y")))
+                      (VarE '(-))
+                      (Just (LitE (IntegerL 1)))))))))))
+
+    apply y (MaybeP typ', _, _) =
+      (InfixE
+        (Just
+          (AppE
+            (AppE
+              (case typ' of
+                Pointer -> (VarE 'getMaybePtr)
+                Assign -> (VarE 'getMaybeVal)
+                _ -> error "impossible data defn")
+              (AppE
+                (VarE
+                  (mkName (n' ++ "__" ++ nameBase y)))
+                (VarE xv)))
+            (AppE
+              (VarE
+                (mkName (n' ++ "__has_" ++ nameBase y)))
+              (VarE xv))))
+        (VarE '(>>=))
+        (Just (VarE 'from)))
+
+    -- Assign and Pointer
+    apply y (_, _, _) =
+      (InfixE
+        (Just (AppE (VarE (mkName (n' ++ "__" ++ nameBase y))) (VarE xv)))
+        (VarE '(>>=))
+        (Just (VarE 'from)))
+    xv = mkName "x"
+  in funD 'load
+      -- TODO: todo for loading an array NEXT
+      [clause [varP xv]
+        (normalB
+          (return (foldl
+            (\x (y, _, t) -> InfixE (Just x) (VarE '(<*>))
+              (Just (apply y (typeinfo t)))
+            )
+            (InfixE (Just (ConE conName)) (VarE '(<$>))
+              (Just (apply fstField (typeinfo fstType))))
+            (tail fields)
+        )))
+      [] ]
+loadDef _ _ = error "invalid constructor definition"
+
+assignDef :: String -> Con -> DecQ
+assignDef name (RecC _conName fields) =
+  let
+    n' = let (x:xs) = init name in toLower x:xs
+    ((fstField, _, fstType):_) = fields
+    sety y (Vector, ftyp, _) =
+      (InfixE
+        (Just
+          (AppE (VarE 'to)
+            (AppE (VarE (mkName (show y))) (VarE xv))))
+        -- TODO: should clear the array first?
+        (VarE '(>>=))
+        (Just
+          (AppE
+            (VarE 'mapM_)
+            (setApply n' "__add_" ftyp pp pv y Nothing))))
+
+    sety y (Pointer, _, _) =
+      (InfixE
+        (Just
+          (AppE (VarE 'to)
+            (AppE (VarE (mkName (show y))) (VarE xv))))
+        (VarE '(>>=))
+        (Just
+          (AppE
+            (VarE (mkName (n' ++ "__set_allocated_" ++ (nameBase y))))
+            (VarE pv))))
+
+    sety y (Assign, ftyp, _) =
+      (InfixE
+        (Just
+          (AppE (VarE 'to)
+            (AppE (VarE (mkName (show y))) (VarE xv))))
+        (VarE '(>>=))
+        (Just
+          (setApply n' "__set_" ftyp pp pv y Nothing)))
+
+    sety y (MaybeP typ', ftyp, _) =
+      (InfixE
+        (Just
+          (AppE (VarE 'to)
+            (AppE (VarE (mkName (show y))) (VarE xv))))
+        (VarE '(>>=))
+        (Just
+          (AppE
+            (AppE
+              (case typ' of
+                Pointer -> (VarE 'setMaybePtr)
+                Assign -> (VarE 'setMaybeVal)
+                _ -> error "invalid data defn")
+              (setApply n' "__set_" ftyp pp pv y (Just typ')))
+            (AppE
+              (VarE (mkName (n' ++ "__clear_" ++ (nameBase y))))
+              (VarE pv)))))
+
+    pv = mkName "pv"
+    pp = mkName "pp"
+    xv = mkName "x"
+  in funD 'assign
+      [clause [varP pp, varP xv]
+        (normalB
+          (appE
+            (appE (varE 'withForeignPtr) (varE pp))
+            (lamE [varP pv]
+              (return
+                (foldl
+                  (\x (y, _, t) ->
+                    InfixE
+                      (Just x)
+                      (VarE '(>>))
+                      (Just (sety y (typeinfo t))))
+                  (sety fstField (typeinfo fstType))
+                  (tail fields))))))
+      []]
+assignDef _ _ = error "invalid constructor definition"
+
+setApply :: String -> String -> Type -> Name -> Name -> Name -> Maybe TypeKind -> Exp
+setApply n' meth ftyp pp pv y typ' =
+  (AppE
+    (VarE (mkName (n' ++ meth
+      ++ (if typ' == Just Pointer then "allocated_" else "")
+      ++ (if ftyp == ConT ''Foreign.CPlusPlusStdLib.Std__basic_string
+            then "ret_"
+            else "")
+      ++ (nameBase y))))
+      (if ftyp == ConT ''Foreign.CPlusPlusStdLib.Std__basic_string
+                            then (VarE pp)
+                            else (VarE pv))
+      )
+
+deHaskell :: String -> String -> (String, Type) -> Q [Dec]
+deHaskell objfile tname (name, typ) = do
+  let ptrTypName = appT (conT ''Ptr) (conT (mkName tname))
+  gets <- genGetter tname name ptrTypName objfile typ
+  sets <- genSetter tname name ptrTypName objfile typ
+  return $ gets ++ sets
+
+genGetter :: String -> String -> TypeQ -> String -> Type -> Q [Dec]
+genGetter tname name ptrTypName objfile typ = do
+  let (typeKind, ftype, _ctype) = typeinfo typ
+  case typeKind of
+    Vector -> do
+      x <- cplusplus
+            (tname ++ "::" ++ name ++ "(int) const") objfile
+            (appT (appT arrowT ptrTypName) (appT (appT arrowT (conT ''Int)) (appT (conT ''IO) (return ftype))))
+      y <- cplusplus
+            (tname ++ "::" ++ name ++ "_size() const") objfile
+            (appT (appT arrowT ptrTypName) (appT (conT ''IO) (conT ''Int)))
+      return (x ++ y)
+    MaybeP _typ -> do
+      g <- cplusplus
+            (tname ++ "::" ++ name ++ "() const")    objfile
+            (appT (appT arrowT ptrTypName) (appT (conT ''IO) (return ftype)))
+      s <- cplusplus
+            (tname ++ "::has_" ++ name ++ "() const")    objfile
+            (appT (appT arrowT ptrTypName) (appT (conT ''IO) (conT ''Bool)))
+      return (g ++ s)
+    -- Assign and Pointer
+    _ -> cplusplus
+      (tname ++ "::" ++ name ++ "() const")    objfile
+      (appT (appT arrowT ptrTypName) (appT (conT ''IO) (return ftype)))
+
+genSetter :: String -> String -> TypeQ -> String -> Type -> Q [Dec]
+genSetter tname name ptrTypName objfile typ = do
+  let (typeKind, ftype, ctype) = typeinfo typ
+      isAllocated Pointer n = "allocated_" ++ n
+      isAllocated _ n = n
+  case typeKind of
+    Assign -> do
+      s <- cplusplus
+            (tname ++ "::set_" ++ name ++ "(" ++ ctype ++ ")") objfile
+            (appT (appT arrowT ptrTypName)
+              (appT (appT arrowT (return ftype)) (appT (conT ''IO) (conT ''()))))
+      al <- if ftype == ConT ''Foreign.CPlusPlusStdLib.Std__basic_string
+              then sequence [allocated_set tname name "set"]
+              else return []
+      return (s ++ al)
+    Vector -> do
+      s <- cplusplus
+          (tname ++ "::add_" ++ name ++ "(" ++ ctype ++ ")") objfile
+            (appT
+              (appT arrowT ptrTypName)
+              (appT (appT arrowT (return ftype)) (appT (conT ''IO) (conT ''()))))
+      al <- if ftype == ConT ''Foreign.CPlusPlusStdLib.Std__basic_string
+              then sequence [allocated_set tname name "add"]
+              else return []
+      return (s ++ al)
+    Pointer -> cplusplus
+        (tname ++ "::set_allocated_" ++ name ++ "(" ++ ctype ++ ")") objfile
+        (appT (appT arrowT ptrTypName) (appT (appT arrowT (return ftype)) (appT (conT ''IO) (conT ''()))))
+    MaybeP typ' -> do
+      set <- cplusplus
+        (tname ++ "::set_" ++ isAllocated typ' name ++ "(" ++ ctype ++ ")") objfile
+        (appT (appT arrowT ptrTypName) (appT (appT arrowT (return ftype)) (appT (conT ''IO) (conT ''()))))
+      al <- if ftype == ConT ''Foreign.CPlusPlusStdLib.Std__basic_string
+              then sequence [allocated_set tname name "set"]
+              else return []
+      clear <- cplusplus
+        (tname ++ "::clear_" ++ name ++ "()") objfile
+        (appT (appT arrowT ptrTypName) (appT (conT ''IO) (conT ''())))
+      return (set ++ al ++ clear)
+
+allocated_set :: String -> String -> String -> DecQ
+allocated_set tname name meth =
+  (funD (mkName (lower tname ++ "__" ++ meth ++  "_ret_" ++ name))
+    [clause [varP (mkName "p"), varP (mkName "v")]
+      (normalB
+        (appE
+          (appE (varE 'withForeignPtr) (varE (mkName "p")))
+          (lamE [varP (mkName "pv")]
+            (infixE
+              (Just
+                (appE
+                  (appE
+                    (varE (mkName (lower tname ++ "__" ++ meth ++ "_" ++ name)))
+                    (varE (mkName "pv")))
+                  (varE (mkName "v"))))
+              (varE '(>>))
+              (Just
+                (appE
+                  (appE
+                    (varE 'retainForeign)
+                    (varE (mkName "p")))
+                  (varE (mkName "v"))))
+        )))) []])
+
+typeinfo :: Type -> (TypeKind, Type, String)
+typeinfo t
+  | t == ConT ''String = (Assign, ConT ''Std__basic_string, "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&")
+  | t == ConT ''Bool   = (Assign, ConT ''CChar, "bool")
+  | t == ConT ''Int    = (Assign, ConT ''CInt, "int")
+  | t == ConT ''Int32  = (Assign, ConT ''CInt, "int")
+  | t == ConT ''Int64  = (Assign, ConT ''CLLong, "long long")
+  | t == ConT ''Word32  = (Assign, ConT ''CUInt, "unsigned int")
+  | t == ConT ''ByteString  = (Assign, ConT ''Std__basic_string, "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&")
+  | otherwise =
+    case t of
+      ConT x ->
+        if last (nameBase x) == 'T'
+          then
+            -- TODO: reify x to check it is in Protobuf, that way it can be extended by client code, like enum
+            (Pointer, AppT (ConT ''Ptr) (ConT (mkName (init (nameBase x)))), init (nameBase x) ++ "*")
+          else
+            (Assign, t, nameBase x)
+            -- error $ "unknown type to typeinfo: " ++ nameBase x
+      AppT (ConT innerT) x -> typeinfo' innerT x
+      AppT ListT x -> typeinfoList x
+      _ -> error $ "type info not implemented for: " ++ show t
+  where
+    typeinfo' innerT x
+      | innerT == ''Maybe = let (i, y, z) = typeinfo x in (MaybeP i, y, z)
+      -- | innerT == ''V.Vector = let (_, y, z) = typeinfo x in (Vector, y, z)
+      | otherwise = error $ "type info not implemented for: " ++ show innerT
+    typeinfoList x =
+      let (_, y, z) = typeinfo x in (Vector, y, z)
+
+lower :: String -> String
+lower (x:xs) = toLower x:xs
+lower _ = error "NEL"
diff --git a/tests/Main.hs b/tests/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/Main.hs
@@ -0,0 +1,17 @@
+{-# LANGUAGE TemplateHaskell, EmptyDataDecls, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings #-}
+module Main where
+
+import Person
+import Data.Protobuf
+
+main :: IO ()
+main = do
+  let val = (Person (Name (Just "Max") Nothing) 1 (Just "maxwell.swadling@nicta.com.au"))
+  person <- newPb :: IO PersonPtr
+  assign person val
+  writeProtobuf "person.pb" person
+  
+  person2 <- newPb :: IO PersonPtr
+  readProtobuf "person.pb" person2
+  val2 <- derefPb person2
+  print $ val == val2
diff --git a/tests/Person.hs b/tests/Person.hs
new file mode 100644
--- /dev/null
+++ b/tests/Person.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE TemplateHaskell, MultiParamTypeClasses #-}
+module Person where
+
+import Data.Protobuf
+import Data.ByteString (ByteString)
+
+data NameT = Name { firstname :: Maybe ByteString, lastname :: Maybe ByteString }
+  deriving (Show, Eq)
+protobuf "tests/person.pb.o" ''NameT
+
+data PersonT = Person { name :: NameT, id :: Int, email :: Maybe ByteString }
+  deriving (Show, Eq)
+protobuf "tests/person.pb.o" ''PersonT
diff --git a/tests/Tests.hs b/tests/Tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/Tests.hs
@@ -0,0 +1,31 @@
+module Main where
+
+import Data.Protobuf
+import Person
+
+import Data.ByteString
+import Test.QuickCheck
+import Test.QuickCheck.Monadic
+
+main = quickCheckWith stdArgs { maxSuccess = 1000 } $ monadicIO $ do
+  b <- pick arbitrary :: PropertyM IO PersonT
+  testProtobuf b
+  b <- pick arbitrary :: PropertyM IO NameT
+  testProtobuf b
+
+testProtobuf b = do
+  x <- run $ do
+    p <- newPb
+    assign p b
+    v <- derefPb p
+    return $ b == v
+  assert x
+
+instance Arbitrary ByteString where
+  arbitrary = fmap pack arbitrary
+
+instance Arbitrary NameT where
+  arbitrary = Name <$> arbitrary <*> arbitrary
+
+instance Arbitrary PersonT where
+  arbitrary = Person <$> arbitrary <*> arbitrary <*> arbitrary
diff --git a/tests/person.pb.cc b/tests/person.pb.cc
new file mode 100644
--- /dev/null
+++ b/tests/person.pb.cc
@@ -0,0 +1,759 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: tests/person.proto
+
+#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
+#include "person.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/wire_format_lite_inl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/generated_message_reflection.h>
+#include <google/protobuf/reflection_ops.h>
+#include <google/protobuf/wire_format.h>
+// @@protoc_insertion_point(includes)
+
+namespace {
+
+const ::google::protobuf::Descriptor* Name_descriptor_ = NULL;
+const ::google::protobuf::internal::GeneratedMessageReflection*
+  Name_reflection_ = NULL;
+const ::google::protobuf::Descriptor* Person_descriptor_ = NULL;
+const ::google::protobuf::internal::GeneratedMessageReflection*
+  Person_reflection_ = NULL;
+
+}  // namespace
+
+
+void protobuf_AssignDesc_tests_2fperson_2eproto() {
+  protobuf_AddDesc_tests_2fperson_2eproto();
+  const ::google::protobuf::FileDescriptor* file =
+    ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
+      "tests/person.proto");
+  GOOGLE_CHECK(file != NULL);
+  Name_descriptor_ = file->message_type(0);
+  static const int Name_offsets_[2] = {
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Name, firstname_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Name, lastname_),
+  };
+  Name_reflection_ =
+    new ::google::protobuf::internal::GeneratedMessageReflection(
+      Name_descriptor_,
+      Name::default_instance_,
+      Name_offsets_,
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Name, _has_bits_[0]),
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Name, _unknown_fields_),
+      -1,
+      ::google::protobuf::DescriptorPool::generated_pool(),
+      ::google::protobuf::MessageFactory::generated_factory(),
+      sizeof(Name));
+  Person_descriptor_ = file->message_type(1);
+  static const int Person_offsets_[3] = {
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Person, name_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Person, id_),
+    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Person, email_),
+  };
+  Person_reflection_ =
+    new ::google::protobuf::internal::GeneratedMessageReflection(
+      Person_descriptor_,
+      Person::default_instance_,
+      Person_offsets_,
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Person, _has_bits_[0]),
+      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Person, _unknown_fields_),
+      -1,
+      ::google::protobuf::DescriptorPool::generated_pool(),
+      ::google::protobuf::MessageFactory::generated_factory(),
+      sizeof(Person));
+}
+
+namespace {
+
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);
+inline void protobuf_AssignDescriptorsOnce() {
+  ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
+                 &protobuf_AssignDesc_tests_2fperson_2eproto);
+}
+
+void protobuf_RegisterTypes(const ::std::string&) {
+  protobuf_AssignDescriptorsOnce();
+  ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+    Name_descriptor_, &Name::default_instance());
+  ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+    Person_descriptor_, &Person::default_instance());
+}
+
+}  // namespace
+
+void protobuf_ShutdownFile_tests_2fperson_2eproto() {
+  delete Name::default_instance_;
+  delete Name_reflection_;
+  delete Person::default_instance_;
+  delete Person_reflection_;
+}
+
+void protobuf_AddDesc_tests_2fperson_2eproto() {
+  static bool already_here = false;
+  if (already_here) return;
+  already_here = true;
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
+    "\n\022tests/person.proto\"+\n\004Name\022\021\n\tfirstnam"
+    "e\030\001 \001(\t\022\020\n\010lastname\030\002 \001(\t\"8\n\006Person\022\023\n\004n"
+    "ame\030\001 \002(\0132\005.Name\022\n\n\002id\030\002 \002(\005\022\r\n\005email\030\003 "
+    "\001(\t", 123);
+  ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
+    "tests/person.proto", &protobuf_RegisterTypes);
+  Name::default_instance_ = new Name();
+  Person::default_instance_ = new Person();
+  Name::default_instance_->InitAsDefaultInstance();
+  Person::default_instance_->InitAsDefaultInstance();
+  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_tests_2fperson_2eproto);
+}
+
+// Force AddDescriptors() to be called at static initialization time.
+struct StaticDescriptorInitializer_tests_2fperson_2eproto {
+  StaticDescriptorInitializer_tests_2fperson_2eproto() {
+    protobuf_AddDesc_tests_2fperson_2eproto();
+  }
+} static_descriptor_initializer_tests_2fperson_2eproto_;
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int Name::kFirstnameFieldNumber;
+const int Name::kLastnameFieldNumber;
+#endif  // !_MSC_VER
+
+Name::Name()
+  : ::google::protobuf::Message() {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:Name)
+}
+
+void Name::InitAsDefaultInstance() {
+}
+
+Name::Name(const Name& from)
+  : ::google::protobuf::Message() {
+  SharedCtor();
+  MergeFrom(from);
+  // @@protoc_insertion_point(copy_constructor:Name)
+}
+
+void Name::SharedCtor() {
+  ::google::protobuf::internal::GetEmptyString();
+  _cached_size_ = 0;
+  firstname_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  lastname_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+Name::~Name() {
+  // @@protoc_insertion_point(destructor:Name)
+  SharedDtor();
+}
+
+void Name::SharedDtor() {
+  if (firstname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+    delete firstname_;
+  }
+  if (lastname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+    delete lastname_;
+  }
+  if (this != default_instance_) {
+  }
+}
+
+void Name::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ::google::protobuf::Descriptor* Name::descriptor() {
+  protobuf_AssignDescriptorsOnce();
+  return Name_descriptor_;
+}
+
+const Name& Name::default_instance() {
+  if (default_instance_ == NULL) protobuf_AddDesc_tests_2fperson_2eproto();
+  return *default_instance_;
+}
+
+Name* Name::default_instance_ = NULL;
+
+Name* Name::New() const {
+  return new Name;
+}
+
+void Name::Clear() {
+  if (_has_bits_[0 / 32] & 3) {
+    if (has_firstname()) {
+      if (firstname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+        firstname_->clear();
+      }
+    }
+    if (has_lastname()) {
+      if (lastname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+        lastname_->clear();
+      }
+    }
+  }
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+  mutable_unknown_fields()->Clear();
+}
+
+bool Name::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+  ::google::protobuf::uint32 tag;
+  // @@protoc_insertion_point(parse_start:Name)
+  for (;;) {
+    ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+    tag = p.first;
+    if (!p.second) goto handle_unusual;
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // optional string firstname = 1;
+      case 1: {
+        if (tag == 10) {
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_firstname()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+            this->firstname().data(), this->firstname().length(),
+            ::google::protobuf::internal::WireFormat::PARSE,
+            "firstname");
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectTag(18)) goto parse_lastname;
+        break;
+      }
+
+      // optional string lastname = 2;
+      case 2: {
+        if (tag == 18) {
+         parse_lastname:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_lastname()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+            this->lastname().data(), this->lastname().length(),
+            ::google::protobuf::internal::WireFormat::PARSE,
+            "lastname");
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectAtEnd()) goto success;
+        break;
+      }
+
+      default: {
+      handle_unusual:
+        if (tag == 0 ||
+            ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          goto success;
+        }
+        DO_(::google::protobuf::internal::WireFormat::SkipField(
+              input, tag, mutable_unknown_fields()));
+        break;
+      }
+    }
+  }
+success:
+  // @@protoc_insertion_point(parse_success:Name)
+  return true;
+failure:
+  // @@protoc_insertion_point(parse_failure:Name)
+  return false;
+#undef DO_
+}
+
+void Name::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // @@protoc_insertion_point(serialize_start:Name)
+  // optional string firstname = 1;
+  if (has_firstname()) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->firstname().data(), this->firstname().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE,
+      "firstname");
+    ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+      1, this->firstname(), output);
+  }
+
+  // optional string lastname = 2;
+  if (has_lastname()) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->lastname().data(), this->lastname().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE,
+      "lastname");
+    ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+      2, this->lastname(), output);
+  }
+
+  if (!unknown_fields().empty()) {
+    ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+        unknown_fields(), output);
+  }
+  // @@protoc_insertion_point(serialize_end:Name)
+}
+
+::google::protobuf::uint8* Name::SerializeWithCachedSizesToArray(
+    ::google::protobuf::uint8* target) const {
+  // @@protoc_insertion_point(serialize_to_array_start:Name)
+  // optional string firstname = 1;
+  if (has_firstname()) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->firstname().data(), this->firstname().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE,
+      "firstname");
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        1, this->firstname(), target);
+  }
+
+  // optional string lastname = 2;
+  if (has_lastname()) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->lastname().data(), this->lastname().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE,
+      "lastname");
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        2, this->lastname(), target);
+  }
+
+  if (!unknown_fields().empty()) {
+    target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+        unknown_fields(), target);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:Name)
+  return target;
+}
+
+int Name::ByteSize() const {
+  int total_size = 0;
+
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    // optional string firstname = 1;
+    if (has_firstname()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->firstname());
+    }
+
+    // optional string lastname = 2;
+    if (has_lastname()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->lastname());
+    }
+
+  }
+  if (!unknown_fields().empty()) {
+    total_size +=
+      ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+        unknown_fields());
+  }
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void Name::MergeFrom(const ::google::protobuf::Message& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  const Name* source =
+    ::google::protobuf::internal::dynamic_cast_if_available<const Name*>(
+      &from);
+  if (source == NULL) {
+    ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+  } else {
+    MergeFrom(*source);
+  }
+}
+
+void Name::MergeFrom(const Name& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    if (from.has_firstname()) {
+      set_firstname(from.firstname());
+    }
+    if (from.has_lastname()) {
+      set_lastname(from.lastname());
+    }
+  }
+  mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+}
+
+void Name::CopyFrom(const ::google::protobuf::Message& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void Name::CopyFrom(const Name& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Name::IsInitialized() const {
+
+  return true;
+}
+
+void Name::Swap(Name* other) {
+  if (other != this) {
+    std::swap(firstname_, other->firstname_);
+    std::swap(lastname_, other->lastname_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
+::google::protobuf::Metadata Name::GetMetadata() const {
+  protobuf_AssignDescriptorsOnce();
+  ::google::protobuf::Metadata metadata;
+  metadata.descriptor = Name_descriptor_;
+  metadata.reflection = Name_reflection_;
+  return metadata;
+}
+
+
+// ===================================================================
+
+#ifndef _MSC_VER
+const int Person::kNameFieldNumber;
+const int Person::kIdFieldNumber;
+const int Person::kEmailFieldNumber;
+#endif  // !_MSC_VER
+
+Person::Person()
+  : ::google::protobuf::Message() {
+  SharedCtor();
+  // @@protoc_insertion_point(constructor:Person)
+}
+
+void Person::InitAsDefaultInstance() {
+  name_ = const_cast< ::Name*>(&::Name::default_instance());
+}
+
+Person::Person(const Person& from)
+  : ::google::protobuf::Message() {
+  SharedCtor();
+  MergeFrom(from);
+  // @@protoc_insertion_point(copy_constructor:Person)
+}
+
+void Person::SharedCtor() {
+  ::google::protobuf::internal::GetEmptyString();
+  _cached_size_ = 0;
+  name_ = NULL;
+  id_ = 0;
+  email_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+Person::~Person() {
+  // @@protoc_insertion_point(destructor:Person)
+  SharedDtor();
+}
+
+void Person::SharedDtor() {
+  if (email_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+    delete email_;
+  }
+  if (this != default_instance_) {
+    delete name_;
+  }
+}
+
+void Person::SetCachedSize(int size) const {
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ::google::protobuf::Descriptor* Person::descriptor() {
+  protobuf_AssignDescriptorsOnce();
+  return Person_descriptor_;
+}
+
+const Person& Person::default_instance() {
+  if (default_instance_ == NULL) protobuf_AddDesc_tests_2fperson_2eproto();
+  return *default_instance_;
+}
+
+Person* Person::default_instance_ = NULL;
+
+Person* Person::New() const {
+  return new Person;
+}
+
+void Person::Clear() {
+  if (_has_bits_[0 / 32] & 7) {
+    if (has_name()) {
+      if (name_ != NULL) name_->::Name::Clear();
+    }
+    id_ = 0;
+    if (has_email()) {
+      if (email_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+        email_->clear();
+      }
+    }
+  }
+  ::memset(_has_bits_, 0, sizeof(_has_bits_));
+  mutable_unknown_fields()->Clear();
+}
+
+bool Person::MergePartialFromCodedStream(
+    ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+  ::google::protobuf::uint32 tag;
+  // @@protoc_insertion_point(parse_start:Person)
+  for (;;) {
+    ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+    tag = p.first;
+    if (!p.second) goto handle_unusual;
+    switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+      // required .Name name = 1;
+      case 1: {
+        if (tag == 10) {
+          DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+               input, mutable_name()));
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectTag(16)) goto parse_id;
+        break;
+      }
+
+      // required int32 id = 2;
+      case 2: {
+        if (tag == 16) {
+         parse_id:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &id_)));
+          set_has_id();
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectTag(26)) goto parse_email;
+        break;
+      }
+
+      // optional string email = 3;
+      case 3: {
+        if (tag == 26) {
+         parse_email:
+          DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+                input, this->mutable_email()));
+          ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+            this->email().data(), this->email().length(),
+            ::google::protobuf::internal::WireFormat::PARSE,
+            "email");
+        } else {
+          goto handle_unusual;
+        }
+        if (input->ExpectAtEnd()) goto success;
+        break;
+      }
+
+      default: {
+      handle_unusual:
+        if (tag == 0 ||
+            ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+          goto success;
+        }
+        DO_(::google::protobuf::internal::WireFormat::SkipField(
+              input, tag, mutable_unknown_fields()));
+        break;
+      }
+    }
+  }
+success:
+  // @@protoc_insertion_point(parse_success:Person)
+  return true;
+failure:
+  // @@protoc_insertion_point(parse_failure:Person)
+  return false;
+#undef DO_
+}
+
+void Person::SerializeWithCachedSizes(
+    ::google::protobuf::io::CodedOutputStream* output) const {
+  // @@protoc_insertion_point(serialize_start:Person)
+  // required .Name name = 1;
+  if (has_name()) {
+    ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
+      1, this->name(), output);
+  }
+
+  // required int32 id = 2;
+  if (has_id()) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->id(), output);
+  }
+
+  // optional string email = 3;
+  if (has_email()) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->email().data(), this->email().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE,
+      "email");
+    ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+      3, this->email(), output);
+  }
+
+  if (!unknown_fields().empty()) {
+    ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+        unknown_fields(), output);
+  }
+  // @@protoc_insertion_point(serialize_end:Person)
+}
+
+::google::protobuf::uint8* Person::SerializeWithCachedSizesToArray(
+    ::google::protobuf::uint8* target) const {
+  // @@protoc_insertion_point(serialize_to_array_start:Person)
+  // required .Name name = 1;
+  if (has_name()) {
+    target = ::google::protobuf::internal::WireFormatLite::
+      WriteMessageNoVirtualToArray(
+        1, this->name(), target);
+  }
+
+  // required int32 id = 2;
+  if (has_id()) {
+    target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->id(), target);
+  }
+
+  // optional string email = 3;
+  if (has_email()) {
+    ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+      this->email().data(), this->email().length(),
+      ::google::protobuf::internal::WireFormat::SERIALIZE,
+      "email");
+    target =
+      ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+        3, this->email(), target);
+  }
+
+  if (!unknown_fields().empty()) {
+    target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+        unknown_fields(), target);
+  }
+  // @@protoc_insertion_point(serialize_to_array_end:Person)
+  return target;
+}
+
+int Person::ByteSize() const {
+  int total_size = 0;
+
+  if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    // required .Name name = 1;
+    if (has_name()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+          this->name());
+    }
+
+    // required int32 id = 2;
+    if (has_id()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->id());
+    }
+
+    // optional string email = 3;
+    if (has_email()) {
+      total_size += 1 +
+        ::google::protobuf::internal::WireFormatLite::StringSize(
+          this->email());
+    }
+
+  }
+  if (!unknown_fields().empty()) {
+    total_size +=
+      ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+        unknown_fields());
+  }
+  GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+  _cached_size_ = total_size;
+  GOOGLE_SAFE_CONCURRENT_WRITES_END();
+  return total_size;
+}
+
+void Person::MergeFrom(const ::google::protobuf::Message& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  const Person* source =
+    ::google::protobuf::internal::dynamic_cast_if_available<const Person*>(
+      &from);
+  if (source == NULL) {
+    ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+  } else {
+    MergeFrom(*source);
+  }
+}
+
+void Person::MergeFrom(const Person& from) {
+  GOOGLE_CHECK_NE(&from, this);
+  if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+    if (from.has_name()) {
+      mutable_name()->::Name::MergeFrom(from.name());
+    }
+    if (from.has_id()) {
+      set_id(from.id());
+    }
+    if (from.has_email()) {
+      set_email(from.email());
+    }
+  }
+  mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+}
+
+void Person::CopyFrom(const ::google::protobuf::Message& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+void Person::CopyFrom(const Person& from) {
+  if (&from == this) return;
+  Clear();
+  MergeFrom(from);
+}
+
+bool Person::IsInitialized() const {
+  if ((_has_bits_[0] & 0x00000003) != 0x00000003) return false;
+
+  return true;
+}
+
+void Person::Swap(Person* other) {
+  if (other != this) {
+    std::swap(name_, other->name_);
+    std::swap(id_, other->id_);
+    std::swap(email_, other->email_);
+    std::swap(_has_bits_[0], other->_has_bits_[0]);
+    _unknown_fields_.Swap(&other->_unknown_fields_);
+    std::swap(_cached_size_, other->_cached_size_);
+  }
+}
+
+::google::protobuf::Metadata Person::GetMetadata() const {
+  protobuf_AssignDescriptorsOnce();
+  ::google::protobuf::Metadata metadata;
+  metadata.descriptor = Person_descriptor_;
+  metadata.reflection = Person_reflection_;
+  return metadata;
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+// @@protoc_insertion_point(global_scope)
