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 +30/−0
- README.md +225/−0
- Setup.hs +32/−0
- benchmark/Benchmark.hs +26/−0
- benchmark/DataProtobuf.hs +14/−0
- benchmark/DataProtocolBuffers.hs +37/−0
- benchmark/TestsPerson/Name.hs +59/−0
- benchmark/TestsPerson/Person.hs +63/−0
- benchmark/TextProtocolBuffers.hs +12/−0
- cbits/hsprotobuf.cc +31/−0
- protobuf-native.cabal +55/−0
- src/Data/Protobuf.hs +409/−0
- tests/Main.hs +17/−0
- tests/Person.hs +13/−0
- tests/Tests.hs +31/−0
- tests/person.pb.cc +759/−0
+ 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)