NanoPB: Comment gérer les types bytes en C++
Voir aussi : Version C : Comment gérer les types bytes en C
NanoPB est une implémentation de Protocol Buffers optimisée pour la taille du code, destinée aux systèmes embarqués. Cet article montre comment gérer les types bytes en C++ avec NanoPB.
Définition Proto
Créez d’abord un fichier .proto avec des champs bytes :
syntax = "proto3";
package example;
message BytesMessage {
bytes data = 1;
bytes signature = 2;
}Générer le code NanoPB
Générez le code NanoPB avec un fichier .options pour spécifier les tailles des tampons bytes :
Créez bytes.options :
example.BytesMessage.data max_size:32
example.BytesMessage.signature max_size:16Générez ensuite :
protoc --nanopb_out=. bytes.protoCela générera bytes.pb.h et bytes.pb.c.
Exemple C++ avec tampons de taille fixe
Voici un exemple C++ complet utilisant des tampons de taille fixe :
#include <stdio.h>
#include <string.h>
#include "bytes.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
// Tampon pour le message encodé
uint8_t buffer[256];
size_t message_length;
// --- ENCODAGE ---
example_BytesMessage message = example_BytesMessage_init_zero;
// Définir les données bytes
const uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05};
const uint8_t signature[] = {0xAA, 0xBB, 0xCC, 0xDD};
message.data.size = sizeof(data);
memcpy(message.data.bytes, data, sizeof(data));
message.signature.size = sizeof(signature);
memcpy(message.signature.bytes, signature, sizeof(signature));
// Créer le flux pour l'encodage
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// Encoder le message
if (!pb_encode(&ostream, example_BytesMessage_fields, &message)) {
printf("Échec de l'encodage : %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encodé %zu octets\n", message_length);
// Afficher le dump hexadécimal des données encodées
printf("Données encodées : ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// --- DÉCODAGE ---
example_BytesMessage decoded = example_BytesMessage_init_zero;
// Créer le flux pour le décodage
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// Décoder le message
if (!pb_decode(&istream, example_BytesMessage_fields, &decoded)) {
printf("Échec du décodage : %s\n", PB_GET_ERROR(&istream));
return 1;
}
// Afficher les valeurs décodées
printf("Valeurs décodées :\n");
printf(" data : ");
for (size_t i = 0; i < decoded.data.size; i++) {
printf("%02x ", decoded.data.bytes[i]);
}
printf("\n");
printf(" signature : ");
for (size_t i = 0; i < decoded.signature.size; i++) {
printf("%02x ", decoded.signature.bytes[i]);
}
printf("\n");
return 0;
}Commande de compilation
Compilez l’exemple avec nanopb. NanoPB est généralement utilisé en incluant directement les fichiers sources dans votre projet :
g++ -o bytes_example bytes_example.cpp bytes.pb.c pb_common.c pb_encode.c pb_decode.c -I.Remarque : Les fichiers sources NanoPB (pb_common.c, pb_encode.c, pb_decode.c) doivent être compilés directement avec votre projet. Vous pouvez les obtenir depuis le dépôt GitHub de NanoPB.
Script de test Python
Pour vérifier l’encodage, vous pouvez utiliser la bibliothèque protobuf de Python :
import bytes_pb2
# Lire les données binaires
with open('encoded.bin', 'rb') as f:
data = f.read()
# Décoder
msg = bytes_pb2.BytesMessage()
msg.ParseFromString(data)
print("Valeurs décodées par Python :")
print(f" data : {msg.data.hex()}")
print(f" signature : {msg.signature.hex()}")Compilez d’abord les définitions protobuf Python :
protoc --python_out=. bytes.protoModifiez ensuite l’exemple C++ pour enregistrer les données encodées dans un fichier :
// Après l'encodage, ajoutez ceci :
FILE *f = fopen("encoded.bin", "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);Alternative : bytes basés sur des callbacks
Pour une gestion dynamique des bytes, vous pouvez utiliser des callbacks. Créez bytes_callback.options :
# Use callback for dynamic bytes
msg.BytesMessage.data callback
msg.BytesMessage.signature callbackRégénérez ensuite et utilisez cette approche :
#include <stdio.h>
#include <vector>
#include "bytes.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
// Callback d'encodage pour les bytes
bool bytes_encode_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
const std::vector<uint8_t>* data = (const std::vector<uint8_t>*)*arg;
if (!pb_encode_tag_for_field(stream, field))
return false;
return pb_encode_string(stream, data->data(), data->size());
}
// Callback de décodage pour les bytes
bool bytes_decode_callback(pb_istream_t *stream, const pb_field_t *field, void **arg) {
std::vector<uint8_t>* data = (std::vector<uint8_t>*)*arg;
// Redimensionner le vecteur pour contenir les données
size_t size = stream->bytes_left;
data->resize(size);
// Lire les données dans le vecteur
return pb_read(stream, data->data(), size);
}
int main() {
uint8_t buffer[256];
size_t message_length;
std::vector<uint8_t> data = {0x01, 0x02, 0x03, 0x04, 0x05};
std::vector<uint8_t> signature = {0xAA, 0xBB, 0xCC, 0xDD};
// --- ENCODAGE ---
example_BytesMessage message = example_BytesMessage_init_zero;
message.data.funcs.encode = bytes_encode_callback;
message.data.arg = &data;
message.signature.funcs.encode = bytes_encode_callback;
message.signature.arg = &signature;
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_BytesMessage_fields, &message)) {
printf("Échec de l'encodage : %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encodé %zu octets\n", message_length);
// --- DÉCODAGE ---
example_BytesMessage decoded = example_BytesMessage_init_zero;
std::vector<uint8_t> decoded_data, decoded_signature;
decoded.data.funcs.decode = bytes_decode_callback;
decoded.data.arg = &decoded_data;
decoded.signature.funcs.decode = bytes_decode_callback;
decoded.signature.arg = &decoded_signature;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_BytesMessage_fields, &decoded)) {
printf("Échec du décodage : %s\n", PB_GET_ERROR(&istream));
return 1;
}
printf("Valeurs décodées :\n");
printf(" data : ");
for (uint8_t byte : decoded_data) {
printf("%02x ", byte);
}
printf("\n");
printf(" signature : ");
for (uint8_t byte : decoded_signature) {
printf("%02x ", byte);
}
printf("\n");
return 0;
}Points clés
- Tampons de taille fixe : Utilisez
max_sizedans le fichier .options pour une allocation statique simple - Basé sur des callbacks : Utilisez
callbackdans le fichier .options pour une gestion dynamique des bytes - Taille fixe : Définissez le champ
*_sizeet utilisezmemcpypour copier les bytes - Basé sur des callbacks : Implémentez des callbacks d’encodage/décodage pour l’allocation dynamique
- Les bytes sont similaires aux chaînes mais contiennent des données binaires brutes (pas de terminaison nulle)
- Utilisez
std::vector<uint8_t>en C++ pour la gestion dynamique des bytes - Vérifiez toujours les tailles de tampon pour éviter les dépassements
Quand utiliser quelle approche
- Tampons de taille fixe : Lorsque vous connaissez la taille maximale des bytes et que vous voulez un code simple
- Basé sur des callbacks : Lorsque la taille des bytes est variable ou que vous avez besoin d’une allocation dynamique de mémoire
Sortie attendue
Encoded 14 bytes
Encoded data: 0a 05 01 02 03 04 05 12 04 aa bb cc dd
Decoded values:
data: 01 02 03 04 05
signature: aa bb cc dd Cas d’usage des bytes
- Données binaires (images, audio, etc.)
- Signatures cryptographiques
- Données brutes de capteurs
- Protocoles binaires personnalisés
- Toute donnée qui ne doit pas être interprétée comme du texte
Plus d’articles sur NanoPB
- Types scalaires de base en C++
- Types scalaires de base en C
- Types chaîne en C++
- Types chaîne en C
- Types bytes en C
- Champs optionnels en C++
- Champs optionnels en C
- Champs répétés/tableaux en C++
- Champs répétés/tableaux en C
- Enums en C++
- Enums en C
- Messages imbriqués en C++
- Messages imbriqués en C
- Types oneof/union en C++
- Types oneof/union en C
- Convertisseurs de tableaux personnalisés en C++
- Convertisseurs de tableaux personnalisés en C