71 votes

Sont là C++ équivalents pour les Tampons de Protocole, délimité par des fonctions d'e/S de Java?

Je suis en train de lire / écrire plusieurs Tampons de Protocole de messages de fichiers, dans les deux C++ et Java. Google suggère écrit les préfixes de longueur avant de les messages, mais il n'y a aucun moyen de le faire que par défaut (que j'ai pu voir).

Toutefois, l'API Java dans la version 2.1.0 reçu un ensemble de "Délimité" fonctions d'e/S qui, apparemment, ne se travail:

parseDelimitedFrom
mergeDelimitedFrom
writeDelimitedTo

Sont là C++ équivalents? Et si non, quel est le format de câble pour la taille des préfixes de l'API Java attache, afin que je puisse analyser ces messages en C++?

83voto

Kenton Varda Points 2170

Je suis un peu en retard à la fête ici, mais le dessous des implémentations inclure quelques optimisations manquant dans les autres réponses, et ne manquera pas, après 64 MO de données d'entrée (si elle applique la limite de 64 mo sur chaque message, mais pas sur l'ensemble du cours d'eau).

(Je suis l'auteur du C++ et Java protobuf bibliothèques, mais je ne travaille plus pour le compte de Google. Désolé, ce code ne l'a jamais fait dans l'officiel de la lib. C'est ce à quoi il ressemblerait s'il avait.)

bool writeDelimitedTo(
    const google::protobuf::MessageLite& message,
    google::protobuf::io::ZeroCopyOutputStream* rawOutput) {
  // We create a new coded stream for each message.  Don't worry, this is fast.
  google::protobuf::io::CodedOutputStream output(rawOutput);

  // Write the size.
  const int size = message.ByteSize();
  output.WriteVarint32(size);

  uint8_t* buffer = output.GetDirectBufferForNBytesAndAdvance(size);
  if (buffer != NULL) {
    // Optimization:  The message fits in one buffer, so use the faster
    // direct-to-array serialization path.
    message.SerializeWithCachedSizesToArray(buffer);
  } else {
    // Slightly-slower path when the message is multiple buffers.
    message.SerializeWithCachedSizes(&output);
    if (output.HadError()) return false;
  }

  return true;
}

bool readDelimitedFrom(
    google::protobuf::io::ZeroCopyInputStream* rawInput,
    google::protobuf::MessageLite* message) {
  // We create a new coded stream for each message.  Don't worry, this is fast,
  // and it makes sure the 64MB total size limit is imposed per-message rather
  // than on the whole stream.  (See the CodedInputStream interface for more
  // info on this limit.)
  google::protobuf::io::CodedInputStream input(rawInput);

  // Read the size.
  uint32_t size;
  if (!input.ReadVarint32(&size)) return false;

  // Tell the stream not to read beyond that size.
  google::protobuf::io::CodedInputStream::Limit limit =
      input.PushLimit(size);

  // Parse the message.
  if (!message->MergeFromCodedStream(&input)) return false;
  if (!input.ConsumedEntireMessage()) return false;

  // Release the limit.
  input.PopLimit(limit);

  return true;
}

17voto

tzaman Points 13190

Ok, donc je n'ai pas été en mesure de trouver des haut-niveau C++ les fonctions de la mise en œuvre de ce dont j'ai besoin, mais certains de la spéléologie à travers l'API Java de référence allumé la suite, à l'intérieur de la MessageLite interface:

void writeDelimitedTo(OutputStream output)
/*  Like writeTo(OutputStream), but writes the size of 
    the message as a varint before writing the data.   */

Donc, la Java la taille du préfixe est un (Protocol Buffers) varint!

Armés de cette information, je suis allé creuser par le biais de l'API C++ et a trouvé le CodedStream en-tête, qui a ces:

bool CodedInputStream::ReadVarint32(uint32 * value)
void CodedOutputStream::WriteVarint32(uint32 value)

À l'aide de ceux, je devrais être capable de rouler mes propres fonctions C++ qui font le travail.

Ils devraient vraiment ajouter à cela le Message principal de l'API si; c'est qu'il manque des fonctionnalités considérant en Java, et donc n'Marc Gravel excellent protobuf-net C# port (via SerializeWithLengthPrefix et DeserializeWithLengthPrefix).

12voto

Yukiko Points 858

J'ai résolu le même problème à l'aide de CodedOutputStream/ArrayOutputStream pour écrire le message (à la taille) et CodedInputStream/ArrayInputStream pour lire le message (à la taille).

Par exemple, le pseudo-code écrit, la taille du message suivant par le message:

const unsigned bufLength = 256;
unsigned char buffer[bufLength];
Message protoMessage;

google::protobuf::io::ArrayOutputStream arrayOutput(buffer, bufLength);
google::protobuf::io::CodedOutputStream codedOutput(&arrayOutput);

codedOutput.WriteLittleEndian32(protoMessage.ByteSize());
protoMessage.SerializeToCodedStream(&codedOutput);

Lors de l'écriture, vous devez également vérifier que votre tampon est assez grand pour accueillir le message (y compris la taille). Et lors de la lecture, vous devez vérifier que votre tampon contient un message entier (y compris la taille).

C'est très certainement ce serait pratique si ils ont ajouté des méthodes pratiques pour C++ API semblables à ceux fournis par l'API Java.

7voto

jaybny Points 448

Ici, vous allez:

#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>

using namespace google::protobuf::io;

class FASWriter 
{
    std::ofstream mFs;
    OstreamOutputStream *_OstreamOutputStream;
    CodedOutputStream *_CodedOutputStream;
public:
    FASWriter(const std::string &file) : mFs(file,std::ios::out | std::ios::binary)
    {
        assert(mFs.good());

        _OstreamOutputStream = new OstreamOutputStream(&mFs);
        _CodedOutputStream = new CodedOutputStream(_OstreamOutputStream);
    }

    inline void operator()(const ::google::protobuf::Message &msg)
    {
        _CodedOutputStream->WriteVarint32(msg.ByteSize());

        if ( !msg.SerializeToCodedStream(_CodedOutputStream) )
            std::cout << "SerializeToCodedStream error " << std::endl;
    }

    ~FASWriter()
    {
        delete _CodedOutputStream;
        delete _OstreamOutputStream;
        mFs.close();
    }
};

class FASReader
{
    std::ifstream mFs;

    IstreamInputStream *_IstreamInputStream;
    CodedInputStream *_CodedInputStream;
public:
    FASReader(const std::string &file), mFs(file,std::ios::in | std::ios::binary)
    {
        assert(mFs.good());

        _IstreamInputStream = new IstreamInputStream(&mFs);
        _CodedInputStream = new CodedInputStream(_IstreamInputStream);      
    }

    template<class T>
    bool ReadNext()
    {
        T msg;
        unsigned __int32 size;

        bool ret;
        if ( ret = _CodedInputStream->ReadVarint32(&size) )
        {   
            CodedInputStream::Limit msgLimit = _CodedInputStream->PushLimit(size);
            if ( ret = msg.ParseFromCodedStream(_CodedInputStream) )
            {
                _CodedInputStream->PopLimit(msgLimit);      
                std::cout << mFeed << " FASReader ReadNext: " << msg.DebugString() << std::endl;
            }
        }

        return ret;
    }

    ~FASReader()
    {
        delete _CodedInputStream;
        delete _IstreamInputStream;
        mFs.close();
    }
};

3voto

Kim Laurio Points 31

A également été à la recherche d'une solution pour ce. Voici le cœur de notre solution, en supposant un code java a écrit de nombreux MyRecord messages avec writeDelimitedTo dans un fichier. Ouvrez le fichier et en boucle, en faisant:

si(someCodedInputStream->ReadVarint32(&octets)) {
 CodedInputStream::Limite msgLimit = someCodedInputStream->PushLimit(octets);
 si(myRecord->ParseFromCodedStream(someCodedInputStream)) {
 //faire votre truc avec les analysée MyRecord exemple
 } else {
 //erreur d'analyse de la poignée
}
someCodedInputStream->PopLimit(msgLimit);
} else {
 //peut-être à la fin du fichier
}

Espérons que cela aide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X