packages feed

protobuf-native (empty) → 1.0.0.0

raw patch · 16 files changed

+1813/−0 lines, 16 filesdep +QuickCheckdep +basedep +bytestringbuild-type:Customsetup-changed

Dependencies added: QuickCheck, base, bytestring, cereal, cplusplus-th, criterion, hprotoc-fork, protobuf, protobuf-native, protocol-buffers-fork, template-haskell, text, utf8-string

Files

+ LICENSE view
@@ -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.
+ README.md view
@@ -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.
+ Setup.hs view
@@ -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
+ benchmark/Benchmark.hs view
@@ -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++"+  ]
+ benchmark/DataProtobuf.hs view
@@ -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 ()
+ benchmark/DataProtocolBuffers.hs view
@@ -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 ()
+ benchmark/TestsPerson/Name.hs view
@@ -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}"
+ benchmark/TestsPerson/Person.hs view
@@ -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}"
+ benchmark/TextProtocolBuffers.hs view
@@ -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 ()
+ cbits/hsprotobuf.cc view
@@ -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;+}++}
+ protobuf-native.cabal view
@@ -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
+ src/Data/Protobuf.hs view
@@ -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"
+ tests/Main.hs view
@@ -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
+ tests/Person.hs view
@@ -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
+ tests/Tests.hs view
@@ -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
+ tests/person.pb.cc view
@@ -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)